solenv/clang-format/excludelist | 1 vcl/Library_vcl.mk | 1 vcl/inc/ImplLayoutRuns.hxx | 53 ++++++++++ vcl/inc/sallayout.hxx | 45 ++------ vcl/qa/cppunit/text.cxx | 190 ++++++++++++++++++++++++++++++++++++- vcl/source/gdi/sallayout.cxx | 157 ------------------------------ vcl/source/text/ImplLayoutRuns.cxx | 179 ++++++++++++++++++++++++++++++++++ 7 files changed, 435 insertions(+), 191 deletions(-)
New commits: commit db0a6e1bd98a9430e9ca4edfaabc3b11da986592 Author: Chris Sherlock <chris.sherloc...@gmail.com> AuthorDate: Sat Aug 28 22:50:26 2021 +1000 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Mon Sep 6 12:52:38 2021 +0200 vcl: migrate ImplLayoutRuns to own files Wrote a set of unit tests for ImplLayoutRuns, and added ImplLayoutRuns to vcl::text namespace. Change-Id: Id6ae8882acb8e3d821bb38551e78019cbdcaa662 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121204 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> diff --git a/solenv/clang-format/excludelist b/solenv/clang-format/excludelist index a484535c29b1..0c69494826dd 100644 --- a/solenv/clang-format/excludelist +++ b/solenv/clang-format/excludelist @@ -15049,6 +15049,7 @@ vcl/source/outdev/textline.cxx vcl/source/outdev/transparent.cxx vcl/source/outdev/vclreferencebase.cxx vcl/source/outdev/wallpaper.cxx +vcl/source/text/ImplLayoutRuns.cxx vcl/source/toolkit/group.cxx vcl/source/toolkit/morebtn.cxx vcl/source/treelist/headbar.cxx diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index f9403f25b7e9..99632be5c5ef 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -255,6 +255,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/treelist/svimpbox \ vcl/source/treelist/svlbitm \ vcl/source/treelist/uiobject \ + vcl/source/text/ImplLayoutRuns \ vcl/source/gdi/configsettings \ vcl/source/gdi/cvtgrf \ vcl/source/gdi/embeddedfontshelper \ diff --git a/vcl/inc/ImplLayoutRuns.hxx b/vcl/inc/ImplLayoutRuns.hxx new file mode 100644 index 000000000000..30ac642f3779 --- /dev/null +++ b/vcl/inc/ImplLayoutRuns.hxx @@ -0,0 +1,53 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/dllapi.h> + +#include <vector> + +// used for managing runs e.g. for BiDi, glyph and script fallback +class VCL_DLLPUBLIC ImplLayoutRuns +{ +private: + int mnRunIndex; + std::vector<int> maRuns; + +public: + ImplLayoutRuns() + { + mnRunIndex = 0; + maRuns.reserve(8); + } + + void Clear() { maRuns.clear(); } + void AddPos(int nCharPos, bool bRTL); + void AddRun(int nMinRunPos, int nEndRunPos, bool bRTL); + + bool IsEmpty() const { return maRuns.empty(); } + void ResetPos() { mnRunIndex = 0; } + void NextRun() { mnRunIndex += 2; } + bool GetRun(int* nMinRunPos, int* nEndRunPos, bool* bRTL) const; + bool GetNextPos(int* nCharPos, bool* bRTL); + bool PosIsInRun(int nCharPos) const; + bool PosIsInAnyRun(int nCharPos) const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/sallayout.hxx b/vcl/inc/sallayout.hxx index 594202b23239..b31aa202019c 100644 --- a/vcl/inc/sallayout.hxx +++ b/vcl/inc/sallayout.hxx @@ -20,25 +20,27 @@ #ifndef INCLUDED_VCL_INC_SALLAYOUT_HXX #define INCLUDED_VCL_INC_SALLAYOUT_HXX -#include <iostream> -#include <memory> -#include <vector> - -#include <hb.h> - -#include <com/sun/star/i18n/XBreakIterator.hpp> - #include <basegfx/polygon/b2dpolypolygon.hxx> -#include <i18nlangtag/languagetag.hxx> #include <tools/gen.hxx> #include <tools/degree.hxx> +#include <i18nlangtag/languagetag.hxx> + #include <vcl/dllapi.h> -#include <vcl/vclenum.hxx> // for typedef sal_UCS4 #include <vcl/devicecoordinate.hxx> +#include <vcl/vclenum.hxx> // for typedef sal_UCS4 #include <vcl/vcllayout.hxx> +#include "ImplLayoutRuns.hxx" #include "impglyphitem.hxx" +#include <com/sun/star/i18n/XBreakIterator.hpp> + +#include <hb.h> + +#include <iostream> +#include <memory> +#include <vector> + #define MAX_FALLBACK 16 @@ -50,29 +52,6 @@ namespace vcl::text { class TextLayoutCache; } -// used for managing runs e.g. for BiDi, glyph and script fallback -class ImplLayoutRuns -{ -private: - int mnRunIndex; - std::vector<int> maRuns; - -public: - ImplLayoutRuns() { mnRunIndex = 0; maRuns.reserve(8); } - - void Clear() { maRuns.clear(); } - void AddPos( int nCharPos, bool bRTL ); - void AddRun( int nMinRunPos, int nEndRunPos, bool bRTL ); - - bool IsEmpty() const { return maRuns.empty(); } - void ResetPos() { mnRunIndex = 0; } - void NextRun() { mnRunIndex += 2; } - bool GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL ) const; - bool GetNextPos( int* nCharPos, bool* bRTL ); - bool PosIsInRun( int nCharPos ) const; - bool PosIsInAnyRun( int nCharPos ) const; -}; - class MultiSalLayout final : public SalLayout { public: diff --git a/vcl/qa/cppunit/text.cxx b/vcl/qa/cppunit/text.cxx index c9f0d36c50c3..e56d1f27823e 100644 --- a/vcl/qa/cppunit/text.cxx +++ b/vcl/qa/cppunit/text.cxx @@ -10,7 +10,6 @@ #include <test/bootstrapfixture.hxx> #include <sal/log.hxx> #include <tools/stream.hxx> -#include <i18nlangtag/languagetag.hxx> #include <vcl/BitmapReadAccess.hxx> #include <vcl/graphicfilter.hxx> @@ -47,6 +46,10 @@ public: void testSimpleText(); void testVerticalText(); void testTextLayoutCache(); + void testImplLayoutRuns_AddPos(); + void testImplLayoutRuns_AddRuns(); + void testImplLayoutRuns_PosIsInRun(); + void testImplLayoutRuns_PosIsInAnyRun(); void testImplLayoutArgsBiDiStrong(); void testImplLayoutArgsBiDiRtl(); void testImplLayoutArgsRightAlign(); @@ -56,6 +59,10 @@ public: CPPUNIT_TEST(testSimpleText); CPPUNIT_TEST(testVerticalText); CPPUNIT_TEST(testTextLayoutCache); + CPPUNIT_TEST(testImplLayoutRuns_AddPos); + CPPUNIT_TEST(testImplLayoutRuns_AddRuns); + CPPUNIT_TEST(testImplLayoutRuns_PosIsInRun); + CPPUNIT_TEST(testImplLayoutRuns_PosIsInAnyRun); CPPUNIT_TEST(testImplLayoutArgsBiDiStrong); CPPUNIT_TEST(testImplLayoutArgsBiDiRtl); CPPUNIT_TEST(testImplLayoutArgsRightAlign); @@ -407,6 +414,187 @@ void VclTextTest::testTextLayoutCache() CPPUNIT_ASSERT_EQUAL(51, run2.nEnd); } +void 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* pCharPos = new int(0); + bool* pRightToLeftMarker = new bool(false); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(1, *pCharPos); + CPPUNIT_ASSERT(!*pRightToLeftMarker); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(2, *pCharPos); + CPPUNIT_ASSERT(!*pRightToLeftMarker); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(3, *pCharPos); + CPPUNIT_ASSERT(!*pRightToLeftMarker); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(4, *pCharPos); + CPPUNIT_ASSERT(*pRightToLeftMarker); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(5, *pCharPos); + CPPUNIT_ASSERT(!*pRightToLeftMarker); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(6, *pCharPos); + CPPUNIT_ASSERT(*pRightToLeftMarker); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(7, *pCharPos); + CPPUNIT_ASSERT(!*pRightToLeftMarker); + + // no next position, we are running off the end + CPPUNIT_ASSERT(!aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + + aRuns.ResetPos(); + + int nMinRunPos, nEndRunPos; + bool* pRightToLeft = new bool(false); + + CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, pRightToLeft)); + CPPUNIT_ASSERT_EQUAL(1, nMinRunPos); + CPPUNIT_ASSERT_EQUAL(4, nEndRunPos); + CPPUNIT_ASSERT(!*pRightToLeft); + + aRuns.NextRun(); + CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, pRightToLeft)); + CPPUNIT_ASSERT_EQUAL(4, nMinRunPos); + CPPUNIT_ASSERT_EQUAL(5, nEndRunPos); + CPPUNIT_ASSERT(*pRightToLeft); + + aRuns.NextRun(); + CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, pRightToLeft)); + CPPUNIT_ASSERT_EQUAL(5, nMinRunPos); + CPPUNIT_ASSERT_EQUAL(6, nEndRunPos); + CPPUNIT_ASSERT(!*pRightToLeft); + + aRuns.NextRun(); + CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, pRightToLeft)); + CPPUNIT_ASSERT_EQUAL(6, nMinRunPos); + CPPUNIT_ASSERT_EQUAL(7, nEndRunPos); + CPPUNIT_ASSERT(*pRightToLeft); + + // test clear + aRuns.Clear(); + CPPUNIT_ASSERT(aRuns.IsEmpty()); +} + +void 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* pCharPos = new int(0); + bool* pRightToLeftMarker = new bool(false); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(1, *pCharPos); + CPPUNIT_ASSERT(!*pRightToLeftMarker); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(2, *pCharPos); + CPPUNIT_ASSERT(!*pRightToLeftMarker); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(3, *pCharPos); + CPPUNIT_ASSERT(!*pRightToLeftMarker); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(4, *pCharPos); + CPPUNIT_ASSERT(*pRightToLeftMarker); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(5, *pCharPos); + CPPUNIT_ASSERT(!*pRightToLeftMarker); + + CPPUNIT_ASSERT(aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + CPPUNIT_ASSERT_EQUAL(6, *pCharPos); + CPPUNIT_ASSERT(*pRightToLeftMarker); + + // no next position, we are running off the end + CPPUNIT_ASSERT(!aRuns.GetNextPos(pCharPos, pRightToLeftMarker)); + + aRuns.ResetPos(); + + int nMinRunPos, nEndRunPos; + bool* pRightToLeft = new bool(false); + + CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, pRightToLeft)); + CPPUNIT_ASSERT_EQUAL(1, nMinRunPos); + CPPUNIT_ASSERT_EQUAL(4, nEndRunPos); + CPPUNIT_ASSERT(!*pRightToLeft); + + aRuns.NextRun(); + CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, pRightToLeft)); + CPPUNIT_ASSERT_EQUAL(4, nMinRunPos); + CPPUNIT_ASSERT_EQUAL(5, nEndRunPos); + CPPUNIT_ASSERT(*pRightToLeft); + + aRuns.NextRun(); + CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, pRightToLeft)); + CPPUNIT_ASSERT_EQUAL(5, nMinRunPos); + CPPUNIT_ASSERT_EQUAL(6, nEndRunPos); + CPPUNIT_ASSERT(!*pRightToLeft); + + aRuns.NextRun(); + CPPUNIT_ASSERT(aRuns.GetRun(&nMinRunPos, &nEndRunPos, pRightToLeft)); + CPPUNIT_ASSERT_EQUAL(6, nMinRunPos); + CPPUNIT_ASSERT_EQUAL(7, nEndRunPos); + CPPUNIT_ASSERT(*pRightToLeft); +} + +void 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)); +} + +void 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)); +} + void VclTextTest::testImplLayoutArgsBiDiStrong() { OUString sTestString = u"The quick brown fox\n jumped over the lazy dog" diff --git a/vcl/source/gdi/sallayout.cxx b/vcl/source/gdi/sallayout.cxx index debe3017a92a..7a69defd60f5 100644 --- a/vcl/source/gdi/sallayout.cxx +++ b/vcl/source/gdi/sallayout.cxx @@ -129,163 +129,6 @@ sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang ) return nChar; } -void ImplLayoutRuns::AddPos( int nCharPos, bool bRTL ) -{ - // check if charpos could extend current run - int nIndex = maRuns.size(); - if( nIndex >= 2 ) - { - int nRunPos0 = maRuns[ nIndex-2 ]; - int nRunPos1 = maRuns[ nIndex-1 ]; - if( ((nCharPos + int(bRTL)) == nRunPos1) && ((nRunPos0 > nRunPos1) == bRTL) ) - { - // extend current run by new charpos - maRuns[ nIndex-1 ] = nCharPos + int(!bRTL); - return; - } - // ignore new charpos when it is in current run - if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) ) - return; - if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) ) - return; - } - - // else append a new run consisting of the new charpos - maRuns.push_back( nCharPos + (bRTL ? 1 : 0) ); - maRuns.push_back( nCharPos + (bRTL ? 0 : 1) ); -} - -void ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL ) -{ - if( nCharPos0 == nCharPos1 ) - return; - - // swap if needed - if( bRTL == (nCharPos0 < nCharPos1) ) - { - int nTemp = nCharPos0; - nCharPos0 = nCharPos1; - nCharPos1 = nTemp; - } - - if (maRuns.size() >= 2 && nCharPos0 == maRuns[maRuns.size() - 2] && nCharPos1 == maRuns[maRuns.size() - 1]) - { - //this run is the same as the last - return; - } - - // append new run - maRuns.push_back( nCharPos0 ); - maRuns.push_back( nCharPos1 ); -} - -bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const -{ - if( mnRunIndex >= static_cast<int>(maRuns.size()) ) - return false; - - int nMinCharPos = maRuns[ mnRunIndex+0 ]; - int nEndCharPos = maRuns[ mnRunIndex+1 ]; - if( nMinCharPos > nEndCharPos ) // reversed in RTL case - { - int nTemp = nMinCharPos; - nMinCharPos = nEndCharPos; - nEndCharPos = nTemp; - } - - if( nCharPos < nMinCharPos ) - return false; - if( nCharPos >= nEndCharPos ) - return false; - return true; -} - -bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const -{ - bool bRet = false; - int nRunIndex = mnRunIndex; - - ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this); - - pThis->ResetPos(); - - for (size_t i = 0; i < maRuns.size(); i+=2) - { - bRet = PosIsInRun( nCharPos ); - if( bRet ) - break; - pThis->NextRun(); - } - - pThis->mnRunIndex = nRunIndex; - return bRet; -} - -bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft ) -{ - // negative nCharPos => reset to first run - if( *nCharPos < 0 ) - mnRunIndex = 0; - - // return false when all runs completed - if( mnRunIndex >= static_cast<int>(maRuns.size()) ) - return false; - - int nRunPos0 = maRuns[ mnRunIndex+0 ]; - int nRunPos1 = maRuns[ mnRunIndex+1 ]; - *bRightToLeft = (nRunPos0 > nRunPos1); - - if( *nCharPos < 0 ) - { - // get first valid nCharPos in run - *nCharPos = nRunPos0; - } - else - { - // advance to next nCharPos for LTR case - if( !*bRightToLeft ) - ++(*nCharPos); - - // advance to next run if current run is completed - if( *nCharPos == nRunPos1 ) - { - if( (mnRunIndex += 2) >= static_cast<int>(maRuns.size()) ) - return false; - nRunPos0 = maRuns[ mnRunIndex+0 ]; - nRunPos1 = maRuns[ mnRunIndex+1 ]; - *bRightToLeft = (nRunPos0 > nRunPos1); - *nCharPos = nRunPos0; - } - } - - // advance to next nCharPos for RTL case - if( *bRightToLeft ) - --(*nCharPos); - - return true; -} - -bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const -{ - if( mnRunIndex >= static_cast<int>(maRuns.size()) ) - return false; - - int nRunPos0 = maRuns[ mnRunIndex+0 ]; - int nRunPos1 = maRuns[ mnRunIndex+1 ]; - *bRightToLeft = (nRunPos1 < nRunPos0) ; - if( !*bRightToLeft ) - { - *nMinRunPos = nRunPos0; - *nEndRunPos = nRunPos1; - } - else - { - *nMinRunPos = nRunPos1; - *nEndRunPos = nRunPos0; - } - return true; -} - SalLayout::SalLayout() : mnMinCharPos( -1 ), mnEndCharPos( -1 ), diff --git a/vcl/source/text/ImplLayoutRuns.cxx b/vcl/source/text/ImplLayoutRuns.cxx new file mode 100644 index 000000000000..a560e17e1b8b --- /dev/null +++ b/vcl/source/text/ImplLayoutRuns.cxx @@ -0,0 +1,179 @@ +/* -*- 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 <ImplLayoutRuns.hxx> + +void ImplLayoutRuns::AddPos( int nCharPos, bool bRTL ) +{ + // check if charpos could extend current run + int nIndex = maRuns.size(); + if( nIndex >= 2 ) + { + int nRunPos0 = maRuns[ nIndex-2 ]; + int nRunPos1 = maRuns[ nIndex-1 ]; + if( ((nCharPos + int(bRTL)) == nRunPos1) && ((nRunPos0 > nRunPos1) == bRTL) ) + { + // extend current run by new charpos + maRuns[ nIndex-1 ] = nCharPos + int(!bRTL); + return; + } + // ignore new charpos when it is in current run + if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) ) + return; + if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) ) + return; + } + + // else append a new run consisting of the new charpos + maRuns.push_back( nCharPos + (bRTL ? 1 : 0) ); + maRuns.push_back( nCharPos + (bRTL ? 0 : 1) ); +} + +void ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL ) +{ + if( nCharPos0 == nCharPos1 ) + return; + + // swap if needed + if( bRTL == (nCharPos0 < nCharPos1) ) + { + int nTemp = nCharPos0; + nCharPos0 = nCharPos1; + nCharPos1 = nTemp; + } + + if (maRuns.size() >= 2 && nCharPos0 == maRuns[maRuns.size() - 2] && nCharPos1 == maRuns[maRuns.size() - 1]) + { + //this run is the same as the last + return; + } + + // append new run + maRuns.push_back( nCharPos0 ); + maRuns.push_back( nCharPos1 ); +} + +bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const +{ + if( mnRunIndex >= static_cast<int>(maRuns.size()) ) + return false; + + int nMinCharPos = maRuns[ mnRunIndex+0 ]; + int nEndCharPos = maRuns[ mnRunIndex+1 ]; + if( nMinCharPos > nEndCharPos ) // reversed in RTL case + { + int nTemp = nMinCharPos; + nMinCharPos = nEndCharPos; + nEndCharPos = nTemp; + } + + if( nCharPos < nMinCharPos ) + return false; + if( nCharPos >= nEndCharPos ) + return false; + return true; +} + +bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const +{ + bool bRet = false; + int nRunIndex = mnRunIndex; + + ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this); + + pThis->ResetPos(); + + for (size_t i = 0; i < maRuns.size(); i+=2) + { + bRet = PosIsInRun( nCharPos ); + if( bRet ) + break; + pThis->NextRun(); + } + + pThis->mnRunIndex = nRunIndex; + return bRet; +} + +bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft ) +{ + // negative nCharPos => reset to first run + if( *nCharPos < 0 ) + mnRunIndex = 0; + + // return false when all runs completed + if( mnRunIndex >= static_cast<int>(maRuns.size()) ) + return false; + + int nRunPos0 = maRuns[ mnRunIndex+0 ]; + int nRunPos1 = maRuns[ mnRunIndex+1 ]; + *bRightToLeft = (nRunPos0 > nRunPos1); + + if( *nCharPos < 0 ) + { + // get first valid nCharPos in run + *nCharPos = nRunPos0; + } + else + { + // advance to next nCharPos for LTR case + if( !*bRightToLeft ) + ++(*nCharPos); + + // advance to next run if current run is completed + if( *nCharPos == nRunPos1 ) + { + if( (mnRunIndex += 2) >= static_cast<int>(maRuns.size()) ) + return false; + nRunPos0 = maRuns[ mnRunIndex+0 ]; + nRunPos1 = maRuns[ mnRunIndex+1 ]; + *bRightToLeft = (nRunPos0 > nRunPos1); + *nCharPos = nRunPos0; + } + } + + // advance to next nCharPos for RTL case + if( *bRightToLeft ) + --(*nCharPos); + + return true; +} + +bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const +{ + if( mnRunIndex >= static_cast<int>(maRuns.size()) ) + return false; + + int nRunPos0 = maRuns[ mnRunIndex+0 ]; + int nRunPos1 = maRuns[ mnRunIndex+1 ]; + *bRightToLeft = (nRunPos1 < nRunPos0) ; + if( !*bRightToLeft ) + { + *nMinRunPos = nRunPos0; + *nEndRunPos = nRunPos1; + } + else + { + *nMinRunPos = nRunPos1; + *nEndRunPos = nRunPos0; + } + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */