include/vcl/outdev.hxx | 8 include/vcl/vcllayout.hxx | 6 vcl/Library_vcl.mk | 1 vcl/inc/ImplLayoutArgs.hxx | 69 +++++++ vcl/inc/salgdi.hxx | 1 vcl/inc/sallayout.hxx | 62 ------ vcl/qa/cppunit/complextext.cxx | 5 vcl/qa/cppunit/text.cxx | 142 +++++++++++++++ vcl/source/gdi/CommonSalLayout.cxx | 7 vcl/source/gdi/sallayout.cxx | 278 ------------------------------ vcl/source/outdev/font.cxx | 5 vcl/source/outdev/text.cxx | 9 vcl/source/text/ImplLayoutArgs.cxx | 339 +++++++++++++++++++++++++++++++++++++ 13 files changed, 585 insertions(+), 347 deletions(-)
New commits: commit 8381242c2e0bfda1b37e7e82525926c0497c81d4 Author: Chris Sherlock <chris.sherloc...@gmail.com> AuthorDate: Sun Mar 7 13:59:04 2021 +1100 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Mon Sep 6 12:52:14 2021 +0200 vcl: move ImplLayoutArgs to own header and source files Add unit tests for ImplLayoutArgs. Also add to the vcl::text namespace. Change-Id: I9fa0943548be8ca17ea3dac104e9cb609337a70e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121209 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx index 26376fedc9e2..6bf789793af6 100644 --- a/include/vcl/outdev.hxx +++ b/include/vcl/outdev.hxx @@ -89,7 +89,6 @@ class LineInfo; class AlphaMask; class FontCharMap; class SalLayout; -class ImplLayoutArgs; class VirtualDevice; struct SalTwoRect; class Printer; @@ -109,6 +108,7 @@ namespace vcl } namespace text { + class ImplLayoutArgs; class TextLayoutCache; } } @@ -1226,18 +1226,18 @@ public: const tools::Long* pLogicDXArray=nullptr, SalLayoutFlags flags = SalLayoutFlags::NONE, vcl::text::TextLayoutCache const* = nullptr, const SalLayoutGlyphs* pGlyphs = nullptr) const; - SAL_DLLPRIVATE ImplLayoutArgs ImplPrepareLayoutArgs( OUString&, const sal_Int32 nIndex, const sal_Int32 nLen, + SAL_DLLPRIVATE vcl::text::ImplLayoutArgs ImplPrepareLayoutArgs( OUString&, const sal_Int32 nIndex, const sal_Int32 nLen, DeviceCoordinate nPixelWidth, const DeviceCoordinate* pPixelDXArray, SalLayoutFlags flags = SalLayoutFlags::NONE, vcl::text::TextLayoutCache const* = nullptr) const; SAL_DLLPRIVATE std::unique_ptr<SalLayout> ImplGlyphFallbackLayout( std::unique_ptr<SalLayout>, - ImplLayoutArgs&, + vcl::text::ImplLayoutArgs&, const SalLayoutGlyphs* ) const; SAL_DLLPRIVATE std::unique_ptr<SalLayout> getFallbackLayout( LogicalFontInstance* pLogicalFont, int nFallbackLevel, - ImplLayoutArgs& rLayoutArgs, const SalLayoutGlyphs* ) const; + vcl::text::ImplLayoutArgs& rLayoutArgs, const SalLayoutGlyphs* ) const; // Enabling/disabling RTL only makes sense for OutputDevices that use a mirroring SalGraphicsLayout diff --git a/include/vcl/vcllayout.hxx b/include/vcl/vcllayout.hxx index 670e8aaad0c3..2546b5f72c09 100644 --- a/include/vcl/vcllayout.hxx +++ b/include/vcl/vcllayout.hxx @@ -28,7 +28,7 @@ #include <vcl/dllapi.h> class LogicalFontInstance; -class ImplLayoutArgs; +namespace vcl::text { class ImplLayoutArgs; } class PhysicalFontFace; class SalGraphics; class GlyphItem; @@ -74,8 +74,8 @@ public: const Point& DrawOffset() const { return maDrawOffset; } Point GetDrawPosition( const Point& rRelative = Point(0,0) ) const; - virtual bool LayoutText( ImplLayoutArgs&, const SalLayoutGlyphsImpl* ) = 0; // first step of layouting - virtual void AdjustLayout( ImplLayoutArgs& ); // adjusting after fallback etc. + virtual bool LayoutText( vcl::text::ImplLayoutArgs&, const SalLayoutGlyphsImpl* ) = 0; // first step of layouting + virtual void AdjustLayout( vcl::text::ImplLayoutArgs& ); // adjusting after fallback etc. virtual void InitFont() const {} virtual void DrawText( SalGraphics& ) const = 0; diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 05f33b07115d..f9403f25b7e9 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -236,6 +236,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/outdev/vclreferencebase \ vcl/source/outdev/nativecontrols \ vcl/source/outdev/map \ + vcl/source/text/ImplLayoutArgs \ vcl/source/text/TextLayoutCache \ vcl/source/treelist/headbar \ vcl/source/treelist/iconview \ diff --git a/vcl/inc/ImplLayoutArgs.hxx b/vcl/inc/ImplLayoutArgs.hxx new file mode 100644 index 000000000000..865470b7897a --- /dev/null +++ b/vcl/inc/ImplLayoutArgs.hxx @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "sallayout.hxx" + +namespace vcl::text +{ +class VCL_DLLPUBLIC ImplLayoutArgs +{ +public: + // string related inputs + LanguageTag maLanguageTag; + SalLayoutFlags mnFlags; + const OUString& mrStr; + int mnMinCharPos; + int mnEndCharPos; + + // performance hack + vcl::text::TextLayoutCache const* m_pTextLayoutCache; + + // positioning related inputs + const DeviceCoordinate* mpDXArray; // in pixel units + DeviceCoordinate mnLayoutWidth; // in pixel units + Degree10 mnOrientation; // in 0-3600 system + + // data for bidi and glyph+script fallback + ImplLayoutRuns maRuns; + ImplLayoutRuns maFallbackRuns; + + ImplLayoutArgs(OUString const& rStr, int nMinCharPos, int nEndCharPos, SalLayoutFlags nFlags, + LanguageTag const& rLanguageTag, vcl::text::TextLayoutCache const* pLayoutCache); + + void SetLayoutWidth(DeviceCoordinate nWidth); + void SetDXArray(const DeviceCoordinate* pDXArray); + void SetOrientation(Degree10 nOrientation); + + void ResetPos(); + bool GetNextPos(int* nCharPos, bool* bRTL); + bool GetNextRun(int* nMinRunPos, int* nEndRunPos, bool* bRTL); + void AddFallbackRun(int nMinRunPos, int nEndRunPos, bool bRTL); + // methods used by BiDi and glyph fallback + bool HasFallbackRun() const; + bool PrepareFallback(const SalLayoutGlyphsImpl* pGlyphsImpl); + +private: + void AddRun(int nMinCharPos, int nEndCharPos, bool bRTL); +}; +} + +// For nice SAL_INFO logging of ImplLayoutArgs values +std::ostream& operator<<(std::ostream& s, vcl::text::ImplLayoutArgs const& rArgs); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/inc/salgdi.hxx b/vcl/inc/salgdi.hxx index 9c99fcd5fa0b..65e56367d2f2 100644 --- a/vcl/inc/salgdi.hxx +++ b/vcl/inc/salgdi.hxx @@ -40,7 +40,6 @@ class FontSelectPattern; class FontAttributes; class PhysicalFontFace; class SalLayout; -class ImplLayoutArgs; namespace tools { class Rectangle; } class FontSubsetInfo; class OutputDevice; diff --git a/vcl/inc/sallayout.hxx b/vcl/inc/sallayout.hxx index b2bdcc2d4cb0..594202b23239 100644 --- a/vcl/inc/sallayout.hxx +++ b/vcl/inc/sallayout.hxx @@ -73,56 +73,6 @@ public: bool PosIsInAnyRun( int nCharPos ) const; }; -class VCL_DLLPUBLIC ImplLayoutArgs -{ -public: - // string related inputs - LanguageTag maLanguageTag; - SalLayoutFlags mnFlags; - const OUString& mrStr; - int mnMinCharPos; - int mnEndCharPos; - - // performance hack - vcl::text::TextLayoutCache const* m_pTextLayoutCache; - - // positioning related inputs - const DeviceCoordinate* mpDXArray; // in pixel units - DeviceCoordinate mnLayoutWidth; // in pixel units - Degree10 mnOrientation; // in 0-3600 system - - // data for bidi and glyph+script fallback - ImplLayoutRuns maRuns; - ImplLayoutRuns maFallbackRuns; - - ImplLayoutArgs( const OUString& rStr, - int nMinCharPos, int nEndCharPos, SalLayoutFlags nFlags, - const LanguageTag& rLanguageTag, - vcl::text::TextLayoutCache const* pLayoutCache); - - void SetLayoutWidth( DeviceCoordinate nWidth ) { mnLayoutWidth = nWidth; } - void SetDXArray( const DeviceCoordinate* pDXArray ) { mpDXArray = pDXArray; } - void SetOrientation( Degree10 nOrientation ) { mnOrientation = nOrientation; } - - void ResetPos() - { maRuns.ResetPos(); } - bool GetNextPos( int* nCharPos, bool* bRTL ) - { return maRuns.GetNextPos( nCharPos, bRTL ); } - bool GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL ); - void AddFallbackRun( int nMinRunPos, int nEndRunPos, bool bRTL ) - { maFallbackRuns.AddRun( nMinRunPos, nEndRunPos, bRTL ); } - // methods used by BiDi and glyph fallback - bool HasFallbackRun() const - { return !maFallbackRuns.IsEmpty(); } - bool PrepareFallback(const SalLayoutGlyphsImpl* pGlyphsImpl); - -private: - void AddRun( int nMinCharPos, int nEndCharPos, bool bRTL ); -}; - -// For nice SAL_INFO logging of ImplLayoutArgs values -std::ostream &operator <<(std::ostream& s, ImplLayoutArgs const &rArgs); - class MultiSalLayout final : public SalLayout { public: @@ -142,8 +92,8 @@ public: void AddFallback(std::unique_ptr<SalLayout> pFallbackLayout, ImplLayoutRuns const &); // give up ownership of the initial pBaseLayout taken by the ctor std::unique_ptr<SalLayout> ReleaseBaseLayout(); - bool LayoutText(ImplLayoutArgs&, const SalLayoutGlyphsImpl*) override; - void AdjustLayout(ImplLayoutArgs&) override; + bool LayoutText(vcl::text::ImplLayoutArgs&, const SalLayoutGlyphsImpl*) override; + void AdjustLayout(vcl::text::ImplLayoutArgs&) override; void InitFont() const override; void SetIncomplete(bool bIncomplete); @@ -163,14 +113,14 @@ private: class VCL_DLLPUBLIC GenericSalLayout : public SalLayout { - friend void MultiSalLayout::AdjustLayout(ImplLayoutArgs&); + friend void MultiSalLayout::AdjustLayout(vcl::text::ImplLayoutArgs&); public: GenericSalLayout(LogicalFontInstance&); ~GenericSalLayout() override; - void AdjustLayout(ImplLayoutArgs&) final override; - bool LayoutText(ImplLayoutArgs&, const SalLayoutGlyphsImpl*) final override; + void AdjustLayout(vcl::text::ImplLayoutArgs&) final override; + bool LayoutText(vcl::text::ImplLayoutArgs&, const SalLayoutGlyphsImpl*) final override; void DrawText(SalGraphics&) const final override; static std::shared_ptr<vcl::text::TextLayoutCache> CreateTextLayoutCache(OUString const&); SalLayoutGlyphs GetGlyphs() const final override; @@ -208,7 +158,7 @@ private: void GetCharWidths(std::vector<DeviceCoordinate>& rCharWidths) const; - void SetNeedFallback(ImplLayoutArgs&, sal_Int32, bool); + void SetNeedFallback(vcl::text::ImplLayoutArgs&, sal_Int32, bool); bool HasVerticalAlternate(sal_UCS4 aChar, sal_UCS4 aNextChar); diff --git a/vcl/qa/cppunit/complextext.cxx b/vcl/qa/cppunit/complextext.cxx index 77a98aa7afdb..5b16b4d06e74 100644 --- a/vcl/qa/cppunit/complextext.cxx +++ b/vcl/qa/cppunit/complextext.cxx @@ -25,6 +25,9 @@ static std::ostream& operator<<(std::ostream& rStream, const std::vector<tools:: #include <sallayout.hxx> #include <salgdi.hxx> + +#include <ImplLayoutArgs.hxx> + #if HAVE_MORE_FONTS static std::ostream& operator<<(std::ostream& rStream, const std::vector<tools::Long>& rVec) { @@ -129,7 +132,7 @@ void VclComplexTextTest::testKashida() CPPUNIT_ASSERT(aGlyphs.Impl(0) != nullptr); // Now lay it out using the cached glyph list. - ImplLayoutArgs aLayoutArgs(aText, 0, aText.getLength(), SalLayoutFlags::NONE, + vcl::text::ImplLayoutArgs aLayoutArgs(aText, 0, aText.getLength(), SalLayoutFlags::NONE, pOutputDevice->GetFont().GetLanguageTag(), nullptr); pLayout = pOutputDevice->GetGraphics()->GetTextLayout(0); CPPUNIT_ASSERT(pLayout->LayoutText(aLayoutArgs, aGlyphs.Impl(0))); diff --git a/vcl/qa/cppunit/text.cxx b/vcl/qa/cppunit/text.cxx index 548690674f47..c9f0d36c50c3 100644 --- a/vcl/qa/cppunit/text.cxx +++ b/vcl/qa/cppunit/text.cxx @@ -10,6 +10,7 @@ #include <test/bootstrapfixture.hxx> #include <sal/log.hxx> #include <tools/stream.hxx> +#include <i18nlangtag/languagetag.hxx> #include <vcl/BitmapReadAccess.hxx> #include <vcl/graphicfilter.hxx> @@ -17,6 +18,7 @@ #include <vcl/svapp.hxx> #include <vcl/virdev.hxx> +#include <ImplLayoutArgs.hxx> #include <TextLayoutCache.hxx> #include <salgdi.hxx> @@ -45,11 +47,19 @@ public: void testSimpleText(); void testVerticalText(); void testTextLayoutCache(); + void testImplLayoutArgsBiDiStrong(); + void testImplLayoutArgsBiDiRtl(); + void testImplLayoutArgsRightAlign(); + void testImplLayoutArgs_PrepareFallback_precalculatedglyphs(); CPPUNIT_TEST_SUITE(VclTextTest); CPPUNIT_TEST(testSimpleText); CPPUNIT_TEST(testVerticalText); CPPUNIT_TEST(testTextLayoutCache); + CPPUNIT_TEST(testImplLayoutArgsBiDiStrong); + CPPUNIT_TEST(testImplLayoutArgsBiDiRtl); + CPPUNIT_TEST(testImplLayoutArgsRightAlign); + CPPUNIT_TEST(testImplLayoutArgs_PrepareFallback_precalculatedglyphs); CPPUNIT_TEST_SUITE_END(); }; @@ -397,6 +407,138 @@ void VclTextTest::testTextLayoutCache() CPPUNIT_ASSERT_EQUAL(51, run2.nEnd); } +void VclTextTest::testImplLayoutArgsBiDiStrong() +{ + OUString sTestString = u"The quick brown fox\n jumped over the lazy dog" + "العاشر"; + vcl::text::ImplLayoutArgs aArgs(sTestString, 0, sTestString.getLength(), + SalLayoutFlags::BiDiStrong, LanguageTag(LANGUAGE_NONE), + nullptr); + + int* nMinRunPos = new int(0); + int* nEndRunPos = new int(0); + bool* pRTL = new bool(false); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(0, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(19, *nEndRunPos); + CPPUNIT_ASSERT(!*pRTL); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(20, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(51, *nEndRunPos); + CPPUNIT_ASSERT(!*pRTL); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(20, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(51, *nEndRunPos); +} + +void VclTextTest::testImplLayoutArgsBiDiRtl() +{ + OUString sTestString = u"The quick brown fox\n jumped over the lazy dog" + "العاشر"; + vcl::text::ImplLayoutArgs aArgs(sTestString, 0, sTestString.getLength(), + SalLayoutFlags::BiDiRtl, LanguageTag(LANGUAGE_NONE), nullptr); + + int* nMinRunPos = new int(0); + int* nEndRunPos = new int(0); + bool* pRTL = new bool(false); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(45, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(51, *nEndRunPos); + CPPUNIT_ASSERT(*pRTL); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(21, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(45, *nEndRunPos); + CPPUNIT_ASSERT(!*pRTL); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(20, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(21, *nEndRunPos); + CPPUNIT_ASSERT(*pRTL); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(0, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(19, *nEndRunPos); + CPPUNIT_ASSERT(!*pRTL); +} + +void VclTextTest::testImplLayoutArgsRightAlign() +{ + OUString sTestString = u"The quick brown fox\n jumped over the lazy dog" + "العاشر"; + vcl::text::ImplLayoutArgs aArgs(sTestString, 0, sTestString.getLength(), + SalLayoutFlags::RightAlign, LanguageTag(LANGUAGE_NONE), + nullptr); + + int* nMinRunPos = new int(0); + int* nEndRunPos = new int(0); + bool* pRTL = new bool(false); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(0, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(19, *nEndRunPos); + CPPUNIT_ASSERT(!*pRTL); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(20, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(45, *nEndRunPos); + CPPUNIT_ASSERT(!*pRTL); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(45, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(51, *nEndRunPos); + CPPUNIT_ASSERT(*pRTL); +} + +void VclTextTest::testImplLayoutArgs_PrepareFallback_precalculatedglyphs() +{ + // this font has no latin characters and thus needs fallback + const vcl::Font aFont("KacstBook", Size(0, 36)); + + ScopedVclPtrInstance<VirtualDevice> pVirDev; + pVirDev->SetFont(aFont); + + const OUString sTestString = "The quick\n jumped over"; + std::unique_ptr<SalLayout> pLayout + = pVirDev->ImplLayout(sTestString, 0, sTestString.getLength(), Point(0, 0), 0, nullptr, + SalLayoutFlags::GlyphItemsOnly); + SalLayoutGlyphs aGlyphs = pLayout->GetGlyphs(); + SalLayoutGlyphsImpl* pGlyphsImpl = aGlyphs.Impl(1); + + vcl::text::ImplLayoutArgs aArgs(sTestString, 0, sTestString.getLength(), + SalLayoutFlags::BiDiRtl, LanguageTag(LANGUAGE_LATIN), nullptr); + + aArgs.PrepareFallback(pGlyphsImpl); + + int* nMinRunPos = new int(0); + int* nEndRunPos = new int(0); + bool* pRTL = new bool(false); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(0, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(3, *nEndRunPos); + CPPUNIT_ASSERT(!*pRTL); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(4, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(9, *nEndRunPos); + CPPUNIT_ASSERT(!*pRTL); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(11, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(17, *nEndRunPos); + CPPUNIT_ASSERT(!*pRTL); + + aArgs.GetNextRun(nMinRunPos, nEndRunPos, pRTL); + CPPUNIT_ASSERT_EQUAL(18, *nMinRunPos); + CPPUNIT_ASSERT_EQUAL(22, *nEndRunPos); + CPPUNIT_ASSERT(!*pRTL); +} + CPPUNIT_TEST_SUITE_REGISTRATION(VclTextTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/vcl/source/gdi/CommonSalLayout.cxx b/vcl/source/gdi/CommonSalLayout.cxx index b6a489a88a9a..fbfacdee6b02 100644 --- a/vcl/source/gdi/CommonSalLayout.cxx +++ b/vcl/source/gdi/CommonSalLayout.cxx @@ -25,6 +25,7 @@ #include <vcl/font/Feature.hxx> #include <vcl/font/FeatureParser.hxx> +#include <ImplLayoutArgs.hxx> #include <TextLayoutCache.hxx> #include <fontselect.hxx> #include <salgdi.hxx> @@ -162,7 +163,7 @@ SalLayoutGlyphs GenericSalLayout::GetGlyphs() const return glyphs; } -void GenericSalLayout::SetNeedFallback(ImplLayoutArgs& rArgs, sal_Int32 nCharPos, bool bRightToLeft) +void GenericSalLayout::SetNeedFallback(vcl::text::ImplLayoutArgs& rArgs, sal_Int32 nCharPos, bool bRightToLeft) { if (nCharPos < 0 || mbFuzzing) return; @@ -190,7 +191,7 @@ void GenericSalLayout::SetNeedFallback(ImplLayoutArgs& rArgs, sal_Int32 nCharPos rArgs.AddFallbackRun(nGraphemeStartPos, nGraphemeEndPos, bRightToLeft); } -void GenericSalLayout::AdjustLayout(ImplLayoutArgs& rArgs) +void GenericSalLayout::AdjustLayout(vcl::text::ImplLayoutArgs& rArgs) { SalLayout::AdjustLayout(rArgs); @@ -251,7 +252,7 @@ bool GenericSalLayout::HasVerticalAlternate(sal_UCS4 aChar, sal_UCS4 aVariationS return hb_set_has(mpVertGlyphs, nGlyphIndex) != 0; } -bool GenericSalLayout::LayoutText(ImplLayoutArgs& rArgs, const SalLayoutGlyphsImpl* pGlyphs) +bool GenericSalLayout::LayoutText(vcl::text::ImplLayoutArgs& rArgs, const SalLayoutGlyphsImpl* pGlyphs) { // No need to touch m_GlyphItems at all for an empty string. if (rArgs.mnEndCharPos - rArgs.mnMinCharPos <= 0) diff --git a/vcl/source/gdi/sallayout.cxx b/vcl/source/gdi/sallayout.cxx index e731d6782cca..debe3017a92a 100644 --- a/vcl/source/gdi/sallayout.cxx +++ b/vcl/source/gdi/sallayout.cxx @@ -27,6 +27,7 @@ #include <math.h> +#include <ImplLayoutArgs.hxx> #include <salgdi.hxx> #include <sallayout.hxx> #include <basegfx/polygon/b2dpolypolygon.hxx> @@ -49,85 +50,6 @@ #define GF_FONTSHIFT 28 -std::ostream &operator <<(std::ostream& s, ImplLayoutArgs const &rArgs) -{ -#ifndef SAL_LOG_INFO - (void) rArgs; -#else - s << "ImplLayoutArgs{"; - - s << "Flags="; - if (rArgs.mnFlags == SalLayoutFlags::NONE) - s << 0; - else { - bool need_or = false; - s << "{"; -#define TEST(x) if (rArgs.mnFlags & SalLayoutFlags::x) { if (need_or) s << "|"; s << #x; need_or = true; } - TEST(BiDiRtl); - TEST(BiDiStrong); - TEST(RightAlign); - TEST(DisableKerning); - TEST(KerningAsian); - TEST(Vertical); - TEST(KashidaJustification); - TEST(ForFallback); -#undef TEST - s << "}"; - } - - const int nLength = rArgs.mrStr.getLength(); - - s << ",Length=" << nLength; - s << ",MinCharPos=" << rArgs.mnMinCharPos; - s << ",EndCharPos=" << rArgs.mnEndCharPos; - - s << ",Str=\""; - int lim = nLength; - if (lim > 10) - lim = 7; - for (int i = 0; i < lim; i++) { - if (rArgs.mrStr[i] == '\n') - s << "\\n"; - else if (rArgs.mrStr[i] < ' ' || (rArgs.mrStr[i] >= 0x7F && rArgs.mrStr[i] <= 0xFF)) - s << "\\0x" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(rArgs.mrStr[i]) << std::setfill(' ') << std::setw(1) << std::dec; - else if (rArgs.mrStr[i] < 0x7F) - s << static_cast<char>(rArgs.mrStr[i]); - else - s << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(rArgs.mrStr[i]) << std::setfill(' ') << std::setw(1) << std::dec; - } - if (nLength > lim) - s << "..."; - s << "\""; - - s << ",DXArray="; - if (rArgs.mpDXArray) { - s << "["; - int count = rArgs.mnEndCharPos - rArgs.mnMinCharPos; - lim = count; - if (lim > 10) - lim = 7; - for (int i = 0; i < lim; i++) { - s << rArgs.mpDXArray[i]; - if (i < lim-1) - s << ","; - } - if (count > lim) { - if (count > lim + 1) - s << "..."; - s << rArgs.mpDXArray[count-1]; - } - s << "]"; - } else - s << "NULL"; - - s << ",LayoutWidth=" << rArgs.mnLayoutWidth; - - s << "}"; - -#endif - return s; -} - sal_UCS4 GetMirroredChar( sal_UCS4 nChar ) { nChar = u_charMirror( nChar ); @@ -207,27 +129,6 @@ sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang ) return nChar; } -static bool IsControlChar( sal_UCS4 cChar ) -{ - // C0 control characters - if( (0x0001 <= cChar) && (cChar <= 0x001F) ) - return true; - // formatting characters - if( (0x200E <= cChar) && (cChar <= 0x200F) ) - return true; - if( (0x2028 <= cChar) && (cChar <= 0x202E) ) - return true; - // deprecated formatting characters - if( (0x206A <= cChar) && (cChar <= 0x206F) ) - return true; - if( 0x2060 == cChar ) - return true; - // byte order markers and invalid unicode - if( (cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF) ) - return true; - return false; -} - void ImplLayoutRuns::AddPos( int nCharPos, bool bRTL ) { // check if charpos could extend current run @@ -385,175 +286,6 @@ bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLef return true; } -ImplLayoutArgs::ImplLayoutArgs(const OUString& rStr, - int nMinCharPos, int nEndCharPos, SalLayoutFlags nFlags, const LanguageTag& rLanguageTag, - vcl::text::TextLayoutCache const*const pLayoutCache) -: - maLanguageTag( rLanguageTag ), - mnFlags( nFlags ), - mrStr( rStr ), - mnMinCharPos( nMinCharPos ), - mnEndCharPos( nEndCharPos ), - m_pTextLayoutCache(pLayoutCache), - mpDXArray( nullptr ), - mnLayoutWidth( 0 ), - mnOrientation( 0 ) -{ - if( mnFlags & SalLayoutFlags::BiDiStrong ) - { - // handle strong BiDi mode - - // do not bother to BiDi analyze strong LTR/RTL - // TODO: can we assume these strings do not have unicode control chars? - // if not remove the control characters from the runs - bool bRTL(mnFlags & SalLayoutFlags::BiDiRtl); - AddRun( mnMinCharPos, mnEndCharPos, bRTL ); - } - else - { - // handle weak BiDi mode - UBiDiLevel nLevel = (mnFlags & SalLayoutFlags::BiDiRtl)? 1 : 0; - - // prepare substring for BiDi analysis - // TODO: reuse allocated pParaBidi - UErrorCode rcI18n = U_ZERO_ERROR; - const int nLength = mrStr.getLength(); - UBiDi* pParaBidi = ubidi_openSized(nLength, 0, &rcI18n); - if( !pParaBidi ) - return; - ubidi_setPara(pParaBidi, reinterpret_cast<const UChar *>(mrStr.getStr()), nLength, nLevel, nullptr, &rcI18n); - - UBiDi* pLineBidi = pParaBidi; - int nSubLength = mnEndCharPos - mnMinCharPos; - if (nSubLength != nLength) - { - pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n ); - ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n ); - } - - // run BiDi algorithm - const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n ); - //maRuns.resize( 2 * nRunCount ); - for( int i = 0; i < nRunCount; ++i ) - { - int32_t nMinPos, nRunLength; - const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nRunLength ); - const int nPos0 = nMinPos + mnMinCharPos; - const int nPos1 = nPos0 + nRunLength; - - const bool bRTL = (nDir == UBIDI_RTL); - AddRun( nPos0, nPos1, bRTL ); - } - - // cleanup BiDi engine - if( pLineBidi != pParaBidi ) - ubidi_close( pLineBidi ); - ubidi_close( pParaBidi ); - } - - // prepare calls to GetNextPos/GetNextRun - maRuns.ResetPos(); -} - -// add a run after splitting it up to get rid of control chars -void ImplLayoutArgs::AddRun( int nCharPos0, int nCharPos1, bool bRTL ) -{ - SAL_WARN_IF( nCharPos0 > nCharPos1, "vcl", "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1" ); - - // remove control characters from runs by splitting them up - if( !bRTL ) - { - for( int i = nCharPos0; i < nCharPos1; ++i ) - if( IsControlChar( mrStr[i] ) ) - { - // add run until control char - maRuns.AddRun( nCharPos0, i, bRTL ); - nCharPos0 = i + 1; - } - } - else - { - for( int i = nCharPos1; --i >= nCharPos0; ) - if( IsControlChar( mrStr[i] ) ) - { - // add run until control char - maRuns.AddRun( i+1, nCharPos1, bRTL ); - nCharPos1 = i; - } - } - - // add remainder of run - maRuns.AddRun( nCharPos0, nCharPos1, bRTL ); -} - -bool ImplLayoutArgs::PrepareFallback(const SalLayoutGlyphsImpl* pGlyphsImpl) -{ - // Generate runs with pre-calculated glyph items instead maFallbackRuns. - if( pGlyphsImpl != nullptr ) - { - maRuns.Clear(); - maFallbackRuns.Clear(); - - for (auto const& aGlyphItem : *pGlyphsImpl) - { - for(int i = aGlyphItem.charPos(); i < aGlyphItem.charPos() + aGlyphItem.charCount(); ++i) - maRuns.AddPos(i, aGlyphItem.IsRTLGlyph()); - } - - return !maRuns.IsEmpty(); - } - - // short circuit if no fallback is needed - if( maFallbackRuns.IsEmpty() ) - { - maRuns.Clear(); - return false; - } - - // convert the fallback requests to layout requests - bool bRTL; - int nMin, nEnd; - - // get the individual fallback requests - std::vector<int> aPosVector; - aPosVector.reserve(mrStr.getLength()); - maFallbackRuns.ResetPos(); - for(; maFallbackRuns.GetRun( &nMin, &nEnd, &bRTL ); maFallbackRuns.NextRun() ) - for( int i = nMin; i < nEnd; ++i ) - aPosVector.push_back( i ); - maFallbackRuns.Clear(); - - // sort the individual fallback requests - std::sort( aPosVector.begin(), aPosVector.end() ); - - // adjust fallback runs to have the same order and limits of the original runs - ImplLayoutRuns aNewRuns; - maRuns.ResetPos(); - for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() ) - { - if( !bRTL) { - auto it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin ); - for(; (it != aPosVector.end()) && (*it < nEnd); ++it ) - aNewRuns.AddPos( *it, bRTL ); - } else { - auto it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd ); - while( (it != aPosVector.begin()) && (*--it >= nMin) ) - aNewRuns.AddPos( *it, bRTL ); - } - } - - maRuns = aNewRuns; // TODO: use vector<>::swap() - maRuns.ResetPos(); - return true; -} - -bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL ) -{ - bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL ); - maRuns.NextRun(); - return bValid; -} - SalLayout::SalLayout() : mnMinCharPos( -1 ), mnEndCharPos( -1 ), @@ -565,7 +297,7 @@ SalLayout::SalLayout() SalLayout::~SalLayout() {} -void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs ) +void SalLayout::AdjustLayout( vcl::text::ImplLayoutArgs& rArgs ) { mnMinCharPos = rArgs.mnMinCharPos; mnEndCharPos = rArgs.mnEndCharPos; @@ -1047,7 +779,7 @@ void MultiSalLayout::AddFallback( std::unique_ptr<SalLayout> pFallback, ++mnLevel; } -bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs, const SalLayoutGlyphsImpl* ) +bool MultiSalLayout::LayoutText( vcl::text::ImplLayoutArgs& rArgs, const SalLayoutGlyphsImpl* ) { if( mnLevel <= 1 ) return false; @@ -1056,10 +788,10 @@ bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs, const SalLayoutGlyphsImp return true; } -void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs ) +void MultiSalLayout::AdjustLayout( vcl::text::ImplLayoutArgs& rArgs ) { SalLayout::AdjustLayout( rArgs ); - ImplLayoutArgs aMultiArgs = rArgs; + vcl::text::ImplLayoutArgs aMultiArgs = rArgs; std::vector<DeviceCoordinate> aJustificationArray; if( !rArgs.mpDXArray && rArgs.mnLayoutWidth ) diff --git a/vcl/source/outdev/font.cxx b/vcl/source/outdev/font.cxx index 1875394eaf93..06c339dd795c 100644 --- a/vcl/source/outdev/font.cxx +++ b/vcl/source/outdev/font.cxx @@ -35,6 +35,7 @@ #include <outdev.h> #include <window.h> +#include <ImplLayoutArgs.hxx> #include <PhysicalFontCollection.hxx> #include <drawmode.hxx> #include <font/FeatureCollector.hxx> @@ -1253,7 +1254,7 @@ void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout ) std::unique_ptr<SalLayout> OutputDevice::getFallbackLayout( LogicalFontInstance* pLogicalFont, int nFallbackLevel, - ImplLayoutArgs& rLayoutArgs, const SalLayoutGlyphs* pGlyphs) const + vcl::text::ImplLayoutArgs& rLayoutArgs, const SalLayoutGlyphs* pGlyphs) const { // we need a graphics if (!mpGraphics && !AcquireGraphics()) @@ -1278,7 +1279,7 @@ std::unique_ptr<SalLayout> OutputDevice::getFallbackLayout( } std::unique_ptr<SalLayout> OutputDevice::ImplGlyphFallbackLayout( std::unique_ptr<SalLayout> pSalLayout, - ImplLayoutArgs& rLayoutArgs, const SalLayoutGlyphs* pGlyphs ) const + vcl::text::ImplLayoutArgs& rLayoutArgs, const SalLayoutGlyphs* pGlyphs ) const { // This function relies on a valid mpFontInstance, if it doesn't exist bail out // - we'd have crashed later on anyway. At least here we can catch the error in debug diff --git a/vcl/source/outdev/text.cxx b/vcl/source/outdev/text.cxx index 7d5fe5256353..801615401fcd 100644 --- a/vcl/source/outdev/text.cxx +++ b/vcl/source/outdev/text.cxx @@ -41,6 +41,7 @@ #include <config_fuzzers.h> #include <outdev.h> +#include <ImplLayoutArgs.hxx> #include <drawmode.hxx> #include <salgdi.hxx> #include <svdata.hxx> @@ -1095,7 +1096,7 @@ void OutputDevice::DrawStretchText( const Point& rStartPt, sal_uLong nWidth, mpAlphaVDev->DrawStretchText( rStartPt, nWidth, rStr, nIndex, nLen ); } -ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( OUString& rStr, +vcl::text::ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( OUString& rStr, const sal_Int32 nMinIndex, const sal_Int32 nLen, DeviceCoordinate nPixelWidth, const DeviceCoordinate* pDXArray, SalLayoutFlags nLayoutFlags, @@ -1184,7 +1185,7 @@ ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( OUString& rStr, nLayoutFlags |= SalLayoutFlags::RightAlign; // set layout options - ImplLayoutArgs aLayoutArgs(rStr, nMinIndex, nEndIndex, nLayoutFlags, maFont.GetLanguageTag(), pLayoutCache); + vcl::text::ImplLayoutArgs aLayoutArgs(rStr, nMinIndex, nEndIndex, nLayoutFlags, maFont.GetLanguageTag(), pLayoutCache); Degree10 nOrientation = mpFontInstance ? mpFontInstance->mnOrientation : 0_deg10; aLayoutArgs.SetOrientation( nOrientation ); @@ -1267,7 +1268,7 @@ std::unique_ptr<SalLayout> OutputDevice::ImplLayout(const OUString& rOrigStr, } } - ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, + vcl::text::ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, nPixelWidth, pDXPixelArray, flags, pLayoutCache); // get matching layout object for base font @@ -1320,7 +1321,7 @@ std::shared_ptr<vcl::text::TextLayoutCache> OutputDevice::CreateTextLayoutCache( bool OutputDevice::GetTextIsRTL( const OUString& rString, sal_Int32 nIndex, sal_Int32 nLen ) const { OUString aStr( rString ); - ImplLayoutArgs aArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, nullptr ); + vcl::text::ImplLayoutArgs aArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, nullptr ); bool bRTL = false; int nCharPos = -1; if (!aArgs.GetNextPos(&nCharPos, &bRTL)) diff --git a/vcl/source/text/ImplLayoutArgs.cxx b/vcl/source/text/ImplLayoutArgs.cxx new file mode 100644 index 000000000000..7957d1ace824 --- /dev/null +++ b/vcl/source/text/ImplLayoutArgs.cxx @@ -0,0 +1,339 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <ImplLayoutArgs.hxx> + +#include <unicode/ubidi.h> +#include <unicode/uchar.h> + +#include <algorithm> +#include <memory> + +namespace vcl::text +{ +ImplLayoutArgs::ImplLayoutArgs(const OUString& rStr, int nMinCharPos, int nEndCharPos, + SalLayoutFlags nFlags, const LanguageTag& rLanguageTag, + vcl::text::TextLayoutCache const* const pLayoutCache) + : maLanguageTag(rLanguageTag) + , mnFlags(nFlags) + , mrStr(rStr) + , mnMinCharPos(nMinCharPos) + , mnEndCharPos(nEndCharPos) + , m_pTextLayoutCache(pLayoutCache) + , mpDXArray(nullptr) + , mnLayoutWidth(0) + , mnOrientation(0) +{ + if (mnFlags & SalLayoutFlags::BiDiStrong) + { + // handle strong BiDi mode + + // do not bother to BiDi analyze strong LTR/RTL + // TODO: can we assume these strings do not have unicode control chars? + // if not remove the control characters from the runs + bool bRTL(mnFlags & SalLayoutFlags::BiDiRtl); + AddRun(mnMinCharPos, mnEndCharPos, bRTL); + } + else + { + // handle weak BiDi mode + UBiDiLevel nLevel = (mnFlags & SalLayoutFlags::BiDiRtl) ? 1 : 0; + + // prepare substring for BiDi analysis + // TODO: reuse allocated pParaBidi + UErrorCode rcI18n = U_ZERO_ERROR; + const int nLength = mrStr.getLength(); + UBiDi* pParaBidi = ubidi_openSized(nLength, 0, &rcI18n); + if (!pParaBidi) + return; + ubidi_setPara(pParaBidi, reinterpret_cast<const UChar*>(mrStr.getStr()), nLength, nLevel, + nullptr, &rcI18n); + + UBiDi* pLineBidi = pParaBidi; + int nSubLength = mnEndCharPos - mnMinCharPos; + if (nSubLength != nLength) + { + pLineBidi = ubidi_openSized(nSubLength, 0, &rcI18n); + ubidi_setLine(pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n); + } + + // run BiDi algorithm + const int nRunCount = ubidi_countRuns(pLineBidi, &rcI18n); + //maRuns.resize( 2 * nRunCount ); + for (int i = 0; i < nRunCount; ++i) + { + int32_t nMinPos, nRunLength; + const UBiDiDirection nDir = ubidi_getVisualRun(pLineBidi, i, &nMinPos, &nRunLength); + const int nPos0 = nMinPos + mnMinCharPos; + const int nPos1 = nPos0 + nRunLength; + + const bool bRTL = (nDir == UBIDI_RTL); + AddRun(nPos0, nPos1, bRTL); + } + + // cleanup BiDi engine + if (pLineBidi != pParaBidi) + ubidi_close(pLineBidi); + ubidi_close(pParaBidi); + } + + // prepare calls to GetNextPos/GetNextRun + maRuns.ResetPos(); +} + +void ImplLayoutArgs::SetLayoutWidth(DeviceCoordinate nWidth) { mnLayoutWidth = nWidth; } + +void ImplLayoutArgs::SetDXArray(DeviceCoordinate const* pDXArray) { mpDXArray = pDXArray; } + +void ImplLayoutArgs::SetOrientation(Degree10 nOrientation) { mnOrientation = nOrientation; } + +void ImplLayoutArgs::ResetPos() { maRuns.ResetPos(); } + +bool ImplLayoutArgs::GetNextPos(int* nCharPos, bool* bRTL) +{ + return maRuns.GetNextPos(nCharPos, bRTL); +} + +void ImplLayoutArgs::AddFallbackRun(int nMinRunPos, int nEndRunPos, bool bRTL) +{ + maFallbackRuns.AddRun(nMinRunPos, nEndRunPos, bRTL); +} + +bool ImplLayoutArgs::HasFallbackRun() const { return !maFallbackRuns.IsEmpty(); } + +static bool IsControlChar(sal_UCS4 cChar) +{ + // C0 control characters + if ((0x0001 <= cChar) && (cChar <= 0x001F)) + return true; + // formatting characters + if ((0x200E <= cChar) && (cChar <= 0x200F)) + return true; + if ((0x2028 <= cChar) && (cChar <= 0x202E)) + return true; + // deprecated formatting characters + if ((0x206A <= cChar) && (cChar <= 0x206F)) + return true; + if (0x2060 == cChar) + return true; + // byte order markers and invalid unicode + if ((cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF)) + return true; + return false; +} + +// add a run after splitting it up to get rid of control chars +void ImplLayoutArgs::AddRun(int nCharPos0, int nCharPos1, bool bRTL) +{ + SAL_WARN_IF(nCharPos0 > nCharPos1, "vcl", "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1"); + + // remove control characters from runs by splitting them up + if (!bRTL) + { + for (int i = nCharPos0; i < nCharPos1; ++i) + if (IsControlChar(mrStr[i])) + { + // add run until control char + maRuns.AddRun(nCharPos0, i, bRTL); + nCharPos0 = i + 1; + } + } + else + { + for (int i = nCharPos1; --i >= nCharPos0;) + if (IsControlChar(mrStr[i])) + { + // add run until control char + maRuns.AddRun(i + 1, nCharPos1, bRTL); + nCharPos1 = i; + } + } + + // add remainder of run + maRuns.AddRun(nCharPos0, nCharPos1, bRTL); +} + +bool ImplLayoutArgs::PrepareFallback(const SalLayoutGlyphsImpl* pGlyphsImpl) +{ + // Generate runs with pre-calculated glyph items instead maFallbackRuns. + if (pGlyphsImpl != nullptr) + { + maRuns.Clear(); + maFallbackRuns.Clear(); + + for (auto const& aGlyphItem : *pGlyphsImpl) + { + for (int i = aGlyphItem.charPos(); i < aGlyphItem.charPos() + aGlyphItem.charCount(); + ++i) + maRuns.AddPos(i, aGlyphItem.IsRTLGlyph()); + } + + return !maRuns.IsEmpty(); + } + + // short circuit if no fallback is needed + if (maFallbackRuns.IsEmpty()) + { + maRuns.Clear(); + return false; + } + + // convert the fallback requests to layout requests + bool bRTL; + int nMin, nEnd; + + // get the individual fallback requests + std::vector<int> aPosVector; + aPosVector.reserve(mrStr.getLength()); + maFallbackRuns.ResetPos(); + for (; maFallbackRuns.GetRun(&nMin, &nEnd, &bRTL); maFallbackRuns.NextRun()) + for (int i = nMin; i < nEnd; ++i) + aPosVector.push_back(i); + maFallbackRuns.Clear(); + + // sort the individual fallback requests + std::sort(aPosVector.begin(), aPosVector.end()); + + // adjust fallback runs to have the same order and limits of the original runs + ImplLayoutRuns aNewRuns; + maRuns.ResetPos(); + for (; maRuns.GetRun(&nMin, &nEnd, &bRTL); maRuns.NextRun()) + { + if (!bRTL) + { + auto it = std::lower_bound(aPosVector.begin(), aPosVector.end(), nMin); + for (; (it != aPosVector.end()) && (*it < nEnd); ++it) + aNewRuns.AddPos(*it, bRTL); + } + else + { + auto it = std::upper_bound(aPosVector.begin(), aPosVector.end(), nEnd); + while ((it != aPosVector.begin()) && (*--it >= nMin)) + aNewRuns.AddPos(*it, bRTL); + } + } + + maRuns = aNewRuns; // TODO: use vector<>::swap() + maRuns.ResetPos(); + return true; +} + +bool ImplLayoutArgs::GetNextRun(int* nMinRunPos, int* nEndRunPos, bool* bRTL) +{ + bool bValid = maRuns.GetRun(nMinRunPos, nEndRunPos, bRTL); + maRuns.NextRun(); + return bValid; +} +} + +std::ostream& operator<<(std::ostream& s, vcl::text::ImplLayoutArgs const& rArgs) +{ +#ifndef SAL_LOG_INFO + (void)rArgs; +#else + s << "ImplLayoutArgs{"; + + s << "Flags="; + if (rArgs.mnFlags == SalLayoutFlags::NONE) + s << 0; + else + { + bool need_or = false; + s << "{"; +#define TEST(x) \ + if (rArgs.mnFlags & SalLayoutFlags::x) \ + { \ + if (need_or) \ + s << "|"; \ + s << #x; \ + need_or = true; \ + } + TEST(BiDiRtl); + TEST(BiDiStrong); + TEST(RightAlign); + TEST(DisableKerning); + TEST(KerningAsian); + TEST(Vertical); + TEST(KashidaJustification); + TEST(ForFallback); +#undef TEST + s << "}"; + } + + const int nLength = rArgs.mrStr.getLength(); + + s << ",Length=" << nLength; + s << ",MinCharPos=" << rArgs.mnMinCharPos; + s << ",EndCharPos=" << rArgs.mnEndCharPos; + + s << ",Str=\""; + int lim = nLength; + if (lim > 10) + lim = 7; + for (int i = 0; i < lim; i++) + { + if (rArgs.mrStr[i] == '\n') + s << "\\n"; + else if (rArgs.mrStr[i] < ' ' || (rArgs.mrStr[i] >= 0x7F && rArgs.mrStr[i] <= 0xFF)) + s << "\\0x" << std::hex << std::setw(2) << std::setfill('0') + << static_cast<int>(rArgs.mrStr[i]) << std::setfill(' ') << std::setw(1) << std::dec; + else if (rArgs.mrStr[i] < 0x7F) + s << static_cast<char>(rArgs.mrStr[i]); + else + s << "\\u" << std::hex << std::setw(4) << std::setfill('0') + << static_cast<int>(rArgs.mrStr[i]) << std::setfill(' ') << std::setw(1) << std::dec; + } + if (nLength > lim) + s << "..."; + s << "\""; + + s << ",DXArray="; + if (rArgs.mpDXArray) + { + s << "["; + int count = rArgs.mnEndCharPos - rArgs.mnMinCharPos; + lim = count; + if (lim > 10) + lim = 7; + for (int i = 0; i < lim; i++) + { + s << rArgs.mpDXArray[i]; + if (i < lim - 1) + s << ","; + } + if (count > lim) + { + if (count > lim + 1) + s << "..."; + s << rArgs.mpDXArray[count - 1]; + } + s << "]"; + } + else + s << "NULL"; + + s << ",LayoutWidth=" << rArgs.mnLayoutWidth; + + s << "}"; + +#endif + return s; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */