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>

Reply via email to