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: */