sw/qa/extras/ooxmlexport/data/tdf124472.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport18.cxx | 12 ++++++ sw/qa/extras/ww8export/ww8export4.cxx | 1 writerfilter/source/dmapper/DomainMapper.cxx | 11 ++++++ writerfilter/source/dmapper/DomainMapper_Impl.cxx | 11 +++++- writerfilter/source/dmapper/DomainMapper_Impl.hxx | 13 ++++++- writerfilter/source/ooxml/model.xml | 39 ++++++++++++++++++++-- 7 files changed, 79 insertions(+), 8 deletions(-)
New commits: commit 8bf897186de81b39064340c44a54db344d6878f6 Author: Czeber László Ádám <czeber.laszloa...@nisz.hu> AuthorDate: Thu Mar 9 10:35:10 2023 +0100 Commit: László Németh <nem...@numbertext.org> CommitDate: Wed Mar 29 14:58:04 2023 +0000 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 <nem...@numbertext.org> Reviewed-by: László Németh <nem...@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 000000000000..7d7f275be7b9 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf124472.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx index 90e9cf2e6543..b871961406c3 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:i...@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 2fbe6911a2df..b620a0672958 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 1438ba4fee2e..b0030e781ab7 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 3f402c50b3ee..45128e43565c 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 3150a890f88d..4731a8e31375 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 352623d3e86d..65295a94936c 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>