/* -*- 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/.
*/
CPPUNIT_ASSERT_EQUAL(aRefCharWidths, aCharWidths); // 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(aOneTwoThree));
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, aOneTwoThree);
CPPUNIT_ASSERT_EQUAL(tools::Long(145), aBoundRect.Left());
CPPUNIT_ASSERT_EQUAL(tools::Long(212), aBoundRect.Top());
CPPUNIT_ASSERT_EQUAL(tools::Long(12294), aBoundRect.GetWidth());
CPPUNIT_ASSERT_EQUAL(tools::Long(2279), aBoundRect.getOpenHeight());
// Check that we did do the rotation...
CPPUNIT_ASSERT_EQUAL( aRectRot.GetWidth(), aRect.GetHeight() );
CPPUNIT_ASSERT_EQUAL( aRectRot.GetHeight(), aRect.GetWidth() ); #endif
}
CPPUNIT_TEST_FIXTURE(VclComplexTextTest, testTdf95650)
{ static constexpr OUStringLiteral aTxt =
u"\u0131\u0302\u0504\u4E44\u3031\u3030\u3531\u2D30" "\u3037\u0706\u0908\u0B0A\u0D0C\u0F0E\u072E\u100A" "\u0D11\u1312\u0105\u020A\u0512\u1403\u030C\u1528" "\u2931\u632E\u7074\u0D20\u0E0A\u100A\uF00D\u0D20" "\u030A\u0C0B\u20E0\u0A0D";
ScopedVclPtrInstance<VirtualDevice> pOutDev; // Check that the following executes without failing assertion
pOutDev->ImplLayout(aTxt, 9, 1, Point(), 0, {}, {}, SalLayoutFlags::BiDiRtl);
}
// Check that caching using SalLayoutGlyphs gives same results as without caching. // This should preferably use fonts that come with LO.
CPPUNIT_TEST_FIXTURE(VclComplexTextTest, testCaching)
{ // Just something basic, no font fallback.
testCachedGlyphs( u"test"_ustr, u"Dejavu Sans"_ustr ); // This font does not have latin characters, will need fallback.
testCachedGlyphs( u"test"_ustr, u"Noto Kufi Arabic"_ustr ); // see tdf#103492
testCachedGlyphs( u"يوسف My name is"_ustr, u"Liberation Sans"_ustr);
}
staticvoid testCachedGlyphsSubstring( const OUString& aText, const OUString& aFontName, bool rtl )
{ const std::string prefix( OUString("Font: " + aFontName + ", text: '" + aText + "'").toUtf8() );
ScopedVclPtrInstance<VirtualDevice> pOutputDevice; // BiDiStrong is needed, otherwise SalLayoutGlyphsImpl::cloneCharRange() will not do anything.
vcl::text::ComplexTextLayoutFlags layoutFlags = vcl::text::ComplexTextLayoutFlags::BiDiStrong; if(rtl)
layoutFlags |= vcl::text::ComplexTextLayoutFlags::BiDiRtl;
pOutputDevice->SetLayoutMode( layoutFlags );
vcl::Font aFont( aFontName, Size(0, 12));
pOutputDevice->SetFont( aFont );
SalLayoutGlyphsCache::self()->clear();
std::shared_ptr<const vcl::text::TextLayoutCache> layoutCache = OutputDevice::CreateTextLayoutCache(aText); // Get the glyphs for the entire text once, to ensure the cache can built subsets from it.
pOutputDevice->ImplLayout( aText, 0, aText.getLength(), Point(0, 0), 0, {}, {}, SalLayoutFlags::GlyphItemsOnly,
layoutCache.get()); // Now check for all subsets. Some of them possibly do not make sense in practice, but the code // should cope with them. for( sal_Int32 len = 1; len <= aText.getLength(); ++len ) for( sal_Int32 pos = 0; pos < aText.getLength() - len; ++pos )
{
std::string message = prefix + " (" + std::to_string(pos) + "/" + std::to_string(len) + ")";
std::unique_ptr<SalLayout> pLayout1 = pOutputDevice->ImplLayout(
aText, pos, len, Point(0, 0), 0, {}, {}, SalLayoutFlags::GlyphItemsOnly, layoutCache.get());
SalLayoutGlyphs aGlyphs1 = pLayout1->GetGlyphs(); const SalLayoutGlyphs* aGlyphs2 = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(
pOutputDevice, aText, pos, len, 0, layoutCache.get());
CPPUNIT_ASSERT_MESSAGE(message, aGlyphs2 != nullptr);
checkCompareGlyphs(aGlyphs1, *aGlyphs2, message);
}
}
// Check that SalLayoutGlyphsCache works properly when it builds a subset // of glyphs using SalLayoutGlyphsImpl::cloneCharRange(). // This should preferably use fonts that come with LO.
CPPUNIT_TEST_FIXTURE(VclComplexTextTest, testCachingSubstring)
{ // Just something basic.
testCachedGlyphsSubstring( u"test"_ustr, u"Dejavu Sans"_ustr, false ); // And complex arabic text, taken from tdf104649.docx .
OUString text(u"فصل (پاره 2): درخواست حاجت از ديگران و برآوردن حاجت ديگران 90"_ustr);
testCachedGlyphsSubstring( text, u"Dejavu Sans"_ustr, true ); // The text is RTL, but Writer will sometimes try to lay it out as LTR, for whatever reason // (tdf#149264)./ So make sure that gets handled properly too (SalLayoutGlyphsCache should // not use glyph subsets in that case).
testCachedGlyphsSubstring( text, u"Dejavu Sans"_ustr, false );
}
CPPUNIT_TEST_FIXTURE(VclComplexTextTest, testCaret)
{ #if HAVE_MORE_FONTS // Test caret placement in fonts *without* ligature carets in GDEF table.
// Set font size to its UPEM to decrease rounding issues
vcl::Font aFont(u"DejaVu Sans"_ustr, u"Book"_ustr, Size(0, 2048));
// 1) Regular DX array, the ligature width is given to the first components // and the next ones are all zero width.
nRefTextWidth = 3611;
aRefCharWidths = { 1168, 1168, 1819, 2389, 3611, 3611 };
nTextWidth = basegfx::fround<tools::Long>(
pOutDev->GetTextArray(aText, &aCharWidths, 0, -1, /*bCaret*/ false).nWidth);
CPPUNIT_ASSERT_EQUAL(aRefCharWidths, aCharWidths);
CPPUNIT_ASSERT_EQUAL(nRefTextWidth, nTextWidth);
CPPUNIT_ASSERT_EQUAL(nTextWidth, tools::Long(aCharWidths.back()));
// 1) Regular DX array, the ligature width is given to the first components // and the next ones are all zero width.
nRefTextWidth = 8493;
aRefCharWidths = { 1290, 1290, 1941, 3231, 3231, 3882, 5862, 5862, 5862, 6513, 8493, 8493, 8493 };
nTextWidth = basegfx::fround<tools::Long>(
pOutDev->GetTextArray(aText, &aCharWidths, 0, -1, /*bCaret*/ false).nWidth);
CPPUNIT_ASSERT_EQUAL(aRefCharWidths, aCharWidths);
CPPUNIT_ASSERT_EQUAL(nRefTextWidth, nTextWidth);
CPPUNIT_ASSERT_EQUAL(nTextWidth, tools::Long(aCharWidths.back()));
// A. RTL text // Set font size to its UPEM to decrease rounding issues
aFont = vcl::Font(u"Noto Sans Arabic"_ustr, u"Regular"_ustr, Size(0, 1000));
pOutDev->SetFont(aFont);
aText = u"لا بلا"_ustr;
// 1) Regular DX array, the ligature width is given to the first components // and the next ones are all zero width.
nRefTextWidth = 1710;
aRefCharWidths= { 582, 582, 842, 1111, 1710, 1710 };
nTextWidth = basegfx::fround<tools::Long>(
pOutDev->GetTextArray(aText, &aCharWidths, 0, -1, /*bCaret*/ false).nWidth);
CPPUNIT_ASSERT_EQUAL(aRefCharWidths, aCharWidths);
CPPUNIT_ASSERT_EQUAL(nRefTextWidth, nTextWidth);
CPPUNIT_ASSERT_EQUAL(nTextWidth, tools::Long(aCharWidths.back()));
// B. LTR text // Set font size to its UPEM to decrease rounding issues
aFont = vcl::Font(u"Amiri"_ustr, u"Regular"_ustr, Size(0, 1000));
pOutDev->SetFont(aFont);
aText = u"fi ffi fl ffl fb ffb"_ustr;
// 1) Regular DX array, the ligature width is given to the first components // and the next ones are all zero width.
nRefTextWidth = 5996;
aRefCharWidths = { 519, 519, 811, 1606, 1606, 1606, 1898, 2439, 2439, 2731,
3544, 3544, 3544, 3836, 4634, 4634, 4926, 5996, 5996, 5996 };
nTextWidth = basegfx::fround<tools::Long>(
pOutDev->GetTextArray(aText, &aCharWidths, 0, -1, /*bCaret*/ false).nWidth);
CPPUNIT_ASSERT_EQUAL(aRefCharWidths, aCharWidths);
CPPUNIT_ASSERT_EQUAL(nRefTextWidth, nTextWidth);
CPPUNIT_ASSERT_EQUAL(nTextWidth, tools::Long(aCharWidths.back()));
// Simulate Kashida insertion using Kashida array and extending text array // to have room for Kashida.
std::vector<sal_Bool> aKashidaArray{ false, false, false, true, false, false }; auto nKashida = 4000;
// get an compare the default text array
KernArray aCharWidths; auto nTextWidth
= basegfx::fround<tools::Long>(pOutDev->GetTextArray(u"ع a ع"_ustr, &aCharWidths).nWidth);
// Text width should always be equal to the width of the last glyph in the // kern array. // Without the fix this fails with: // - Expected: 158 // - Actual : 118
CPPUNIT_ASSERT_EQUAL(tools::Long(aCharWidths.back()), nTextWidth); #endif
}
#if !defined _WIN32 // TODO: Fails on jenkins but passes locally // Add an emoji font so that we are sure a font will be found for the // emoji. The font is subset and supports only . bool bAdded = addFont(pOutDev, u"tdf153440.ttf", u"Noto Emoji");
CPPUNIT_ASSERT_EQUAL(true, bAdded); #endif
int nStart = 0;
basegfx::B2DPoint aPos; const GlyphItem* pGlyphItem; while (pLayout->GetNextGlyph(&pGlyphItem, aPos, nStart))
{ // Assert glyph ID is not 0, if it is 0 then font fallback didn’t // happen.
CPPUNIT_ASSERT(pGlyphItem->glyphId());
// Assert that we are indeed doing RTL layout for RTL text since // the bug does not happen for LTR text.
CPPUNIT_ASSERT_EQUAL(bRTL, pGlyphItem->IsRTLGlyph());
}
} #endif
}
ScopedVclPtrInstance<VirtualDevice> pOutDev; // note you can only run this once and it was designed for tdf#107718 bool bAdded = addFont(pOutDev, u"tdf107718.otf", u"Source Han Sans");
CPPUNIT_ASSERT_EQUAL(true, bAdded);
vcl::Font aFont(u"Source Han Sans"_ustr, u"Regular"_ustr, Size(0, 72));
pOutDev->SetFont( aFont );
// text advance width and line height
CPPUNIT_ASSERT_EQUAL(nRefTextWidth, pOutDev->GetTextWidth(aTestScript));
CPPUNIT_ASSERT_EQUAL(tools::Long(105), pOutDev->GetTextHeight()); #endif #endif
}
CPPUNIT_TEST_FIXTURE(VclComplexTextTest, testTdf107718)
{ #if HAVE_MORE_FONTS #if !defined _WIN32 // TODO: Fails on jenkins but passes locally
vcl::Font aFont(u"Source Han Sans"_ustr, u"Regular"_ustr, Size(0, 72));
int nStart = 0;
basegfx::B2DPoint aPos; const GlyphItem* pGlyphItem; while (pLayout->GetNextGlyph(&pGlyphItem, aPos, nStart))
{ // Check that we found a font for all characters, a zero glyph ID // means no font was found so the rest of the test would be // meaningless.
CPPUNIT_ASSERT(pGlyphItem->glyphId());
// Assert that we are indeed doing vertical layout for vertical // font since the bug does not happen for horizontal text.
CPPUNIT_ASSERT_EQUAL(bVertical, pGlyphItem->IsVertical());
// For the second glyph, assert that it is a composition of characters 1 to 4 // Without the fix this fails with: // - Expected: 4 // - Actual : 1 if (nStart == 2)
{
CPPUNIT_ASSERT_EQUAL(1, pGlyphItem->charPos());
CPPUNIT_ASSERT_EQUAL(4, pGlyphItem->charCount());
}
}
// Assert there are only three glyphs // Without the fix this fails with: // - Expected: 3 // - Actual : 5
CPPUNIT_ASSERT_EQUAL(3, nStart);
} #endif #endif
}
// If font fallback happened, then the returned layout must be a // MultiSalLayout instance. auto pMultiLayout = dynamic_cast<MultiSalLayout*>(pLayout.get());
CPPUNIT_ASSERT(pMultiLayout);
auto pFallbackRuns = pMultiLayout->GetFallbackRuns();
CPPUNIT_ASSERT(!pFallbackRuns->IsEmpty());
bool bRTL; int nCharPos = -1;
std::vector<sal_Int32> aFallbacks; while (pFallbackRuns->GetNextPos(&nCharPos, &bRTL))
aFallbacks.push_back(nCharPos);
// Assert that U+202F is included in the fallback run. // Without the fix this fails with: // - Expected: { 1, 2 } // - Actual : { 2 }
std::vector<sal_Int32> aExpctedFallbacks = { 1, 2 };
CPPUNIT_ASSERT_EQUAL(aExpctedFallbacks, aExpctedFallbacks); #endif
}
// Absolute character widths for the complete array.
KernArray aCompleteWidths; auto nCompleteWidth = pOutDev->GetTextArray(aStr, &aCompleteWidths).nWidth;
// Absolute character widths for the complete array.
KernArray aCompleteWidths; auto nCompleteWidth = pOutDev->GetTextArray(aStr, &aCompleteWidths).nWidth;
// Characteristic case with kashida position validation auto pLayout1 = pOutDev->ImplLayout(aStr, 0, aStr.getLength(), Point(), 0, {}, {},
SalLayoutFlags::GlyphItemsOnly);
CPPUNIT_ASSERT(pLayout1->HasFontKashidaPositions());
SalLayoutGlyphs aGlyphs1 = pLayout1->GetGlyphs();
std::vector<bool> aFoundPositions1; for (constauto& stGlyph : *aGlyphs1.Impl(0))
{
aFoundPositions1.push_back(stGlyph.IsSafeToInsertKashida());
}
// Case with kashida position validation disabled auto pLayout2 = pOutDev->ImplLayout(aStr, 0, aStr.getLength(), Point(), 0, {}, {},
SalLayoutFlags::GlyphItemsOnly
| SalLayoutFlags::DisableKashidaValidation);
CPPUNIT_ASSERT(!pLayout2->HasFontKashidaPositions());
SalLayoutGlyphs aGlyphs2 = pLayout2->GetGlyphs();
std::vector<bool> aFoundPositions2; for (constauto& stGlyph : *aGlyphs2.Impl(0))
{
aFoundPositions2.push_back(stGlyph.IsSafeToInsertKashida());
}
// With position validation disabled, all positions must be marked as valid
CPPUNIT_ASSERT_EQUAL(size_t(5), aFoundPositions2.size());
CPPUNIT_ASSERT(aFoundPositions2.at(0));
CPPUNIT_ASSERT(aFoundPositions2.at(1));
CPPUNIT_ASSERT(aFoundPositions2.at(2));
CPPUNIT_ASSERT(aFoundPositions2.at(3));
CPPUNIT_ASSERT(aFoundPositions2.at(4));
}
auto aText = u"ab(ح)cd(د)ef"_ustr; auto pLayout = pOutDev->ImplLayout(aText, /*nIndex*/ 0, /*nLen*/ aText.getLength());
// Fallback must have happened for this test to be meaningful auto pMultiLayout = dynamic_cast<MultiSalLayout*>(pLayout.get());
CPPUNIT_ASSERT(pMultiLayout);
std::vector<int> aCharIndices;
const GlyphItem* pGlyph = nullptr;
basegfx::B2DPoint stPos; int nCurrPos = 0; while (pLayout->GetNextGlyph(&pGlyph, stPos, nCurrPos))
{
aCharIndices.push_back(pGlyph->charPos());
}
// tdf#165510 caused failure to remove dropped glyphs from the base layout. // Without the fix, the base layout will contain an errant copy of char 8. // { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 3, 8 };
std::vector<int> aRefCharIndices{ 0, 1, 2, 4, 5, 6, 7, 9, 10, 11, 3, 8 };
CPPUNIT_ASSERT_EQUAL(aRefCharIndices.size(), aCharIndices.size()); for (size_t i = 0; i < aRefCharIndices.size(); ++i)
{
CPPUNIT_ASSERT_EQUAL(aRefCharIndices.at(i), aCharIndices.at(i));
}
}
// Fallback must have happened for this test to be meaningful auto pMultiLayout = dynamic_cast<MultiSalLayout*>(pLayout.get());
CPPUNIT_ASSERT(pMultiLayout);
std::vector<double> aCharX;
const GlyphItem* pGlyph = nullptr;
basegfx::B2DPoint stPos; int nCurrPos = 0; while (pLayout->GetNextGlyph(&pGlyph, stPos, nCurrPos))
{
aCharX.push_back(stPos.getX());
}
auto aText = u"\u05DC\u0020\u05E0\u05EA\u05D9\u05D1\u05D9\u05BE\u200F\u05E4"_ustr; auto pLayout = pOutDev->ImplLayout(aText, /*nIndex*/ 0, /*nLen*/ aText.getLength());
// Fallback must have happened for this test to be meaningful auto pMultiLayout = dynamic_cast<MultiSalLayout*>(pLayout.get());
CPPUNIT_ASSERT(pMultiLayout);
std::vector<int> aCharIndices;
const GlyphItem* pGlyph = nullptr;
basegfx::B2DPoint stPos; int nCurrPos = 0; while (pLayout->GetNextGlyph(&pGlyph, stPos, nCurrPos))
{
aCharIndices.push_back(pGlyph->charPos());
}
// tdf#163761 caused failure to remove dropped glyphs from the base layout. // Without the fix, the base layout will contain an errant copy of char 0. // { 1, 0, 9, 7, 6, 5, 4, 3, 2, 0 }
std::vector<int> aRefCharIndices{ 1, 9, 7, 6, 5, 4, 3, 2, 0 };
CPPUNIT_ASSERT_EQUAL(aRefCharIndices.size(), aCharIndices.size()); for (size_t i = 0; i < aRefCharIndices.size(); ++i)
{
CPPUNIT_ASSERT_EQUAL(aRefCharIndices.at(i), aCharIndices.at(i));
}
}
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 und die Messung sind noch experimentell.