[Libreoffice-commits] core.git: 6 commits - sw/qa sw/source writerfilter/CppunitTest_writerfilter_misc.mk writerfilter/Module_writerfilter.mk writerfilter/qa writerfilter/source

Michael Stahl mstahl at redhat.com
Mon Mar 3 04:58:26 PST 2014


 sw/qa/extras/rtfimport/data/cont-section-pagebreak.rtf |   16 +
 sw/qa/extras/rtfimport/data/footer-para.rtf            |    5 
 sw/qa/extras/rtfimport/rtfimport.cxx                   |   42 ++
 sw/source/core/unocore/unofield.cxx                    |    2 
 writerfilter/CppunitTest_writerfilter_misc.mk          |   36 ++
 writerfilter/Module_writerfilter.mk                    |    1 
 writerfilter/qa/cppunittests/misc/misc.cxx             |  162 ++++++++++
 writerfilter/source/dmapper/DomainMapper.cxx           |    4 
 writerfilter/source/dmapper/DomainMapper_Impl.cxx      |  252 +++++++++++------
 writerfilter/source/dmapper/DomainMapper_Impl.hxx      |   10 
 writerfilter/source/rtftok/rtfdocumentimpl.cxx         |   71 +++-
 writerfilter/source/rtftok/rtfdocumentimpl.hxx         |    3 
 12 files changed, 502 insertions(+), 102 deletions(-)

New commits:
commit e24ae38c8ef233e4b44840058e35959194724743
Author: Michael Stahl <mstahl at redhat.com>
Date:   Sun Mar 2 00:32:17 2014 +0100

    fdo#47811: RTF import: fix Database field content
    
    1. the Database field master does not have a "Content" property
    2. SetFieldContent was called once for every chunk of text in the field
       result, always overwriting the previous value; accumulate the text.
    
    Change-Id: I63cfda19e2e416c52d100d9071796305a5d813c6

diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx
index e01df18..8023f3d 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -2666,7 +2666,7 @@ void DomainMapper::lcl_text(const sal_uInt8 * data_, size_t len)
         else if( m_pImpl->IsOpenField() && m_pImpl->IsFieldResultAsString())
              /*depending on the success of the field insert operation this result will be
               set at the field or directly inserted into the text*/
-            m_pImpl->SetFieldResult( sText );
+            m_pImpl->AppendFieldResult(sText);
         else
         {
             if (pContext == 0)
@@ -2782,7 +2782,7 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len)
             else if( m_pImpl->IsOpenField() && m_pImpl->IsFieldResultAsString())
                 /*depending on the success of the field insert operation this result will be
                   set at the field or directly inserted into the text*/
-                m_pImpl->SetFieldResult( sText );
+                m_pImpl->AppendFieldResult(sText);
             else
             {
                 if (pContext == 0)
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 43a0f3b..3ed405d 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -3331,7 +3331,6 @@ void DomainMapper_Impl::CloseFieldCommand()
     //                             uno::makeAny( pContext->GetCommand().copy( nIndex + 1 )));
                         uno::Reference< text::XDependentTextField > xDependentField( xFieldInterface, uno::UNO_QUERY_THROW );
                         xDependentField->attachTextFieldMaster( xMaster );
-                        m_bSetUserFieldContent = true;
                     }
                     break;
                     case FIELD_MERGEREC     : break;
@@ -3599,8 +3598,18 @@ bool DomainMapper_Impl::IsFieldResultAsString()
     return bRet;
 }
 
+void DomainMapper_Impl::AppendFieldResult(OUString const& rString)
+{
+    assert(!m_aFieldStack.empty());
+    FieldContextPtr pContext = m_aFieldStack.top();
+    SAL_WARN_IF(!pContext.get(), "writerfilter.dmapper", "no field context");
+    if (pContext.get())
+    {
+        pContext->AppendResult(rString);
+    }
+}
 
-void DomainMapper_Impl::SetFieldResult( OUString& rResult )
+void DomainMapper_Impl::SetFieldResult(OUString const& rResult)
 {
 #ifdef DEBUG_DOMAINMAPPER
     dmapper_logger->startElement("setFieldResult");
@@ -3656,9 +3665,10 @@ void DomainMapper_Impl::SetFieldResult( OUString& rResult )
                 }
             }
         }
-        catch( const uno::Exception& )
+        catch (const uno::Exception& e)
         {
-
+            SAL_WARN("writerfilter.dmapper",
+                "DomainMapper_Impl::SetFieldResult: exception: " << e.Message);
         }
     }
 }
@@ -3702,6 +3712,9 @@ void DomainMapper_Impl::PopFieldContext()
         if( !pContext->IsCommandCompleted() )
             CloseFieldCommand();
 
+        if (!pContext->GetResult().isEmpty())
+            SetFieldResult(pContext->GetResult());
+
         //insert the field, TC or TOC
         uno::Reference< text::XTextAppend >  xTextAppend;
         if (!m_aTextAppendStack.empty())
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index 249f75c..ddd7775 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -124,6 +124,7 @@ class FieldContext
     ::com::sun::star::uno::Reference< ::com::sun::star::text::XTextRange >          m_xStartRange;
 
     OUString                                                                 m_sCommand;
+    OUString m_sResult;
 
     ::com::sun::star::uno::Reference< ::com::sun::star::text::XTextField >          m_xTextField;
     ::com::sun::star::uno::Reference< ::com::sun::star::text::XFormField >          m_xFormField;
@@ -142,6 +143,9 @@ public:
     void                    AppendCommand(const OUString& rPart);
     const OUString&  GetCommand() const {return m_sCommand; }
 
+    void AppendResult(OUString const& rResult) { m_sResult += rResult; }
+    const OUString&  GetResult() const { return m_sResult; }
+
     void                    SetCommandCompleted() { m_bFieldCommandCompleted = true; }
     bool                    IsCommandCompleted() const { return m_bFieldCommandCompleted;    }
 
@@ -601,8 +605,9 @@ public:
     void CloseFieldCommand();
     //the _current_ fields require a string type result while TOCs accept richt results
     bool IsFieldResultAsString();
+    void AppendFieldResult(OUString const& rResult);
     //apply the result text to the related field
-    void SetFieldResult( OUString& rResult );
+    void SetFieldResult(OUString const& rResult);
     // set FFData of top field context
     void SetFieldFFData( FFDataHandler::Pointer_t pFFDataHandler );
     //the end of field is reached (0x15 appeared) - the command might still be open
commit c06f686fe001392ceb7f606f5dc3c775997cc7de
Author: Michael Stahl <mstahl at redhat.com>
Date:   Sat Mar 1 23:13:39 2014 +0100

    fdo#47811: fix setPropertyValue("Name") of Database fieldmaster
    
    This one is assigning to the wrong pType variable.
    
    (regression from CWS swwarnings)
    
    Change-Id: I9a74734d22313f215ed69c9a57edf7eb035736ea

diff --git a/sw/source/core/unocore/unofield.cxx b/sw/source/core/unocore/unofield.cxx
index d529fab..044dcb1 100644
--- a/sw/source/core/unocore/unofield.cxx
+++ b/sw/source/core/unocore/unofield.cxx
@@ -645,7 +645,7 @@ throw (beans::UnknownPropertyException, beans::PropertyVetoException,
             case RES_DBFLD :
             {
                 rValue >>= m_pImpl->m_sParam3;
-                pType = GetFldType();
+                pType2 = GetFldType();
             }
             break;
         }
commit 3dc548476c7e88f7a67cc38daf622631a34e34dd
Author: Michael Stahl <mstahl at redhat.com>
Date:   Sat Mar 1 22:05:51 2014 +0100

    writerfilter: salvage a field parameter parsing train wreck
    
    Field parameters get horribly maimed by lcl_ExtractParameter which
    clearly has never worked in its 7 years of existence (and looking at the
    inanity at the call sites makes one wonder what the author was smoking).
    
    The format is actually quite annoying, since spaces between parameters
    are optional.
    
    The old RTF filter was at least able to parse "PAGEREF bookmark" fields,
    so this fixes such regressions (related: rhbz#1065629).
    
    Change-Id: I9b2e32c3c7264be0fc1077cb8fb3f1bc5c1955bb

diff --git a/writerfilter/CppunitTest_writerfilter_misc.mk b/writerfilter/CppunitTest_writerfilter_misc.mk
new file mode 100644
index 0000000..1cdcd80
--- /dev/null
+++ b/writerfilter/CppunitTest_writerfilter_misc.mk
@@ -0,0 +1,36 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,writerfilter_misc))
+
+$(eval $(call gb_CppunitTest_use_api,writerfilter_misc,\
+	offapi \
+	udkapi \
+))
+
+$(eval $(call gb_CppunitTest_use_external,writerfilter_misc,boost_headers))
+
+$(eval $(call gb_CppunitTest_use_libraries,writerfilter_misc, \
+	writerfilter \
+	cppu \
+	sal \
+	$(gb_UWINAPI) \
+))
+
+$(eval $(call gb_CppunitTest_set_include,writerfilter_misc, \
+	$$(INCLUDE) \
+	-I$(SRCDIR)/writerfilter/inc \
+))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,writerfilter_misc, \
+	writerfilter/qa/cppunittests/misc/misc \
+))
+
+
+# vim: set noet sw=4 ts=4:
diff --git a/writerfilter/Module_writerfilter.mk b/writerfilter/Module_writerfilter.mk
index 783b6ca..2fcb9e6 100644
--- a/writerfilter/Module_writerfilter.mk
+++ b/writerfilter/Module_writerfilter.mk
@@ -16,6 +16,7 @@ $(eval $(call gb_Module_add_targets,writerfilter,\
 
 $(eval $(call gb_Module_add_slowcheck_targets,writerfilter,\
     CppunitTest_writerfilter_rtftok \
+    CppunitTest_writerfilter_misc \
 ))
 
 # vim: set noet sw=4 ts=4:
diff --git a/writerfilter/qa/cppunittests/misc/misc.cxx b/writerfilter/qa/cppunittests/misc/misc.cxx
new file mode 100644
index 0000000..f7031b4
--- /dev/null
+++ b/writerfilter/qa/cppunittests/misc/misc.cxx
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <limits>
+#include <vector>
+
+#include <boost/tuple/tuple.hpp>
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+#include <sal/types.h>
+
+#include <rtl/ustring.hxx>
+
+#include <WriterFilterDllApi.hxx>
+
+
+using namespace std;
+
+
+namespace writerfilter { namespace dmapper {
+
+SAL_DLLPUBLIC_IMPORT // export just for test
+boost::tuple<OUString, vector<OUString>, vector<OUString> >
+lcl_SplitFieldCommand(const OUString& rCommand);
+
+} }
+
+
+namespace {
+
+class WriterfilterMiscTest
+    : public ::CppUnit::TestFixture
+{
+public:
+    virtual void setUp();
+    virtual void tearDown();
+
+    void testFieldParameters();
+
+    CPPUNIT_TEST_SUITE(WriterfilterMiscTest);
+    CPPUNIT_TEST(testFieldParameters);
+    CPPUNIT_TEST_SUITE_END();
+};
+
+void WriterfilterMiscTest::setUp()
+{
+}
+
+void WriterfilterMiscTest::tearDown()
+{
+}
+
+void WriterfilterMiscTest::testFieldParameters()
+{
+    using writerfilter::dmapper::lcl_SplitFieldCommand;
+    boost::tuple<OUString, vector<OUString>, vector<OUString> > result;
+
+    result = lcl_SplitFieldCommand("PAGEREF last_page");
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT_EQUAL(size_t(1), boost::get<1>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("last_page"), boost::get<1>(result)[0]);
+    CPPUNIT_ASSERT(boost::get<2>(result).empty());
+
+    result = lcl_SplitFieldCommand(" PAGEREF last_page ");
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT_EQUAL(size_t(1), boost::get<1>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("last_page"), boost::get<1>(result)[0]);
+
+    result = lcl_SplitFieldCommand("pageref last_page");
+    CPPUNIT_ASSERT(boost::get<2>(result).empty());
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT_EQUAL(size_t(1), boost::get<1>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("last_page"), boost::get<1>(result)[0]);
+    CPPUNIT_ASSERT(boost::get<2>(result).empty());
+
+    result = lcl_SplitFieldCommand("pageref \"last_page\"");
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT_EQUAL(size_t(1), boost::get<1>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("last_page"), boost::get<1>(result)[0]);
+    CPPUNIT_ASSERT(boost::get<2>(result).empty());
+
+    result = lcl_SplitFieldCommand("\"PAGEREF\" \"last_page\" \"\" ");
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT_EQUAL(size_t(2), boost::get<1>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("last_page"), boost::get<1>(result)[0]);
+    CPPUNIT_ASSERT_EQUAL(OUString(), boost::get<1>(result)[1]);
+    CPPUNIT_ASSERT(boost::get<2>(result).empty());
+
+    result = lcl_SplitFieldCommand("\"PAGEREF\"\"last_page\"  ");
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT_EQUAL(size_t(1), boost::get<1>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("last_page"), boost::get<1>(result)[0]);
+    CPPUNIT_ASSERT(boost::get<2>(result).empty());
+
+    result = lcl_SplitFieldCommand("PAGEREF\"last_page\"  ");
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT_EQUAL(size_t(1), boost::get<1>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("last_page"), boost::get<1>(result)[0]);
+    CPPUNIT_ASSERT(boost::get<2>(result).empty());
+
+    result = lcl_SplitFieldCommand("\"PAGEREF\"last_page \"\"");
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT_EQUAL(size_t(2), boost::get<1>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("last_page"), boost::get<1>(result)[0]);
+    CPPUNIT_ASSERT_EQUAL(OUString(), boost::get<1>(result)[1]);
+    CPPUNIT_ASSERT(boost::get<2>(result).empty());
+
+    result = lcl_SplitFieldCommand("\"PAGEREF\"last_page \"\"");
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT_EQUAL(size_t(2), boost::get<1>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("last_page"), boost::get<1>(result)[0]);
+    CPPUNIT_ASSERT_EQUAL(OUString(), boost::get<1>(result)[1]);
+    CPPUNIT_ASSERT(boost::get<2>(result).empty());
+
+    result = lcl_SplitFieldCommand("pageref \"last\\\\pa\\\"ge\"");
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT_EQUAL(size_t(1), boost::get<1>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("last\\pa\"ge"), boost::get<1>(result)[0]);
+    CPPUNIT_ASSERT(boost::get<2>(result).empty());
+
+    result = lcl_SplitFieldCommand("PAGEREF\"last_page\"\\*");
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT_EQUAL(size_t(1), boost::get<1>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("last_page"), boost::get<1>(result)[0]);
+    CPPUNIT_ASSERT_EQUAL(size_t(1), boost::get<2>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("\\*"), boost::get<2>(result)[0]);
+
+    result = lcl_SplitFieldCommand("PAGEREF  last_page   \\b   foobar ");
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT_EQUAL(size_t(1), boost::get<1>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("last_page"), boost::get<1>(result)[0]);
+    CPPUNIT_ASSERT_EQUAL(size_t(2), boost::get<2>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("\\B"), boost::get<2>(result)[0]);
+    CPPUNIT_ASSERT_EQUAL(OUString("foobar"), boost::get<2>(result)[1]);
+
+    result = lcl_SplitFieldCommand("PAGEREF\\bfoobar\\A\"\"");
+    CPPUNIT_ASSERT_EQUAL(OUString("PAGEREF"), boost::get<0>(result));
+    CPPUNIT_ASSERT(boost::get<1>(result).empty());
+    CPPUNIT_ASSERT_EQUAL(size_t(4), boost::get<2>(result).size());
+    CPPUNIT_ASSERT_EQUAL(OUString("\\B"), boost::get<2>(result)[0]);
+    CPPUNIT_ASSERT_EQUAL(OUString("foobar"), boost::get<2>(result)[1]);
+    CPPUNIT_ASSERT_EQUAL(OUString("\\A"), boost::get<2>(result)[2]);
+    CPPUNIT_ASSERT_EQUAL(OUString(), boost::get<2>(result)[3]);
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(WriterfilterMiscTest);
+
+}
+
+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 bf6155b9..43a0f3b 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -68,6 +68,7 @@
 #include <ooxml/OOXMLFastTokens.hxx>
 
 #include <map>
+#include <boost/tuple/tuple.hpp>
 
 #include <vcl/svapp.hxx>
 #include <vcl/outdev.hxx>
@@ -1925,37 +1926,133 @@ OUString lcl_ParseFormat( const OUString& rCommand )
 /*-------------------------------------------------------------------------
 extract a parameter (with or without quotes) between the command and the following backslash
   -----------------------------------------------------------------------*/
-OUString lcl_ExtractParameter(const OUString& rCommand, sal_Int32 nCommandLength )
+static OUString lcl_ExtractToken(OUString const& rCommand,
+        sal_Int32 & rIndex, bool & rHaveToken, bool & rIsSwitch)
 {
-    sal_Int32 nStartIndex = nCommandLength;
-    sal_Int32 nEndIndex = 0;
-    sal_Int32 nQuoteIndex = rCommand.indexOf( '\"', nStartIndex);
-    if( nQuoteIndex >= 0)
+    rHaveToken = false;
+    rIsSwitch = false;
+
+    OUStringBuffer token;
+    bool bQuoted(false);
+    for (; rIndex < rCommand.getLength(); ++rIndex)
+    {
+        sal_Unicode const currentChar(rCommand[rIndex]);
+        switch (currentChar)
+        {
+            case '\\':
+            {
+                if (rIndex == rCommand.getLength() - 1)
+                {
+                    SAL_INFO("writerfilter.dmapper", "field: trailing escape");
+                    ++rIndex;
+                    return OUString();
+                }
+                sal_Unicode const nextChar(rCommand[rIndex+1]);
+                if (bQuoted || '\\' == nextChar)
+                {
+                    ++rIndex; // read 2 chars
+                    token.append(nextChar);
+                }
+                else // field switch (case insensitive)
+                {
+                    rHaveToken = true;
+                    if (token.isEmpty())
+                    {
+                        rIsSwitch = true;
+                        rIndex += 2; // read 2 chars
+                        return rCommand.copy(rIndex - 2, 2).toAsciiUpperCase();
+                    }
+                    else
+                    {   // leave rIndex, read it again next time
+                        return token.makeStringAndClear();
+                    }
+                }
+            }
+            break;
+            case '\"':
+                if (bQuoted || !token.isEmpty())
+                {
+                    rHaveToken = true;
+                    if (bQuoted)
+                    {
+                        ++rIndex;
+                    }
+                    return token.makeStringAndClear();
+                }
+                else
+                {
+                    bQuoted = true;
+                }
+            break;
+            case ' ':
+                if (bQuoted)
+                {
+                    token.append(' ');
+                }
+                else
+                {
+                    if (!token.isEmpty())
+                    {
+                        rHaveToken = true;
+                        ++rIndex;
+                        return token.makeStringAndClear();
+                    }
+                }
+            break;
+            default:
+                token.append(currentChar);
+            break;
+        }
+    }
+    assert(rIndex == rCommand.getLength());
+    if (bQuoted)
     {
-        nStartIndex = nQuoteIndex + 1;
-        nEndIndex = rCommand.indexOf( '\"', nStartIndex + 1) - 1;
+        SAL_INFO("writerfilter.dmapper",
+                    "field argument with unterminated quote");
+        return OUString();
     }
     else
     {
-        nEndIndex = rCommand.indexOf(" \\", nStartIndex);
+        rHaveToken = !token.isEmpty();
+        return token.makeStringAndClear();
     }
-    OUString sRet;
-    if( nEndIndex > nStartIndex + 1 )
+}
+
+SAL_DLLPUBLIC_EXPORT // export just for test
+boost::tuple<OUString, vector<OUString>, vector<OUString>>
+lcl_SplitFieldCommand(const OUString& rCommand)
+{
+    OUString sType;
+    vector<OUString> arguments;
+    vector<OUString> switches;
+    sal_Int32 nStartIndex(0);
+
+    do
     {
-        //remove spaces at start and end of the result
-        if(nQuoteIndex <= 0)
+        bool bHaveToken;
+        bool bIsSwitch;
+        OUString const token =
+            lcl_ExtractToken(rCommand, nStartIndex, bHaveToken, bIsSwitch);
+        assert(nStartIndex <= rCommand.getLength());
+        if (bHaveToken)
         {
-            const sal_Unicode* pCommandStr = rCommand.getStr();
-            while( nStartIndex < nEndIndex && pCommandStr[nStartIndex] == ' ')
-                    ++nStartIndex;
-            while( nEndIndex > nStartIndex && pCommandStr[nEndIndex] == ' ')
-                    --nEndIndex;
+            if (sType.isEmpty())
+            {
+                sType = token.toAsciiUpperCase();
+            }
+            else if (bIsSwitch || !switches.empty())
+            {
+                switches.push_back(token);
+            }
+            else
+            {
+                arguments.push_back(token);
+            }
         }
-        sRet = rCommand.copy( nStartIndex, nEndIndex - nStartIndex + 1);
-    }
-    return sRet;
-}
+    } while (nStartIndex < rCommand.getLength());
 
+    return boost::make_tuple(sType, arguments, switches);
+}
 
 
 OUString lcl_ExctractAskVariableAndHint( const OUString& rCommand, OUString& rHint )
@@ -2405,7 +2502,7 @@ void DomainMapper_Impl::handleAutoNum
 }
 
 void DomainMapper_Impl::handleAuthor
-    (FieldContextPtr pContext,
+    (OUString const& rFirstParam,
     PropertyNameSupplier& rPropNameSupplier,
      uno::Reference< uno::XInterface > & /*xFieldInterface*/,
      uno::Reference< beans::XPropertySet > xFieldProperties,
@@ -2415,19 +2512,7 @@ void DomainMapper_Impl::handleAuthor
         xFieldProperties->setPropertyValue
             ( rPropNameSupplier.GetName(PROP_FULL_NAME), uno::makeAny( true ));
 
-    sal_Int32 nLen = sizeof( " AUTHOR" );
-    if ( eFieldId != FIELD_AUTHOR )
-    {
-        if (  eFieldId == FIELD_USERINITIALS )
-            nLen = sizeof( " USERINITIALS" );
-        else if (  eFieldId == FIELD_USERNAME )
-            nLen = sizeof( " USERNAME" );
-    }
-
-    OUString sParam =
-        lcl_ExtractParameter(pContext->GetCommand(), nLen );
-
-    if(!sParam.isEmpty())
+    if (!rFirstParam.isEmpty())
     {
         xFieldProperties->setPropertyValue(
                 rPropNameSupplier.GetName( PROP_IS_FIXED ),
@@ -2438,16 +2523,14 @@ void DomainMapper_Impl::handleAuthor
 
     void DomainMapper_Impl::handleDocProperty
         (FieldContextPtr pContext,
+        OUString const& rFirstParam,
         PropertyNameSupplier& rPropNameSupplier,
         uno::Reference< uno::XInterface > & xFieldInterface,
         uno::Reference< beans::XPropertySet > xFieldProperties)
 {
     //some docproperties should be imported as document statistic fields, some as DocInfo fields
     //others should be user fields
-    OUString sParam =
-        lcl_ExtractParameter(pContext->GetCommand(), sizeof(" DOCPROPERTY") );
-
-    if(!sParam.isEmpty())
+    if (!rFirstParam.isEmpty())
     {
         #define SET_ARABIC      0x01
         #define SET_FULL_NAME   0x02
@@ -2487,7 +2570,7 @@ void DomainMapper_Impl::handleAuthor
         for( ; nMap < sizeof(aDocProperties) / sizeof(DocPropertyMap);
             ++nMap )
         {
-            if(sParam.equalsAscii(aDocProperties[nMap].pDocPropertyName))
+            if (rFirstParam.equalsAscii(aDocProperties[nMap].pDocPropertyName))
             {
                 sFieldServiceName =
                 OUString::createFromAscii
@@ -2514,7 +2597,7 @@ void DomainMapper_Impl::handleAuthor
                 uno::UNO_QUERY_THROW);
         if( bIsCustomField )
             xFieldProperties->setPropertyValue(
-                rPropNameSupplier.GetName(PROP_NAME), uno::makeAny( sParam ));
+                rPropNameSupplier.GetName(PROP_NAME), uno::makeAny(rFirstParam));
         else
         {
             if(0 != (aDocProperties[nMap].nFlags & SET_ARABIC))
@@ -2907,13 +2990,14 @@ void DomainMapper_Impl::CloseFieldCommand()
         try
         {
             uno::Reference< uno::XInterface > xFieldInterface;
-            //at first determine the field type - erase leading and trailing whitespaces
-            OUString sCommand( pContext->GetCommand().trim() );
-            sal_Int32 nSpaceIndex = sCommand.indexOf( ' ' );
-            if( 0 <= nSpaceIndex )
-                sCommand = sCommand.copy(0, nSpaceIndex).toAsciiUpperCase();
 
-            FieldConversionMap_t::iterator aIt = aFieldConversionMap.find(sCommand);
+            boost::tuple<OUString, vector<OUString>, vector<OUString> > const
+                field(lcl_SplitFieldCommand(pContext->GetCommand()));
+            OUString const sFirstParam(boost::get<1>(field).empty()
+                    ? OUString() : boost::get<1>(field).front());
+
+            FieldConversionMap_t::iterator const aIt =
+                aFieldConversionMap.find(boost::get<0>(field));
             if(aIt != aFieldConversionMap.end())
             {
                 bool bCreateEnhancedField = false;
@@ -2964,7 +3048,8 @@ void DomainMapper_Impl::CloseFieldCommand()
                     if ( bCreateEnhancedField )
                     {
                         FieldConversionMap_t aEnhancedFieldConversionMap = lcl_GetEnhancedFieldConversion();
-                        FieldConversionMap_t::iterator aEnhancedIt = aEnhancedFieldConversionMap.find(sCommand);
+                        FieldConversionMap_t::iterator aEnhancedIt =
+                            aEnhancedFieldConversionMap.find(boost::get<0>(field));
                         if ( aEnhancedIt != aEnhancedFieldConversionMap.end())
                             sServiceName += OUString::createFromAscii(aEnhancedIt->second.cFieldServiceName );
                     }
@@ -3002,7 +3087,9 @@ void DomainMapper_Impl::CloseFieldCommand()
                     case FIELD_AUTHOR       :
                     case FIELD_USERNAME     :
                     case FIELD_USERINITIALS :
-                        handleAuthor(pContext, rPropNameSupplier, xFieldInterface, xFieldProperties, aIt->second.eFieldId  );
+                        handleAuthor(sFirstParam, rPropNameSupplier,
+                            xFieldInterface, xFieldProperties,
+                            aIt->second.eFieldId);
                     break;
                     case FIELD_DATE:
                     if (xFieldProperties.is())
@@ -3041,14 +3128,14 @@ void DomainMapper_Impl::CloseFieldCommand()
                     }
                     break;
                     case FIELD_DOCPROPERTY :
-                        handleDocProperty(pContext, rPropNameSupplier, xFieldInterface, xFieldProperties);
+                        handleDocProperty(pContext, sFirstParam, rPropNameSupplier,
+                                xFieldInterface, xFieldProperties);
                     break;
                     case FIELD_DOCVARIABLE  :
                     {
-                        OUString sParam = lcl_ExtractParameter(pContext->GetCommand(), sizeof(" DOCVARIABLE") );
                         //create a user field and type
                         uno::Reference< beans::XPropertySet > xMaster =
-                            FindOrCreateFieldMaster( "com.sun.star.text.FieldMaster.User", sParam );
+                            FindOrCreateFieldMaster("com.sun.star.text.FieldMaster.User", sFirstParam);
                         uno::Reference< text::XDependentTextField > xDependentField( xFieldInterface, uno::UNO_QUERY_THROW );
                         xDependentField->attachTextFieldMaster( xMaster );
                         m_bSetUserFieldContent = true;
@@ -3074,7 +3161,7 @@ void DomainMapper_Impl::CloseFieldCommand()
                         else
                         {
                             //merge Read_SubF_Ruby into filter/.../util.cxx and reuse that ?
-                            nSpaceIndex = aCommand.indexOf(' ');
+                            sal_Int32 nSpaceIndex = aCommand.indexOf(' ');
                             if(nSpaceIndex > 0)
                                 aCommand = aCommand.copy(nSpaceIndex).trim();
                             if (aCommand.startsWith("\\s"))
@@ -3205,8 +3292,7 @@ void DomainMapper_Impl::CloseFieldCommand()
                     case FIELD_INCLUDEPICTURE: break;
                     case FIELD_KEYWORDS     :
                     {
-                        OUString sParam = lcl_ExtractParameter(pContext->GetCommand(), sizeof(" KEYWORDS") );
-                        if(!sParam.isEmpty())
+                        if (!sFirstParam.isEmpty())
                         {
                             xFieldProperties->setPropertyValue(
                                     rPropNameSupplier.GetName( PROP_IS_FIXED ), uno::makeAny( true ));
@@ -3236,10 +3322,9 @@ void DomainMapper_Impl::CloseFieldCommand()
                     case FIELD_MERGEFIELD  :
                     {
                         //todo: create a database field and fieldmaster pointing to a column, only
-                        OUString sParam = lcl_ExtractParameter(pContext->GetCommand(), sizeof(" MERGEFIELD") );
                         //create a user field and type
                         uno::Reference< beans::XPropertySet > xMaster =
-                            FindOrCreateFieldMaster( "com.sun.star.text.FieldMaster.Database", sParam );
+                            FindOrCreateFieldMaster("com.sun.star.text.FieldMaster.Database", sFirstParam);
 
     //                    xFieldProperties->setPropertyValue(
     //                             "FieldCode",
@@ -3270,21 +3355,21 @@ void DomainMapper_Impl::CloseFieldCommand()
                     if (xFieldProperties.is() && !m_bStartTOC)
                     {
                         bool bPageRef = aIt->second.eFieldId == FIELD_PAGEREF;
-                        OUString sBookmark = lcl_ExtractParameter(pContext->GetCommand(),
-                                (bPageRef ? sizeof(" PAGEREF") : sizeof(" REF")));
 
                         // Do we need a GetReference (default) or a GetExpression field?
                         uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY );
                         uno::Reference< container::XNameAccess > xFieldMasterAccess = xFieldsSupplier->getTextFieldMasters();
 
-                        if (!xFieldMasterAccess->hasByName("com.sun.star.text.FieldMaster.SetExpression." + sBookmark))
+                        if (!xFieldMasterAccess->hasByName(
+                                "com.sun.star.text.FieldMaster.SetExpression."
+                                + sFirstParam))
                         {
                         xFieldProperties->setPropertyValue(
                             rPropNameSupplier.GetName(PROP_REFERENCE_FIELD_SOURCE),
                             uno::makeAny( sal_Int16(text::ReferenceFieldSource::BOOKMARK)) );
                         xFieldProperties->setPropertyValue(
                             rPropNameSupplier.GetName(PROP_SOURCE_NAME),
-                            uno::makeAny( sBookmark) );
+                            uno::makeAny(sFirstParam) );
                         sal_Int16 nFieldPart = (bPageRef ? text::ReferenceFieldPart::PAGE : text::ReferenceFieldPart::TEXT);
                         OUString sValue;
                         if( lcl_FindInCommand( pContext->GetCommand(), 'p', sValue ))
@@ -3314,7 +3399,9 @@ void DomainMapper_Impl::CloseFieldCommand()
                         {
                             xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.TextField.GetExpression");
                             xFieldProperties.set(xFieldInterface, uno::UNO_QUERY);
-                            xFieldProperties->setPropertyValue(rPropNameSupplier.GetName(PROP_CONTENT), uno::makeAny(sBookmark));
+                            xFieldProperties->setPropertyValue(
+                                rPropNameSupplier.GetName(PROP_CONTENT),
+                                uno::makeAny(sFirstParam));
                             xFieldProperties->setPropertyValue(rPropNameSupplier.GetName(PROP_SUB_TYPE), uno::makeAny(text::SetVariableType::STRING));
                         }
                     }
@@ -3381,8 +3468,7 @@ void DomainMapper_Impl::CloseFieldCommand()
                     case FIELD_STYLEREF     : break;
                     case FIELD_SUBJECT      :
                     {
-                        OUString sParam = lcl_ExtractParameter(pContext->GetCommand(), sizeof(" SUBJECT") );
-                        if(!sParam.isEmpty())
+                        if (!sFirstParam.isEmpty())
                         {
                             xFieldProperties->setPropertyValue(
                                     rPropNameSupplier.GetName( PROP_IS_FIXED ), uno::makeAny( true ));
@@ -3397,8 +3483,7 @@ void DomainMapper_Impl::CloseFieldCommand()
                     break;
                     case FIELD_TITLE        :
                     {
-                        OUString sParam = lcl_ExtractParameter(pContext->GetCommand(), sizeof(" TITLE") );
-                        if(!sParam.isEmpty())
+                        if (!sFirstParam.isEmpty())
                         {
                             xFieldProperties->setPropertyValue(
                                     rPropNameSupplier.GetName( PROP_IS_FIXED ), uno::makeAny( true ));
@@ -3426,10 +3511,11 @@ void DomainMapper_Impl::CloseFieldCommand()
                                 m_xTextFactory->createInstance(
                                         OUString::createFromAscii(aIt->second.cFieldServiceName)),
                                         uno::UNO_QUERY_THROW);
-                        OUString sTCText = lcl_ExtractParameter(pContext->GetCommand(), sizeof("XE ") );
-                        if( !sTCText.isEmpty())
+                        if (!sFirstParam.isEmpty())
+                        {
                             xTC->setPropertyValue("PrimaryKey",
-                                    uno::makeAny(sTCText));
+                                    uno::makeAny(sFirstParam));
+                        }
                         uno::Reference< text::XTextContent > xToInsert( xTC, uno::UNO_QUERY );
                         uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
                         if (xTextAppend.is())
@@ -3451,10 +3537,11 @@ void DomainMapper_Impl::CloseFieldCommand()
                             m_xTextFactory->createInstance(
                                 OUString::createFromAscii(aIt->second.cFieldServiceName)),
                                 uno::UNO_QUERY_THROW);
-                        OUString sTCText = lcl_ExtractParameter(pContext->GetCommand(), sizeof(" TC") );
-                        if( !sTCText.isEmpty())
+                        if (!sFirstParam.isEmpty())
+                        {
                             xTC->setPropertyValue(rPropNameSupplier.GetName(PROP_ALTERNATIVE_TEXT),
-                                uno::makeAny(sTCText));
+                                uno::makeAny(sFirstParam));
+                        }
                         OUString sValue;
                         // \f TC entry in doc with multiple tables
     //                    if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index fba09b9..249f75c 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -569,13 +569,14 @@ public:
         uno::Reference< uno::XInterface > & xFieldInterface,
         uno::Reference< beans::XPropertySet > xFieldProperties);
     void handleAuthor
-        (FieldContextPtr pContext,
+        (OUString const& rFirstParam,
         PropertyNameSupplier& rPropNameSupplier,
         uno::Reference< uno::XInterface > & xFieldInterface,
         uno::Reference< beans::XPropertySet > xFieldProperties,
         FieldId eFieldId);
     void handleDocProperty
         (FieldContextPtr pContext,
+        OUString const& rFirstParam,
         PropertyNameSupplier& rPropNameSupplier,
         uno::Reference< uno::XInterface > & xFieldInterface,
         uno::Reference< beans::XPropertySet > xFieldProperties);
commit 74b3f4f00766d199df3b017d056cf7a00ae52988
Author: Michael Stahl <mstahl at redhat.com>
Date:   Fri Feb 28 20:14:12 2014 +0100

    RTF import: fix paragraphs in header/footer
    
    Change-Id: I91f04cad7a39428ce6f9555d18b974f0d45181f7

diff --git a/sw/qa/extras/rtfimport/data/footer-para.rtf b/sw/qa/extras/rtfimport/data/footer-para.rtf
new file mode 100644
index 0000000..28863b2
--- /dev/null
+++ b/sw/qa/extras/rtfimport/data/footer-para.rtf
@@ -0,0 +1,5 @@
+{\rtf1\fbidis\ansi\ansicpg0\uc0\deff0\deflang0\deflangfe0\paperw11905\paperh16838\margl1200\margr1200\margt1200\margb1200\headery600\footery600\viewscale100\viewzk0\titlepg
+{\fonttbl{\f0\fnil Arial;}}
+{\footerf
+\pard\s0\fi0\li0\qc\ri0\sb0\sa0\itap0 \plain \f0\fs18 All Rights Reserved.\par}
+\pard\par}
diff --git a/sw/qa/extras/rtfimport/rtfimport.cxx b/sw/qa/extras/rtfimport/rtfimport.cxx
index 379475e..6010c7c 100644
--- a/sw/qa/extras/rtfimport/rtfimport.cxx
+++ b/sw/qa/extras/rtfimport/rtfimport.cxx
@@ -1445,6 +1445,20 @@ DECLARE_RTFIMPORT_TEST(testContSectionPageBreak, "cont-section-pagebreak.rtf")
     CPPUNIT_ASSERT_EQUAL(2, getPages());
 }
 
+DECLARE_RTFIMPORT_TEST(testFooterPara, "footer-para.rtf")
+{
+    // check that paragraph properties in footer are imported
+    uno::Reference<text::XText> xFooterText =
+        getProperty< uno::Reference<text::XText> >(
+            getStyles("PageStyles")->getByName("First Page"), "FooterText");
+    uno::Reference<text::XTextContent> xParagraph =
+        getParagraphOrTable(1, xFooterText);
+    CPPUNIT_ASSERT_EQUAL(OUString("All Rights Reserved."),
+        uno::Reference<text::XTextRange>(xParagraph, uno::UNO_QUERY)->getString());
+    CPPUNIT_ASSERT_EQUAL((sal_Int16)style::ParagraphAdjust_CENTER,
+        getProperty</*style::ParagraphAdjust*/sal_Int16>(xParagraph, "ParaAdjust"));
+}
+
 DECLARE_RTFIMPORT_TEST(testCp1000016, "hello.rtf")
 {
     // The single-line document had a second fake empty para on Windows.
diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx
index 38d427f..7464202 100644
--- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx
+++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx
@@ -424,8 +424,9 @@ void RTFDocumentImpl::setNeedSect(bool bNeedSect)
         if (!m_pSuperstream) // no sections in header/footer!
         {
             Mapper().startSectionGroup();
-            m_bNeedSect = bNeedSect;
         }
+        // set flag in substream too - otherwise multiple startParagraphGroup
+        m_bNeedSect = bNeedSect;
         Mapper().startParagraphGroup();
         setNeedPar(true);
     }
commit f03218f43e8c25c2e136d364455f3cdaf95362b6
Author: Michael Stahl <mstahl at redhat.com>
Date:   Fri Feb 28 11:55:39 2014 +0100

    RTF import: add unit test for page break in continuous section
    
    Change-Id: I8b766aa61b1121f9b7068f5ccbdc24ef97253432

diff --git a/sw/qa/extras/rtfimport/data/cont-section-pagebreak.rtf b/sw/qa/extras/rtfimport/data/cont-section-pagebreak.rtf
new file mode 100644
index 0000000..888dc2d
--- /dev/null
+++ b/sw/qa/extras/rtfimport/data/cont-section-pagebreak.rtf
@@ -0,0 +1,16 @@
+{\rtf1 \ansi
+\fet0 \ftnbj \paperw11905 \paperh16837 \margt2267 \margb1133 \margl1417 \margr1417
+
+\sectd
+\sbknone
+FIRST
+\par
+\sect
+SECOND
+\par
+\page
+\sect
+THIRD
+\par
+\sect
+}
diff --git a/sw/qa/extras/rtfimport/rtfimport.cxx b/sw/qa/extras/rtfimport/rtfimport.cxx
index 2c119f3..379475e 100644
--- a/sw/qa/extras/rtfimport/rtfimport.cxx
+++ b/sw/qa/extras/rtfimport/rtfimport.cxx
@@ -17,6 +17,7 @@
 #include <com/sun/star/drawing/LineStyle.hpp>
 #include <com/sun/star/graphic/GraphicType.hpp>
 #include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/style/BreakType.hpp>
 #include <com/sun/star/style/CaseMap.hpp>
 #include <com/sun/star/style/LineSpacing.hpp>
 #include <com/sun/star/style/LineSpacingMode.hpp>
@@ -1420,6 +1421,30 @@ DECLARE_RTFIMPORT_TEST(testNestedTable, "rhbz1065629.rtf")
     CPPUNIT_ASSERT_EQUAL(1, getPages());
 }
 
+DECLARE_RTFIMPORT_TEST(testContSectionPageBreak, "cont-section-pagebreak.rtf")
+{
+    uno::Reference<text::XTextRange> xParaSecond = getParagraph(2);
+    CPPUNIT_ASSERT_EQUAL(OUString("SECOND"), xParaSecond->getString());
+    CPPUNIT_ASSERT_EQUAL(style::BreakType_NONE,
+            getProperty<style::BreakType>(xParaSecond, "BreakType"));
+    CPPUNIT_ASSERT_EQUAL(OUString(""),
+            getProperty<OUString>(xParaSecond, "PageDescName"));
+    // actually not sure how many paragraph there should be between
+    // SECOND and THIRD - important is that the page break is on there
+    uno::Reference<text::XTextRange> xParaNext = getParagraph(3);
+    CPPUNIT_ASSERT_EQUAL(OUString(""), xParaNext->getString());
+    CPPUNIT_ASSERT_EQUAL(OUString("Converted1"),
+            getProperty<OUString>(xParaNext, "PageDescName"));
+    uno::Reference<text::XTextRange> xParaThird = getParagraph(4);
+    CPPUNIT_ASSERT_EQUAL(OUString("THIRD"), xParaThird->getString());
+    CPPUNIT_ASSERT_EQUAL(style::BreakType_NONE,
+            getProperty<style::BreakType>(xParaThird, "BreakType"));
+    CPPUNIT_ASSERT_EQUAL(OUString(""),
+            getProperty<OUString>(xParaThird, "PageDescName"));
+
+    CPPUNIT_ASSERT_EQUAL(2, getPages());
+}
+
 DECLARE_RTFIMPORT_TEST(testCp1000016, "hello.rtf")
 {
     // The single-line document had a second fake empty para on Windows.
commit e3f254ab8211fbab7541cde2100a35c875b0c240
Author: Michael Stahl <mstahl at redhat.com>
Date:   Thu Feb 27 23:48:59 2014 +0100

    RTF import: fix spurious page breaks at doc end (related: rhbz#1065629)
    
    When a document ends with \sect it's possible that a spurious page break
    is created.  In fact the spurious page break is always created by the
    RTF importer, sometimes it is deleted again by
    DomainMapper_Impl::RemoveLastParagraph() and sometimes not.
    
    It is created because on the final \sect RTFDocumentImpl::sectBreak()
    still calls startSectionGroup(), and the popState() for the \rtf1 group
    then calls sectBreak() another time.
    
    To prevent this, do not call startSectionGroup() from sectBreak() but
    instead from setNeedSect(), and ensure that it is called as soon as
    anything after \sect is read.
    
    One unit test fails because the \page is not handled properly: the
    conversion to \skbpage \sect \skbnone is not correct, because the \skb*
    keywords are an exception and affect the \sect that precedes them, not
    the following one; sending the \skbpage later unfortunately requires
    additional cleanup later.
    
    Change-Id: I3c1a3bceb2c8b75bbecdc748170562451ce5f5c3

diff --git a/sw/qa/extras/rtfimport/rtfimport.cxx b/sw/qa/extras/rtfimport/rtfimport.cxx
index 88b8ca1..2c119f3 100644
--- a/sw/qa/extras/rtfimport/rtfimport.cxx
+++ b/sw/qa/extras/rtfimport/rtfimport.cxx
@@ -1415,6 +1415,9 @@ DECLARE_RTFIMPORT_TEST(testNestedTable, "rhbz1065629.rtf")
         getProperty<table::BorderLine2>(xCell, "RightBorder"));
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0xffffffff),
             getProperty<sal_Int32>(xCell, "BackColor"));
+
+    // \sect at the end resulted in spurious page break
+    CPPUNIT_ASSERT_EQUAL(1, getPages());
 }
 
 DECLARE_RTFIMPORT_TEST(testCp1000016, "hello.rtf")
diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx
index 8da5ede..38d427f 100644
--- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx
+++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx
@@ -280,7 +280,8 @@ RTFDocumentImpl::RTFDocumentImpl(uno::Reference<uno::XComponentContext> const& x
     m_aHexBuffer(),
     m_bMathNor(false),
     m_bIgnoreNextContSectBreak(false),
-    m_bNeedSect(true),
+    m_nResetBreakOnSectBreak(static_cast<RTFKeyword>(-1)),
+    m_bNeedSect(false), // done by checkFirstRun
     m_bWasInFrame(false),
     m_bHadPicture(false),
     m_bHadSect(false),
@@ -393,16 +394,15 @@ void RTFDocumentImpl::checkFirstRun()
         writerfilter::Reference<Table>::Pointer_t const pTable(new RTFReferenceTable(aSettingsTableEntries));
         Mapper().table(NS_ooxml::LN_settings_settings, pTable);
         // start initial paragraph
-        if (!m_pSuperstream)
-            Mapper().startSectionGroup();
-        Mapper().startParagraphGroup();
+        m_bFirstRun = false;
+        assert(!m_bNeedSect);
+        setNeedSect(); // first call that succeeds
 
         // set the requested default font, if there are none
         RTFValue::Pointer_t pFont = m_aDefaultState.aCharacterSprms.find(NS_sprm::LN_CRgFtc0);
         RTFValue::Pointer_t pCurrentFont = m_aStates.top().aCharacterSprms.find(NS_sprm::LN_CRgFtc0);
         if (pFont && !pCurrentFont)
             dispatchValue(RTF_F, pFont->getInt());
-        m_bFirstRun = false;
     }
 }
 
@@ -418,7 +418,21 @@ void RTFDocumentImpl::setNeedPar(bool bNeedPar)
 
 void RTFDocumentImpl::setNeedSect(bool bNeedSect)
 {
-    m_bNeedSect = bNeedSect;
+    // ignore setting before checkFirstRun - every keyword calls setNeedSect!
+    if (!m_bNeedSect && bNeedSect && !m_bFirstRun)
+    {
+        if (!m_pSuperstream) // no sections in header/footer!
+        {
+            Mapper().startSectionGroup();
+            m_bNeedSect = bNeedSect;
+        }
+        Mapper().startParagraphGroup();
+        setNeedPar(true);
+    }
+    else if (m_bNeedSect && !bNeedSect)
+    {
+        m_bNeedSect = bNeedSect;
+    }
 }
 
 writerfilter::Reference<Properties>::Pointer_t RTFDocumentImpl::getProperties(RTFSprms& rAttributes, RTFSprms& rSprms)
@@ -543,6 +557,7 @@ void RTFDocumentImpl::sectBreak(bool bFinal = false)
     {
         dispatchFlag(RTF_PARD);
         dispatchSymbol(RTF_PAR);
+        m_bNeedSect = bNeedSect;
     }
     while (!m_nHeaderFooterPositions.empty())
     {
@@ -573,12 +588,7 @@ void RTFDocumentImpl::sectBreak(bool bFinal = false)
     Mapper().endParagraphGroup();
     if (!m_pSuperstream)
         Mapper().endSectionGroup();
-    if (!bFinal)
-    {
-        Mapper().startSectionGroup();
-        Mapper().startParagraphGroup();
-    }
-    m_bNeedPar = true;
+    m_bNeedPar = false;
     m_bNeedSect = false;
 }
 
@@ -1368,8 +1378,8 @@ void RTFDocumentImpl::replayBuffer(RTFBuffer_t& rBuffer,
 
 int RTFDocumentImpl::dispatchDestination(RTFKeyword nKeyword)
 {
-    checkUnicode(/*bUnicode =*/ true, /*bHex =*/ true);
     setNeedSect();
+    checkUnicode(/*bUnicode =*/ true, /*bHex =*/ true);
     RTFSkipDestination aSkip(*this);
     switch (nKeyword)
     {
@@ -1885,11 +1895,11 @@ int RTFDocumentImpl::dispatchDestination(RTFKeyword nKeyword)
 
 int RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword)
 {
+    setNeedSect();
     if (nKeyword != RTF_HEXCHAR)
         checkUnicode(/*bUnicode =*/ true, /*bHex =*/ true);
     else
         checkUnicode(/*bUnicode =*/ true, /*bHex =*/ false);
-    setNeedSect();
     RTFSkipDestination aSkip(*this);
 
     if (RTF_LINE == nKeyword)
@@ -1968,7 +1978,15 @@ int RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword)
                 if (m_bIgnoreNextContSectBreak)
                     m_bIgnoreNextContSectBreak = false;
                 else
+                {
                     sectBreak();
+                    if (m_nResetBreakOnSectBreak != -1)
+                    {   // this should run on _second_ \sect after \page
+                        dispatchSymbol(m_nResetBreakOnSectBreak); // lazy reset
+                        m_nResetBreakOnSectBreak = static_cast<RTFKeyword>(-1);
+                        m_bNeedSect = false; // dispatchSymbol set it
+                    }
+                }
             }
             break;
         case RTF_NOBREAK:
@@ -2140,19 +2158,24 @@ int RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword)
                 RTFValue::Pointer_t pBreak = m_aStates.top().aSectionSprms.find(NS_ooxml::LN_EG_SectPrContents_type);
                 // Unless we're on a title page.
                 RTFValue::Pointer_t pTitlePg = m_aStates.top().aSectionSprms.find(NS_ooxml::LN_EG_SectPrContents_titlePg);
-                if ((pBreak.get() && !pBreak->getInt()) && !(pTitlePg.get() && pTitlePg->getInt()))
+                if (((pBreak.get() && !pBreak->getInt())
+                        || m_nResetBreakOnSectBreak == RTF_SBKNONE)
+                    && !(pTitlePg.get() && pTitlePg->getInt()))
                 {
                     if (m_bWasInFrame)
                     {
                         dispatchSymbol(RTF_PAR);
                         m_bWasInFrame = false;
                     }
-                    dispatchFlag(RTF_SBKPAGE);
                     sectBreak();
-                    dispatchFlag(RTF_SBKNONE);
+                    // note: this will not affect the following section break
+                    // but the one just pushed
+                    dispatchFlag(RTF_SBKPAGE);
                     if (m_bNeedPar)
                         dispatchSymbol(RTF_PAR);
                     m_bIgnoreNextContSectBreak = true;
+                    // arrange to clean up the syntetic RTF_SBKPAGE
+                    m_nResetBreakOnSectBreak = RTF_SBKNONE;
                 }
                 else
                 {
@@ -2192,8 +2215,8 @@ int RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword)
 
 int RTFDocumentImpl::dispatchFlag(RTFKeyword nKeyword)
 {
-    checkUnicode(/*bUnicode =*/ true, /*bHex =*/ true);
     setNeedSect();
+    checkUnicode(/*bUnicode =*/ true, /*bHex =*/ true);
     RTFSkipDestination aSkip(*this);
     int nParam = -1;
     int nSprm = -1;
@@ -2325,6 +2348,10 @@ int RTFDocumentImpl::dispatchFlag(RTFKeyword nKeyword)
     }
     if (nParam >= 0)
     {
+        if (m_nResetBreakOnSectBreak != -1)
+        {
+            m_nResetBreakOnSectBreak = nKeyword;
+        }
         RTFValue::Pointer_t pValue(new RTFValue(nParam));
         m_aStates.top().aSectionSprms.set(NS_ooxml::LN_EG_SectPrContents_type, pValue);
         return 0;
@@ -2932,8 +2959,8 @@ int RTFDocumentImpl::dispatchFlag(RTFKeyword nKeyword)
 
 int RTFDocumentImpl::dispatchValue(RTFKeyword nKeyword, int nParam)
 {
-    checkUnicode(/*bUnicode =*/ nKeyword != RTF_U, /*bHex =*/ true);
     setNeedSect();
+    checkUnicode(/*bUnicode =*/ nKeyword != RTF_U, /*bHex =*/ true);
     RTFSkipDestination aSkip(*this);
     int nSprm = 0;
     RTFValue::Pointer_t pIntValue(new RTFValue(nParam));
@@ -3931,8 +3958,8 @@ int RTFDocumentImpl::dispatchValue(RTFKeyword nKeyword, int nParam)
 
 int RTFDocumentImpl::dispatchToggle(RTFKeyword nKeyword, bool bParam, int nParam)
 {
-    checkUnicode(/*bUnicode =*/ true, /*bHex =*/ true);
     setNeedSect();
+    checkUnicode(/*bUnicode =*/ true, /*bHex =*/ true);
     RTFSkipDestination aSkip(*this);
     int nSprm = -1;
     RTFValue::Pointer_t pBoolValue(new RTFValue(int(!bParam || nParam != 0)));
@@ -4805,7 +4832,8 @@ int RTFDocumentImpl::popState()
         // not in case of other substreams, like headers.
         if (m_bNeedCr && !(m_nStreamType == NS_ooxml::LN_footnote || m_nStreamType == NS_ooxml::LN_endnote))
             dispatchSymbol(RTF_PAR);
-        sectBreak(true);
+        if (m_bNeedSect) // may be set by dispatchSymbol above!
+            sectBreak(true);
     }
 
     m_aStates.pop();
diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.hxx b/writerfilter/source/rtftok/rtfdocumentimpl.hxx
index cbc8ed9..1c9ff06 100644
--- a/writerfilter/source/rtftok/rtfdocumentimpl.hxx
+++ b/writerfilter/source/rtftok/rtfdocumentimpl.hxx
@@ -526,6 +526,9 @@ namespace writerfilter {
                 bool m_bMathNor;
                 /// If the next continuous section break should be ignored.
                 bool m_bIgnoreNextContSectBreak;
+                /// clean up a synthetic page break, see RTF_PAGE
+                /// if inactive value is -1, otherwise the RTF_SKB* to restore
+                RTFKeyword m_nResetBreakOnSectBreak;
                 /// If a section break is needed before the end of the doc (false right after a section break).
                 bool m_bNeedSect;
                 /// If aFrame.inFrame() was true in the previous state.


More information about the Libreoffice-commits mailing list