sw/source/filter/ww8/docxexport.cxx      |   97 +++++++++++++++++--------------
 sw/source/filter/ww8/docxexport.hxx      |    4 -
 sw/source/filter/ww8/docxsdrexport.cxx   |    2 
 sw/source/filter/ww8/docxtableexport.cxx |   23 -------
 4 files changed, 59 insertions(+), 67 deletions(-)

New commits:
commit b3f3d1ffeca42cbe6028f81b5a07c342f84e8c88
Author:     Justin Luth <justin.l...@collabora.com>
AuthorDate: Wed Jan 8 20:02:37 2025 -0500
Commit:     Justin Luth <justin.l...@collabora.com>
CommitDate: Fri Jan 10 01:10:12 2025 +0100

    tdf#164627 docx export: consolidate getWordCompatibilityMode()
    
    Solves TODO: this is duplicated, better store it as DocxExport member?
    
    It is generally useful to be able to query what compatibility mode
    the DOCX will be exported as, so calculate the value once
    and cache it.
    
    I re-arranged things a bit, and I think it is clearer now
    what is happening here.
    
    Change-Id: I5cd619e1696ecef956a54957fb57f49803e7bc67
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/179981
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <jl...@mail.com>

diff --git a/sw/source/filter/ww8/docxexport.cxx 
b/sw/source/filter/ww8/docxexport.cxx
index 5472a0de8460..8a309d7da39c 100644
--- a/sw/source/filter/ww8/docxexport.cxx
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -1007,17 +1007,12 @@ void DocxExport::WriteDocVars(const 
sax_fastparser::FSHelperPtr& pFS)
 }
 
 static auto
-WriteCompat(SwDoc const& rDoc, ::sax_fastparser::FSHelperPtr const& rpFS,
-        sal_Int32 & rTargetCompatibilityMode) -> void
+WriteCompat(SwDoc const& rDoc, ::sax_fastparser::FSHelperPtr const& rpFS) -> 
void
 {
     const IDocumentSettingAccess& rIDSA = rDoc.getIDocumentSettingAccess();
     if (!rIDSA.get(DocumentSettingId::ADD_EXT_LEADING))
     {
         rpFS->singleElementNS(XML_w, XML_noLeading);
-        if (rTargetCompatibilityMode > 14)
-        {   // Word ignores noLeading in compatibilityMode 15
-            rTargetCompatibilityMode = 14;
-        }
     }
     // Do not justify lines with manual break
     if (rIDSA.get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK))
@@ -1271,24 +1266,6 @@ void DocxExport::WriteSettings()
             bWriterWantsToProtect = bWriterWantsToProtectRedline = true;
     }
 
-    /* Compatibility Mode (tdf#131304)
-     * 11:  .doc level    [Word 97-2003]
-     * 12:  .docx default [Word 2007]  [LO < 7.0] [ECMA 376 1st ed.]
-     * 14:                [Word 2010]
-     * 15:                [Word 2013/2016/2019]  [LO >= 7.0]
-     *
-     * The PRIMARY purpose of compatibility mode does not seem to be related 
to layout etc.
-     * Its focus is on sharing files between multiple users, tracking the 
lowest supported mode in the group.
-     * It is to BENEFIT older programs by not using certain new features that 
they don't understand.
-     *
-     * The next time the compat mode needs to be changed, I foresee the 
following steps:
-     * 1.) Accept the new mode: Start round-tripping the new value, indicating 
we understand that format.
-     * 2.) Many years later, change the TargetCompatilityMode for new 
documents, when we no longer care
-     *     about working with perfect compatibility with older versions of MS 
Word.
-     */
-    sal_Int32 nTargetCompatibilityMode =
-        (GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION)
-        ? 12 : 15; //older versions might not open our files well
     bool bHasCompatibilityMode = false;
     const OUString aGrabBagName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG;
     if ( xPropSetInfo->hasPropertyByName( aGrabBagName ) )
@@ -1321,7 +1298,7 @@ void DocxExport::WriteSettings()
             {
                 pFS->startElementNS(XML_w, XML_compat);
 
-                WriteCompat(m_rDoc, pFS, nTargetCompatibilityMode);
+                WriteCompat(m_rDoc, pFS);
 
                 uno::Sequence< beans::PropertyValue > aCompatSettingsSequence;
                 rProp.Value >>= aCompatSettingsSequence;
@@ -1346,13 +1323,7 @@ void DocxExport::WriteSettings()
                     if ( aName == "compatibilityMode" )
                     {
                         bHasCompatibilityMode = true;
-                        // Among the group of programs sharing this document, 
the lowest mode is retained.
-                        // Reduce this number if we are not comfortable with 
the new/unknown mode yet.
-                        // Step 1 in accepting a new mode would be to comment 
out the following clause
-                        // and roundtrip the new value instead of overwriting 
with the older number.
-                        // There are no newer modes at the time this code was 
written.
-                        if ( aValue.toInt32() > nTargetCompatibilityMode )
-                            aValue = 
OUString::number(nTargetCompatibilityMode);
+                        aValue = OUString::number(getWordCompatibilityMode());
                     }
 
                     pFS->singleElementNS( XML_w, XML_compatSetting,
@@ -1366,7 +1337,7 @@ void DocxExport::WriteSettings()
                     pFS->singleElementNS( XML_w, XML_compatSetting,
                         FSNS( XML_w, XML_name ), "compatibilityMode",
                         FSNS( XML_w, XML_uri ),  
"http://schemas.microsoft.com/office/word";,
-                        FSNS( XML_w, XML_val ),  
OString::number(nTargetCompatibilityMode));
+                        FSNS( XML_w, XML_val ),  
OString::number(getWordCompatibilityMode()));
                     bHasCompatibilityMode = true;
                 }
 
@@ -1451,12 +1422,13 @@ void DocxExport::WriteSettings()
     {
         pFS->startElementNS(XML_w, XML_compat);
 
-        WriteCompat(m_rDoc, pFS, nTargetCompatibilityMode);
+        WriteCompat(m_rDoc, pFS);
 
+        const sal_Int32 nWordCompatibilityMode = getWordCompatibilityMode();
         pFS->singleElementNS( XML_w, XML_compatSetting,
             FSNS( XML_w, XML_name ), "compatibilityMode",
             FSNS( XML_w, XML_uri ),  
"http://schemas.microsoft.com/office/word";,
-            FSNS( XML_w, XML_val ),  
OString::number(nTargetCompatibilityMode));
+            FSNS( XML_w, XML_val ),  OString::number(nWordCompatibilityMode));
 
         const IDocumentSettingAccess& rIDSA = 
m_rDoc.getIDocumentSettingAccess();
         if 
(rIDSA.get(DocumentSettingId::ALLOW_TEXT_AFTER_FLOATING_TABLE_BREAK))
@@ -1471,7 +1443,7 @@ void DocxExport::WriteSettings()
 
         // export useWord2013TrackBottomHyphenation and
         // allowHyphenationAtTrackBottom for Word 2013/2016/2019
-        if ( nTargetCompatibilityMode >= 12 )
+        if (nWordCompatibilityMode >= 12)
         {
             pFS->singleElementNS(XML_w, XML_compatSetting,
                     FSNS(XML_w, XML_name), "useWord2013TrackBottomHyphenation",
@@ -2123,17 +2095,36 @@ sal_Int32 DocxExport::WriteOutliner(const 
OutlinerParaObject& rParaObj, sal_uInt
 
 //Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
 //Since this is not import code, "-1" needs to be handled as the mode that LO 
will save as.
-//To identify how your code should handle a "-1", look in 
DocxExport::WriteSettings().
-sal_Int32 DocxExport::getWordCompatibilityModeFromGrabBag() const
+sal_Int32 DocxExport::getWordCompatibilityMode()
 {
-    sal_Int32 nWordCompatibilityMode = -1;
+    if (m_nWordCompatibilityMode > 0)
+        return m_nWordCompatibilityMode; // cached result
+
+    /* Compatibility Mode (tdf#131304)
+     * 11:  .doc level    [Word 97-2003]
+     * 12:  .docx default [Word 2007]  [LO < 7.0] [ECMA 376 1st ed.]
+     * 14:                [Word 2010]
+     * 15:                [Word 2013/2016/2019/2021/2024]  [LO >= 7.0]
+     *
+     * The PRIMARY purpose of compatibility mode does not seem to be related 
to layout etc.
+     * Its focus is on sharing files between multiple users, tracking the 
LOWEST supported mode in the group.
+     * It is to BENEFIT older programs by not using certain new features that 
they don't understand.
+     *
+     * The next time the compat mode needs to be changed, I foresee the 
following steps:
+     * 1.) Accept the new mode: Start round-tripping the new value, indicating 
we understand that format.
+     * 2.) Many years later, change the initial m_nWordCompatibilityMode for 
new documents,
+     *     when we no longer care about working with perfect compatibility 
with older versions of MS Word.
+     */
+    m_nWordCompatibilityMode = 15; //older versions might not open our files 
well
     rtl::Reference< SwXTextDocument > 
xPropSet(m_rDoc.GetDocShell()->GetBaseModel());
     uno::Reference< beans::XPropertySetInfo > xPropSetInfo = 
xPropSet->getPropertySetInfo();
+    // Round-trip the existing compatibilityMode
     if (xPropSetInfo->hasPropertyByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG))
     {
         uno::Sequence< beans::PropertyValue > propList;
         xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= 
propList;
 
+        sal_Int32 nImportedWordCompatbilityMode = -1;
         for (const auto& rProp : propList)
         {
             if (rProp.Name == "CompatSettings")
@@ -2161,16 +2152,35 @@ sal_Int32 
DocxExport::getWordCompatibilityModeFromGrabBag() const
                     {
                         const sal_Int32 nValidMode = sVal.toInt32();
                         // if repeated, highest mode wins in MS Word. 11 is 
the first valid mode.
-                        if (nValidMode > 10 && nValidMode > 
nWordCompatibilityMode)
-                            nWordCompatibilityMode = nValidMode;
-
+                        if (nValidMode > 10 && nValidMode > 
nImportedWordCompatbilityMode)
+                            nImportedWordCompatbilityMode = nValidMode;
                     }
                 }
             }
         }
+        // Keep the imported compatiblity mode (unless it is unknown / 
unsupported)
+        const bool bPreventRoundTrippingUnknownMode
+            = nImportedWordCompatbilityMode > m_nWordCompatibilityMode;
+        assert(!bPreventRoundTrippingUnknownMode && "create a new meta bug for 
new compat mode");
+        if (nImportedWordCompatbilityMode > 0 && 
!bPreventRoundTrippingUnknownMode)
+            m_nWordCompatibilityMode = nImportedWordCompatbilityMode;
     }
 
-    return nWordCompatibilityMode;
+    // Note: up to this point, WordCompatibilityMode is allowed to be 
increased.
+    // Place any maximum compatibility mode restrictions after this comment.
+    if (m_nWordCompatibilityMode > 12)
+    {
+        // If this is a Word 2007-only format, don't allow a compatibility 
mode greater than that
+        if (GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION)
+            m_nWordCompatibilityMode = 12;
+    }
+    if (m_nWordCompatibilityMode > 14)
+    {
+        // Word ignores noLeading in compatibilityMode 15
+        if 
(!m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING))
+            m_nWordCompatibilityMode = 14;
+    }
+    return m_nWordCompatibilityMode;
 }
 
 void DocxExport::SetFS( ::sax_fastparser::FSHelperPtr const & pFS )
@@ -2190,6 +2200,7 @@ DocxExport::DocxExport(DocxExportFilter& rFilter, SwDoc& 
rDocument,
       m_nHeadersFootersInSection(0),
       m_bDocm(bDocm),
       m_bTemplate(bTemplate),
+      m_nWordCompatibilityMode(-1),
       m_pAuthorIDs(new SvtSecurityMapPersonalInfo)
 {
     // Write the document properties
diff --git a/sw/source/filter/ww8/docxexport.hxx 
b/sw/source/filter/ww8/docxexport.hxx
index 7ed6457d6339..7588586579d9 100644
--- a/sw/source/filter/ww8/docxexport.hxx
+++ b/sw/source/filter/ww8/docxexport.hxx
@@ -119,6 +119,7 @@ class DocxExport : public MSWordExportBase
     bool const m_bTemplate;
 
     DocxSettingsData m_aSettings;
+    sal_Int32 m_nWordCompatibilityMode;
 
     /// Pointer to the Frame of a floating table it is nested in
     const ww8::Frame *m_pFloatingTableFrame = nullptr;
@@ -319,8 +320,7 @@ public:
     // Get author id to remove personal info
     size_t GetInfoID( const OUString& sPersonalInfo ) const { return 
m_pAuthorIDs->GetInfoID(sPersonalInfo); }
 
-    // needed in docxsdrexport.cxx and docxattributeoutput.cxx
-    sal_Int32 getWordCompatibilityModeFromGrabBag() const;
+    sal_Int32 getWordCompatibilityMode();
 
     /// return true if Page Layout is set as Mirrored
     bool isMirroredMargin();
diff --git a/sw/source/filter/ww8/docxsdrexport.cxx 
b/sw/source/filter/ww8/docxsdrexport.cxx
index de0e3769a561..4ca3628ecf58 100644
--- a/sw/source/filter/ww8/docxsdrexport.cxx
+++ b/sw/source/filter/ww8/docxsdrexport.cxx
@@ -782,7 +782,7 @@ void DocxSdrExport::startDMLAnchorInline(const 
SwFrameFormat* pFrameFormat, cons
     else // other objects than frames. pObj exists.
     {
         // Word 2007 makes no width-height-swap for images. Detect this 
situation.
-        sal_Int32 nMode = 
m_pImpl->getExport().getWordCompatibilityModeFromGrabBag();
+        sal_Int32 nMode = m_pImpl->getExport().getWordCompatibilityMode();
         bool bIsWord2007Image(nMode > 0 && nMode < 14
                               && pObj->GetObjIdentifier() == 
SdrObjKind::Graphic);
 
diff --git a/sw/source/filter/ww8/docxtableexport.cxx 
b/sw/source/filter/ww8/docxtableexport.cxx
index e1fd3f3d48c1..b8ca4eb2ac99 100644
--- a/sw/source/filter/ww8/docxtableexport.cxx
+++ b/sw/source/filter/ww8/docxtableexport.cxx
@@ -64,25 +64,6 @@ OString lcl_padStartToLength(OString const& aString, 
sal_Int32 nLen, char cFill)
         return aString;
 }
 
-//Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
-//Since this is not import code, "-1" needs to be handled as the mode that LO 
will save as.
-//To identify how your code should handle a "-1", look in 
DocxExport::WriteSettings().
-sal_Int32 lcl_getWordCompatibilityMode(const DocxExport& rDocExport)
-{
-    sal_Int32 nWordCompatibilityMode = 
rDocExport.getWordCompatibilityModeFromGrabBag();
-
-    // TODO: this is duplicated, better store it in DocxExport member?
-    if 
(!rDocExport.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING))
-    {
-        if (nWordCompatibilityMode == -1 || 14 < nWordCompatibilityMode)
-        {
-            nWordCompatibilityMode = 14;
-        }
-    }
-
-    return nWordCompatibilityMode;
-}
-
 void CollectFloatingTableAttributes(DocxExport& rExport, const ww8::Frame& 
rFrame,
                                     ww8::WW8TableNodeInfoInner::Pointer_t 
pTableTextNodeInfoInner,
                                     rtl::Reference<FastAttributeList>& 
pAttributes)
@@ -137,7 +118,7 @@ void CollectFloatingTableAttributes(DocxExport& rExport, 
const ww8::Frame& rFram
         const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
         const SwFrameFormat* pFrameFormat = pTabBox->GetFrameFormat();
         const SvxBoxItem& rBox = pFrameFormat->GetBox();
-        sal_Int32 nMode = lcl_getWordCompatibilityMode(rExport);
+        sal_Int32 nMode = rExport.getWordCompatibilityMode();
         if (nMode < 15)
         {
             sal_uInt16 nLeftDistance = rBox.GetDistance(SvxBoxItemLine::LEFT);
@@ -469,7 +450,7 @@ void DocxAttributeOutput::TableDefinition(
             // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), 
top-level tables are handled the same as nested tables;
             // if no compatibilityMode is defined (which now should only 
happen on a new export to .docx),
             // LO uses a higher compatibility than 2010's 14.
-            sal_Int32 nMode = lcl_getWordCompatibilityMode(m_rExport);
+            sal_Int32 nMode = m_rExport.getWordCompatibilityMode();
 
             const SwFrameFormat* pFrameFormat
                 = pTableTextNodeInfoInner->getTableBox()->GetFrameFormat();

Reply via email to