sw/qa/extras/ooxmlexport/data/comment-annotationref.docx |binary sw/qa/extras/ooxmlexport/data/tdf139418.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport22.cxx | 39 +++++++++++++++ sw/source/core/text/itrform2.cxx | 8 +-- sw/source/core/txtnode/fntcache.cxx | 9 +++ sw/source/filter/ww8/docxattributeoutput.cxx | 6 +- sw/source/filter/ww8/docxexport.cxx | 9 +++ sw/source/filter/ww8/docxexport.hxx | 3 - sw/source/writerfilter/dmapper/PropertyMap.cxx | 33 ++++++++++-- 9 files changed, 94 insertions(+), 13 deletions(-)
New commits: commit 4c534294a3e8af1f1dcde12034d8271dd8e5538b Author: Jaume Pujantell <jaume.pujant...@collabora.com> AuthorDate: Tue Apr 8 17:56:13 2025 +0200 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Fri Apr 11 21:08:48 2025 +0200 tdf#166126 sw: docx: add annotationRef on comments Right now we never add <w:annotationRef/> to any comment when saving to docx. According to the standard, not having it is the same as having it in the beginning. But when round-tripping a document with multiple comments without text from MS Word, the comments get mangled if the annotationRef tags aren't kept. So it's better to always write the implicit annotationRef at the beginning of comments. Change-Id: I6c395bbc691a29533e2be9a93663993e083db65b Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183856 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183975 Tested-by: Jenkins Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184012 diff --git a/sw/qa/extras/ooxmlexport/data/comment-annotationref.docx b/sw/qa/extras/ooxmlexport/data/comment-annotationref.docx new file mode 100644 index 000000000000..2350a8c6bb1d Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/comment-annotationref.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx index d57d22bb7b73..7f5ef51c2783 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx @@ -118,6 +118,19 @@ DECLARE_OOXMLEXPORT_TEST(testTdf139418, "tdf139418.docx") CPPUNIT_ASSERT_EQUAL(sal_Int32(0), nPorLen3); } +CPPUNIT_TEST_FIXTURE(Test, testAnnotationRef) +{ + loadAndSave("comment-annotationref.docx"); + + // Check that the exported comment also has annotationRef + xmlDocUniquePtr pXmlComments = parseExport(u"word/comments.xml"_ustr); + CPPUNIT_ASSERT(pXmlComments); + // Wihtout the fix it fails with + // - Expected: 1 + // - Actual : 0 + assertXPath(pXmlComments, "//w:comments/w:comment[1]/w:p[1]/w:r[1]/w:annotationRef"); +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index f4f3c8d802c3..04089b7b86e6 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -8563,7 +8563,8 @@ DocxAttributeOutput::hasProperties DocxAttributeOutput::WritePostitFields() if (f->GetTextObject() != nullptr) { // richtext - data.lastParaId = GetExport().WriteOutliner(*f->GetTextObject(), TXT_ATN, bNeedParaId); + data.lastParaId + = GetExport().WriteOutliner(*f->GetTextObject(), TXT_ATN, bNeedParaId, true); } else { @@ -8577,6 +8578,9 @@ DocxAttributeOutput::hasProperties DocxAttributeOutput::WritePostitFields() } m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, XML_paraId), aParaId); m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->singleElementNS(XML_w, XML_annotationRef); + m_pSerializer->endElementNS(XML_w, XML_r); + m_pSerializer->startElementNS(XML_w, XML_r); RunText(f->GetText()); m_pSerializer->endElementNS(XML_w, XML_r); m_pSerializer->endElementNS(XML_w, XML_p); diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx index 5472a0de8460..88f609411a9b 100644 --- a/sw/source/filter/ww8/docxexport.cxx +++ b/sw/source/filter/ww8/docxexport.cxx @@ -2063,7 +2063,7 @@ bool DocxExport::ignoreAttributeForStyleDefaults( sal_uInt16 nWhich ) const } sal_Int32 DocxExport::WriteOutliner(const OutlinerParaObject& rParaObj, sal_uInt8 nTyp, - bool bNeedsLastParaId) + bool bNeedsLastParaId, bool bWriteAnnotRef) { const EditTextObject& rEditObj = rParaObj.GetTextObject(); MSWord_SdrAttrIter aAttrIter( *this, rEditObj, nTyp ); @@ -2088,6 +2088,13 @@ sal_Int32 DocxExport::WriteOutliner(const OutlinerParaObject& rParaObj, sal_uInt SfxItemSet aParagraphMarkerProperties(m_rDoc.GetAttrPool()); AttrOutput().EndParagraphProperties(aParagraphMarkerProperties, nullptr, nullptr, nullptr); + if (bWriteAnnotRef && n == 0) + { + m_pAttrOutput->GetSerializer()->startElementNS(XML_w, XML_r); + m_pAttrOutput->GetSerializer()->singleElementNS(XML_w, XML_annotationRef); + m_pAttrOutput->GetSerializer()->endElementNS(XML_w, XML_r); + } + do { AttrOutput().StartRun( nullptr, 0 ); const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd); diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx index 7ed6457d6339..43cdccce8a5c 100644 --- a/sw/source/filter/ww8/docxexport.hxx +++ b/sw/source/filter/ww8/docxexport.hxx @@ -205,7 +205,8 @@ public: /// Writes the shape using drawingML syntax. void OutputDML( css::uno::Reference< css::drawing::XShape > const & xShape ); - sal_Int32 WriteOutliner(const OutlinerParaObject& rOutliner, sal_uInt8 nTyp, bool bNeedsLastParaId); + sal_Int32 WriteOutliner(const OutlinerParaObject& rOutliner, sal_uInt8 nTyp, + bool bNeedsLastParaId, bool bWriteAnnotRef = false); virtual ExportFormat GetExportFormat() const override { return ExportFormat::DOCX; } commit 576d6f773e1a04a1fc76cc121e948be67ba16ff6 Author: Jonathan Clark <jonat...@libreoffice.org> AuthorDate: Wed Apr 9 03:52:44 2025 -0600 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Fri Apr 11 21:08:42 2025 +0200 tdf#139418 DOCX import: Fix generated genko yoshi grid layout issues This change includes fixes for three minor grid layout issues affecting correct display of generated genko yoshi manuscript documents. - Due to the way OOXML stores grid parameters, the default Asian font height must be known in order to correctly calculate the grid character pitch. This change fixes a regression from tdf#107359 which caused the OOXML importer to always use the hard-coded default character width in certain situations. - The fix for tdf#149089 caused a regression by also removing the grid kerning portions from vertical text, when it correctly should only have affected horizontal text. - For correct interop, the ADD_EXT_LEADING compatibility flag is now ignored for vertical text when the MS_WORD_COMP_GRID_METRICS compatibility flag is set. Change-Id: I5aaa245293932d64ff5c4dd4a3f45c4d8bc4a22e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183959 Reviewed-by: Jonathan Clark <jonat...@libreoffice.org> Tested-by: Jenkins Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184011 diff --git a/sw/qa/extras/ooxmlexport/data/tdf139418.docx b/sw/qa/extras/ooxmlexport/data/tdf139418.docx new file mode 100644 index 000000000000..e32747e818d3 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf139418.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx index 7f5d94092aa2..d57d22bb7b73 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx @@ -92,6 +92,32 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf165933_noDelTextOnMove) assertXPath(pXmlDoc, "//w:moveFrom/w:r/w:delText", 0); } +DECLARE_OOXMLEXPORT_TEST(testTdf139418, "tdf139418.docx") +{ + uno::Reference<beans::XPropertySet> xPropertySet( + getStyles(u"PageStyles"_ustr)->getByName(u"Standard"_ustr), uno::UNO_QUERY); + + sal_Int32 nBaseWidth; + xPropertySet->getPropertyValue(u"GridBaseWidth"_ustr) >>= nBaseWidth; + // Without the fix, this will fail with + // - Expected: ~795 + // - Actual : 848 + CPPUNIT_ASSERT_GREATEREQUAL(sal_Int32(794), nBaseWidth); + CPPUNIT_ASSERT_LESSEQUAL(sal_Int32(796), nBaseWidth); + + auto pXmlDoc = parseLayoutDump(); + + // Vertical DOCX should insert kern portions to align text to the grid + sal_Int32 nPorLen1 = getXPath(pXmlDoc, "(//SwLinePortion)[1]", "length").toInt32(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), nPorLen1); + + sal_Int32 nPorLen2 = getXPath(pXmlDoc, "(//SwLinePortion)[2]", "length").toInt32(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(42), nPorLen2); + + sal_Int32 nPorLen3 = getXPath(pXmlDoc, "(//SwLinePortion)[3]", "length").toInt32(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), nPorLen3); +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx index 290a7ebd558d..ad77006f2d5d 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -445,10 +445,12 @@ void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf ) // tdf#149089: For compatibility with MSO grid layout, do not insert kern portions to // align successive portions to the char grid when MS_WORD_COMP_GRID_METRICS is set. // See also tdf#161145. + // tdf#139418: However, in testing, this only seems to apply to horizontal text. + const bool bUseGridKernPors = GetTextFrame()->IsVertical() + || !GetTextFrame()->GetDoc().getIDocumentSettingAccess().get( + DocumentSettingId::MS_WORD_COMP_GRID_METRICS); const bool bHasGrid = pGrid && rInf.SnapToGrid() - && GRID_LINES_CHARS == pGrid->GetGridType() - && !GetTextFrame()->GetDoc().getIDocumentSettingAccess().get( - DocumentSettingId::MS_WORD_COMP_GRID_METRICS); + && GRID_LINES_CHARS == pGrid->GetGridType() && bUseGridKernPors; const SwDoc & rDoc = rInf.GetTextFrame()->GetDoc(); const sal_uInt16 nGridWidth = bHasGrid ? GetGridWidth(*pGrid, rDoc) : 0; diff --git a/sw/source/core/txtnode/fntcache.cxx b/sw/source/core/txtnode/fntcache.cxx index 75acf618eed2..f685e58f894e 100644 --- a/sw/source/core/txtnode/fntcache.cxx +++ b/sw/source/core/txtnode/fntcache.cxx @@ -384,7 +384,14 @@ sal_uInt16 SwFntObj::GetFontLeading( const SwViewShell *pSh, const OutputDevice& pSh->GetViewOptions()->getBrowseMode() && !pSh->GetViewOptions()->IsPrtFormat() ); - if ( !bBrowse && rIDSA.get(DocumentSettingId::ADD_EXT_LEADING) ) + const bool bAddExtLeading = rIDSA.get(DocumentSettingId::ADD_EXT_LEADING); + + // tdf#139418: MSO never applies ext leading to vertical text, even if the + // NoLeading compatibility flag is unset. + const bool bDisableExtLeading + = rIDSA.get(DocumentSettingId::MS_WORD_COMP_GRID_METRICS) && GetFont().IsVertical(); + + if (!bBrowse && bAddExtLeading && !bDisableExtLeading) nRet = m_nExtLeading; else nRet = m_nGuessedLeading; diff --git a/sw/source/writerfilter/dmapper/PropertyMap.cxx b/sw/source/writerfilter/dmapper/PropertyMap.cxx index ce373b092890..fc3ee8ff3a38 100644 --- a/sw/source/writerfilter/dmapper/PropertyMap.cxx +++ b/sw/source/writerfilter/dmapper/PropertyMap.cxx @@ -1826,15 +1826,36 @@ void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl ) Insert( PROP_GRID_MODE, uno::Any( nGridType ) ); sal_Int32 nCharWidth = 423; //240 twip/ 12 pt - const StyleSheetEntryPtr pEntry = rDM_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( u"Standard" ); - if ( pEntry ) + + // tdf#139418: Fetch the default Asian text height for grid pitch calculation + if (const auto pEntry = rDM_Impl.GetStyleSheetTable()->GetDefaultCharProps(); pEntry) { - std::optional< PropertyMap::Property > pPropHeight = pEntry->m_pProperties->getProperty( PROP_CHAR_HEIGHT_ASIAN ); - if ( pPropHeight ) + const auto pPropHeight = pEntry->getProperty(PROP_CHAR_HEIGHT_ASIAN); + if (pPropHeight.has_value()) { double fHeight = 0; - if ( pPropHeight->second >>= fHeight ) - nCharWidth = ConversionHelper::convertTwipToMm100_Limited( static_cast<sal_Int32>(fHeight * 20.0 + 0.5) ); + if (pPropHeight->second >>= fHeight) + { + nCharWidth = ConversionHelper::convertTwipToMm100_Limited( + static_cast<sal_Int32>(fHeight * 20.0 + 0.5)); + } + } + } + + // tdf#107359: Override the default Asian height from the Standard style, if present. + if (const auto pEntry + = rDM_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(u"Standard"); + pEntry) + { + const auto pPropHeight = pEntry->m_pProperties->getProperty(PROP_CHAR_HEIGHT_ASIAN); + if (pPropHeight.has_value()) + { + double fHeight = 0; + if (pPropHeight->second >>= fHeight) + { + nCharWidth = ConversionHelper::convertTwipToMm100_Limited( + static_cast<sal_Int32>(fHeight * 20.0 + 0.5)); + } } }