/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include class VclTextTest : public test::BootstrapFixture { // if enabled - check the result images with: // "xdg-open ./workdir/CppunitTest/vcl_text_test.test.core/" static constexpr const bool mbExportBitmap = false; public: void exportDevice(const OUString& filename, const VclPtr& device) { if (mbExportBitmap) { BitmapEx aBitmapEx(device->GetBitmapEx(Point(0, 0), device->GetOutputSizePixel())); OUString cwd; CPPUNIT_ASSERT_EQUAL(osl_Process_E_None, osl_getProcessWorkingDir(&cwd.pData)); OUString url; CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, osl::FileBase::getAbsoluteFileURL(cwd, filename, url)); SvFileStream aStream(url, StreamMode::WRITE | StreamMode::TRUNC); CPPUNIT_ASSERT_EQUAL( ERRCODE_NONE, GraphicFilter::GetGraphicFilter().compressAsPNG(aBitmapEx, aStream)); } } VclTextTest() : BootstrapFixture(true, false) { } }; static std::ostream& operator<<(std::ostream& s, const ImplLayoutRuns::Run& rRun) { return s << "{" << rRun.m_nMinRunPos << ", " << rRun.m_nEndRunPos << ", " << rRun.m_bRTL << "}"; } // Avoid issues when colorized antialiasing generates slightly tinted rather than truly black // pixels: static bool isBlack(Color col) { return col.GetRed() < 25 && col.GetGreen() < 25 && col.GetBlue() < 25; } // Return pixel width of the base of the given character located above // the starting position. // In other words, go up in y direction until a black pixel is found, // then return the horizontal width of the area of those pixels. // For 'L' this gives the width of the base of the character. static tools::Long getCharacterBaseWidth(VirtualDevice* device, const Point& start) { Bitmap bitmap = device->GetBitmap(Point(), device->GetOutputSizePixel()); BitmapScopedReadAccess access(bitmap); tools::Long y = start.Y(); while (y >= 0 && !isBlack(access->GetColor(y, start.X()))) --y; if (y < 0) return -1; tools::Long xmin = start.X(); while (xmin >= 0 && access->GetColor(y, xmin) != COL_WHITE) --xmin; tools::Long xmax = start.X(); while (xmax < bitmap.GetSizePixel().Width() && access->GetColor(y, xmax) != COL_WHITE) ++xmax; return xmax - xmin + 1; } // Similar to above but this time from the top, for U+30E8 (it's straight at the top, not at the bottom). static tools::Long getCharacterTopWidth(VirtualDevice* device, const Point& start) { Bitmap bitmap = device->GetBitmap(Point(), device->GetOutputSizePixel()); BitmapScopedReadAccess access(bitmap); tools::Long y = start.Y(); while (y < bitmap.GetSizePixel().Height() && !isBlack(access->GetColor(y, start.X()))) ++y; if (y >= bitmap.GetSizePixel().Height()) return -1; tools::Long xmin = start.X(); while (xmin >= 0 && access->GetColor(y, xmin) != COL_WHITE) --xmin; tools::Long xmax = start.X(); while (xmax < bitmap.GetSizePixel().Width() && access->GetColor(y, xmax) != COL_WHITE) ++xmax; return xmax - xmin + 1; } // Similar to above, but this time return the pixel height of the left-most // line of the character, going right from the starting point. // For 'L' this gives the height of the left line. static tools::Long getCharacterLeftSideHeight(VirtualDevice* device, const Point& start) { Bitmap bitmap = device->GetBitmap(Point(), device->GetOutputSizePixel()); BitmapScopedReadAccess access(bitmap); tools::Long x = start.X(); while (x < bitmap.GetSizePixel().Width() && !isBlack(access->GetColor(start.Y(), x))) ++x; if (x >= bitmap.GetSizePixel().Width()) return -1; tools::Long ymin = start.Y(); while (ymin >= 0 && access->GetColor(ymin, x) != COL_WHITE) --ymin; tools::Long ymax = start.Y(); while (ymax < bitmap.GetSizePixel().Width() && access->GetColor(ymax, x) != COL_WHITE) ++ymax; return ymax - ymin + 1; } // Test rendering of the 'L' character (chosen because L is a simple shape). // Check things like using a double font size doubling the size of the character, correct rotation, etc. // IMPORTANT: If you modify this, check also the VclCjkTextTest::testVerticalText(). CPPUNIT_TEST_FIXTURE(VclTextTest, testSimpleText) { OUString text(u"L"_ustr); ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(100, 100)); device->SetBackground(Wallpaper(COL_WHITE)); // Disable AA, to make all pixels be black or white. device->SetAntialiasing(AntialiasingFlags::DisableText); // Bail out on all backends that do not work (or I didn't test). Opt-out rather than opt-in // to make sure new backends fail initially. if (device->GetGraphics()->getRenderBackendName() == "qt" || device->GetGraphics()->getRenderBackendName() == "qtsvp" || device->GetGraphics()->getRenderBackendName() == "gtk3svp" || device->GetGraphics()->getRenderBackendName() == "aqua" || device->GetGraphics()->getRenderBackendName() == "gen" || device->GetGraphics()->getRenderBackendName() == "genpsp") return; // Use Dejavu fonts, they are shipped with LO, so they should be ~always available. // Use Sans variant for simpler glyph shapes (no serifs). vcl::Font font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 36)); device->Erase(); device->SetFont(font); device->DrawText(Point(10, 10), text); exportDevice(u"simple-text-36.png"_ustr, device); // Height of 'L' with font 36 size should be roughly 28 pixels. // Use the 'doubles' variant of the test, since that one allows // a delta, and allow several pixels of delta to account // for different rendering methods and whatnot. tools::Long height36 = getCharacterLeftSideHeight(device, Point(0, 30)); CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(28), height36, 4); tools::Long width36 = getCharacterBaseWidth(device, Point(20, 99)); CPPUNIT_ASSERT_DOUBLES_EQUAL(tools::Long(19), width36, 4); font.SetOrientation(2700_deg10); device->Erase(); device->SetFont(font); device->DrawText(Point(90, 10), text); exportDevice(u"simple-text-36-270deg.png"_ustr, device); // Width and height here should be swapped, again allowing for some imprecisions. tools::Long height36Rotated = getCharacterLeftSideHeight(device, Point(0, 20)); CPPUNIT_ASSERT_DOUBLES_EQUAL(width36, height36Rotated, 2); tools::Long width36Rotated = getCharacterTopWidth(device, Point(70, 0)); CPPUNIT_ASSERT_DOUBLES_EQUAL(height36, width36Rotated, 2); font = vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 72)); device->Erase(); device->SetFont(font); device->DrawText(Point(10, 10), text); exportDevice(u"simple-text-72.png"_ustr, device); // Font size is doubled, so pixel sizes should also roughly double. tools::Long height72 = getCharacterLeftSideHeight(device, Point(0, 30)); CPPUNIT_ASSERT_DOUBLES_EQUAL(height36 * 2, height72, 4); tools::Long width72 = getCharacterBaseWidth(device, Point(20, 99)); CPPUNIT_ASSERT_DOUBLES_EQUAL(width36 * 2, width72, 5); font.SetOrientation(2700_deg10); device->Erase(); device->SetFont(font); device->DrawText(Point(90, 10), text); exportDevice(u"simple-text-72-270deg.png"_ustr, device); tools::Long height72Rotated = getCharacterLeftSideHeight(device, Point(0, 35)); CPPUNIT_ASSERT_DOUBLES_EQUAL(width72, height72Rotated, 2); tools::Long width72Rotated = getCharacterTopWidth(device, Point(50, 0)); CPPUNIT_ASSERT_DOUBLES_EQUAL(height72, width72Rotated, 2); // Test width scaled to 200%. font = vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(72, 36)); #ifdef _WIN32 // TODO: What is the proper way to draw 200%-wide text? This is needed on Windows // but it breaks Linux. font.SetAverageFontWidth(2 * font.GetOrCalculateAverageFontWidth()); #endif device->Erase(); device->SetFont(font); device->DrawText(Point(10, 10), text); exportDevice(u"simple-text-36-200pct.png"_ustr, device); tools::Long height36pct200 = getCharacterLeftSideHeight(device, Point(0, 30)); CPPUNIT_ASSERT_DOUBLES_EQUAL(height36, height36pct200, 2); tools::Long width36pct200 = getCharacterBaseWidth(device, Point(20, 99)); CPPUNIT_ASSERT_DOUBLES_EQUAL(width36 * 2, width36pct200, 5); // Test width scaled to 50%. font = vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(18, 36)); #ifdef _WIN32 font.SetAverageFontWidth(0.5 * font.GetOrCalculateAverageFontWidth()); #endif device->Erase(); device->SetFont(font); device->DrawText(Point(10, 10), text); exportDevice(u"simple-text-36-50pct.png"_ustr, device); tools::Long height36pct50 = getCharacterLeftSideHeight(device, Point(0, 40)); CPPUNIT_ASSERT_DOUBLES_EQUAL(height36, height36pct50, 2); tools::Long width36pct50 = getCharacterBaseWidth(device, Point(15, 99)); CPPUNIT_ASSERT_DOUBLES_EQUAL(width36 / 2, width36pct50, 2); } CPPUNIT_TEST_FIXTURE(VclTextTest, testSimpleTextFontSpecificKerning) { OUString aAV(u"AV"_ustr); vcl::Font aFont(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 2048)); ScopedVclPtrInstance pOutDev; pOutDev->SetFont(aFont); // absolute character widths AKA text array. tools::Long nRefTextWidth = 2671; KernArray aRefCharWidths{ 1270, 2671 }; KernArray aCharWidths; tools::Long nTextWidth = basegfx::fround(pOutDev->GetTextArray(aAV, &aCharWidths).nWidth); CPPUNIT_ASSERT_EQUAL(aRefCharWidths[0], aCharWidths[0]); CPPUNIT_ASSERT_EQUAL(aRefCharWidths[1], aCharWidths[1]); // this sporadically returns 75 or 74 on some of the windows tinderboxes eg. tb73 CPPUNIT_ASSERT_EQUAL(nRefTextWidth, nTextWidth); CPPUNIT_ASSERT_EQUAL(nTextWidth, tools::Long(aCharWidths.back())); // text advance width and line height CPPUNIT_ASSERT_EQUAL(nRefTextWidth, pOutDev->GetTextWidth(aAV)); CPPUNIT_ASSERT_EQUAL(tools::Long(2384), pOutDev->GetTextHeight()); // exact bounding rectangle, not essentially the same as text width/height tools::Rectangle aBoundRect; pOutDev->GetTextBoundRect(aBoundRect, aAV); CPPUNIT_ASSERT_EQUAL(tools::Long(16), aBoundRect.Left()); CPPUNIT_ASSERT_EQUAL(tools::Long(408), aBoundRect.Top()); CPPUNIT_ASSERT_EQUAL(tools::Long(2639), aBoundRect.GetWidth()); CPPUNIT_ASSERT_EQUAL(tools::Long(1493), aBoundRect.getOpenHeight()); // normal orientation tools::Rectangle aInput; tools::Rectangle aRect = pOutDev->GetTextRect(aInput, aAV); // now rotate 270 degrees vcl::Font aRotated(aFont); aRotated.SetOrientation(2700_deg10); pOutDev->SetFont(aRotated); tools::Rectangle aRectRot = pOutDev->GetTextRect(aInput, aAV); // Check that we did do the rotation... CPPUNIT_ASSERT_EQUAL(aRectRot.GetWidth(), aRect.GetHeight()); CPPUNIT_ASSERT_EQUAL(aRectRot.GetHeight(), aRect.GetWidth()); } CPPUNIT_TEST_FIXTURE(VclTextTest, testSimpleTextNoKerning) { OUString aAV(u"AV"_ustr); vcl::Font aFont(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 2048)); aFont.SetKerning(FontKerning::NONE); ScopedVclPtrInstance pOutDev; pOutDev->SetFont(aFont); // absolute character widths AKA text array. tools::Long nRefTextWidth = 2802; KernArray aRefCharWidths{ 1401, 2802 }; KernArray aCharWidths; tools::Long nTextWidth = basegfx::fround(pOutDev->GetTextArray(aAV, &aCharWidths).nWidth); CPPUNIT_ASSERT_EQUAL(aRefCharWidths[0], aCharWidths[0]); CPPUNIT_ASSERT_EQUAL(aRefCharWidths[1], aCharWidths[1]); // this sporadically returns 75 or 74 on some of the windows tinderboxes eg. tb73 CPPUNIT_ASSERT_EQUAL(nRefTextWidth, nTextWidth); CPPUNIT_ASSERT_EQUAL(nTextWidth, tools::Long(aCharWidths.back())); // text advance width and line height CPPUNIT_ASSERT_EQUAL(nRefTextWidth, pOutDev->GetTextWidth(aAV)); CPPUNIT_ASSERT_EQUAL(tools::Long(2384), pOutDev->GetTextHeight()); // exact bounding rectangle, not essentially the same as text width/height tools::Rectangle aBoundRect; pOutDev->GetTextBoundRect(aBoundRect, aAV); CPPUNIT_ASSERT_EQUAL(tools::Long(16), aBoundRect.Left()); CPPUNIT_ASSERT_EQUAL(tools::Long(408), aBoundRect.Top()); CPPUNIT_ASSERT_EQUAL(tools::Long(2770), aBoundRect.GetWidth()); CPPUNIT_ASSERT_EQUAL(tools::Long(1493), aBoundRect.getOpenHeight()); // normal orientation tools::Rectangle aInput; tools::Rectangle aRect = pOutDev->GetTextRect(aInput, aAV); // now rotate 270 degrees vcl::Font aRotated(aFont); aRotated.SetOrientation(2700_deg10); pOutDev->SetFont(aRotated); tools::Rectangle aRectRot = pOutDev->GetTextRect(aInput, aAV); // Check that we did do the rotation... CPPUNIT_ASSERT_EQUAL(aRectRot.GetWidth(), aRect.GetHeight()); CPPUNIT_ASSERT_EQUAL(aRectRot.GetHeight(), aRect.GetWidth()); } CPPUNIT_TEST_FIXTURE(VclTextTest, testTextLayoutCache) { OUString sTestString = u"The quick brown fox\n jumped over the lazy dogالعاشر"_ustr; vcl::text::TextLayoutCache cache(sTestString.getStr(), sTestString.getLength()); vcl::text::Run run1 = cache.runs[0]; vcl::text::Run run2 = cache.runs[1]; bool bCorrectRuns = (cache.runs.size() == 2); CPPUNIT_ASSERT_MESSAGE("Wrong number of runs", bCorrectRuns); CPPUNIT_ASSERT_EQUAL(USCRIPT_LATIN, run1.nCode); CPPUNIT_ASSERT_EQUAL(0, run1.nStart); CPPUNIT_ASSERT_EQUAL(45, run1.nEnd); CPPUNIT_ASSERT_EQUAL(USCRIPT_ARABIC, run2.nCode); CPPUNIT_ASSERT_EQUAL(45, run2.nStart); CPPUNIT_ASSERT_EQUAL(51, run2.nEnd); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_AddPos) { ImplLayoutRuns aRuns; aRuns.AddPos(1, false); aRuns.AddPos(2, false); aRuns.AddPos(3, false); aRuns.AddPos(4, true); // add RTL marker glyph aRuns.AddPos(5, false); aRuns.AddPos(6, true); // add RTL marker glyph aRuns.AddPos(7, false); int nCharPos(0); bool bRightToLeftMarker(false); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(1, nCharPos); CPPUNIT_ASSERT(!bRightToLeftMarker); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(2, nCharPos); CPPUNIT_ASSERT(!bRightToLeftMarker); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(3, nCharPos); CPPUNIT_ASSERT(!bRightToLeftMarker); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(4, nCharPos); CPPUNIT_ASSERT(bRightToLeftMarker); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(5, nCharPos); CPPUNIT_ASSERT(!bRightToLeftMarker); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(6, nCharPos); CPPUNIT_ASSERT(bRightToLeftMarker); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(7, nCharPos); CPPUNIT_ASSERT(!bRightToLeftMarker); // no next position, we are running off the end CPPUNIT_ASSERT(!aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); aRuns.ResetPos(); int nMinRunPos, nEndRunPos; bool bRightToLeft(false); CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft)); CPPUNIT_ASSERT_EQUAL(1, nMinRunPos); CPPUNIT_ASSERT_EQUAL(4, nEndRunPos); CPPUNIT_ASSERT(!bRightToLeft); aRuns.NextRun(); CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft)); CPPUNIT_ASSERT_EQUAL(4, nMinRunPos); CPPUNIT_ASSERT_EQUAL(5, nEndRunPos); CPPUNIT_ASSERT(bRightToLeft); aRuns.NextRun(); CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft)); CPPUNIT_ASSERT_EQUAL(5, nMinRunPos); CPPUNIT_ASSERT_EQUAL(6, nEndRunPos); CPPUNIT_ASSERT(!bRightToLeft); aRuns.NextRun(); CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft)); CPPUNIT_ASSERT_EQUAL(6, nMinRunPos); CPPUNIT_ASSERT_EQUAL(7, nEndRunPos); CPPUNIT_ASSERT(bRightToLeft); // test clear aRuns.Clear(); CPPUNIT_ASSERT(aRuns.IsEmpty()); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_AddRuns) { ImplLayoutRuns aRuns; aRuns.AddRun(1, 4, false); aRuns.AddRun(5, 4, true); aRuns.AddRun(5, 6, false); aRuns.AddRun(6, 7, true); int nCharPos(0); bool bRightToLeftMarker(false); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(1, nCharPos); CPPUNIT_ASSERT(!bRightToLeftMarker); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(2, nCharPos); CPPUNIT_ASSERT(!bRightToLeftMarker); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(3, nCharPos); CPPUNIT_ASSERT(!bRightToLeftMarker); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(4, nCharPos); CPPUNIT_ASSERT(bRightToLeftMarker); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(5, nCharPos); CPPUNIT_ASSERT(!bRightToLeftMarker); CPPUNIT_ASSERT(aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); CPPUNIT_ASSERT_EQUAL(6, nCharPos); CPPUNIT_ASSERT(bRightToLeftMarker); // no next position, we are running off the end CPPUNIT_ASSERT(!aRuns.GetNextPos(&nCharPos, &bRightToLeftMarker)); aRuns.ResetPos(); int nMinRunPos, nEndRunPos; bool bRightToLeft(false); CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft)); CPPUNIT_ASSERT_EQUAL(1, nMinRunPos); CPPUNIT_ASSERT_EQUAL(4, nEndRunPos); CPPUNIT_ASSERT(!bRightToLeft); aRuns.NextRun(); CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft)); CPPUNIT_ASSERT_EQUAL(4, nMinRunPos); CPPUNIT_ASSERT_EQUAL(5, nEndRunPos); CPPUNIT_ASSERT(bRightToLeft); aRuns.NextRun(); CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft)); CPPUNIT_ASSERT_EQUAL(5, nMinRunPos); CPPUNIT_ASSERT_EQUAL(6, nEndRunPos); CPPUNIT_ASSERT(!bRightToLeft); aRuns.NextRun(); CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, &bRightToLeft)); CPPUNIT_ASSERT_EQUAL(6, nMinRunPos); CPPUNIT_ASSERT_EQUAL(7, nEndRunPos); CPPUNIT_ASSERT(bRightToLeft); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_PosIsInRun) { ImplLayoutRuns aRuns; aRuns.AddRun(1, 4, false); aRuns.AddRun(4, 5, true); aRuns.AddRun(5, 6, false); aRuns.AddRun(6, 7, true); CPPUNIT_ASSERT(aRuns.PosIsInRun(1)); CPPUNIT_ASSERT(aRuns.PosIsInRun(2)); CPPUNIT_ASSERT(aRuns.PosIsInRun(3)); aRuns.NextRun(); CPPUNIT_ASSERT(aRuns.PosIsInRun(4)); aRuns.NextRun(); CPPUNIT_ASSERT(aRuns.PosIsInRun(5)); aRuns.NextRun(); CPPUNIT_ASSERT(aRuns.PosIsInRun(6)); CPPUNIT_ASSERT(!aRuns.PosIsInRun(7)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_PosIsInAnyRun) { ImplLayoutRuns aRuns; aRuns.AddRun(1, 4, false); aRuns.AddRun(4, 5, true); aRuns.AddRun(5, 6, false); aRuns.AddRun(6, 7, true); CPPUNIT_ASSERT(aRuns.PosIsInAnyRun(1)); CPPUNIT_ASSERT(!aRuns.PosIsInAnyRun(7)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_Normalize) { ImplLayoutRuns aRuns; aRuns.AddRun(8, 10, true); aRuns.AddRun(5, 8, false); aRuns.AddRun(2, 5, false); aRuns.AddRun(1, 3, false); aRuns.AddRun(14, 15, false); CPPUNIT_ASSERT_EQUAL(size_t(5), aRuns.size()); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(8, 10, true), aRuns.at(0)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(5, 8, false), aRuns.at(1)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(2, 5, false), aRuns.at(2)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(1, 3, false), aRuns.at(3)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(14, 15, false), aRuns.at(4)); aRuns.Normalize(); CPPUNIT_ASSERT_EQUAL(size_t(2), aRuns.size()); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(1, 10, false), aRuns.at(0)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(14, 15, false), aRuns.at(1)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_PrepareFallbackRuns_LTR) { ImplLayoutRuns aRuns; aRuns.AddRun(0, 10, false); // First 5 characters excluded aRuns.AddRun(11, 15, false); // Entire run included aRuns.AddRun(16, 25, false); // First 4 characters included aRuns.AddRun(26, 30, false); // Entire run excluded aRuns.AddRun(31, 35, false); // Exact match CPPUNIT_ASSERT_EQUAL(size_t(5), aRuns.size()); ImplLayoutRuns aFallbackRuns; aFallbackRuns.AddRun(5, 20, false); aFallbackRuns.AddRun(31, 35, false); CPPUNIT_ASSERT_EQUAL(size_t(2), aFallbackRuns.size()); ImplLayoutRuns::PrepareFallbackRuns(&aRuns, &aFallbackRuns); CPPUNIT_ASSERT_EQUAL(size_t(0), aFallbackRuns.size()); CPPUNIT_ASSERT_EQUAL(size_t(4), aRuns.size()); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(5, 10, false), aRuns.at(0)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(11, 15, false), aRuns.at(1)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(16, 20, false), aRuns.at(2)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(31, 35, false), aRuns.at(3)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_PrepareFallbackRuns_LTR_PreservesOrder) { ImplLayoutRuns aRuns; aRuns.AddRun(16, 25, false); // First 4 characters included aRuns.AddRun(31, 35, false); // Exact match aRuns.AddRun(0, 10, false); // First 5 characters excluded aRuns.AddRun(26, 30, false); // Entire run excluded aRuns.AddRun(11, 15, false); // Entire run included CPPUNIT_ASSERT_EQUAL(size_t(5), aRuns.size()); ImplLayoutRuns aFallbackRuns; aFallbackRuns.AddRun(5, 20, false); aFallbackRuns.AddRun(31, 35, false); CPPUNIT_ASSERT_EQUAL(size_t(2), aFallbackRuns.size()); ImplLayoutRuns::PrepareFallbackRuns(&aRuns, &aFallbackRuns); CPPUNIT_ASSERT_EQUAL(size_t(0), aFallbackRuns.size()); CPPUNIT_ASSERT_EQUAL(size_t(4), aRuns.size()); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(16, 20, false), aRuns.at(0)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(31, 35, false), aRuns.at(1)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(5, 10, false), aRuns.at(2)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(11, 15, false), aRuns.at(3)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_PrepareFallbackRuns_RTL) { ImplLayoutRuns aRuns; aRuns.AddRun(0, 10, false); aRuns.AddRun(10, 90, true); aRuns.AddRun(90, 100, false); CPPUNIT_ASSERT_EQUAL(size_t(3), aRuns.size()); ImplLayoutRuns aFallbackRuns; aFallbackRuns.AddRun(0, 5, false); aFallbackRuns.AddRun(6, 10, false); aFallbackRuns.AddRun(10, 20, true); aFallbackRuns.AddRun(21, 30, true); aFallbackRuns.AddRun(31, 40, true); aFallbackRuns.AddRun(41, 50, true); aFallbackRuns.AddRun(92, 95, false); aFallbackRuns.AddRun(96, 98, false); CPPUNIT_ASSERT_EQUAL(size_t(8), aFallbackRuns.size()); ImplLayoutRuns::PrepareFallbackRuns(&aRuns, &aFallbackRuns); CPPUNIT_ASSERT_EQUAL(size_t(0), aFallbackRuns.size()); CPPUNIT_ASSERT_EQUAL(size_t(8), aRuns.size()); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(0, 5, false), aRuns.at(0)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(6, 10, false), aRuns.at(1)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(41, 50, true), aRuns.at(2)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(31, 40, true), aRuns.at(3)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(21, 30, true), aRuns.at(4)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(10, 20, true), aRuns.at(5)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(92, 95, false), aRuns.at(6)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(96, 98, false), aRuns.at(7)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_tdf161397) { // Fallback run characteristic test from a particular case ImplLayoutRuns aRuns; aRuns.AddRun(0, 13, true); ImplLayoutRuns aFallbackRuns; aFallbackRuns.AddRun(12, 13, true); aFallbackRuns.AddRun(7, 12, true); aFallbackRuns.AddRun(5, 6, true); aFallbackRuns.AddRun(0, 5, true); ImplLayoutRuns::PrepareFallbackRuns(&aRuns, &aFallbackRuns); CPPUNIT_ASSERT_EQUAL(size_t(2), aRuns.size()); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(7, 13, true), aRuns.at(0)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(0, 6, true), aRuns.at(1)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_GrowBidirectional) { ImplLayoutRuns aRuns; aRuns.AddPos(16, true); aRuns.AddPos(17, true); aRuns.AddPos(18, true); aRuns.AddPos(15, true); aRuns.AddPos(19, true); aRuns.AddPos(14, true); CPPUNIT_ASSERT_EQUAL(size_t(1), aRuns.size()); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(14, 20, true), aRuns.at(0)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutRuns_ReverseTail) { ImplLayoutRuns aRuns; aRuns.AddRun(10, 20, true); aRuns.AddRun(30, 40, false); aRuns.AddRun(50, 60, true); aRuns.AddRun(70, 80, true); aRuns.AddRun(90, 100, false); aRuns.ReverseTail(size_t(2)); CPPUNIT_ASSERT_EQUAL(size_t(5), aRuns.size()); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(10, 20, true), aRuns.at(0)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(30, 40, false), aRuns.at(1)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(90, 100, false), aRuns.at(2)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(70, 80, true), aRuns.at(3)); CPPUNIT_ASSERT_EQUAL(ImplLayoutRuns::Run(50, 60, true), aRuns.at(4)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutArgsBiDiStrong) { OUString sTestString = u"The quick brown fox\n jumped over the lazy dog" "العاشر"_ustr; vcl::text::ImplLayoutArgs aArgs(sTestString, 0, sTestString.getLength(), SalLayoutFlags::BiDiStrong, LanguageTag(LANGUAGE_NONE), nullptr); int nMinRunPos(0); int nEndRunPos(0); bool bRTL(false); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(0, nMinRunPos); CPPUNIT_ASSERT_EQUAL(19, nEndRunPos); CPPUNIT_ASSERT(!bRTL); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(20, nMinRunPos); CPPUNIT_ASSERT_EQUAL(51, nEndRunPos); CPPUNIT_ASSERT(!bRTL); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(20, nMinRunPos); CPPUNIT_ASSERT_EQUAL(51, nEndRunPos); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutArgsBiDiRtl) { OUString sTestString = u"The quick brown fox\n jumped over the lazy dog" "العاشر"_ustr; vcl::text::ImplLayoutArgs aArgs(sTestString, 0, sTestString.getLength(), SalLayoutFlags::BiDiRtl, LanguageTag(LANGUAGE_NONE), nullptr); int nMinRunPos(0); int nEndRunPos(0); bool bRTL(false); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(45, nMinRunPos); CPPUNIT_ASSERT_EQUAL(51, nEndRunPos); CPPUNIT_ASSERT(&bRTL); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(21, nMinRunPos); CPPUNIT_ASSERT_EQUAL(45, nEndRunPos); CPPUNIT_ASSERT(!bRTL); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(20, nMinRunPos); CPPUNIT_ASSERT_EQUAL(21, nEndRunPos); CPPUNIT_ASSERT(bRTL); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(0, nMinRunPos); CPPUNIT_ASSERT_EQUAL(19, nEndRunPos); CPPUNIT_ASSERT(!bRTL); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutArgsRightAlign) { OUString sTestString = u"The quick brown fox\n jumped over the lazy dog" "العاشر"_ustr; vcl::text::ImplLayoutArgs aArgs(sTestString, 0, sTestString.getLength(), SalLayoutFlags::RightAlign, LanguageTag(LANGUAGE_NONE), nullptr); int nMinRunPos(0); int nEndRunPos(0); bool bRTL(false); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(0, nMinRunPos); CPPUNIT_ASSERT_EQUAL(19, nEndRunPos); CPPUNIT_ASSERT(!bRTL); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(20, nMinRunPos); CPPUNIT_ASSERT_EQUAL(45, nEndRunPos); CPPUNIT_ASSERT(!bRTL); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(45, nMinRunPos); CPPUNIT_ASSERT_EQUAL(51, nEndRunPos); CPPUNIT_ASSERT(bRTL); } CPPUNIT_TEST_FIXTURE(VclTextTest, testImplLayoutArgs_PrepareFallback_precalculatedglyphs) { // this font has no Cyrillic characters and thus needs fallback const vcl::Font aFont(u"Amiri"_ustr, Size(0, 36)); ScopedVclPtrInstance pVirDev; pVirDev->SetFont(aFont); static constexpr OStringLiteral sUTF8String(u8"Тхе яуицк\n ыумпед овер"); const OUString sTestString(OUString::fromUtf8(sUTF8String)); std::unique_ptr pLayout = pVirDev->ImplLayout(sTestString, 0, sTestString.getLength(), Point(0, 0), 0, {}, {}, SalLayoutFlags::GlyphItemsOnly); SalLayoutGlyphs aGlyphs = pLayout->GetGlyphs(); SalLayoutGlyphsImpl* pGlyphsImpl = aGlyphs.Impl(1); vcl::text::ImplLayoutArgs aArgs(sTestString, 0, sTestString.getLength(), SalLayoutFlags::BiDiRtl, LanguageTag(LANGUAGE_RUSSIAN), nullptr); aArgs.PrepareFallback(pGlyphsImpl); int nMinRunPos(0); int nEndRunPos(0); bool bRTL(false); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(0, nMinRunPos); CPPUNIT_ASSERT_EQUAL(3, nEndRunPos); CPPUNIT_ASSERT(!bRTL); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(4, nMinRunPos); CPPUNIT_ASSERT_EQUAL(9, nEndRunPos); CPPUNIT_ASSERT(!bRTL); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(11, nMinRunPos); CPPUNIT_ASSERT_EQUAL(17, nEndRunPos); CPPUNIT_ASSERT(!bRTL); aArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL); CPPUNIT_ASSERT_EQUAL(18, nMinRunPos); CPPUNIT_ASSERT_EQUAL(22, nEndRunPos); CPPUNIT_ASSERT(!bRTL); } CPPUNIT_TEST_FIXTURE(VclTextTest, testGetStringWithCenterEllpsis) { ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); device->SetFont(vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 11))); CPPUNIT_ASSERT_EQUAL( u"a b c d ...v w x y z"_ustr, 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, DrawTextFlags::CenterEllipsis)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testGetStringWithEndEllpsis) { ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); device->SetFont(vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 11))); CPPUNIT_ASSERT_EQUAL(u"a"_ustr, device->GetEllipsisString(u"abcde. f g h i j ..."_ustr, 10, DrawTextFlags::EndEllipsis)); CPPUNIT_ASSERT_EQUAL( u"a b c d e f g h i j ..."_ustr, 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, DrawTextFlags::EndEllipsis)); CPPUNIT_ASSERT_EQUAL( u"a"_ustr, device->GetEllipsisString(u"abcde. f g h i j ..."_ustr, 1, DrawTextFlags::EndEllipsis | DrawTextFlags::Clip)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testGetStringWithNewsEllpsis) { ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); device->SetFont(vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 11))); CPPUNIT_ASSERT_EQUAL(u"a"_ustr, device->GetEllipsisString(u"abcde. f g h i j ..."_ustr, 10, DrawTextFlags::NewsEllipsis)); CPPUNIT_ASSERT_EQUAL( u"a b .... x y z"_ustr, 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, DrawTextFlags::NewsEllipsis)); CPPUNIT_ASSERT_EQUAL( u"a b .... x y z"_ustr, 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, DrawTextFlags::NewsEllipsis)); CPPUNIT_ASSERT_EQUAL( u"a b c d e f g h i j ..."_ustr, 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, DrawTextFlags::NewsEllipsis)); CPPUNIT_ASSERT_EQUAL( u"a..... x y z"_ustr, 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, DrawTextFlags::NewsEllipsis)); CPPUNIT_ASSERT_EQUAL( u"ab. cde..."_ustr, device->GetEllipsisString(u"ab. cde. x y z"_ustr, 50, DrawTextFlags::NewsEllipsis)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testGetTextBreak_invalid_index) { ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); device->SetFont(vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 11))); const OUString sTestStr(u"textline_ text_"_ustr); const auto nLen = sTestStr.getLength(); const auto nTextWidth = device->GetTextWidth(u"text"_ustr); const auto nInvalidIndex = sTestStr.getLength() + 2; CPPUNIT_ASSERT_EQUAL(static_cast(-1), device->GetTextBreak(sTestStr, nTextWidth, nInvalidIndex, nLen)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testGetTextBreak) { ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); device->SetFont(vcl::Font(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 11))); const OUString sTestStr(u"textline_ text_"_ustr); const auto nLen = sTestStr.getLength(); const auto nTextWidth = device->GetTextWidth(u"text"_ustr); CPPUNIT_ASSERT_EQUAL(static_cast(4), device->GetTextBreak(sTestStr, nTextWidth, 0, nLen)); CPPUNIT_ASSERT_EQUAL(static_cast(7), device->GetTextBreak(sTestStr, nTextWidth, 3, nLen)); CPPUNIT_ASSERT_EQUAL(static_cast(9), device->GetTextBreak(sTestStr, nTextWidth, 6, nLen)); CPPUNIT_ASSERT_EQUAL(static_cast(12), device->GetTextBreak(sTestStr, nTextWidth, 8, nLen)); CPPUNIT_ASSERT_EQUAL(static_cast(14), device->GetTextBreak(sTestStr, nTextWidth, 11, nLen)); CPPUNIT_ASSERT_EQUAL(static_cast(-1), device->GetTextBreak(sTestStr, nTextWidth, 13, nLen)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testGetSingleLineTextRect) { ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11))); CPPUNIT_ASSERT_EQUAL( tools::Rectangle(Point(), Size(75, 12)), device->GetTextRect(tools::Rectangle(Point(), Point(100, 100)), u"This is test text"_ustr)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testGetSingleLineTextRectWithEndEllipsis) { ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11))); CPPUNIT_ASSERT_EQUAL( tools::Rectangle(Point(), Size(52, 12)), device->GetTextRect(tools::Rectangle(Point(), Point(50, 50)), u"This is test text"_ustr, DrawTextFlags::WordBreak | DrawTextFlags::EndEllipsis)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testGetRightBottomAlignedSingleLineTextRect) { ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11))); CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(926, 989), Size(75, 12)), device->GetTextRect(tools::Rectangle(Point(), Point(1000, 1000)), u"This is test text"_ustr, DrawTextFlags::Right | DrawTextFlags::Bottom)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testGetRotatedSingleLineTextRect) { ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11))); vcl::Font aFont(device->GetFont()); aFont.SetOrientation(45_deg10); device->SetFont(aFont); CPPUNIT_ASSERT_EQUAL( tools::Rectangle(Point(0, -3), Size(75, 18)), device->GetTextRect(tools::Rectangle(Point(), Point(100, 100)), u"This is test text"_ustr)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testGetMultiLineTextRect) { ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11))); CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(), Size(75, 12)), device->GetTextRect(tools::Rectangle(Point(), Point(100, 100)), u"This is test text"_ustr, DrawTextFlags::WordBreak | DrawTextFlags::MultiLine)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testGetMultiLineTextRectWithEndEllipsis) { ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11))); CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(), Size(52, 48)), device->GetTextRect(tools::Rectangle(Point(), Point(50, 50)), u"This is test text xyzabc123abcdefghijk"_ustr, DrawTextFlags::WordBreak | DrawTextFlags::EndEllipsis | DrawTextFlags::MultiLine)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testGetRightBottomAlignedMultiLineTextRect) { ScopedVclPtr device = VclPtr::Create(DeviceFormat::WITHOUT_ALPHA); device->SetOutputSizePixel(Size(1000, 1000)); device->SetFont(vcl::Font(u"Liberation Sans"_ustr, Size(0, 11))); CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(926, 989), Size(75, 12)), device->GetTextRect(tools::Rectangle(Point(), Point(1000, 1000)), u"This is test text"_ustr, DrawTextFlags::Right | DrawTextFlags::Bottom | DrawTextFlags::MultiLine)); } CPPUNIT_TEST_FIXTURE(VclTextTest, testPartialTextArraySizeMatch) { OUString aWater = u"Water"_ustr; vcl::Font aFont(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 2048)); ScopedVclPtrInstance pOutDev; pOutDev->SetFont(aFont); // Absolute character widths for the complete array. KernArray aCompleteWidths; auto nCompleteWidth = pOutDev->GetTextArray(aWater, &aCompleteWidths).nWidth; CPPUNIT_ASSERT_EQUAL(size_t{ 5 }, aCompleteWidths.size()); // Accumulate partial widths double nPartialWidth = 0.0; sal_Int32 nPrevWidth = 0; for (sal_Int32 i = 0; i < 5; ++i) { KernArray aFragmentWidths; auto nFragmentWidth = pOutDev ->GetPartialTextArray(aWater, &aFragmentWidths, /*nIndex*/ 0, /*nLen*/ 5, /*nPartIndex*/ i, /*nPartLen*/ 1) .nWidth; nPartialWidth += nFragmentWidth; CPPUNIT_ASSERT_EQUAL(size_t{ 1 }, aFragmentWidths.size()); CPPUNIT_ASSERT_EQUAL(aCompleteWidths[i] - nPrevWidth, aFragmentWidths[0]); nPrevWidth = aCompleteWidths[i]; } CPPUNIT_ASSERT_DOUBLES_EQUAL(nCompleteWidth, nPartialWidth, /*delta*/ 0.01); } CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */