tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / vcl / qa / cppunit / text.cxx
blobf417b574fee80e87201a3ee3118dbf658750db23
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <osl/file.hxx>
11 #include <osl/process.h>
12 #include <test/bootstrapfixture.hxx>
13 #include <sal/log.hxx>
14 #include <tools/stream.hxx>
16 #include <vcl/BitmapReadAccess.hxx>
17 #include <comphelper/errcode.hxx>
18 #include <vcl/graphicfilter.hxx>
19 #include <vcl/settings.hxx>
20 #include <vcl/svapp.hxx>
21 #include <vcl/virdev.hxx>
23 #include <ImplLayoutArgs.hxx>
24 #include <TextLayoutCache.hxx>
25 #include <salgdi.hxx>
27 class VclTextTest : public test::BootstrapFixture
29 // if enabled - check the result images with:
30 // "xdg-open ./workdir/CppunitTest/vcl_text_test.test.core/"
31 static constexpr const bool mbExportBitmap = false;
33 public:
34 void exportDevice(const OUString& filename, const VclPtr<VirtualDevice>& device)
36 if (mbExportBitmap)
38 BitmapEx aBitmapEx(device->GetBitmapEx(Point(0, 0), device->GetOutputSizePixel()));
39 OUString cwd;
40 CPPUNIT_ASSERT_EQUAL(osl_Process_E_None, osl_getProcessWorkingDir(&cwd.pData));
41 OUString url;
42 CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None,
43 osl::FileBase::getAbsoluteFileURL(cwd, filename, url));
44 SvFileStream aStream(url, StreamMode::WRITE | StreamMode::TRUNC);
45 CPPUNIT_ASSERT_EQUAL(
46 ERRCODE_NONE, GraphicFilter::GetGraphicFilter().compressAsPNG(aBitmapEx, aStream));
50 VclTextTest()
51 : BootstrapFixture(true, false)
56 static std::ostream& operator<<(std::ostream& s, const ImplLayoutRuns::Run& rRun)
58 return s << "{" << rRun.m_nMinRunPos << ", " << rRun.m_nEndRunPos << ", " << rRun.m_bRTL << "}";
61 // Avoid issues when colorized antialiasing generates slightly tinted rather than truly black
62 // pixels:
63 static bool isBlack(Color col)
65 return col.GetRed() < 25 && col.GetGreen() < 25 && col.GetBlue() < 25;
68 // Return pixel width of the base of the given character located above
69 // the starting position.
70 // In other words, go up in y direction until a black pixel is found,
71 // then return the horizontal width of the area of those pixels.
72 // For 'L' this gives the width of the base of the character.
73 static tools::Long getCharacterBaseWidth(VirtualDevice* device, const Point& start)
75 Bitmap bitmap = device->GetBitmap(Point(), device->GetOutputSizePixel());
76 BitmapScopedReadAccess access(bitmap);
77 tools::Long y = start.Y();
78 while (y >= 0 && !isBlack(access->GetColor(y, start.X())))
79 --y;
80 if (y < 0)
81 return -1;
82 tools::Long xmin = start.X();
83 while (xmin >= 0 && access->GetColor(y, xmin) != COL_WHITE)
84 --xmin;
85 tools::Long xmax = start.X();
86 while (xmax < bitmap.GetSizePixel().Width() && access->GetColor(y, xmax) != COL_WHITE)
87 ++xmax;
88 return xmax - xmin + 1;
91 // Similar to above but this time from the top, for U+30E8 (it's straight at the top, not at the bottom).
92 static tools::Long getCharacterTopWidth(VirtualDevice* device, const Point& start)
94 Bitmap bitmap = device->GetBitmap(Point(), device->GetOutputSizePixel());
95 BitmapScopedReadAccess access(bitmap);
96 tools::Long y = start.Y();
97 while (y < bitmap.GetSizePixel().Height() && !isBlack(access->GetColor(y, start.X())))
98 ++y;
99 if (y >= bitmap.GetSizePixel().Height())
100 return -1;
101 tools::Long xmin = start.X();
102 while (xmin >= 0 && access->GetColor(y, xmin) != COL_WHITE)
103 --xmin;
104 tools::Long xmax = start.X();
105 while (xmax < bitmap.GetSizePixel().Width() && access->GetColor(y, xmax) != COL_WHITE)
106 ++xmax;
107 return xmax - xmin + 1;
110 // Similar to above, but this time return the pixel height of the left-most
111 // line of the character, going right from the starting point.
112 // For 'L' this gives the height of the left line.
113 static tools::Long getCharacterLeftSideHeight(VirtualDevice* device, const Point& start)
115 Bitmap bitmap = device->GetBitmap(Point(), device->GetOutputSizePixel());
116 BitmapScopedReadAccess access(bitmap);
117 tools::Long x = start.X();
118 while (x < bitmap.GetSizePixel().Width() && !isBlack(access->GetColor(start.Y(), x)))
119 ++x;
120 if (x >= bitmap.GetSizePixel().Width())
121 return -1;
122 tools::Long ymin = start.Y();
123 while (ymin >= 0 && access->GetColor(ymin, x) != COL_WHITE)
124 --ymin;
125 tools::Long ymax = start.Y();
126 while (ymax < bitmap.GetSizePixel().Width() && access->GetColor(ymax, x) != COL_WHITE)
127 ++ymax;
128 return ymax - ymin + 1;
131 // Test rendering of the 'L' character (chosen because L is a simple shape).
132 // Check things like using a double font size doubling the size of the character, correct rotation, etc.
133 // IMPORTANT: If you modify this, check also the VclCjkTextTest::testVerticalText().
134 CPPUNIT_TEST_FIXTURE(VclTextTest, testSimpleText)
136 OUString text(u"L"_ustr);
137 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
138 device->SetOutputSizePixel(Size(100, 100));
139 device->SetBackground(Wallpaper(COL_WHITE));
140 // Disable AA, to make all pixels be black or white.
141 device->SetAntialiasing(AntialiasingFlags::DisableText);
143 // Bail out on all backends that do not work (or I didn't test). Opt-out rather than opt-in
144 // to make sure new backends fail initially.
145 if (device->GetGraphics()->getRenderBackendName() == "qt"
146 || device->GetGraphics()->getRenderBackendName() == "qtsvp"
147 || device->GetGraphics()->getRenderBackendName() == "gtk3svp"
148 || device->GetGraphics()->getRenderBackendName() == "aqua"
149 || device->GetGraphics()->getRenderBackendName() == "gen"
150 || device->GetGraphics()->getRenderBackendName() == "genpsp")
151 return;
153 // Use Dejavu fonts, they are shipped with LO, so they should be ~always available.
154 // Use Sans variant for simpler glyph shapes (no serifs).
155 vcl::Font font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 36));
156 device->Erase();
157 device->SetFont(font);
158 device->DrawText(Point(10, 10), text);
159 exportDevice(u"simple-text-36.png"_ustr, device);
160 // Height of 'L' with font 36 size should be roughly 28 pixels.
161 // Use the 'doubles' variant of the test, since that one allows
162 // a delta, and allow several pixels of delta to account
163 // for different rendering methods and whatnot.
164 tools::Long height36 = getCharacterLeftSideHeight(device, Point(0, 30));
165 CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(28), height36, 4);
166 tools::Long width36 = getCharacterBaseWidth(device, Point(20, 99));
167 CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(19), width36, 4);
169 font.SetOrientation(2700_deg10);
170 device->Erase();
171 device->SetFont(font);
172 device->DrawText(Point(90, 10), text);
173 exportDevice(u"simple-text-36-270deg.png"_ustr, device);
174 // Width and height here should be swapped, again allowing for some imprecisions.
175 tools::Long height36Rotated = getCharacterLeftSideHeight(device, Point(0, 20));
176 CPPUNIT_ASSERT_DOUBLES_EQUAL(width36, height36Rotated, 2);
177 tools::Long width36Rotated = getCharacterTopWidth(device, Point(70, 0));
178 CPPUNIT_ASSERT_DOUBLES_EQUAL(height36, width36Rotated, 2);
180 font = vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 72));
181 device->Erase();
182 device->SetFont(font);
183 device->DrawText(Point(10, 10), text);
184 exportDevice(u"simple-text-72.png"_ustr, device);
185 // Font size is doubled, so pixel sizes should also roughly double.
186 tools::Long height72 = getCharacterLeftSideHeight(device, Point(0, 30));
187 CPPUNIT_ASSERT_DOUBLES_EQUAL(height36 * 2, height72, 4);
188 tools::Long width72 = getCharacterBaseWidth(device, Point(20, 99));
189 CPPUNIT_ASSERT_DOUBLES_EQUAL(width36 * 2, width72, 5);
191 font.SetOrientation(2700_deg10);
192 device->Erase();
193 device->SetFont(font);
194 device->DrawText(Point(90, 10), text);
195 exportDevice(u"simple-text-72-270deg.png"_ustr, device);
196 tools::Long height72Rotated = getCharacterLeftSideHeight(device, Point(0, 35));
197 CPPUNIT_ASSERT_DOUBLES_EQUAL(width72, height72Rotated, 2);
198 tools::Long width72Rotated = getCharacterTopWidth(device, Point(50, 0));
199 CPPUNIT_ASSERT_DOUBLES_EQUAL(height72, width72Rotated, 2);
201 // Test width scaled to 200%.
202 font = vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(72, 36));
203 #ifdef _WIN32
204 // TODO: What is the proper way to draw 200%-wide text? This is needed on Windows
205 // but it breaks Linux.
206 font.SetAverageFontWidth(2 * font.GetOrCalculateAverageFontWidth());
207 #endif
208 device->Erase();
209 device->SetFont(font);
210 device->DrawText(Point(10, 10), text);
211 exportDevice(u"simple-text-36-200pct.png"_ustr, device);
212 tools::Long height36pct200 = getCharacterLeftSideHeight(device, Point(0, 30));
213 CPPUNIT_ASSERT_DOUBLES_EQUAL(height36, height36pct200, 2);
214 tools::Long width36pct200 = getCharacterBaseWidth(device, Point(20, 99));
215 CPPUNIT_ASSERT_DOUBLES_EQUAL(width36 * 2, width36pct200, 5);
217 // Test width scaled to 50%.
218 font = vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(18, 36));
219 #ifdef _WIN32
220 font.SetAverageFontWidth(0.5 * font.GetOrCalculateAverageFontWidth());
221 #endif
222 device->Erase();
223 device->SetFont(font);
224 device->DrawText(Point(10, 10), text);
225 exportDevice(u"simple-text-36-50pct.png"_ustr, device);
226 tools::Long height36pct50 = getCharacterLeftSideHeight(device, Point(0, 40));
227 CPPUNIT_ASSERT_DOUBLES_EQUAL(height36, height36pct50, 2);
228 tools::Long width36pct50 = getCharacterBaseWidth(device, Point(15, 99));
229 CPPUNIT_ASSERT_DOUBLES_EQUAL(width36 / 2, width36pct50, 2);
232 CPPUNIT_TEST_FIXTURE(VclTextTest, testSimpleTextFontSpecificKerning)
234 OUString aAV(u"AV"_ustr);
236 vcl::Font aFont(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 2048));
238 ScopedVclPtrInstance<VirtualDevice> pOutDev;
239 pOutDev->SetFont(aFont);
241 // absolute character widths AKA text array.
242 tools::Long nRefTextWidth = 2671;
243 KernArray aRefCharWidths{ 1270, 2671 };
244 KernArray aCharWidths;
245 tools::Long nTextWidth
246 = basegfx::fround<tools::Long>(pOutDev->GetTextArray(aAV, &aCharWidths).nWidth);
248 CPPUNIT_ASSERT_EQUAL(aRefCharWidths[0], aCharWidths[0]);
249 CPPUNIT_ASSERT_EQUAL(aRefCharWidths[1], aCharWidths[1]);
250 // this sporadically returns 75 or 74 on some of the windows tinderboxes eg. tb73
251 CPPUNIT_ASSERT_EQUAL(nRefTextWidth, nTextWidth);
252 CPPUNIT_ASSERT_EQUAL(nTextWidth, tools::Long(aCharWidths.back()));
254 // text advance width and line height
255 CPPUNIT_ASSERT_EQUAL(nRefTextWidth, pOutDev->GetTextWidth(aAV));
256 CPPUNIT_ASSERT_EQUAL(tools::Long(2384), pOutDev->GetTextHeight());
258 // exact bounding rectangle, not essentially the same as text width/height
259 tools::Rectangle aBoundRect;
260 pOutDev->GetTextBoundRect(aBoundRect, aAV);
261 CPPUNIT_ASSERT_EQUAL(tools::Long(16), aBoundRect.Left());
262 CPPUNIT_ASSERT_EQUAL(tools::Long(408), aBoundRect.Top());
263 CPPUNIT_ASSERT_EQUAL(tools::Long(2639), aBoundRect.GetWidth());
264 CPPUNIT_ASSERT_EQUAL(tools::Long(1493), aBoundRect.getOpenHeight());
266 // normal orientation
267 tools::Rectangle aInput;
268 tools::Rectangle aRect = pOutDev->GetTextRect(aInput, aAV);
270 // now rotate 270 degrees
271 vcl::Font aRotated(aFont);
272 aRotated.SetOrientation(2700_deg10);
273 pOutDev->SetFont(aRotated);
274 tools::Rectangle aRectRot = pOutDev->GetTextRect(aInput, aAV);
276 // Check that we did do the rotation...
277 CPPUNIT_ASSERT_EQUAL(aRectRot.GetWidth(), aRect.GetHeight());
278 CPPUNIT_ASSERT_EQUAL(aRectRot.GetHeight(), aRect.GetWidth());
281 CPPUNIT_TEST_FIXTURE(VclTextTest, testSimpleTextNoKerning)
283 OUString aAV(u"AV"_ustr);
285 vcl::Font aFont(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 2048));
286 aFont.SetKerning(FontKerning::NONE);
288 ScopedVclPtrInstance<VirtualDevice> pOutDev;
289 pOutDev->SetFont(aFont);
291 // absolute character widths AKA text array.
292 tools::Long nRefTextWidth = 2802;
293 KernArray aRefCharWidths{ 1401, 2802 };
294 KernArray aCharWidths;
295 tools::Long nTextWidth
296 = basegfx::fround<tools::Long>(pOutDev->GetTextArray(aAV, &aCharWidths).nWidth);
298 CPPUNIT_ASSERT_EQUAL(aRefCharWidths[0], aCharWidths[0]);
299 CPPUNIT_ASSERT_EQUAL(aRefCharWidths[1], aCharWidths[1]);
300 // this sporadically returns 75 or 74 on some of the windows tinderboxes eg. tb73
301 CPPUNIT_ASSERT_EQUAL(nRefTextWidth, nTextWidth);
302 CPPUNIT_ASSERT_EQUAL(nTextWidth, tools::Long(aCharWidths.back()));
304 // text advance width and line height
305 CPPUNIT_ASSERT_EQUAL(nRefTextWidth, pOutDev->GetTextWidth(aAV));
306 CPPUNIT_ASSERT_EQUAL(tools::Long(2384), pOutDev->GetTextHeight());
308 // exact bounding rectangle, not essentially the same as text width/height
309 tools::Rectangle aBoundRect;
310 pOutDev->GetTextBoundRect(aBoundRect, aAV);
311 CPPUNIT_ASSERT_EQUAL(tools::Long(16), aBoundRect.Left());
312 CPPUNIT_ASSERT_EQUAL(tools::Long(408), aBoundRect.Top());
313 CPPUNIT_ASSERT_EQUAL(tools::Long(2770), aBoundRect.GetWidth());
314 CPPUNIT_ASSERT_EQUAL(tools::Long(1493), aBoundRect.getOpenHeight());
316 // normal orientation
317 tools::Rectangle aInput;
318 tools::Rectangle aRect = pOutDev->GetTextRect(aInput, aAV);
320 // now rotate 270 degrees
321 vcl::Font aRotated(aFont);
322 aRotated.SetOrientation(2700_deg10);
323 pOutDev->SetFont(aRotated);
324 tools::Rectangle aRectRot = pOutDev->GetTextRect(aInput, aAV);
326 // Check that we did do the rotation...
327 CPPUNIT_ASSERT_EQUAL(aRectRot.GetWidth(), aRect.GetHeight());
328 CPPUNIT_ASSERT_EQUAL(aRectRot.GetHeight(), aRect.GetWidth());
331 CPPUNIT_TEST_FIXTURE(VclTextTest, testTextLayoutCache)
333 OUString sTestString = u"The quick brown fox\n jumped over the lazy dogالعاشر"_ustr;
334 vcl::text::TextLayoutCache cache(sTestString.getStr(), sTestString.getLength());
336 vcl::text::Run run1 = cache.runs[0];
337 vcl::text::Run run2 = cache.runs[1];
339 bool bCorrectRuns = (cache.runs.size() == 2);
340 CPPUNIT_ASSERT_MESSAGE("Wrong number of runs", bCorrectRuns);
341 CPPUNIT_ASSERT_EQUAL(USCRIPT_LATIN, run1.nCode);
342 CPPUNIT_ASSERT_EQUAL(0, run1.nStart);
343 CPPUNIT_ASSERT_EQUAL(45, run1.nEnd);
344 CPPUNIT_ASSERT_EQUAL(USCRIPT_ARABIC, run2.nCode);
345 CPPUNIT_ASSERT_EQUAL(45, run2.nStart);
346 CPPUNIT_ASSERT_EQUAL(51, run2.nEnd);
349 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_AddPos)
351 ImplLayoutRuns aRuns;
352 aRuns.AddPos(1, false);
353 aRuns.AddPos(2, false);
354 aRuns.AddPos(3, false);
355 aRuns.AddPos(4, true); // add RTL marker glyph
356 aRuns.AddPos(5, false);
357 aRuns.AddPos(6, true); // add RTL marker glyph
358 aRuns.AddPos(7, false);
360 int nCharPos(0);
361 bool bRightToLeftMarker(false);
363 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
364 CPPUNIT_ASSERT_EQUAL(1, nCharPos);
365 CPPUNIT_ASSERT(!bRightToLeftMarker);
367 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
368 CPPUNIT_ASSERT_EQUAL(2, nCharPos);
369 CPPUNIT_ASSERT(!bRightToLeftMarker);
371 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
372 CPPUNIT_ASSERT_EQUAL(3, nCharPos);
373 CPPUNIT_ASSERT(!bRightToLeftMarker);
375 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
376 CPPUNIT_ASSERT_EQUAL(4, nCharPos);
377 CPPUNIT_ASSERT(bRightToLeftMarker);
379 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
380 CPPUNIT_ASSERT_EQUAL(5, nCharPos);
381 CPPUNIT_ASSERT(!bRightToLeftMarker);
383 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
384 CPPUNIT_ASSERT_EQUAL(6, nCharPos);
385 CPPUNIT_ASSERT(bRightToLeftMarker);
387 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
388 CPPUNIT_ASSERT_EQUAL(7, nCharPos);
389 CPPUNIT_ASSERT(!bRightToLeftMarker);
391 // no next position, we are running off the end
392 CPPUNIT_ASSERT(!aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
394 aRuns.ResetPos();
396 int nMinRunPos, nEndRunPos;
397 bool bRightToLeft(false);
399 CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft));
400 CPPUNIT_ASSERT_EQUAL(1, nMinRunPos);
401 CPPUNIT_ASSERT_EQUAL(4, nEndRunPos);
402 CPPUNIT_ASSERT(!bRightToLeft);
404 aRuns.NextRun();
405 CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft));
406 CPPUNIT_ASSERT_EQUAL(4, nMinRunPos);
407 CPPUNIT_ASSERT_EQUAL(5, nEndRunPos);
408 CPPUNIT_ASSERT(bRightToLeft);
410 aRuns.NextRun();
411 CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft));
412 CPPUNIT_ASSERT_EQUAL(5, nMinRunPos);
413 CPPUNIT_ASSERT_EQUAL(6, nEndRunPos);
414 CPPUNIT_ASSERT(!bRightToLeft);
416 aRuns.NextRun();
417 CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft));
418 CPPUNIT_ASSERT_EQUAL(6, nMinRunPos);
419 CPPUNIT_ASSERT_EQUAL(7, nEndRunPos);
420 CPPUNIT_ASSERT(bRightToLeft);
422 // test clear
423 aRuns.Clear();
424 CPPUNIT_ASSERT(aRuns.IsEmpty());
427 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_AddRuns)
429 ImplLayoutRuns aRuns;
430 aRuns.AddRun(1, 4, false);
431 aRuns.AddRun(5, 4, true);
432 aRuns.AddRun(5, 6, false);
433 aRuns.AddRun(6, 7, true);
435 int nCharPos(0);
436 bool bRightToLeftMarker(false);
438 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
439 CPPUNIT_ASSERT_EQUAL(1, nCharPos);
440 CPPUNIT_ASSERT(!bRightToLeftMarker);
442 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
443 CPPUNIT_ASSERT_EQUAL(2, nCharPos);
444 CPPUNIT_ASSERT(!bRightToLeftMarker);
446 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
447 CPPUNIT_ASSERT_EQUAL(3, nCharPos);
448 CPPUNIT_ASSERT(!bRightToLeftMarker);
450 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
451 CPPUNIT_ASSERT_EQUAL(4, nCharPos);
452 CPPUNIT_ASSERT(bRightToLeftMarker);
454 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
455 CPPUNIT_ASSERT_EQUAL(5, nCharPos);
456 CPPUNIT_ASSERT(!bRightToLeftMarker);
458 CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
459 CPPUNIT_ASSERT_EQUAL(6, nCharPos);
460 CPPUNIT_ASSERT(bRightToLeftMarker);
462 // no next position, we are running off the end
463 CPPUNIT_ASSERT(!aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker));
465 aRuns.ResetPos();
467 int nMinRunPos, nEndRunPos;
468 bool bRightToLeft(false);
470 CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft));
471 CPPUNIT_ASSERT_EQUAL(1, nMinRunPos);
472 CPPUNIT_ASSERT_EQUAL(4, nEndRunPos);
473 CPPUNIT_ASSERT(!bRightToLeft);
475 aRuns.NextRun();
476 CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft));
477 CPPUNIT_ASSERT_EQUAL(4, nMinRunPos);
478 CPPUNIT_ASSERT_EQUAL(5, nEndRunPos);
479 CPPUNIT_ASSERT(bRightToLeft);
481 aRuns.NextRun();
482 CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft));
483 CPPUNIT_ASSERT_EQUAL(5, nMinRunPos);
484 CPPUNIT_ASSERT_EQUAL(6, nEndRunPos);
485 CPPUNIT_ASSERT(!bRightToLeft);
487 aRuns.NextRun();
488 CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft));
489 CPPUNIT_ASSERT_EQUAL(6, nMinRunPos);
490 CPPUNIT_ASSERT_EQUAL(7, nEndRunPos);
491 CPPUNIT_ASSERT(bRightToLeft);
494 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_PosIsInRun)
496 ImplLayoutRuns aRuns;
497 aRuns.AddRun(1, 4, false);
498 aRuns.AddRun(4, 5, true);
499 aRuns.AddRun(5, 6, false);
500 aRuns.AddRun(6, 7, true);
502 CPPUNIT_ASSERT(aRuns.PosIsInRun(1));
503 CPPUNIT_ASSERT(aRuns.PosIsInRun(2));
504 CPPUNIT_ASSERT(aRuns.PosIsInRun(3));
506 aRuns.NextRun();
507 CPPUNIT_ASSERT(aRuns.PosIsInRun(4));
509 aRuns.NextRun();
510 CPPUNIT_ASSERT(aRuns.PosIsInRun(5));
512 aRuns.NextRun();
513 CPPUNIT_ASSERT(aRuns.PosIsInRun(6));
515 CPPUNIT_ASSERT(!aRuns.PosIsInRun(7));
518 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_PosIsInAnyRun)
520 ImplLayoutRuns aRuns;
521 aRuns.AddRun(1, 4, false);
522 aRuns.AddRun(4, 5, true);
523 aRuns.AddRun(5, 6, false);
524 aRuns.AddRun(6, 7, true);
526 CPPUNIT_ASSERT(aRuns.PosIsInAnyRun(1));
527 CPPUNIT_ASSERT(!aRuns.PosIsInAnyRun(7));
530 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_Normalize)
532 ImplLayoutRuns aRuns;
533 aRuns.AddRun(8, 10, true);
534 aRuns.AddRun(5, 8, false);
535 aRuns.AddRun(2, 5, false);
536 aRuns.AddRun(1, 3, false);
537 aRuns.AddRun(14, 15, false);
539 CPPUNIT_ASSERT_EQUAL(size_t(5), aRuns.size());
540 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(8, 10, true), aRuns.at(0));
541 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(5, 8, false), aRuns.at(1));
542 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(2, 5, false), aRuns.at(2));
543 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(1, 3, false), aRuns.at(3));
544 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(14, 15, false), aRuns.at(4));
546 aRuns.Normalize();
548 CPPUNIT_ASSERT_EQUAL(size_t(2), aRuns.size());
549 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(1, 10, false), aRuns.at(0));
550 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(14, 15, false), aRuns.at(1));
553 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_PrepareFallbackRuns_LTR)
555 ImplLayoutRuns aRuns;
556 aRuns.AddRun(0, 10, false); // First 5 characters excluded
557 aRuns.AddRun(11, 15, false); // Entire run included
558 aRuns.AddRun(16, 25, false); // First 4 characters included
559 aRuns.AddRun(26, 30, false); // Entire run excluded
560 aRuns.AddRun(31, 35, false); // Exact match
562 CPPUNIT_ASSERT_EQUAL(size_t(5), aRuns.size());
564 ImplLayoutRuns aFallbackRuns;
565 aFallbackRuns.AddRun(5, 20, false);
566 aFallbackRuns.AddRun(31, 35, false);
568 CPPUNIT_ASSERT_EQUAL(size_t(2), aFallbackRuns.size());
570 ImplLayoutRuns::PrepareFallbackRuns(&aRuns, &aFallbackRuns);
572 CPPUNIT_ASSERT_EQUAL(size_t(0), aFallbackRuns.size());
574 CPPUNIT_ASSERT_EQUAL(size_t(4), aRuns.size());
575 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(5, 10, false), aRuns.at(0));
576 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(11, 15, false), aRuns.at(1));
577 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(16, 20, false), aRuns.at(2));
578 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(31, 35, false), aRuns.at(3));
581 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_PrepareFallbackRuns_LTR_PreservesOrder)
583 ImplLayoutRuns aRuns;
584 aRuns.AddRun(16, 25, false); // First 4 characters included
585 aRuns.AddRun(31, 35, false); // Exact match
586 aRuns.AddRun(0, 10, false); // First 5 characters excluded
587 aRuns.AddRun(26, 30, false); // Entire run excluded
588 aRuns.AddRun(11, 15, false); // Entire run included
590 CPPUNIT_ASSERT_EQUAL(size_t(5), aRuns.size());
592 ImplLayoutRuns aFallbackRuns;
593 aFallbackRuns.AddRun(5, 20, false);
594 aFallbackRuns.AddRun(31, 35, false);
596 CPPUNIT_ASSERT_EQUAL(size_t(2), aFallbackRuns.size());
598 ImplLayoutRuns::PrepareFallbackRuns(&aRuns, &aFallbackRuns);
600 CPPUNIT_ASSERT_EQUAL(size_t(0), aFallbackRuns.size());
602 CPPUNIT_ASSERT_EQUAL(size_t(4), aRuns.size());
603 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(16, 20, false), aRuns.at(0));
604 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(31, 35, false), aRuns.at(1));
605 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(5, 10, false), aRuns.at(2));
606 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(11, 15, false), aRuns.at(3));
609 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_PrepareFallbackRuns_RTL)
611 ImplLayoutRuns aRuns;
612 aRuns.AddRun(0, 10, false);
613 aRuns.AddRun(10, 90, true);
614 aRuns.AddRun(90, 100, false);
616 CPPUNIT_ASSERT_EQUAL(size_t(3), aRuns.size());
618 ImplLayoutRuns aFallbackRuns;
619 aFallbackRuns.AddRun(0, 5, false);
620 aFallbackRuns.AddRun(6, 10, false);
621 aFallbackRuns.AddRun(10, 20, true);
622 aFallbackRuns.AddRun(21, 30, true);
623 aFallbackRuns.AddRun(31, 40, true);
624 aFallbackRuns.AddRun(41, 50, true);
625 aFallbackRuns.AddRun(92, 95, false);
626 aFallbackRuns.AddRun(96, 98, false);
628 CPPUNIT_ASSERT_EQUAL(size_t(8), aFallbackRuns.size());
630 ImplLayoutRuns::PrepareFallbackRuns(&aRuns, &aFallbackRuns);
632 CPPUNIT_ASSERT_EQUAL(size_t(0), aFallbackRuns.size());
634 CPPUNIT_ASSERT_EQUAL(size_t(8), aRuns.size());
635 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(0, 5, false), aRuns.at(0));
636 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(6, 10, false), aRuns.at(1));
637 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(41, 50, true), aRuns.at(2));
638 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(31, 40, true), aRuns.at(3));
639 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(21, 30, true), aRuns.at(4));
640 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(10, 20, true), aRuns.at(5));
641 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(92, 95, false), aRuns.at(6));
642 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(96, 98, false), aRuns.at(7));
645 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_tdf161397)
647 // Fallback run characteristic test from a particular case
649 ImplLayoutRuns aRuns;
650 aRuns.AddRun(0, 13, true);
652 ImplLayoutRuns aFallbackRuns;
653 aFallbackRuns.AddRun(12, 13, true);
654 aFallbackRuns.AddRun(7, 12, true);
655 aFallbackRuns.AddRun(5, 6, true);
656 aFallbackRuns.AddRun(0, 5, true);
658 ImplLayoutRuns::PrepareFallbackRuns(&aRuns, &aFallbackRuns);
660 CPPUNIT_ASSERT_EQUAL(size_t(2), aRuns.size());
661 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(7, 13, true), aRuns.at(0));
662 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(0, 6, true), aRuns.at(1));
665 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_GrowBidirectional)
667 ImplLayoutRuns aRuns;
668 aRuns.AddPos(16, true);
669 aRuns.AddPos(17, true);
670 aRuns.AddPos(18, true);
671 aRuns.AddPos(15, true);
672 aRuns.AddPos(19, true);
673 aRuns.AddPos(14, true);
675 CPPUNIT_ASSERT_EQUAL(size_t(1), aRuns.size());
676 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(14, 20, true), aRuns.at(0));
679 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_ReverseTail)
681 ImplLayoutRuns aRuns;
682 aRuns.AddRun(10, 20, true);
683 aRuns.AddRun(30, 40, false);
684 aRuns.AddRun(50, 60, true);
685 aRuns.AddRun(70, 80, true);
686 aRuns.AddRun(90, 100, false);
688 aRuns.ReverseTail(size_t(2));
690 CPPUNIT_ASSERT_EQUAL(size_t(5), aRuns.size());
691 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(10, 20, true), aRuns.at(0));
692 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(30, 40, false), aRuns.at(1));
693 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(90, 100, false), aRuns.at(2));
694 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(70, 80, true), aRuns.at(3));
695 CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(50, 60, true), aRuns.at(4));
698 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutArgsBiDiStrong)
700 OUString sTestString = u"The quick brown fox\n jumped over the lazy dog"
701 "العاشر"_ustr;
702 vcl::text::ImplLayoutArgs aArgs(sTestString, 0, sTestString.getLength(),
703 SalLayoutFlags::BiDiStrong, LanguageTag(LANGUAGE_NONE),
704 nullptr);
706 int nMinRunPos(0);
707 int nEndRunPos(0);
708 bool bRTL(false);
710 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
711 CPPUNIT_ASSERT_EQUAL(0, nMinRunPos);
712 CPPUNIT_ASSERT_EQUAL(19, nEndRunPos);
713 CPPUNIT_ASSERT(!bRTL);
715 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
716 CPPUNIT_ASSERT_EQUAL(20, nMinRunPos);
717 CPPUNIT_ASSERT_EQUAL(51, nEndRunPos);
718 CPPUNIT_ASSERT(!bRTL);
720 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
721 CPPUNIT_ASSERT_EQUAL(20, nMinRunPos);
722 CPPUNIT_ASSERT_EQUAL(51, nEndRunPos);
725 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutArgsBiDiRtl)
727 OUString sTestString = u"The quick brown fox\n jumped over the lazy dog"
728 "العاشر"_ustr;
729 vcl::text::ImplLayoutArgs aArgs(sTestString, 0, sTestString.getLength(),
730 SalLayoutFlags::BiDiRtl, LanguageTag(LANGUAGE_NONE), nullptr);
732 int nMinRunPos(0);
733 int nEndRunPos(0);
734 bool bRTL(false);
736 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
737 CPPUNIT_ASSERT_EQUAL(45, nMinRunPos);
738 CPPUNIT_ASSERT_EQUAL(51, nEndRunPos);
739 CPPUNIT_ASSERT(&bRTL);
741 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
742 CPPUNIT_ASSERT_EQUAL(21, nMinRunPos);
743 CPPUNIT_ASSERT_EQUAL(45, nEndRunPos);
744 CPPUNIT_ASSERT(!bRTL);
746 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
747 CPPUNIT_ASSERT_EQUAL(20, nMinRunPos);
748 CPPUNIT_ASSERT_EQUAL(21, nEndRunPos);
749 CPPUNIT_ASSERT(bRTL);
751 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
752 CPPUNIT_ASSERT_EQUAL(0, nMinRunPos);
753 CPPUNIT_ASSERT_EQUAL(19, nEndRunPos);
754 CPPUNIT_ASSERT(!bRTL);
757 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutArgsRightAlign)
759 OUString sTestString = u"The quick brown fox\n jumped over the lazy dog"
760 "العاشر"_ustr;
761 vcl::text::ImplLayoutArgs aArgs(sTestString, 0, sTestString.getLength(),
762 SalLayoutFlags::RightAlign, LanguageTag(LANGUAGE_NONE),
763 nullptr);
765 int nMinRunPos(0);
766 int nEndRunPos(0);
767 bool bRTL(false);
769 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
770 CPPUNIT_ASSERT_EQUAL(0, nMinRunPos);
771 CPPUNIT_ASSERT_EQUAL(19, nEndRunPos);
772 CPPUNIT_ASSERT(!bRTL);
774 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
775 CPPUNIT_ASSERT_EQUAL(20, nMinRunPos);
776 CPPUNIT_ASSERT_EQUAL(45, nEndRunPos);
777 CPPUNIT_ASSERT(!bRTL);
779 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
780 CPPUNIT_ASSERT_EQUAL(45, nMinRunPos);
781 CPPUNIT_ASSERT_EQUAL(51, nEndRunPos);
782 CPPUNIT_ASSERT(bRTL);
785 CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutArgs_PrepareFallback_precalculatedglyphs)
787 // this font has no Cyrillic characters and thus needs fallback
788 const vcl::Font aFont(u"Amiri"_ustr, Size(0, 36));
790 ScopedVclPtrInstance<VirtualDevice> pVirDev;
791 pVirDev->SetFont(aFont);
793 static constexpr OStringLiteral sUTF8String(u8"Тхе яуицк\n ыумпед овер");
794 const OUString sTestString(OUString::fromUtf8(sUTF8String));
795 std::unique_ptr<SalLayout> pLayout
796 = pVirDev->ImplLayout(sTestString, 0, sTestString.getLength(), Point(0, 0), 0, {}, {},
797 SalLayoutFlags::GlyphItemsOnly);
798 SalLayoutGlyphs aGlyphs = pLayout->GetGlyphs();
799 SalLayoutGlyphsImpl* pGlyphsImpl = aGlyphs.Impl(1);
801 vcl::text::ImplLayoutArgs aArgs(sTestString, 0, sTestString.getLength(),
802 SalLayoutFlags::BiDiRtl, LanguageTag(LANGUAGE_RUSSIAN),
803 nullptr);
805 aArgs.PrepareFallback(pGlyphsImpl);
807 int nMinRunPos(0);
808 int nEndRunPos(0);
809 bool bRTL(false);
811 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
812 CPPUNIT_ASSERT_EQUAL(0, nMinRunPos);
813 CPPUNIT_ASSERT_EQUAL(3, nEndRunPos);
814 CPPUNIT_ASSERT(!bRTL);
816 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
817 CPPUNIT_ASSERT_EQUAL(4, nMinRunPos);
818 CPPUNIT_ASSERT_EQUAL(9, nEndRunPos);
819 CPPUNIT_ASSERT(!bRTL);
821 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
822 CPPUNIT_ASSERT_EQUAL(11, nMinRunPos);
823 CPPUNIT_ASSERT_EQUAL(17, nEndRunPos);
824 CPPUNIT_ASSERT(!bRTL);
826 aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL);
827 CPPUNIT_ASSERT_EQUAL(18, nMinRunPos);
828 CPPUNIT_ASSERT_EQUAL(22, nEndRunPos);
829 CPPUNIT_ASSERT(!bRTL);
832 CPPUNIT_TEST_FIXTURE(VclTextTest, testGetStringWithCenterEllpsis)
834 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
835 device->SetOutputSizePixel(Size(1000, 1000));
836 device->SetFont(vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 11)));
838 CPPUNIT_ASSERT_EQUAL(
839 u"a b c d ...v w x y z"_ustr,
840 device->GetEllipsisString(u"a b c d e f g h i j k l m n o p q r s t u v w x y z"_ustr, 100,
841 DrawTextFlags::CenterEllipsis));
844 CPPUNIT_TEST_FIXTURE(VclTextTest, testGetStringWithEndEllpsis)
846 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
847 device->SetOutputSizePixel(Size(1000, 1000));
848 device->SetFont(vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 11)));
850 CPPUNIT_ASSERT_EQUAL(u"a"_ustr, device->GetEllipsisString(u"abcde. f g h i j ..."_ustr, 10,
851 DrawTextFlags::EndEllipsis));
853 CPPUNIT_ASSERT_EQUAL(
854 u"a b c d e f g h i j ..."_ustr,
855 device->GetEllipsisString(u"a b c d e f g h i j k l m n o p q r s t u v w x y z"_ustr, 100,
856 DrawTextFlags::EndEllipsis));
858 CPPUNIT_ASSERT_EQUAL(
859 u"a"_ustr, device->GetEllipsisString(u"abcde. f g h i j ..."_ustr, 1,
860 DrawTextFlags::EndEllipsis | DrawTextFlags::Clip));
863 CPPUNIT_TEST_FIXTURE(VclTextTest, testGetStringWithNewsEllpsis)
865 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
866 device->SetOutputSizePixel(Size(1000, 1000));
867 device->SetFont(vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 11)));
869 CPPUNIT_ASSERT_EQUAL(u"a"_ustr, device->GetEllipsisString(u"abcde. f g h i j ..."_ustr, 10,
870 DrawTextFlags::NewsEllipsis));
872 CPPUNIT_ASSERT_EQUAL(
873 u"a b .... x y z"_ustr,
874 device->GetEllipsisString(u"a b c d. e f g. h i j k l m n o p q r s t u v w. x y z"_ustr,
875 100, DrawTextFlags::NewsEllipsis));
877 CPPUNIT_ASSERT_EQUAL(
878 u"a b .... x y z"_ustr,
879 device->GetEllipsisString(u"a b c d. e f g h i j k l m n o p q r s t u v w. x y z"_ustr,
880 100, DrawTextFlags::NewsEllipsis));
882 CPPUNIT_ASSERT_EQUAL(
883 u"a b c d e f g h i j ..."_ustr,
884 device->GetEllipsisString(u"a b c d e f g h i j k l m n o p q r s t u v w. x y z"_ustr, 100,
885 DrawTextFlags::NewsEllipsis));
887 CPPUNIT_ASSERT_EQUAL(
888 u"a..... x y z"_ustr,
889 device->GetEllipsisString(u"a. b c d e f g h i j k l m n o p q r s t u v w. x y z"_ustr,
890 100, DrawTextFlags::NewsEllipsis));
892 CPPUNIT_ASSERT_EQUAL(
893 u"ab. cde..."_ustr,
894 device->GetEllipsisString(u"ab. cde. x y z"_ustr, 50, DrawTextFlags::NewsEllipsis));
897 CPPUNIT_TEST_FIXTURE(VclTextTest, testGetTextBreak_invalid_index)
899 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
900 device->SetOutputSizePixel(Size(1000, 1000));
901 device->SetFont(vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 11)));
903 const OUString sTestStr(u"textline_ text_"_ustr);
904 const auto nLen = sTestStr.getLength();
905 const auto nTextWidth = device->GetTextWidth(u"text"_ustr);
906 const auto nInvalidIndex = sTestStr.getLength() + 2;
908 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-1),
909 device->GetTextBreak(sTestStr, nTextWidth, nInvalidIndex, nLen));
912 CPPUNIT_TEST_FIXTURE(VclTextTest, testGetTextBreak)
914 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
915 device->SetOutputSizePixel(Size(1000, 1000));
916 device->SetFont(vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 11)));
918 const OUString sTestStr(u"textline_ text_"_ustr);
919 const auto nLen = sTestStr.getLength();
920 const auto nTextWidth = device->GetTextWidth(u"text"_ustr);
922 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4),
923 device->GetTextBreak(sTestStr, nTextWidth, 0, nLen));
924 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(7),
925 device->GetTextBreak(sTestStr, nTextWidth, 3, nLen));
926 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(9),
927 device->GetTextBreak(sTestStr, nTextWidth, 6, nLen));
928 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(12),
929 device->GetTextBreak(sTestStr, nTextWidth, 8, nLen));
930 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(14),
931 device->GetTextBreak(sTestStr, nTextWidth, 11, nLen));
932 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-1),
933 device->GetTextBreak(sTestStr, nTextWidth, 13, nLen));
936 CPPUNIT_TEST_FIXTURE(VclTextTest, testGetSingleLineTextRect)
938 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
939 device->SetOutputSizePixel(Size(1000, 1000));
940 device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11)));
942 CPPUNIT_ASSERT_EQUAL(
943 tools::Rectangle(Point(), Size(75, 12)),
944 device->GetTextRect(tools::Rectangle(Point(), Point(100, 100)), u"This is test text"_ustr));
947 CPPUNIT_TEST_FIXTURE(VclTextTest, testGetSingleLineTextRectWithEndEllipsis)
949 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
950 device->SetOutputSizePixel(Size(1000, 1000));
951 device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11)));
953 CPPUNIT_ASSERT_EQUAL(
954 tools::Rectangle(Point(), Size(52, 12)),
955 device->GetTextRect(tools::Rectangle(Point(), Point(50, 50)), u"This is test text"_ustr,
956 DrawTextFlags::WordBreak | DrawTextFlags::EndEllipsis));
959 CPPUNIT_TEST_FIXTURE(VclTextTest, testGetRightBottomAlignedSingleLineTextRect)
961 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
962 device->SetOutputSizePixel(Size(1000, 1000));
963 device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11)));
965 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(926, 989), Size(75, 12)),
966 device->GetTextRect(tools::Rectangle(Point(), Point(1000, 1000)),
967 u"This is test text"_ustr,
968 DrawTextFlags::Right | DrawTextFlags::Bottom));
971 CPPUNIT_TEST_FIXTURE(VclTextTest, testGetRotatedSingleLineTextRect)
973 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
974 device->SetOutputSizePixel(Size(1000, 1000));
975 device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11)));
977 vcl::Font aFont(device->GetFont());
978 aFont.SetOrientation(45_deg10);
979 device->SetFont(aFont);
981 CPPUNIT_ASSERT_EQUAL(
982 tools::Rectangle(Point(0, -3), Size(75, 18)),
983 device->GetTextRect(tools::Rectangle(Point(), Point(100, 100)), u"This is test text"_ustr));
986 CPPUNIT_TEST_FIXTURE(VclTextTest, testGetMultiLineTextRect)
988 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
989 device->SetOutputSizePixel(Size(1000, 1000));
990 device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11)));
992 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(), Size(75, 12)),
993 device->GetTextRect(tools::Rectangle(Point(), Point(100, 100)),
994 u"This is test text"_ustr,
995 DrawTextFlags::WordBreak | DrawTextFlags::MultiLine));
998 CPPUNIT_TEST_FIXTURE(VclTextTest, testGetMultiLineTextRectWithEndEllipsis)
1000 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
1001 device->SetOutputSizePixel(Size(1000, 1000));
1002 device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11)));
1004 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(), Size(52, 48)),
1005 device->GetTextRect(tools::Rectangle(Point(), Point(50, 50)),
1006 u"This is test text xyzabc123abcdefghijk"_ustr,
1007 DrawTextFlags::WordBreak | DrawTextFlags::EndEllipsis
1008 | DrawTextFlags::MultiLine));
1011 CPPUNIT_TEST_FIXTURE(VclTextTest, testGetRightBottomAlignedMultiLineTextRect)
1013 ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
1014 device->SetOutputSizePixel(Size(1000, 1000));
1015 device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11)));
1017 CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(926, 989), Size(75, 12)),
1018 device->GetTextRect(tools::Rectangle(Point(), Point(1000, 1000)),
1019 u"This is test text"_ustr,
1020 DrawTextFlags::Right | DrawTextFlags::Bottom
1021 | DrawTextFlags::MultiLine));
1024 CPPUNIT_TEST_FIXTURE(VclTextTest, testPartialTextArraySizeMatch)
1026 OUString aWater = u"Water"_ustr;
1027 vcl::Font aFont(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 2048));
1029 ScopedVclPtrInstance<VirtualDevice> pOutDev;
1030 pOutDev->SetFont(aFont);
1032 // Absolute character widths for the complete array.
1033 KernArray aCompleteWidths;
1034 auto nCompleteWidth = pOutDev->GetTextArray(aWater, &aCompleteWidths).nWidth;
1036 CPPUNIT_ASSERT_EQUAL(size_t{ 5 }, aCompleteWidths.size());
1038 // Accumulate partial widths
1039 double nPartialWidth = 0.0;
1041 sal_Int32 nPrevWidth = 0;
1042 for (sal_Int32 i = 0; i < 5; ++i)
1044 KernArray aFragmentWidths;
1045 auto nFragmentWidth
1046 = pOutDev
1047 ->GetPartialTextArray(aWater, &aFragmentWidths, /*nIndex*/ 0, /*nLen*/ 5,
1048 /*nPartIndex*/ i, /*nPartLen*/ 1)
1049 .nWidth;
1050 nPartialWidth += nFragmentWidth;
1052 CPPUNIT_ASSERT_EQUAL(size_t{ 1 }, aFragmentWidths.size());
1053 CPPUNIT_ASSERT_EQUAL(aCompleteWidths[i] - nPrevWidth, aFragmentWidths[0]);
1054 nPrevWidth = aCompleteWidths[i];
1057 CPPUNIT_ASSERT_DOUBLES_EQUAL(nCompleteWidth, nPartialWidth, /*delta*/ 0.01);
1060 CPPUNIT_PLUGIN_IMPLEMENT();
1062 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */