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));
+                }
             }
         }
 

Reply via email to