Files
libreoffice/vcl/qa/cppunit/text.cxx

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1063 lines
41 KiB
C++
Raw Normal View History

/* -*- 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 <osl/file.hxx>
#include <osl/process.h>
#include <test/bootstrapfixture.hxx>
#include <sal/log.hxx>
#include <tools/stream.hxx>
#include <vcl/BitmapReadAccess.hxx>
#include <comphelper/errcode.hxx>
#include <vcl/graphicfilter.hxx>
#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>
#include <vcl/virdev.hxx>
#include <ImplLayoutArgs.hxx>
#include <TextLayoutCache.hxx>
#include <salgdi.hxx>
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<VirtualDevice>& 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 << "}";
}
Fix VclTextTest with colorized antialiasing The Flathub build for <https://github.com/flathub/org.libreoffice.LibreOffice/commit/d5ac1d2bd680970ba992c8c6836683262f067592> "Merge pull request #206 from flathub/lo-7.4.4" on 2023-01-12 at <https://buildbot.flathub.org/#/builders/6/builds/16778> consistently failed for both aarch64 and x86_64 builds with > text.cxx:260:Assertion > Test name: VclTextTest::testSimpleText > double equality assertion failed > - Expected: 28 > - Actual : -1 > - Delta : 2 during CppunitTest_vcl_text. The reason (as seen with a temporary build setting mbExportBitmap = true and looking at the resulting workdir/CppunitTest/vcl_text.test.core/simple-text-36-50pct.png) is that the stem of the "L" doesn't contain any COL_BLACK pixels, so getCharacterLeftSideHeight returns early with -1. The stem is drawn as three vertical lines of pixels, a yellow (RGB 255/195/111), a black-ish (RGB 23/0/0), and a blue (RGB 31/155/199) one. In comparison, when running the same test on Fedora 37, the three vertical lines use gray-scale instead of colors, RGB 195/195/195, then true black (RGB 0/0/0), and RGB 115/115/115. An earlier test build of <https://github.com/flathub/org.libreoffice.LibreOffice/pull/206> "Update to LibreOffice 7.4.4" for the same libreoffice-7.4.4.2 tag on 2023-01-02 at <https://buildbot.flathub.org/#/builders/6/builds/14712> succeeded, so I assume that it must be some change in the meantime to the underlying org.fedoraproject.sdk//22.08 that started to cause colorized antialiasing here. Change-Id: I7059268eabcfe8e501d0be4f38746707def7bb35 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145411 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2023-01-12 16:42:39 +01:00
// 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();
Fix VclTextTest with colorized antialiasing The Flathub build for <https://github.com/flathub/org.libreoffice.LibreOffice/commit/d5ac1d2bd680970ba992c8c6836683262f067592> "Merge pull request #206 from flathub/lo-7.4.4" on 2023-01-12 at <https://buildbot.flathub.org/#/builders/6/builds/16778> consistently failed for both aarch64 and x86_64 builds with > text.cxx:260:Assertion > Test name: VclTextTest::testSimpleText > double equality assertion failed > - Expected: 28 > - Actual : -1 > - Delta : 2 during CppunitTest_vcl_text. The reason (as seen with a temporary build setting mbExportBitmap = true and looking at the resulting workdir/CppunitTest/vcl_text.test.core/simple-text-36-50pct.png) is that the stem of the "L" doesn't contain any COL_BLACK pixels, so getCharacterLeftSideHeight returns early with -1. The stem is drawn as three vertical lines of pixels, a yellow (RGB 255/195/111), a black-ish (RGB 23/0/0), and a blue (RGB 31/155/199) one. In comparison, when running the same test on Fedora 37, the three vertical lines use gray-scale instead of colors, RGB 195/195/195, then true black (RGB 0/0/0), and RGB 115/115/115. An earlier test build of <https://github.com/flathub/org.libreoffice.LibreOffice/pull/206> "Update to LibreOffice 7.4.4" for the same libreoffice-7.4.4.2 tag on 2023-01-02 at <https://buildbot.flathub.org/#/builders/6/builds/14712> succeeded, so I assume that it must be some change in the meantime to the underlying org.fedoraproject.sdk//22.08 that started to cause colorized antialiasing here. Change-Id: I7059268eabcfe8e501d0be4f38746707def7bb35 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145411 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2023-01-12 16:42:39 +01:00
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();
Fix VclTextTest with colorized antialiasing The Flathub build for <https://github.com/flathub/org.libreoffice.LibreOffice/commit/d5ac1d2bd680970ba992c8c6836683262f067592> "Merge pull request #206 from flathub/lo-7.4.4" on 2023-01-12 at <https://buildbot.flathub.org/#/builders/6/builds/16778> consistently failed for both aarch64 and x86_64 builds with > text.cxx:260:Assertion > Test name: VclTextTest::testSimpleText > double equality assertion failed > - Expected: 28 > - Actual : -1 > - Delta : 2 during CppunitTest_vcl_text. The reason (as seen with a temporary build setting mbExportBitmap = true and looking at the resulting workdir/CppunitTest/vcl_text.test.core/simple-text-36-50pct.png) is that the stem of the "L" doesn't contain any COL_BLACK pixels, so getCharacterLeftSideHeight returns early with -1. The stem is drawn as three vertical lines of pixels, a yellow (RGB 255/195/111), a black-ish (RGB 23/0/0), and a blue (RGB 31/155/199) one. In comparison, when running the same test on Fedora 37, the three vertical lines use gray-scale instead of colors, RGB 195/195/195, then true black (RGB 0/0/0), and RGB 115/115/115. An earlier test build of <https://github.com/flathub/org.libreoffice.LibreOffice/pull/206> "Update to LibreOffice 7.4.4" for the same libreoffice-7.4.4.2 tag on 2023-01-02 at <https://buildbot.flathub.org/#/builders/6/builds/14712> succeeded, so I assume that it must be some change in the meantime to the underlying org.fedoraproject.sdk//22.08 that started to cause colorized antialiasing here. Change-Id: I7059268eabcfe8e501d0be4f38746707def7bb35 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145411 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2023-01-12 16:42:39 +01:00
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();
Fix VclTextTest with colorized antialiasing The Flathub build for <https://github.com/flathub/org.libreoffice.LibreOffice/commit/d5ac1d2bd680970ba992c8c6836683262f067592> "Merge pull request #206 from flathub/lo-7.4.4" on 2023-01-12 at <https://buildbot.flathub.org/#/builders/6/builds/16778> consistently failed for both aarch64 and x86_64 builds with > text.cxx:260:Assertion > Test name: VclTextTest::testSimpleText > double equality assertion failed > - Expected: 28 > - Actual : -1 > - Delta : 2 during CppunitTest_vcl_text. The reason (as seen with a temporary build setting mbExportBitmap = true and looking at the resulting workdir/CppunitTest/vcl_text.test.core/simple-text-36-50pct.png) is that the stem of the "L" doesn't contain any COL_BLACK pixels, so getCharacterLeftSideHeight returns early with -1. The stem is drawn as three vertical lines of pixels, a yellow (RGB 255/195/111), a black-ish (RGB 23/0/0), and a blue (RGB 31/155/199) one. In comparison, when running the same test on Fedora 37, the three vertical lines use gray-scale instead of colors, RGB 195/195/195, then true black (RGB 0/0/0), and RGB 115/115/115. An earlier test build of <https://github.com/flathub/org.libreoffice.LibreOffice/pull/206> "Update to LibreOffice 7.4.4" for the same libreoffice-7.4.4.2 tag on 2023-01-02 at <https://buildbot.flathub.org/#/builders/6/builds/14712> succeeded, so I assume that it must be some change in the meantime to the underlying org.fedoraproject.sdk//22.08 that started to cause colorized antialiasing here. Change-Id: I7059268eabcfe8e501d0be4f38746707def7bb35 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145411 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2023-01-12 16:42:39 +01:00
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<VirtualDevice> device = VclPtr<VirtualDevice>::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<VirtualDevice> 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<tools::Long>(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<VirtualDevice> 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<tools::Long>(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<VirtualDevice> pVirDev;
pVirDev->SetFont(aFont);
static constexpr OStringLiteral sUTF8String(u8"Тхе яуицк\n ыумпед овер");
const OUString sTestString(OUString::fromUtf8(sUTF8String));
std::unique_ptr<SalLayout> pLayout
tdf#104921: Cleanup Kashida insertion logic Communicate Kashida insertion positions in an explicit way. Rest of LibreOffice communicate adjustments to character widths (e.g. for justification or spacing) using so-called DX array. DX array is an array of absolute character positions (e.g. DX[n] is the position after character n from the start of the lines, and its widths is DX[n] - DX[n-1]). This DX array is modified also when Kashidas are inserted after a given character for Arabic justification, by expanding its width. VCL would use this to know where to insert the Kashidas and how many ones. But because DX array is used for both widths adjustments and kashida insertion, this turns out to be a source of bugs since VCL has tosecond guess the DX array to find which is pure width adjustment and which also involves Kashida insertion, and the heuristics it uses are fragile. This change adds a second array of booleans that records where Kashida is inserted and communicates it all the way from where Kashida insertion is decoded in Writer and down to VCL layout. This change passes the Kashida array only when it seems necessary (e.g. during drawing but not when measuring text since the DX array is enough in this case). Hopefully no places where Kashida insertion needs to be passed down were missed. A couple of glyph and layout flags that were used for old heuristics and no longer needed and are removed. This also fixes: tdf#87731 tdf#106309 tdf#108604 tdf#112849 tdf#114257 tdf#127176 tdf#145647 tdf#146199 Change-Id: I4ed0850ef2fdc3e9143341afac649e7e7d463c39 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/138068 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caolanm@redhat.com>
2022-08-08 22:08:37 +02:00
= 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<VirtualDevice> device = VclPtr<VirtualDevice>::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<VirtualDevice> device = VclPtr<VirtualDevice>::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<VirtualDevice> device = VclPtr<VirtualDevice>::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<VirtualDevice> device = VclPtr<VirtualDevice>::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<sal_Int32>(-1),
device->GetTextBreak(sTestStr, nTextWidth, nInvalidIndex, nLen));
}
CPPUNIT_TEST_FIXTURE(VclTextTest, testGetTextBreak)
{
ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::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<sal_Int32>(4),
device->GetTextBreak(sTestStr, nTextWidth, 0, nLen));
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(7),
device->GetTextBreak(sTestStr, nTextWidth, 3, nLen));
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(9),
device->GetTextBreak(sTestStr, nTextWidth, 6, nLen));
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(12),
device->GetTextBreak(sTestStr, nTextWidth, 8, nLen));
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(14),
device->GetTextBreak(sTestStr, nTextWidth, 11, nLen));
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-1),
device->GetTextBreak(sTestStr, nTextWidth, 13, nLen));
}
CPPUNIT_TEST_FIXTURE(VclTextTest, testGetSingleLineTextRect)
{
ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::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<VirtualDevice> device = VclPtr<VirtualDevice>::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<VirtualDevice> device = VclPtr<VirtualDevice>::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<VirtualDevice> device = VclPtr<VirtualDevice>::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<VirtualDevice> device = VclPtr<VirtualDevice>::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<VirtualDevice> device = VclPtr<VirtualDevice>::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<VirtualDevice> device = VclPtr<VirtualDevice>::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<VirtualDevice> 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: */