sw/Library_sw.mk | 1 sw/source/core/txtnode/fntcache.cxx | 67 ++------------------------ sw/source/core/txtnode/justify.cxx | 90 ++++++++++++++++++++++++++++++++++++ sw/source/core/txtnode/justify.hxx | 31 ++++++++++++ 4 files changed, 128 insertions(+), 61 deletions(-)
New commits: commit f8560e09006cec5cc6ef26ccbf4f21aa28c22ac3 Author: Mark Hung <mark...@gmail.com> AuthorDate: Fri May 13 00:06:49 2022 +0800 Commit: Mark Hung <mark...@gmail.com> CommitDate: Wed May 18 14:32:06 2022 +0200 tdf#149017 fix space distribution in SwFntObj::DrawText() - Fix logic problem for the last element. - Some glyphs may be made of many sal_Unicode ( ex. Unicode IVS ), take care ( fix ) of the following case a) 1 as the second glyph b) n-1 as the previous glyph. c) nCnt-1 as the last glyph. - Move the code to justify.cxx and its own namespace. Change-Id: Ice2236a54e8290a489fb2d887a326ccc4768213e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/134243 Tested-by: Jenkins Reviewed-by: Mark Hung <mark...@gmail.com> diff --git a/sw/Library_sw.mk b/sw/Library_sw.mk index 9ce3a482b766..eec16fd5612c 100644 --- a/sw/Library_sw.mk +++ b/sw/Library_sw.mk @@ -437,6 +437,7 @@ $(eval $(call gb_Library_add_exception_objects,sw,\ sw/source/core/txtnode/atrtox \ sw/source/core/txtnode/attrlinebreak \ sw/source/core/txtnode/chrfmt \ + sw/source/core/txtnode/justify \ sw/source/core/txtnode/fmtatr2 \ sw/source/core/txtnode/fntcache \ sw/source/core/txtnode/fntcap \ diff --git a/sw/source/core/txtnode/fntcache.cxx b/sw/source/core/txtnode/fntcache.cxx index 838547282770..acac99fb4701 100644 --- a/sw/source/core/txtnode/fntcache.cxx +++ b/sw/source/core/txtnode/fntcache.cxx @@ -56,6 +56,7 @@ #include <o3tl/hash_combine.hxx> #include <cstdint> #include <memory> +#include "justify.hxx" using namespace ::com::sun::star; @@ -1553,7 +1554,6 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) else nCnt = nCnt - rInf.GetIdx(); nCnt = std::min(nCnt, rInf.GetLen()); - tools::Long nKernSum = rInf.GetKern(); sal_Unicode cChPrev = rInf.GetText()[sal_Int32(rInf.GetIdx())]; // In case of a single underlined space in justified text, @@ -1578,67 +1578,11 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) } else { - // nSpaceSum contains the sum of the intermediate space distributed - // among Spaces by the Justification. - // The Spaces themselves will be positioned in the middle of the - // intermediate space, hence the nSpace/2. - // In case of word-by-word underlining they have to be positioned - // at the beginning of the intermediate space, so that the space - // is not underlined. - // A Space at the beginning or end of the text must be positioned - // before (resp. after) the whole intermediate space, otherwise - // the underline/strike-through would have gaps. - tools::Long nSpaceSum = 0; - // in word line mode and for Arabic, we disable the half space trick: - const tools::Long nHalfSpace = m_pPrtFont->IsWordLineMode() || bNoHalfSpace ? 0 : nSpaceAdd / 2; - const tools::Long nOtherHalf = nSpaceAdd - nHalfSpace; - if ( nSpaceAdd && ( cChPrev == CH_BLANK ) ) - nSpaceSum = nHalfSpace; - for (sal_Int32 i = 1; i < sal_Int32(nCnt); ++i, nKernSum += rInf.GetKern()) - { - sal_Unicode nCh = rInf.GetText()[sal_Int32(rInf.GetIdx()) + i]; - - // Apply SpaceSum - if (cChPrev == CH_BLANK) - { - // no Pixel is lost: - nSpaceSum += nOtherHalf; - } - - if (nCh == CH_BLANK) - { - if (i + 1 == sal_Int32(nCnt)) - nSpaceSum += nSpaceAdd; - else - nSpaceSum += nHalfSpace; - } - - tools::Long nOldValue = aKernArray[i-1]; - - cChPrev = nCh; - aKernArray[i-1] += nKernSum + nSpaceSum; - // In word line mode and for Arabic, we disabled the half space trick. If a portion - // ends with a blank, the full nSpaceAdd value has been added to the character in - // front of the blank. This leads to painting artifacts, therefore we remove the - // nSpaceAdd value again: - if ((bNoHalfSpace || m_pPrtFont->IsWordLineMode()) && i+1 == sal_Int32(nCnt) && nCh == CH_BLANK) - aKernArray[i-1] = aKernArray[i-1] - nSpaceAdd; - - // Some glyph items use more than one sal_Unicode, eg. CJK ideograph extensions - // or unicode IVS. Don't assign space multiple times in case the original text array - // have the same values. - while(i < sal_Int32(nCnt) && aKernArray[i] == nOldValue) - { - aKernArray[i] = aKernArray[i-1]; - ++i; - } - } - - // the layout engine requires the total width of the output - tools::Long nOldValue = aKernArray[sal_Int32(rInf.GetLen()) - 1]; - for(sal_Int32 i = sal_Int32(rInf.GetLen()) - 1; i >= 0 && aKernArray[i] == nOldValue; --i) - aKernArray[i] += nKernSum + nSpaceSum; + if (m_pPrtFont->IsWordLineMode()) + bNoHalfSpace = true; + Justify::SpaceDistribution(aKernArray, rInf.GetText(), sal_Int32(rInf.GetIdx()), + sal_Int32(nCnt), nSpaceAdd, rInf.GetKern(), bNoHalfSpace); if( rInf.GetGreyWave() ) { @@ -1714,6 +1658,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf ) // anything to do? if (rInf.GetWrong() || rInf.GetGrammarCheck() || rInf.GetSmartTags()) { + const tools::Long nHalfSpace = bNoHalfSpace ? 0 : nSpaceAdd / 2; CalcLinePosData aCalcLinePosData(rInf, GetFont(), nCnt, bSwitchH2V, bSwitchH2VLRBT, bSwitchL2R, nHalfSpace, aKernArray.data(), bBidiPor); diff --git a/sw/source/core/txtnode/justify.cxx b/sw/source/core/txtnode/justify.cxx new file mode 100644 index 000000000000..f465d3bd5e9a --- /dev/null +++ b/sw/source/core/txtnode/justify.cxx @@ -0,0 +1,90 @@ +/* -*- 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/. + */ + +#include <vector> +#include <sal/types.h> +#include <swfont.hxx> +#include "justify.hxx" + +namespace Justify +{ +void SpaceDistribution(std::vector<sal_Int32>& rKernArray, const OUString& rText, sal_Int32 nStt, + sal_Int32 nLen, tools::Long nSpaceAdd, tools::Long nKern, bool bNoHalfSpace) +{ + assert(nStt + nLen <= rText.getLength()); + assert(nLen <= sal_Int32(rKernArray.size())); + // nSpaceSum contains the sum of the intermediate space distributed + // among Spaces by the Justification. + // The Spaces themselves will be positioned in the middle of the + // intermediate space, hence the nSpace/2. + // In case of word-by-word underlining they have to be positioned + // at the beginning of the intermediate space, so that the space + // is not underlined. + // A Space at the beginning or end of the text must be positioned + // before (resp. after) the whole intermediate space, otherwise + // the underline/strike-through would have gaps. + tools::Long nSpaceSum = 0; + // in word line mode and for Arabic, we disable the half space trick: + const tools::Long nHalfSpace = bNoHalfSpace ? 0 : nSpaceAdd / 2; + const tools::Long nOtherHalf = nSpaceAdd - nHalfSpace; + tools::Long nKernSum = nKern; + sal_Unicode cChPrev = rText[nStt]; + + if (nSpaceAdd && (cChPrev == CH_BLANK)) + nSpaceSum = nHalfSpace; + + sal_Int32 nPrevIdx = 0; + + for (sal_Int32 i = 1; i < nLen; ++i, nKernSum += nKern) + { + // Find the beginning of the next cluster that has a different kern value. + while (i < nLen && rKernArray[i] == rKernArray[nPrevIdx]) + ++i; + + if (i == nLen) + break; + + sal_Unicode nCh = rText[nStt + i]; + + // Apply SpaceSum + if (cChPrev == CH_BLANK) + { + // no Pixel is lost: + nSpaceSum += nOtherHalf; + } + + if (nCh == CH_BLANK) + { + if (i + 1 == nLen) + nSpaceSum += nSpaceAdd; + else + nSpaceSum += nHalfSpace; + } + + cChPrev = nCh; + rKernArray[nPrevIdx] += nKernSum + nSpaceSum; + // In word line mode and for Arabic, we disabled the half space trick. If a portion + // ends with a blank, the full nSpaceAdd value has been added to the character in + // front of the blank. This leads to painting artifacts, therefore we remove the + // nSpaceAdd value again: + if (bNoHalfSpace && i + 1 == nLen && nCh == CH_BLANK) + rKernArray[nPrevIdx] = rKernArray[nPrevIdx] - nSpaceAdd; + + // Advance nPrevIdx and assign kern values to previous cluster. + for (tools::Long nValue = rKernArray[nPrevIdx++]; nPrevIdx < i; ++nPrevIdx) + rKernArray[nPrevIdx] = nValue; + } + + // the layout engine requires the total width of the output + while (nPrevIdx < nLen) + rKernArray[nPrevIdx++] += nKernSum + nSpaceSum; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/txtnode/justify.hxx b/sw/source/core/txtnode/justify.hxx new file mode 100644 index 000000000000..5c36a8f5fd5e --- /dev/null +++ b/sw/source/core/txtnode/justify.hxx @@ -0,0 +1,31 @@ +/* -*- 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/. + */ + +#pragma once +#include <sal/types.h> + +using namespace ::com::sun::star; +namespace Justify +{ +/// Distribute space between words and letters. +/// @param[in,out] rKernArray text positions from OutDev::GetTextArray(). +/// @param rText string used to determine where space and kern are inserted. +/// @param nStt starting index of rText. +/// @param nLen number of elements to process in rKernArray and rText. +/// @param nSpaceAdd amount of space to insert for each CH_BLANK. +/// @param nKern amount of space to insert between letters. +/// @param bNoHalfSpace whether to split the space into two halves. +/// Splitted spaces are inserted before and after CH_BLANK. +/// Set to true in word line mode and for Arabic text to avoid splitting. +SW_DLLPUBLIC void SpaceDistribution(std::vector<sal_Int32>& rKernArray, const OUString& rText, + sal_Int32 nStt, sal_Int32 nLen, tools::Long nSpaceAdd, + tools::Long nKern, bool bNoHalfSpace); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */