/* -*- 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/.
*/
class VclTextTest : public test::BootstrapFixture
{ // if enabled - check the result images with: // "xdg-open ./workdir/CppunitTest/vcl_text_test.test.core/" static constexpr constbool mbExportBitmap = false;
// Avoid issues when colorized antialiasing generates slightly tinted rather than truly black // pixels: staticbool 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<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);
// 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);
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);
// Check that we did do the rotation...
CPPUNIT_ASSERT_EQUAL(aRectRot.GetWidth(), aRect.GetHeight());
CPPUNIT_ASSERT_EQUAL(aRectRot.GetHeight(), aRect.GetWidth());
}
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);
// 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());
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));
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_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_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));
}
// Absolute character widths for the complete array.
KernArray aCompleteWidths; auto nCompleteWidth = pOutDev->GetTextArray(aWater, &aCompleteWidths).nWidth;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.