tdf#124472 DOCX import: fix broken field command by skipping delInstrText
Process only inserted and not tracked parts of the (partially)
tracked field commands to avoid of broken fields, e.g. hyperlinks
with bad URLs.
For example, instead of the previous bad URL
'https://www.libreoffice.org/"HYPERLINK https://bugs.libreoffice.org/',
now the hypertext field of the test document imported with the
correct URL 'https://www.libreoffice.org/'.
Field commands have change tracking in OOXML, but not in ODF/Writer.
OOXML delInstrText, i.e. deleted part of the field commands was imported
as part of the actual command, resulting broken fields, e.g. hyperlinks.
FieldCommand was splitted into two parts, deleted and non-deleted
elements. This way the commands do not get mixed up if the order
is changed and no fragmentation of deleted items occurs (otherwise
it could fail on the test of tdf#150086).
Change-Id: I97d22e5ee1fe647715206f86c4160ebcc4b9cca0
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148528
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/qa/extras/ooxmlexport/data/tdf124472.docx b/sw/qa/extras/ooxmlexport/data/tdf124472.docx
new file mode 100644
index 0000000..7d7f275
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf124472.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
index 90e9cf2..b871961 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
@@ -658,6 +658,16 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf153664)
assertXPath(pXmlStyles, "/w:styles/w:style[@w:styleId='TableofFigures']/w:name", "val", "Table of Figures");
}
DECLARE_OOXMLEXPORT_TEST(testTdf124472_hyperlink, "tdf124472.docx")
{
CPPUNIT_ASSERT_EQUAL(OUString("https://www.libreoffice.org/"),
getProperty<OUString>(getRun(getParagraph(1), 1), "HyperLinkURL"));
CPPUNIT_ASSERT_EQUAL(OUString("mailto:info@libreoffice.org"),
getProperty<OUString>(getRun(getParagraph(2), 1), "HyperLinkURL"));
CPPUNIT_ASSERT_EQUAL(OUString(""),
getProperty<OUString>(getRun(getParagraph(3), 1), "HyperLinkURL"));
}
DECLARE_OOXMLEXPORT_TEST(testTdf135786, "tdf135786.docx")
{
// Empty first line remain, if the section's initial dummy paragraph is not deleted:
@@ -666,6 +676,6 @@ DECLARE_OOXMLEXPORT_TEST(testTdf135786, "tdf135786.docx")
CPPUNIT_ASSERT_EQUAL(2, getParagraphs());
}
CPPUNIT_PLUGIN_IMPLEMENT();
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/ww8export/ww8export4.cxx b/sw/qa/extras/ww8export/ww8export4.cxx
index 2fbe691..b620a06 100644
--- a/sw/qa/extras/ww8export/ww8export4.cxx
+++ b/sw/qa/extras/ww8export/ww8export4.cxx
@@ -75,7 +75,6 @@ DECLARE_WW8EXPORT_TEST(testTdf141649_conditionalText, "tdf141649_conditionalText
getParagraph(1, "trueResult");
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx
index 1438ba4..b0030e7 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -3428,6 +3428,17 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext )
pProperties->resolve(*this);
}
break;
case NS_ooxml::LN_EG_RunInnerContent_instrText:
{
m_pImpl->SetIsTextDeleted(false);
}
break;
case NS_ooxml::LN_EG_RunInnerContent_delText:
case NS_ooxml::LN_EG_RunInnerContent_delInstrText:
{
m_pImpl->SetIsTextDeleted(true);
}
break;
default:
{
#ifdef DBG_UTIL
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 3f402c5..45128e4 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -349,6 +349,7 @@ DomainMapper_Impl::DomainMapper_Impl(
m_bStartBibliography(false),
m_nStartGenericField(0),
m_bTextInserted(false),
m_bTextDeleted(false),
m_sCurrentPermId(0),
m_bFrameDirectionSet(false),
m_bInDocDefaultsImport(false),
@@ -5350,6 +5351,7 @@ FieldContext::FieldContext(uno::Reference< text::XTextRange > xStart)
: m_bFieldCommandCompleted(false)
, m_xStartRange(std::move( xStart ))
, m_bFieldLocked( false )
, m_bCommandType(false)
{
m_pProperties = new PropertyMap();
}
@@ -5381,7 +5383,7 @@ void FieldContext::CacheVariableValue(const uno::Any& rAny)
void FieldContext::AppendCommand(std::u16string_view rPart)
{
m_sCommand += rPart;
m_sCommand[m_bCommandType] += rPart;
}
::std::vector<OUString> FieldContext::GetCommandParts() const
@@ -5450,6 +5452,8 @@ void DomainMapper_Impl::AppendFieldCommand(OUString const & rPartOfCommand)
OSL_ENSURE( pContext, "no field context available");
if( pContext )
{
// Set command line type: normal or deleted
pContext->SetCommandType(m_bTextDeleted);
pContext->AppendCommand( rPartOfCommand );
}
}
@@ -6730,6 +6734,11 @@ void DomainMapper_Impl::CloseFieldCommand()
m_bSetUserFieldContent = false;
m_bSetCitation = false;
m_bSetDateValue = false;
// tdf#124472: If the normal command line is not empty, use it,
// otherwise, the last active row is evaluated.
if (!pContext->GetCommandIsEmpty(false))
pContext->SetCommandType(false);
const FieldConversionMap_t& aFieldConversionMap = lcl_GetFieldConversion();
try
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index 3150a89..4731a8e 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -177,11 +177,16 @@ class FieldContext : public virtual SvRefBase
bool m_bFieldCommandCompleted;
css::uno::Reference<css::text::XTextRange> m_xStartRange;
OUString m_sCommand;
// Two command string:
// 0: Normal, inserted command line
// 1: Deleted command line
OUString m_sCommand[2];
OUString m_sResult;
OUString m_sVariableValue;
std::optional<FieldId> m_eFieldId;
bool m_bFieldLocked;
// Current command line type: normal or deleted
bool m_bCommandType;
css::uno::Reference<css::text::XTextField> m_xTextField;
css::uno::Reference<css::text::XFormField> m_xFormField;
@@ -207,7 +212,9 @@ public:
const css::uno::Reference<css::text::XTextRange>& GetStartRange() const { return m_xStartRange; }
void AppendCommand(std::u16string_view rPart);
const OUString& GetCommand() const {return m_sCommand; }
const OUString& GetCommand() const {return m_sCommand[m_bCommandType]; }
bool GetCommandIsEmpty(bool bType) const { return m_sCommand[bType].isEmpty(); }
void SetCommandType(bool cType) { m_bCommandType = cType; }
void SetFieldId(FieldId eFieldId ) { m_eFieldId = eFieldId; }
std::optional<FieldId> const & GetFieldId() const { return m_eFieldId; }
@@ -500,6 +507,7 @@ private:
bool m_bStartBibliography;
unsigned int m_nStartGenericField;
bool m_bTextInserted;
bool m_bTextDeleted;
LineNumberSettings m_aLineNumberSettings;
BookmarkMap_t m_aBookmarkMap;
@@ -728,6 +736,7 @@ public:
/// Track if a textframe has been inserted into this section
void SetIsTextFrameInserted( bool bIsInserted );
bool GetIsTextFrameInserted() const { return m_bTextFrameInserted;}
void SetIsTextDeleted(bool bIsTextDeleted) { m_bTextDeleted = bIsTextDeleted; }
void SetIsPreviousParagraphFramed( bool bIsFramed ) { m_bIsPreviousParagraphFramed = bIsFramed; }
bool GetIsPreviousParagraphFramed() const { return m_bIsPreviousParagraphFramed; }
diff --git a/writerfilter/source/ooxml/model.xml b/writerfilter/source/ooxml/model.xml
index 352623d..65295a9 100644
--- a/writerfilter/source/ooxml/model.xml
+++ b/writerfilter/source/ooxml/model.xml
@@ -13405,6 +13405,24 @@
<ref name="ST_ShortHexNumber"/>
</attribute>
</define>
<define name="CT_instrText">
<ref name="ST_String"/>
<attribute name="xml:space">
<data type="string"/>
</attribute>
</define>
<define name="CT_delText">
<ref name="ST_String"/>
<attribute name="xml:space">
<data type="string"/>
</attribute>
</define>
<define name="CT_delInstrText">
<ref name="ST_String"/>
<attribute name="xml:space">
<data type="string"/>
</attribute>
</define>
<define name="CT_ProofErr">
<attribute name="type">
<data type="string"/>
@@ -13437,13 +13455,13 @@
<ref name="CT_Text"/>
</element>
<element name="delText">
<ref name="CT_Text"/>
<ref name="CT_delText"/>
</element>
<element name="instrText">
<ref name="CT_Text"/>
<ref name="CT_instrText"/>
</element>
<element name="delInstrText">
<ref name="CT_Text"/>
<ref name="CT_delInstrText"/>
</element>
<element name="noBreakHyphen">
<ref name="CT_Empty"/>
@@ -18082,6 +18100,21 @@
<attribute name="xml:space" tokenid="ooxml:CT_Text_space"/>
<action name="characters" action="text"/>
</resource>
<resource name="CT_instrText" resource="Stream">
<attribute name="xml:space" tokenid="ooxml:CT_Text_space"/>
<action name="start" action="sendPropertiesWithId" tokenid="ooxml:EG_RunInnerContent_instrText" sendtokenid="ooxml:EG_RunInnerContent_instrText"/>
<action name="characters" action="text"/>
</resource>
<resource name="CT_delText" resource="Stream">
<attribute name="xml:space" tokenid="ooxml:CT_Text_space"/>
<action name="start" action="sendPropertiesWithId" tokenid="ooxml:EG_RunInnerContent_delText" sendtokenid="ooxml:EG_RunInnerContent_delText"/>
<action name="characters" action="text"/>
</resource>
<resource name="CT_delInstrText" resource="Stream">
<attribute name="xml:space" tokenid="ooxml:CT_Text_space"/>
<action name="start" action="sendPropertiesWithId" tokenid="ooxml:EG_RunInnerContent_delInstrText" sendtokenid="ooxml:EG_RunInnerContent_delInstrText"/>
<action name="characters" action="text"/>
</resource>
<resource name="CT_FtnEdnRefChar" resource="Stream">
<action name="end" action="ftnednref"/>
</resource>