sw/qa/extras/uiwriter/data/tdf135083-simple-text-plus-list.fodt |   34 
++++++++++
 sw/qa/extras/uiwriter/uiwriter9.cxx                             |   19 +++++
 writerfilter/inc/dmapper/resourcemodel.hxx                      |    1 
 writerfilter/source/dmapper/DomainMapper.cxx                    |    5 -
 writerfilter/source/dmapper/DomainMapper.hxx                    |    2 
 writerfilter/source/rtftok/rtfdocumentimpl.cxx                  |    9 ++
 6 files changed, 67 insertions(+), 3 deletions(-)

New commits:
commit 7e95e3868ff6a4d87b9051fa6219d8818c47c517
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Tue Jan 16 13:15:01 2024 +0600
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Wed Jan 17 03:40:33 2024 +0100

    tdf#135083: make sure to apply pending paragraph properties, even without
    
    ... trailing \par.
    Commit a6ae84cc296d4d28e9a48a57406e955138c87a80 (tdf#70318: Prevent
    extra empty paragraph in clipboard RTF content, 2015-11-10) made sure
    to not emit trailing \par for RTF clipboard content. The problem was,
    that in the absense of the \par, the settings of the last paragraph
    were not applied (this happens in DomainMapper_Impl::finishParagraph).
    
    This change makes sure to call dispatchSymbol(RTFKeyword::PAR)
    (which eventually calls the mentioned method and applies pending para
    properties). Since this also adds a new paragraph (which needs to be
    avoided), a new flag is introduced, set by Stream::markLastParagraph.
    Its purpose is to make sure that all properties are applied to para,
    as opposed to bRemove argument to DomainMapper::finishParagraph, which
    prevents setting some properties (e.g., related to numbering - see
    DomainMapper::lcl_utext).
    
    Change-Id: Ia5e368e540c30f95058c81a280a62205aa85398b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162154
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

diff --git a/sw/qa/extras/uiwriter/data/tdf135083-simple-text-plus-list.fodt 
b/sw/qa/extras/uiwriter/data/tdf135083-simple-text-plus-list.fodt
new file mode 100644
index 000000000000..cf2aaac6fce9
--- /dev/null
+++ b/sw/qa/extras/uiwriter/data/tdf135083-simple-text-plus-list.fodt
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:font-face-decls>
+  <style:font-face style:name="OpenSymbol" svg:font-family="OpenSymbol" 
style:font-charset="x-symbol"/>
+ </office:font-face-decls>
+ <office:styles>
+   <style:style style:name="Bullet_20_Symbols" style:display-name="Bullet 
Symbols" style:family="text">
+    <style:text-properties style:font-name="OpenSymbol" 
fo:font-family="OpenSymbol" style:font-charset="x-symbol"/>
+   </style:style>
+ </office:styles>
+ <office:automatic-styles>
+  <text:list-style style:name="L1">
+   <text:list-level-style-bullet text:level="1" 
text:style-name="Bullet_20_Symbols" text:bullet-char="•">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab" 
text:list-tab-stop-position="0.5in" fo:text-indent="-0.25in" 
fo:margin-left="0.5in"/>
+    </style:list-level-properties>
+   </text:list-level-style-bullet>
+  </text:list-style>
+ </office:automatic-styles>
+ <office:body>
+  <office:text>
+   <text:p>Lorem</text:p>
+   <text:list text:style-name="L1">
+    <text:list-item>
+     <text:p>ipsum</text:p>
+    </text:list-item>
+    <text:list-item>
+     <text:p>dolor</text:p>
+    </text:list-item>
+   </text:list>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/uiwriter/uiwriter9.cxx 
b/sw/qa/extras/uiwriter/uiwriter9.cxx
index 4d7c87c9ed0f..b7d638a25b73 100644
--- a/sw/qa/extras/uiwriter/uiwriter9.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter9.cxx
@@ -216,6 +216,25 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf159049)
     CPPUNIT_ASSERT_EQUAL(u"Abreak
here"_ustr, getParagraph(1)->getString());
 }
 
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf135083)
+{
+    createSwDoc("tdf135083-simple-text-plus-list.fodt");
+
+    dispatchCommand(mxComponent, u".uno:SelectAll"_ustr, {});
+    dispatchCommand(mxComponent, u".uno:Copy"_ustr, {});
+
+    // Paste special as RTF
+    uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
+        { { u"SelectedFormat"_ustr,
+            uno::Any(static_cast<sal_uInt32>(SotClipboardFormatId::RTF)) } }));
+    dispatchCommand(mxComponent, ".uno:ClipboardFormatItems", aArgs);
+
+    auto xLastPara = getParagraph(3);
+    CPPUNIT_ASSERT_EQUAL(u"dolor"_ustr, xLastPara->getString());
+    // Without the fix in place, the last paragraph would loose its settings. 
ListId would be empty.
+    CPPUNIT_ASSERT(!getProperty<OUString>(xLastPara, 
u"ListId"_ustr).isEmpty());
+}
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/writerfilter/inc/dmapper/resourcemodel.hxx 
b/writerfilter/inc/dmapper/resourcemodel.hxx
index b983dec88f78..5b64de51ba2c 100644
--- a/writerfilter/inc/dmapper/resourcemodel.hxx
+++ b/writerfilter/inc/dmapper/resourcemodel.hxx
@@ -220,6 +220,7 @@ public:
     virtual void endParagraphGroup() = 0;
 
     virtual void markLastParagraphInSection(){};
+    virtual void markLastParagraph() {} // When finishing this paragraph, do 
not add new paragraph
 
     /**
        Receives start mark for group with the same character properties.
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx 
b/writerfilter/source/dmapper/DomainMapper.cxx
index 142d7f2f12eb..00f1d005a5e2 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -4417,8 +4417,6 @@ void DomainMapper::lcl_utext(const sal_Unicode *const 
data_, size_t len)
                 xContext->Erase(PROP_NUMBERING_LEVEL);
             }
             finishParagraph(bRemove, bNoNumbering);
-            if (bRemove)
-                m_pImpl->RemoveLastParagraph();
 
             m_pImpl->SetParaSectpr(false);
         }
@@ -4947,6 +4945,9 @@ void DomainMapper::finishParagraph(const bool bRemove, 
const bool bNoNumbering)
     if (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::datePicker)
         m_pImpl->m_pSdtHelper->createDateContentControl();
     m_pImpl->finishParagraph(m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH), 
bRemove, bNoNumbering);
+    if (bRemove || mbIsLastPara)
+        m_pImpl->RemoveLastParagraph();
+    mbIsLastPara = false; // handle other subdocuments
 }
 
 void DomainMapper::commentProps(const OUString& sId, const CommentProperties& 
rProps)
diff --git a/writerfilter/source/dmapper/DomainMapper.hxx 
b/writerfilter/source/dmapper/DomainMapper.hxx
index a5110a7cc921..db29f10d45eb 100644
--- a/writerfilter/source/dmapper/DomainMapper.hxx
+++ b/writerfilter/source/dmapper/DomainMapper.hxx
@@ -83,6 +83,7 @@ public:
 
     // Stream
     virtual void markLastParagraphInSection() override;
+    virtual void markLastParagraph() override { mbIsLastPara = true; }
     virtual void markLastSectionGroup() override;
 
     // BinaryObj
@@ -188,6 +189,7 @@ private:
     bool mbIsSplitPara;
     bool mbHasControls;
     bool mbWasShapeInPara;
+    bool mbIsLastPara = false;
     std::unique_ptr< GraphicZOrderHelper > m_zOrderHelper;
     OUString m_sGlossaryEntryName;
 };
diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx 
b/writerfilter/source/rtftok/rtfdocumentimpl.cxx
index 62e346e2cfda..b8d15fb70474 100644
--- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx
+++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx
@@ -3655,8 +3655,15 @@ RTFError RTFDocumentImpl::popState()
         // \par means an empty paragraph at the end of footnotes/endnotes, but
         // not in case of other substreams, like headers.
         if (m_bNeedCr && m_nStreamType != NS_ooxml::LN_footnote
-            && m_nStreamType != NS_ooxml::LN_endnote && m_bIsNewDoc)
+            && m_nStreamType != NS_ooxml::LN_endnote)
+        {
+            if (!m_bIsNewDoc)
+            {
+                // Make sure all the paragraph settings are set, but do not 
add next paragraph
+                Mapper().markLastParagraph();
+            }
             dispatchSymbol(RTFKeyword::PAR);
+        }
         if (m_bNeedSect) // may be set by dispatchSymbol above!
             sectBreak(true);
         else if (!m_pSuperstream)

Reply via email to