sw/qa/core/txtnode/justify.cxx      |   15 ++++
 sw/source/core/txtnode/fntcache.cxx |  100 +-----------------------------
 sw/source/core/txtnode/justify.cxx  |  118 ++++++++++++++++++++++++++++++++++++
 sw/source/core/txtnode/justify.hxx  |   18 +++++
 4 files changed, 156 insertions(+), 95 deletions(-)

New commits:
commit 7f77a180dd22e7d07b1840660dc9a6e66463b84f
Author:     Mark Hung <mark...@gmail.com>
AuthorDate: Sat May 21 21:11:24 2022 +0800
Commit:     Mark Hung <mark...@gmail.com>
CommitDate: Wed May 25 16:10:16 2022 +0200

    sw: refactor and create Justify::SnapToGrid()
    
    Move snapt to grid code from SwFntObj::DrawText() to Justify::SnapToGrid()
    and create a simple unit test case testSnapToGrid.
    
    Note that SnapToGrid() is for "Snap to char is on" case.
    
    Change-Id: Ib9b3a08c744216e37dd260434700cbf3f079a0fe
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/134707
    Tested-by: Jenkins
    Reviewed-by: Mark Hung <mark...@gmail.com>

diff --git a/sw/qa/core/txtnode/justify.cxx b/sw/qa/core/txtnode/justify.cxx
index 46a52f851efc..88ae8bef3865 100644
--- a/sw/qa/core/txtnode/justify.cxx
+++ b/sw/qa/core/txtnode/justify.cxx
@@ -109,4 +109,19 @@ CPPUNIT_TEST_FIXTURE(SwCoreJustifyTest, 
testSpaceDistributionUnicodeIVS)
     CPPUNIT_ASSERT_EQUAL(aExpected, aActual);
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreJustifyTest, testSnapToGrid)
+{
+    tools::Long nDelta = 0;
+    // "曰〈道高一尺化太平〉云云"
+    static const OUStringLiteral aText
+        = 
u"\u66f0\u3008\u9053\u9ad8\u4e00\u5c3a\u5316\u592a\u5e73\u3009\u4e91\u4e91";
+    CharWidthArray aActual{ 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, 
880, 880 };
+    CharWidthArray aExpected{
+        1360, 1040, 1200, 1200, 1200, 1200, 1200, 1200, 1040, 1360, 1200, 1040
+    };
+    aActual.InvokeWithKernArray(
+        [&] { nDelta = Justify::SnapToGrid(aActual.maArray, aText, 0, 12, 400, 
14400); });
+    CPPUNIT_ASSERT_EQUAL(aExpected, aActual);
+    CPPUNIT_ASSERT_EQUAL(tools::Long(160), nDelta);
+}
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/fntcache.cxx 
b/sw/source/core/txtnode/fntcache.cxx
index acac99fb4701..8036af0e56c5 100644
--- a/sw/source/core/txtnode/fntcache.cxx
+++ b/sw/source/core/txtnode/fntcache.cxx
@@ -604,27 +604,6 @@ void SwFntObj::SetDevFont( const SwViewShell *pSh, 
OutputDevice& rOut )
  *      on printer, !Kerning   => DrawText
  *      on printer + Kerning   => DrawStretchText
  */
-static sal_uInt8 lcl_WhichPunctuation( sal_Unicode cChar )
-{
-    if ( ( cChar < 0x3001 || cChar > 0x3002 ) &&
-            ( cChar < 0x3008 || cChar > 0x3011 ) &&
-            ( cChar < 0x3014 || cChar > 0x301F ) &&
-              0xFF62 != cChar && 0xFF63 != cChar )
-        // no punctuation
-        return SwScriptInfo::NONE;
-    else if ( 0x3001 == cChar || 0x3002 == cChar ||
-              0x3009 == cChar || 0x300B == cChar ||
-              0x300D == cChar || 0x300F == cChar ||
-              0x3011 == cChar || 0x3015 == cChar ||
-              0x3017 == cChar || 0x3019 == cChar ||
-              0x301B == cChar || 0x301E == cChar ||
-              0x301F == cChar || 0xFF63 == cChar )
-        // right punctuation
-        return SwScriptInfo::SPECIAL_RIGHT;
-
-    return SwScriptInfo::SPECIAL_LEFT;
-}
-
 static bool lcl_IsMonoSpaceFont( const vcl::RenderContext& rOut )
 {
     const tools::Long nWidth1 = rOut.GetTextWidth( OUString( u'\x3008' ) );
@@ -970,81 +949,12 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
             else
                 GetTextArray(rInf.GetOut(), rInf, aKernArray);
 
-            // Change the average width per character to an appropriate grid 
width
-            // basically get the ratio of the avg width to the grid unit 
width, then
-            // multiple this ratio to give the new avg width - which in this 
case
-            // gives a new grid width unit size
-
-            tools::Long nAvgWidthPerChar = aKernArray[sal_Int32(rInf.GetLen()) 
- 1] / sal_Int32(rInf.GetLen());
-
-            const sal_uLong nRatioAvgWidthCharToGridWidth = nAvgWidthPerChar ?
-                                ( nAvgWidthPerChar - 1 ) / nGridWidth + 1:
-                                1;
-
-            nAvgWidthPerChar = nRatioAvgWidthCharToGridWidth * nGridWidth;
-
-            // the absolute end position of the first character is also its 
width
-            tools::Long nCharWidth = aKernArray[ 0 ];
-            sal_uLong nHalfWidth = nAvgWidthPerChar / 2;
-
-            tools::Long nNextFix=0;
-
-            // we work out the start position (origin) of the first character,
-            // and we set the next "fix" offset to half the width of the char.
-            // The exceptions are for punctuation characters that are not 
centered
-            // so in these cases we just add half a regular "average" 
character width
-            // to the first characters actual width to allow the next 
character to
-            // be centered automatically
-            // If the character is "special right", then the offset is correct 
already
-            // so the fix offset is as normal - half the average character 
width
-
-            sal_Unicode cChar = rInf.GetText()[ sal_Int32(rInf.GetIdx()) ];
-            sal_uInt8 nType = lcl_WhichPunctuation( cChar );
-            switch ( nType )
-            {
-            // centre character
-            case SwScriptInfo::NONE :
-                aTextOriginPos.AdjustX(( nAvgWidthPerChar - nCharWidth ) / 2 );
-                nNextFix = nCharWidth / 2;
-                break;
-            case SwScriptInfo::SPECIAL_RIGHT :
-                nNextFix = nHalfWidth;
-                break;
-            // punctuation
-            default:
-                aTextOriginPos.AdjustX(nAvgWidthPerChar - nCharWidth );
-                nNextFix = nCharWidth - nHalfWidth;
-            }
-
-            // calculate offsets
-            for (sal_Int32 j = 1; j < sal_Int32(rInf.GetLen()); ++j)
-            {
-                tools::Long nCurrentCharWidth = aKernArray[ j ] - aKernArray[ 
j - 1 ];
-                nNextFix += nAvgWidthPerChar;
-
-                // almost the same as getting the offset for the first 
character:
-                // punctuation characters are not centered, so just add half an
-                // average character width minus the characters actual char 
width
-                // to get the offset into the centre of the next character
-
-                cChar = rInf.GetText()[ sal_Int32(rInf.GetIdx()) + j ];
-                nType = lcl_WhichPunctuation( cChar );
-                switch ( nType )
-                {
-                case SwScriptInfo::NONE :
-                    aKernArray[ j - 1 ] = nNextFix - ( nCurrentCharWidth / 2 );
-                    break;
-                case SwScriptInfo::SPECIAL_RIGHT :
-                    aKernArray[ j - 1 ] = nNextFix - nHalfWidth;
-                    break;
-                default:
-                    aKernArray[ j - 1 ] = nNextFix + nHalfWidth - 
nCurrentCharWidth;
-                }
-            }
+            tools::Long nDelta
+                = Justify::SnapToGrid(aKernArray, rInf.GetText(), 
sal_Int32(rInf.GetIdx()),
+                                      sal_Int32(rInf.GetLen()), nGridWidth, 
rInf.GetWidth());
 
-            // the layout engine requires the total width of the output
-            aKernArray[sal_Int32(rInf.GetLen()) - 1] = rInf.GetWidth() -
-                                              aTextOriginPos.X() + 
rInf.GetPos().X() ;
+            if (nDelta)
+                aTextOriginPos.AdjustX(nDelta);
 
             if ( bSwitchH2V )
                 rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
diff --git a/sw/source/core/txtnode/justify.cxx 
b/sw/source/core/txtnode/justify.cxx
index f465d3bd5e9a..0d97ed470f96 100644
--- a/sw/source/core/txtnode/justify.cxx
+++ b/sw/source/core/txtnode/justify.cxx
@@ -12,6 +12,33 @@
 #include <swfont.hxx>
 #include "justify.hxx"
 
+namespace
+{
+enum class IdeographicPunctuationClass
+{
+    NONE,
+    OPEN_BRACKET,
+    CLOSE_BRACKET,
+    COMMA_OR_FULLSTOP
+};
+
+IdeographicPunctuationClass lcl_WhichPunctuationClass(sal_Unicode cChar)
+{
+    if ((cChar < 0x3001 || cChar > 0x3002) && (cChar < 0x3008 || cChar > 
0x3011)
+        && (cChar < 0x3014 || cChar > 0x301F) && 0xFF62 != cChar && 0xFF63 != 
cChar)
+        return IdeographicPunctuationClass::NONE;
+    else if (0x3001 == cChar || 0x3002 == cChar)
+        return IdeographicPunctuationClass::COMMA_OR_FULLSTOP;
+    else if (0x3009 == cChar || 0x300B == cChar || 0x300D == cChar || 0x300F 
== cChar
+             || 0x3011 == cChar || 0x3015 == cChar || 0x3017 == cChar || 
0x3019 == cChar
+             || 0x301B == cChar || 0x301E == cChar || 0x301F == cChar || 
0xFF63 == cChar)
+        // right punctuation
+        return IdeographicPunctuationClass::CLOSE_BRACKET;
+
+    return IdeographicPunctuationClass::OPEN_BRACKET;
+}
+}
+
 namespace Justify
 {
 void SpaceDistribution(std::vector<sal_Int32>& rKernArray, const OUString& 
rText, sal_Int32 nStt,
@@ -85,6 +112,97 @@ void SpaceDistribution(std::vector<sal_Int32>& rKernArray, 
const OUString& rText
     while (nPrevIdx < nLen)
         rKernArray[nPrevIdx++] += nKernSum + nSpaceSum;
 }
+
+tools::Long SnapToGrid(std::vector<sal_Int32>& rKernArray, const OUString& 
rText, sal_Int32 nStt,
+                       sal_Int32 nLen, tools::Long nGridWidth, tools::Long 
nWidth)
+{
+    assert(nStt + nLen <= rText.getLength());
+    assert(nLen <= sal_Int32(rKernArray.size()));
+
+    tools::Long nDelta = 0; // delta offset to text origin
+
+    // Change the average width per character to an appropriate grid width
+    // basically get the ratio of the avg width to the grid unit width, then
+    // multiple this ratio to give the new avg width - which in this case
+    // gives a new grid width unit size
+
+    tools::Long nAvgWidthPerChar = rKernArray[nLen - 1] / nLen;
+
+    const sal_uLong nRatioAvgWidthCharToGridWidth
+        = nAvgWidthPerChar ? (nAvgWidthPerChar - 1) / nGridWidth + 1 : 1;
+
+    nAvgWidthPerChar = nRatioAvgWidthCharToGridWidth * nGridWidth;
+
+    // the absolute end position of the first character is also its width
+    tools::Long nCharWidth = rKernArray[0];
+    sal_uLong nHalfWidth = nAvgWidthPerChar / 2;
+
+    tools::Long nNextFix = 0;
+
+    // we work out the start position (origin) of the first character,
+    // and we set the next "fix" offset to half the width of the char.
+    // The exceptions are for punctuation characters that are not centered
+    // so in these cases we just add half a regular "average" character width
+    // to the first characters actual width to allow the next character to
+    // be centered automatically
+    // If the character is "special right", then the offset is correct already
+    // so the fix offset is as normal - half the average character width
+
+    sal_Unicode cChar = rText[nStt];
+    IdeographicPunctuationClass eClass = lcl_WhichPunctuationClass(cChar);
+    switch (eClass)
+    {
+        case IdeographicPunctuationClass::NONE:
+            // Centered
+            nDelta = (nAvgWidthPerChar - nCharWidth) / 2;
+            nNextFix = nCharWidth / 2;
+            break;
+        case IdeographicPunctuationClass::CLOSE_BRACKET:
+        case IdeographicPunctuationClass::COMMA_OR_FULLSTOP:
+            // Closer to previous ideograph
+            nNextFix = nHalfWidth;
+            break;
+        default:
+            // case IdeographicPunctuationClass::OPEN_BRACKET: closer to next 
ideograph.
+            nDelta = nAvgWidthPerChar - nCharWidth;
+            nNextFix = nCharWidth - nHalfWidth;
+    }
+
+    // calculate offsets
+    for (sal_Int32 j = 1; j < nLen; ++j)
+    {
+        tools::Long nCurrentCharWidth = rKernArray[j] - rKernArray[j - 1];
+        nNextFix += nAvgWidthPerChar;
+
+        // almost the same as getting the offset for the first character:
+        // punctuation characters are not centered, so just add half an
+        // average character width minus the characters actual char width
+        // to get the offset into the centre of the next character
+
+        cChar = rText[nStt + j];
+        eClass = lcl_WhichPunctuationClass(cChar);
+        switch (eClass)
+        {
+            case IdeographicPunctuationClass::NONE:
+                // Centered
+                rKernArray[j - 1] = nNextFix - (nCurrentCharWidth / 2);
+                break;
+            case IdeographicPunctuationClass::CLOSE_BRACKET:
+            case IdeographicPunctuationClass::COMMA_OR_FULLSTOP:
+                // Closer to previous ideograph
+                rKernArray[j - 1] = nNextFix - nHalfWidth;
+                break;
+            default:
+                // case IdeographicPunctuationClass::OPEN_BRACKET: closer to 
next ideograph.
+                rKernArray[j - 1] = nNextFix + nHalfWidth - nCurrentCharWidth;
+        }
+    }
+
+    // the layout engine requires the total width of the output
+    rKernArray[nLen - 1] = nWidth - nDelta;
+
+    return nDelta;
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/justify.hxx 
b/sw/source/core/txtnode/justify.hxx
index e98ecb5216c2..5a4c08b3da28 100644
--- a/sw/source/core/txtnode/justify.hxx
+++ b/sw/source/core/txtnode/justify.hxx
@@ -25,6 +25,24 @@ namespace Justify
 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);
+
+/// Snap ideographs to text grids:
+/// a) Ideographic open brackets are aligned to the rightmost edge of spanned 
grids so that
+//  they can be closer to the next ideograph.
+/// b) Ideographic close brackets, ideogrpahic comma, and idographic fullstop 
are aligned
+/// to the leftmost edge of spanned grids so that they can be closer to the 
previous
+/// ideograph.
+/// c) Other ideographs are aligned to the center of the spnaned grids.
+/// @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 nGirdWidth width of a text gird
+/// @param nWidth width of the whole portion.
+/// @return the delta offset of first glyph so text origin can be updated 
accordingly.
+SW_DLLPUBLIC tools::Long SnapToGrid(std::vector<sal_Int32>& rKernArray, const 
OUString& rText,
+                                    sal_Int32 nStt, sal_Int32 nLen, 
tools::Long nGridWidth,
+                                    tools::Long nWidth);
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to