summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Luth <justin.luth@collabora.com>2025-01-08 20:02:37 -0500
committerJustin Luth <justin.luth@collabora.com>2025-01-10 01:10:12 +0100
commitb3f3d1ffeca42cbe6028f81b5a07c342f84e8c88 (patch)
treecaa0ebc32b928403db4c197584394804a01690de
parent89c00618b9cee6e786fd11a7fdbf7aaf24e4fbb7 (diff)
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 <jluth@mail.com>
-rw-r--r--sw/source/filter/ww8/docxexport.cxx97
-rw-r--r--sw/source/filter/ww8/docxexport.hxx4
-rw-r--r--sw/source/filter/ww8/docxsdrexport.cxx2
-rw-r--r--sw/source/filter/ww8/docxtableexport.cxx23
4 files changed, 59 insertions, 67 deletions
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();