[Libreoffice-commits] core.git: 2 commits - sw/qa writerfilter/source

Luke Deller luke at deller.id.au
Fri Jan 8 02:24:35 PST 2016


 sw/qa/extras/ooxmlimport/data/headerfooter-link-to-prev.docx |binary
 sw/qa/extras/ooxmlimport/ooxmlimport.cxx                     |  100 ++++++
 sw/qa/extras/rtfimport/data/tdf96308-tabpos.rtf              |   12 
 sw/qa/extras/rtfimport/rtfimport.cxx                         |   16 +
 writerfilter/source/dmapper/DomainMapper_Impl.cxx            |    4 
 writerfilter/source/dmapper/PropertyMap.cxx                  |  173 +++++++----
 writerfilter/source/dmapper/PropertyMap.hxx                  |   21 +
 writerfilter/source/rtftok/rtfdocumentimpl.cxx               |    2 
 8 files changed, 274 insertions(+), 54 deletions(-)

New commits:
commit faa767dbef7ee9a0a7ab80bf4ad0fb529cec54a8
Author: Luke Deller <luke at deller.id.au>
Date:   Sun Jan 3 23:12:56 2016 +1100

    tdf#93640 Fix import of linked left page header/footer
    
    In a docx section, any header/footer which is not defined in the
    section is considered to be linked to the previous section.
    In the Word UI this is shown as an option named "Link to Previous",
    and editing the header/footer in either section affects both sections.
    
    LO imports this by copying the header/footer contents from the previous
    section, but it did not handle the case where a section had different
    headers for left pages vs right pages.
    
    Fix this, which involved changing the mechanism used to detect
    whether a section has defined a certain header/footer (which unlinks
    it from the previous section):
    rather than trying to figure it out from the converted LO page styles,
    explicitly track which headers/footers have been defined using boolean
    member variables on the SectionPropertyMap instance.
    
    Conflicts:
    	sw/qa/extras/ooxmlimport/ooxmlimport.cxx
    
    Change-Id: Ic43a867356c2cd5df09d39f2a3ddefa584b6765c

diff --git a/sw/qa/extras/ooxmlimport/data/headerfooter-link-to-prev.docx b/sw/qa/extras/ooxmlimport/data/headerfooter-link-to-prev.docx
new file mode 100644
index 0000000..cc4ddc8
Binary files /dev/null and b/sw/qa/extras/ooxmlimport/data/headerfooter-link-to-prev.docx differ
diff --git a/sw/qa/extras/ooxmlimport/ooxmlimport.cxx b/sw/qa/extras/ooxmlimport/ooxmlimport.cxx
index fe67d76..a245be0 100644
--- a/sw/qa/extras/ooxmlimport/ooxmlimport.cxx
+++ b/sw/qa/extras/ooxmlimport/ooxmlimport.cxx
@@ -3047,6 +3047,106 @@ DECLARE_OOXMLIMPORT_TEST(testTdf95213, "tdf95213.docx")
     CPPUNIT_ASSERT_EQUAL(awt::FontWeight::NORMAL, getProperty<float>(xStyle, "CharWeight"));
 }
 
+// base class to supply a helper method for testHFLinkToPrev
+class testHFBase : public Test
+{
+protected:
+    OUString
+    getHFText(const uno::Reference<style::XStyle>& xPageStyle,
+              const OUString &sPropName)
+    {
+        auto xTextRange = getProperty< uno::Reference<text::XTextRange> >(
+            xPageStyle, sPropName);
+        return xTextRange->getString();
+    }
+};
+
+DECLARE_SW_IMPORT_TEST(testHFLinkToPrev, "headerfooter-link-to-prev.docx",
+    testHFBase)
+{
+    uno::Reference<container::XNameAccess> xPageStyles = getStyles("PageStyles");
+
+    // get a page cursor
+    uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
+    uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier(
+        xModel->getCurrentController(), uno::UNO_QUERY);
+    uno::Reference<text::XPageCursor> xCursor(
+        xTextViewCursorSupplier->getViewCursor(), uno::UNO_QUERY);
+
+    // get LO page style for page 1, corresponding to docx section 1 first page
+    xCursor->jumpToFirstPage();
+    OUString pageStyleName = getProperty<OUString>(xCursor, "PageStyleName");
+    uno::Reference<style::XStyle> xPageStyle(
+        xPageStyles->getByName(pageStyleName), uno::UNO_QUERY);
+    // check page 1 header & footer text
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "HeaderText"),
+        OUString("First page header for all sections"));
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "FooterText"),
+        OUString("First page footer for section 1 only"));
+
+    // get LO page style for page 2, corresponding to docx section 1
+    xCursor->jumpToPage(2);
+    pageStyleName = getProperty<OUString>(xCursor, "PageStyleName");
+    xPageStyle.set( xPageStyles->getByName(pageStyleName), uno::UNO_QUERY );
+    // check header & footer text
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "HeaderTextLeft"),
+        OUString("Even page header for section 1 only"));
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "FooterTextLeft"),
+        OUString("Even page footer for all sections"));
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "HeaderText"),
+        OUString("Odd page header for all sections"));
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "FooterText"),
+        OUString("Odd page footer for section 1 only"));
+
+    // get LO page style for page 4, corresponding to docx section 2 first page
+    xCursor->jumpToPage(4);
+    pageStyleName = getProperty<OUString>(xCursor, "PageStyleName");
+    xPageStyle.set( xPageStyles->getByName(pageStyleName), uno::UNO_QUERY );
+    // check header & footer text
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "HeaderText"),
+        OUString("First page header for all sections"));
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "FooterText"),
+        OUString("First page footer for sections 2 and 3 only"));
+
+    // get LO page style for page 5, corresponding to docx section 2
+    xCursor->jumpToPage(5);
+    pageStyleName = getProperty<OUString>(xCursor, "PageStyleName");
+    xPageStyle.set( xPageStyles->getByName(pageStyleName), uno::UNO_QUERY );
+    // check header & footer text
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "HeaderTextLeft"),
+        OUString("Even page header for sections 2 and 3 only"));
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "FooterTextLeft"),
+        OUString("Even page footer for all sections"));
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "HeaderText"),
+        OUString("Odd page header for all sections"));
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "FooterText"),
+        OUString("Odd page footer for sections 2 and 3 only"));
+
+    // get LO page style for page 7, corresponding to docx section 3 first page
+    xCursor->jumpToPage(7);
+    pageStyleName = getProperty<OUString>(xCursor, "PageStyleName");
+    xPageStyle.set( xPageStyles->getByName(pageStyleName), uno::UNO_QUERY );
+    // check header & footer text
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "HeaderText"),
+        OUString("First page header for all sections"));
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "FooterText"),
+        OUString("First page footer for sections 2 and 3 only"));
+
+    // get LO page style for page 8, corresponding to docx section 3
+    xCursor->jumpToPage(8);
+    pageStyleName = getProperty<OUString>(xCursor, "PageStyleName");
+    xPageStyle.set( xPageStyles->getByName(pageStyleName), uno::UNO_QUERY );
+    // check header & footer text
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "HeaderTextLeft"),
+        OUString("Even page header for sections 2 and 3 only"));
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "FooterTextLeft"),
+        OUString("Even page footer for all sections"));
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "HeaderText"),
+        OUString("Odd page header for all sections"));
+    CPPUNIT_ASSERT_EQUAL(getHFText(xPageStyle, "FooterText"),
+        OUString("Odd page footer for sections 2 and 3 only"));
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 605a758..8401be8 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -1476,6 +1476,10 @@ void DomainMapper_Impl::PushPageHeaderFooter(bool bHeader, SectionPropertyMap::P
     SectionPropertyMap* pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
     if(pSectionContext)
     {
+        // clear the "Link To Previous" flag so that the header/footer
+        // content is not copied from the previous section
+        pSectionContext->ClearHeaderFooterLinkToPrevious(bHeader, eType);
+
         uno::Reference< beans::XPropertySet > xPageStyle =
             pSectionContext->GetPageStyle(
                 GetPageStyles(),
diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx
index 90c4da3..846d9d1 100644
--- a/writerfilter/source/dmapper/PropertyMap.cxx
+++ b/writerfilter/source/dmapper/PropertyMap.cxx
@@ -423,6 +423,12 @@ SectionPropertyMap::SectionPropertyMap(bool bIsFirstSection) :
     ,m_nLnc( 0 )
     ,m_ndxaLnn( 0 )
     ,m_nLnnMin( 0 )
+    ,m_bDefaultHeaderLinkToPrevious(true)
+    ,m_bEvenPageHeaderLinkToPrevious(true)
+    ,m_bFirstPageHeaderLinkToPrevious(true)
+    ,m_bDefaultFooterLinkToPrevious(true)
+    ,m_bEvenPageFooterLinkToPrevious(true)
+    ,m_bFirstPageFooterLinkToPrevious(true)
 {
     static sal_Int32 nNumber = 0;
     nSectionNumber = nNumber++;
@@ -798,72 +804,97 @@ bool SectionPropertyMap::HasFooter(bool bFirstPage) const
 
 #define MIN_HEAD_FOOT_HEIGHT 100 //minimum header/footer height
 
-void SectionPropertyMap::CopyHeaderFooter( uno::Reference< beans::XPropertySet > xPrevStyle,
-    uno::Reference< beans::XPropertySet > xStyle )
+void SectionPropertyMap::CopyHeaderFooterTextProperty (
+    uno::Reference< beans::XPropertySet > xPrevStyle,
+    uno::Reference< beans::XPropertySet > xStyle,
+    PropertyIds ePropId )
 {
     try {
-        // Loop over the Header and Footer properties to copy them
-        static const PropertyIds aProperties[] =
-        {
-            PROP_HEADER_TEXT,
-            PROP_FOOTER_TEXT,
-        };
-
-        bool bHasPrevHeader = false;
-        bool bHasHeader = false;
+        OUString sName = getPropertyName( ePropId );
 
-        OUString sHeaderIsOn = getPropertyName( PROP_HEADER_IS_ON );
-        if (xPrevStyle.is())
-            xPrevStyle->getPropertyValue( sHeaderIsOn ) >>= bHasPrevHeader;
+        SAL_INFO("writerfilter", "Copying " << sName);
+        uno::Reference< text::XTextCopy > xTxt;
         if (xStyle.is())
-            xStyle->getPropertyValue( sHeaderIsOn ) >>= bHasHeader;
-        bool bCopyHeader = bHasPrevHeader && !bHasHeader;
+            xTxt.set(xStyle->getPropertyValue( sName ), uno::UNO_QUERY_THROW );
 
-        if ( bCopyHeader )
-            xStyle->setPropertyValue( sHeaderIsOn, uno::makeAny( sal_True ) );
-
-        bool bHasPrevFooter = false;
-        bool bHasFooter = false;
-
-        OUString sFooterIsOn = getPropertyName( PROP_FOOTER_IS_ON );
+        uno::Reference< text::XTextCopy > xPrevTxt;
         if (xPrevStyle.is())
-            xPrevStyle->getPropertyValue( sFooterIsOn ) >>= bHasPrevFooter;
-        if (xStyle.is())
-            xStyle->getPropertyValue( sFooterIsOn ) >>= bHasFooter;
-        bool bCopyFooter = bHasPrevFooter && !bHasFooter;
+            xPrevTxt.set(xPrevStyle->getPropertyValue( sName ), uno::UNO_QUERY_THROW );
 
-        if ( bCopyFooter && xStyle.is() )
-            xStyle->setPropertyValue( sFooterIsOn, uno::makeAny( sal_True ) );
-
-        // Copying the text properties
-        for ( int i = 0, nNbProps = 2; i < nNbProps; i++ )
-        {
-            bool bIsHeader = ( i < nNbProps / 2 );
-            PropertyIds aPropId = aProperties[i];
-            OUString sName = getPropertyName( aPropId );
-
-            if ( ( bIsHeader && bCopyHeader ) || ( !bIsHeader && bCopyFooter ) )
-            {
-                SAL_INFO("writerfilter", "Copying " << sName);
-                // TODO has to be copied
-                uno::Reference< text::XTextCopy > xTxt;
-                if (xStyle.is())
-                    xTxt.set(xStyle->getPropertyValue( sName ), uno::UNO_QUERY_THROW );
+        xTxt->copyText( xPrevTxt );
+    }
+    catch ( const uno::Exception& e )
+    {
+        SAL_INFO("writerfilter", "An exception occurred in SectionPropertyMap::CopyHeaderFooterTextProperty( ) - " << e.Message);
+    }
+}
 
-                uno::Reference< text::XTextCopy > xPrevTxt;
-                if (xPrevStyle.is())
-                    xPrevTxt.set(xPrevStyle->getPropertyValue( sName ), uno::UNO_QUERY_THROW );
+// Copy headers and footers from the previous page style.
+void SectionPropertyMap::CopyHeaderFooter(
+    uno::Reference< beans::XPropertySet > xPrevStyle,
+    uno::Reference< beans::XPropertySet > xStyle,
+    bool bOmitRightHeader, bool bOmitLeftHeader,
+    bool bOmitRightFooter, bool bOmitLeftFooter)
+{
+    bool bHasPrevHeader = false;
+    bool bHeaderIsShared = true;
+    OUString sHeaderIsOn = getPropertyName( PROP_HEADER_IS_ON );
+    OUString sHeaderIsShared = getPropertyName( PROP_HEADER_IS_SHARED );
+    if ( xPrevStyle.is() )
+    {
+        xPrevStyle->getPropertyValue( sHeaderIsOn ) >>= bHasPrevHeader;
+        xPrevStyle->getPropertyValue( sHeaderIsShared ) >>= bHeaderIsShared;
+    }
 
-                xTxt->copyText( xPrevTxt );
-            }
+    if ( bHasPrevHeader )
+    {
+        xStyle->setPropertyValue( sHeaderIsOn, uno::makeAny( sal_True ) );
+        xStyle->setPropertyValue( sHeaderIsShared, uno::makeAny(bHeaderIsShared));
+        if ( !bOmitRightHeader )
+        {
+            CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
+                    PROP_HEADER_TEXT );
+        }
+        if ( !bHeaderIsShared && !bOmitLeftHeader )
+        {
+            CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
+                    PROP_HEADER_TEXT_LEFT );
         }
     }
-    catch ( const uno::Exception& e )
+
+    bool bHasPrevFooter = false;
+    bool bFooterIsShared = true;
+    OUString sFooterIsOn = getPropertyName( PROP_FOOTER_IS_ON );
+    OUString sFooterIsShared = getPropertyName( PROP_FOOTER_IS_SHARED );
+    if ( xPrevStyle.is() )
+    {
+        xPrevStyle->getPropertyValue( sFooterIsOn ) >>= bHasPrevFooter;
+        xPrevStyle->getPropertyValue( sFooterIsShared ) >>= bFooterIsShared;
+    }
+
+    if ( bHasPrevFooter )
     {
-        SAL_INFO("writerfilter", "An exception occurred in SectionPropertyMap::CopyHeaderFooter( ) - " << e.Message);
+        xStyle->setPropertyValue( sFooterIsOn, uno::makeAny( sal_True ) );
+        xStyle->setPropertyValue( sFooterIsShared, uno::makeAny(bFooterIsShared) );
+        if ( !bOmitRightFooter )
+        {
+            CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
+                    PROP_FOOTER_TEXT );
+        }
+        if ( !bFooterIsShared && !bOmitLeftFooter )
+        {
+            CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
+                    PROP_FOOTER_TEXT_LEFT );
+        }
     }
 }
 
+// Copy header and footer content from the previous docx section as needed.
+//
+// Any headers and footers which were not defined in this docx section
+// should be "linked" with the corresponding header or footer from the
+// previous section.  LO does not support linking of header/footer content
+// across page styles so we just copy the content from the previous section.
 void SectionPropertyMap::CopyLastHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl )
 {
     SAL_INFO("writerfilter", "START>>> SectionPropertyMap::CopyLastHeaderFooter()");
@@ -878,7 +909,21 @@ void SectionPropertyMap::CopyLastHeaderFooter( bool bFirstPage, DomainMapper_Imp
                 rDM_Impl.GetPageStyles(),
                 rDM_Impl.GetTextFactory(),
                 bFirstPage );
-        CopyHeaderFooter( xPrevStyle, xStyle );
+
+        if (bFirstPage)
+        {
+            CopyHeaderFooter( xPrevStyle, xStyle,
+                    !m_bFirstPageHeaderLinkToPrevious, true,
+                    !m_bFirstPageFooterLinkToPrevious, true);
+        }
+        else
+        {
+            CopyHeaderFooter( xPrevStyle, xStyle,
+                    !m_bDefaultHeaderLinkToPrevious,
+                    !m_bEvenPageHeaderLinkToPrevious,
+                    !m_bDefaultFooterLinkToPrevious,
+                    !m_bEvenPageFooterLinkToPrevious);
+        }
     }
     SAL_INFO("writerfilter", "END>>> SectionPropertyMap::CopyLastHeaderFooter()");
 }
@@ -1291,6 +1336,30 @@ void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl )
     rDM_Impl.SetIsFirstParagraphInSection(true);
 }
 
+// Clear the flag that says we should take the header/footer content from
+// the previous section.  This should be called when we encounter a header
+// or footer definition for this section.
+void SectionPropertyMap::ClearHeaderFooterLinkToPrevious(
+        bool bHeader, PageType eType )
+{
+    if( bHeader ) {
+        switch( eType ) {
+            case PAGE_FIRST: m_bFirstPageHeaderLinkToPrevious = false; break;
+            case PAGE_LEFT:  m_bEvenPageHeaderLinkToPrevious = false; break;
+            case PAGE_RIGHT: m_bDefaultHeaderLinkToPrevious = false; break;
+            // no default case as all enumeration values have been covered
+        }
+    }
+    else
+    {
+        switch( eType ) {
+            case PAGE_FIRST: m_bFirstPageFooterLinkToPrevious = false; break;
+            case PAGE_LEFT:  m_bEvenPageFooterLinkToPrevious = false; break;
+            case PAGE_RIGHT: m_bDefaultFooterLinkToPrevious = false; break;
+        }
+    }
+}
+
 class NamedPropertyValue {
     OUString m_aName;
 public:
diff --git a/writerfilter/source/dmapper/PropertyMap.hxx b/writerfilter/source/dmapper/PropertyMap.hxx
index cb6b94d..784e730 100644
--- a/writerfilter/source/dmapper/PropertyMap.hxx
+++ b/writerfilter/source/dmapper/PropertyMap.hxx
@@ -233,12 +233,28 @@ class SectionPropertyMap : public PropertyMap
     sal_Int32                               m_ndxaLnn;
     sal_Int32                               m_nLnnMin;
 
+    // The "Link To Previous" flag indicates whether the header/footer
+    // content should be taken from the previous section
+    bool                                    m_bDefaultHeaderLinkToPrevious;
+    bool                                    m_bEvenPageHeaderLinkToPrevious;
+    bool                                    m_bFirstPageHeaderLinkToPrevious;
+    bool                                    m_bDefaultFooterLinkToPrevious;
+    bool                                    m_bEvenPageFooterLinkToPrevious;
+    bool                                    m_bFirstPageFooterLinkToPrevious;
+
     void _ApplyProperties(css::uno::Reference<css::beans::XPropertySet> const& xStyle);
     css::uno::Reference<css::text::XTextColumns> ApplyColumnProperties(css::uno::Reference<css::beans::XPropertySet> const& xFollowPageStyle,
                                                                        DomainMapper_Impl& rDM_Impl);
     void CopyLastHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl );
-    static void CopyHeaderFooter(css::uno::Reference<css::beans::XPropertySet> xPrevStyle,
-                          css::uno::Reference<css::beans::XPropertySet> xStyle);
+    static void CopyHeaderFooter(
+        css::uno::Reference<css::beans::XPropertySet> xPrevStyle,
+        css::uno::Reference<css::beans::XPropertySet> xStyle,
+        bool bOmitRightHeader=false, bool bOmitLeftHeader=false,
+        bool bOmitRightFooter=false, bool bOmitLeftFooter=false);
+    static void CopyHeaderFooterTextProperty(
+        css::uno::Reference<css::beans::XPropertySet> xPrevStyle,
+        css::uno::Reference<css::beans::XPropertySet> xStyle,
+        PropertyIds ePropId );
     void PrepareHeaderFooterProperties( bool bFirstPage );
     bool HasHeader( bool bFirstPage ) const;
     bool HasFooter( bool bFirstPage ) const;
@@ -317,6 +333,7 @@ public:
     void CloseSectionGroup( DomainMapper_Impl& rDM_Impl );
     /// Handling of margins, header and footer for any kind of sections breaks.
     void HandleMarginsHeaderFooter(DomainMapper_Impl& rDM_Impl);
+    void ClearHeaderFooterLinkToPrevious( bool bHeader, PageType eType );
 };
 
 
commit 1ec88cdb82a28851c4b97d7f043d8bcec3c675e8
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Jan 8 11:23:48 2016 +0100

    tdf#96308 RTF import: fix tab stop inheritance inside table cells
    
    The tab stop list is a paragraph property, and RTF requires to repeat it
    after \s as direct formatting, otherwise the parser should be assumed
    that the tab stop list is cleared as a direct formatting.
    
    Non-buffered text handles that in getDefaultSPRM(), handle it directly
    in the RTF_PARD code for buffered text.
    
    Change-Id: I16b09bc4c177df5a74d16653b829b198aa1a800f

diff --git a/sw/qa/extras/rtfimport/data/tdf96308-tabpos.rtf b/sw/qa/extras/rtfimport/data/tdf96308-tabpos.rtf
new file mode 100644
index 0000000..59fdb8f
--- /dev/null
+++ b/sw/qa/extras/rtfimport/data/tdf96308-tabpos.rtf
@@ -0,0 +1,12 @@
+{\rtf1
+{\stylesheet
+{\s30\tx2552 Body Text 3;}
+}
+\deftab284
+\pard\plain\par
+\trowd\cellx2694\cellx4678 \pard\intbl\tx284 A1\cell
+\pard\intbl\tx2694 before\par
+\pard\plain\s30\intbl 7.\tab Champion\par
+\pard\plain\intbl after\cell\row
+\pard\par
+}
diff --git a/sw/qa/extras/rtfimport/rtfimport.cxx b/sw/qa/extras/rtfimport/rtfimport.cxx
index b1f7706..ba78b9a 100644
--- a/sw/qa/extras/rtfimport/rtfimport.cxx
+++ b/sw/qa/extras/rtfimport/rtfimport.cxx
@@ -2391,6 +2391,22 @@ DECLARE_RTFIMPORT_TEST(testTdf96308Deftab, "tdf96308-deftab.rtf")
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(convertTwipToMm100(284)), getProperty<sal_Int32>(xDefaults, "TabStopDistance"));
 }
 
+DECLARE_RTFIMPORT_TEST(testTdf96308Tabpos, "tdf96308-tabpos.rtf")
+{
+    // Get the tab stops of the second para in the B1 cell of the first table in the document.
+    uno::Reference<text::XTextTablesSupplier> xTextTablesSupplier(mxComponent, uno::UNO_QUERY);
+    uno::Reference<container::XIndexAccess> xTables(xTextTablesSupplier->getTextTables(), uno::UNO_QUERY);
+    uno::Reference<text::XTextTable> xTable(xTables->getByIndex(0), uno::UNO_QUERY);
+    uno::Reference<text::XTextRange> xCell(xTable->getCellByName("B1"), uno::UNO_QUERY);
+    uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xCell->getText(), uno::UNO_QUERY);
+    uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
+    xParaEnum->nextElement();
+    uno::Reference<text::XTextRange> xPara(xParaEnum->nextElement(), uno::UNO_QUERY);
+    auto aTabStops = getProperty< uno::Sequence<style::TabStop> >(xPara, "ParaTabStops");
+    // This failed: tab stops were not deleted as direct formatting on the paragraph.
+    CPPUNIT_ASSERT(!aTabStops.hasElements());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx
index 19c0cbe..8ee4bc7 100644
--- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx
+++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx
@@ -2987,6 +2987,8 @@ RTFError RTFDocumentImpl::dispatchFlag(RTFKeyword nKeyword)
         {
             // We are still in a table.
             m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_inTbl, std::make_shared<RTFValue>(1));
+            // Ideally getDefaultSPRM() would take care of this, but it would not when we're buffering.
+            m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_CT_PPrBase_tabs, std::make_shared<RTFValue>());
         }
         m_aStates.top().resetFrame();
 


More information about the Libreoffice-commits mailing list