sw/qa/core/txtnode/justify.cxx      |   44 ++++++++++++++++++++++++++++++--
 sw/source/core/txtnode/fntcache.cxx |   40 +++++++++++++++++++++++------
 sw/source/core/txtnode/justify.cxx  |   49 +++++++++++++++++++++++++++++++++++-
 sw/source/core/txtnode/justify.hxx  |    5 ++-
 4 files changed, 125 insertions(+), 13 deletions(-)

New commits:
commit e62dd2ca2c12ee02c5a2fba7bdd31dbcb17afc7b
Author:     Jonathan Clark <jonat...@libreoffice.org>
AuthorDate: Thu Oct 17 03:41:21 2024 -0600
Commit:     Jonathan Clark <jonat...@libreoffice.org>
CommitDate: Thu Oct 17 16:14:53 2024 +0200

    tdf#161145 sw: Conform doc grid advance layout for DOC/DOCX import
    
    This change modifies the Writer document grid algorithm to behave more
    like Word when the MS_WORD_COMP_GRID_METRICS compatibility flag is set.
    This flag is automatically set when importing DOC/DOCX files.
    
    Change-Id: I99765a3ad689b319ebd14e3620b22f18661efcf4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/175077
    Tested-by: Jenkins
    Reviewed-by: Jonathan Clark <jonat...@libreoffice.org>

diff --git a/sw/qa/core/txtnode/justify.cxx b/sw/qa/core/txtnode/justify.cxx
index f8f178543a1f..98b81babd8ff 100644
--- a/sw/qa/core/txtnode/justify.cxx
+++ b/sw/qa/core/txtnode/justify.cxx
@@ -160,7 +160,7 @@ CPPUNIT_TEST_FIXTURE(SwCoreJustifyTest, testSnapToGridEdge1)
     CharWidthArray aActual{ 640, 640, 640, 640, 640, 640, 320, 960 };
     CharWidthArray aExpected{ 840, 840, 840, 840, 840, 840, 440, 1240 };
     aActual.InvokeWithKernArray(
-        [&] { sw::Justify::SnapToGridEdge(aActual.maArray, 8, 400, 40, 0); });
+        [&] { sw::Justify::SnapToGridEdge(aActual.maArray, 8, 400, 40, 0, 0, 
false); });
     CPPUNIT_ASSERT_EQUAL(aExpected, aActual);
 }
 
@@ -169,7 +169,7 @@ CPPUNIT_TEST_FIXTURE(SwCoreJustifyTest, testSnapToGridEdge2)
     CharWidthArray aActual{ 640, 640, 640, 640, 640, 640, 320, 640 };
     CharWidthArray aExpected{ 840, 840, 840, 840, 840, 840, 440, 840 };
     aActual.InvokeWithKernArray(
-        [&] { sw::Justify::SnapToGridEdge(aActual.maArray, 8, 100, 40, 80); });
+        [&] { sw::Justify::SnapToGridEdge(aActual.maArray, 8, 100, 40, 80, 0, 
false); });
     CPPUNIT_ASSERT_EQUAL(aExpected, aActual);
 }
 
@@ -178,8 +178,46 @@ CPPUNIT_TEST_FIXTURE(SwCoreJustifyTest, 
testSnapToGridEdgeIVS)
     CharWidthArray aActual{ 640, 0, 0, 640, 640, 640, 640, 640 };
     CharWidthArray aExpected{ 840, 0, 0, 840, 840, 840, 840, 840 };
     aActual.InvokeWithKernArray(
-        [&] { sw::Justify::SnapToGridEdge(aActual.maArray, 8, 400, 40, 0); });
+        [&] { sw::Justify::SnapToGridEdge(aActual.maArray, 8, 400, 40, 0, 0, 
false); });
     CPPUNIT_ASSERT_EQUAL(aExpected, aActual);
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreJustifyTest, testSnapToGridEdgeMso)
+{
+    // Base case: Font matches document font, no extra space
+    CharWidthArray aActualBase{ 200, 0, 0, 200, 200, 200, 200, 200 };
+    CharWidthArray aExpectedBase{ 200, 0, 0, 200, 200, 200, 200, 200 };
+    aActualBase.InvokeWithKernArray(
+        [&] { sw::Justify::SnapToGridEdge(aActualBase.maArray, 8, 200, 0, 0, 
200, true); });
+    CPPUNIT_ASSERT_EQUAL(aExpectedBase, aActualBase);
+
+    // Font smaller than document font, no extra space
+    CharWidthArray aActualSmallerBase{ 100, 0, 0, 100, 100, 100, 100, 100 };
+    CharWidthArray aExpectedSmallerBase{ 100, 0, 0, 100, 100, 100, 100, 100 };
+    aActualSmallerBase.InvokeWithKernArray(
+        [&] { sw::Justify::SnapToGridEdge(aActualSmallerBase.maArray, 8, 200, 
0, 0, 200, true); });
+    CPPUNIT_ASSERT_EQUAL(aExpectedSmallerBase, aActualSmallerBase);
+
+    // Font larger than document font, no extra space
+    CharWidthArray aActualBiggerBase{ 300, 0, 0, 300, 300, 300, 300, 300 };
+    CharWidthArray aExpectedBiggerBase{ 300, 0, 0, 300, 300, 300, 300, 300 };
+    aActualBiggerBase.InvokeWithKernArray(
+        [&] { sw::Justify::SnapToGridEdge(aActualBiggerBase.maArray, 8, 200, 
0, 0, 200, true); });
+    CPPUNIT_ASSERT_EQUAL(aExpectedBiggerBase, aActualBiggerBase);
+
+    // Compression: Document font larger than grid
+    CharWidthArray aActualComp{ 200, 0, 0, 200, 200, 200, 200, 200 };
+    CharWidthArray aExpectedComp{ 180, 0, 0, 180, 180, 180, 180, 180 };
+    aActualComp.InvokeWithKernArray(
+        [&] { sw::Justify::SnapToGridEdge(aActualComp.maArray, 8, 80, 0, 0, 
100, true); });
+    CPPUNIT_ASSERT_EQUAL(aExpectedComp, aActualComp);
+
+    // Expansion: Document font smaller than grid
+    CharWidthArray aActualExp{ 200, 0, 0, 200, 200, 200, 200, 200 };
+    CharWidthArray aExpectedExp{ 220, 0, 0, 220, 220, 220, 220, 220 };
+    aActualExp.InvokeWithKernArray(
+        [&] { sw::Justify::SnapToGridEdge(aActualExp.maArray, 8, 120, 0, 0, 
100, true); });
+    CPPUNIT_ASSERT_EQUAL(aExpectedExp, aActualExp);
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/fntcache.cxx 
b/sw/source/core/txtnode/fntcache.cxx
index 5ca414fc1c68..e9407b7af9d7 100644
--- a/sw/source/core/txtnode/fntcache.cxx
+++ b/sw/source/core/txtnode/fntcache.cxx
@@ -828,6 +828,34 @@ static void DrawTextArray(OutputDevice& rOutputDevice, 
const Point& rStartPt, co
     }
 }
 
+namespace
+{
+void lcl_SnapToGridEdge(const SwDrawTextInfo& rInf, KernArray* pKernArray, 
tools::Long nGridWidth,
+                        tools::Long nSpaceAdd)
+{
+    auto bUseMsoCompatibleGrid = false;
+    sal_uInt32 nBaseFontHeight = 0;
+    if (auto pSh = rInf.GetShell(); pSh)
+    {
+        const IDocumentSettingAccess& rIDSA = pSh->getIDocumentSettingAccess();
+        bUseMsoCompatibleGrid = 
rIDSA.get(DocumentSettingId::MS_WORD_COMP_GRID_METRICS);
+
+        SwDocShell* pDocShell = rInf.GetShell()->GetDoc()->GetDocShell();
+        SfxStyleSheetBasePool* pBasePool = pDocShell->GetStyleSheetPool();
+
+        SfxStyleSheetBase* pStyle
+            = pBasePool->Find(SwResId(STR_POOLCOLL_STANDARD), 
SfxStyleFamily::Para);
+        SfxItemSet& aTmpSet = pStyle->GetItemSet();
+        const SvxFontHeightItem& aDefaultFontItem = 
aTmpSet.Get(RES_CHRATR_CJK_FONTSIZE);
+
+        nBaseFontHeight = aDefaultFontItem.GetHeight();
+    }
+
+    sw::Justify::SnapToGridEdge(*pKernArray, sal_Int32(rInf.GetLen()), 
nGridWidth, nSpaceAdd,
+                                rInf.GetKern(), nBaseFontHeight, 
bUseMsoCompatibleGrid);
+}
+}
+
 void SwFntObj::DrawText( SwDrawTextInfo &rInf )
 {
     OSL_ENSURE( rInf.GetShell(), "SwFntObj::DrawText without shell" );
@@ -1017,8 +1045,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
             }
             else
             {
-                sw::Justify::SnapToGridEdge(aKernArray, 
sal_Int32(rInf.GetLen()), nGridWidth,
-                        nSpaceAdd, rInf.GetKern());
+                lcl_SnapToGridEdge(rInf, &aKernArray, nGridWidth, nSpaceAdd);
             }
 
             if (nDelta)
@@ -1672,8 +1699,7 @@ Size SwFntObj::GetTextSize( SwDrawTextInfo& rInf )
             else
             {
                 // use 0 to calculate raw width without rInf.GetSpace().
-                sw::Justify::SnapToGridEdge(aKernArray, 
sal_Int32(rInf.GetLen()), nGridWidth, 0,
-                        rInf.GetKern());
+                lcl_SnapToGridEdge(rInf, &aKernArray, nGridWidth, 0);
             }
 
             aTextSize.setWidth(aKernArray[sal_Int32(nMsrLn) - 1]);
@@ -1796,8 +1822,7 @@ TextFrameIndex 
SwFntObj::GetModelPositionForViewPoint(SwDrawTextInfo &rInf)
             }
             else
             {
-                sw::Justify::SnapToGridEdge(aKernArray, 
sal_Int32(rInf.GetLen()), nGridWidth,
-                        nSpaceAdd, rInf.GetKern());
+                lcl_SnapToGridEdge(rInf, &aKernArray, nGridWidth, nSpaceAdd);
             }
 
             return  TextFrameIndex(sw::Justify::GetModelPosition(aKernArray, 
sal_Int32(rInf.GetLen()),
@@ -2078,8 +2103,7 @@ TextFrameIndex SwFont::GetTextBreak(SwDrawTextInfo const 
& rInf, tools::Long nTe
             else
             {
                 // use 0 to calculate raw width without rInf.GetSpace().
-                sw::Justify::SnapToGridEdge(aKernArray, 
sal_Int32(rInf.GetLen()), nGridWidth,
-                        0, rInf.GetKern());
+                lcl_SnapToGridEdge(rInf, &aKernArray, nGridWidth, 0);
             }
 
             while(nTextBreak < rInf.GetLen() && 
aKernArray[sal_Int32(nTextBreak)] <= nTextWidth)
diff --git a/sw/source/core/txtnode/justify.cxx 
b/sw/source/core/txtnode/justify.cxx
index 40ded5663f17..16a141a5b284 100644
--- a/sw/source/core/txtnode/justify.cxx
+++ b/sw/source/core/txtnode/justify.cxx
@@ -213,11 +213,58 @@ tools::Long SnapToGrid(KernArray& rKernArray, 
std::u16string_view aText, sal_Int
     return nDelta;
 }
 
+namespace
+{
+tools::Long lcl_MsoGridWidth(tools::Long nGridWidth, tools::Long nBaseFontSize,
+                             tools::Long nCellSize)
+{
+    return nCellSize + (nGridWidth - nBaseFontSize);
+}
+
+void lcl_MsoCompatSnapToGridEdge(KernArray& rKernArray, sal_Int32 nLen, 
tools::Long nGridWidth,
+                                 tools::Long nSpace, tools::Long nKern, 
tools::Long nBaseFontSize)
+{
+    tools::Long nCharWidth = rKernArray[0];
+    tools::Long nEdge = lcl_MsoGridWidth(nGridWidth, nBaseFontSize, nCharWidth 
+ nKern) + nSpace;
+
+    sal_Int32 nLast = 0;
+
+    for (sal_Int32 i = 1; i < nLen; ++i)
+    {
+        if (rKernArray[i] == rKernArray[nLast])
+            continue;
+
+        nCharWidth = rKernArray[i] - rKernArray[nLast];
+        tools::Long nMinWidth = lcl_MsoGridWidth(nGridWidth, nBaseFontSize, 
nCharWidth + nKern);
+        while (nLast < i)
+        {
+            rKernArray.set(nLast, nEdge);
+            ++nLast;
+        }
+
+        nEdge += nMinWidth + nSpace;
+    }
+
+    while (nLast < nLen)
+    {
+        rKernArray.set(nLast, nEdge);
+        ++nLast;
+    }
+}
+}
+
 void SnapToGridEdge(KernArray& rKernArray, sal_Int32 nLen, tools::Long 
nGridWidth,
-                    tools::Long nSpace, tools::Long nKern)
+                    tools::Long nSpace, tools::Long nKern, tools::Long 
nBaseFontSize,
+                    bool bUseMsoCompatibleGrid)
 {
     assert(nLen <= sal_Int32(rKernArray.size()));
 
+    if (bUseMsoCompatibleGrid)
+    {
+        lcl_MsoCompatSnapToGridEdge(rKernArray, nLen, nGridWidth, nSpace, 
nKern, nBaseFontSize);
+        return;
+    }
+
     tools::Long nCharWidth = rKernArray[0];
     tools::Long nEdge = lcl_MinGridWidth(nGridWidth, nCharWidth + nKern) + 
nSpace;
 
diff --git a/sw/source/core/txtnode/justify.hxx 
b/sw/source/core/txtnode/justify.hxx
index 454b6c259001..b9755d3efb01 100644
--- a/sw/source/core/txtnode/justify.hxx
+++ b/sw/source/core/txtnode/justify.hxx
@@ -58,8 +58,11 @@ SW_DLLPUBLIC tools::Long SnapToGrid(KernArray& rKernArray, 
std::u16string_view a
 /// @param nGridWidth width of a text grid
 /// @param nSpace amount of space distributed under justify text alignment 
mode.
 /// @param nKern letter spacing.
+/// @param nBaseFontSize document font size, used for MSO-compatible grid
+/// @param bUseMsoCompatibleGrid changes grid algorithm to match MSO
 SW_DLLPUBLIC void SnapToGridEdge(KernArray& rKernArray, sal_Int32 nLen, 
tools::Long nGridWidth,
-                                 tools::Long nSpace, tools::Long nKern);
+                                 tools::Long nSpace, tools::Long nKern, 
tools::Long nBaseFontSize,
+                                 bool bUseMsoCompatibleGrid);
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to