From 5a3da6f4a33f468e27a0d4d528a0a55628429454 Mon Sep 17 00:00:00 2001 From: Michael Stahl Date: Mon, 10 Mar 2014 12:09:59 +0100 Subject: [PATCH] RTF import fixes related to rhbz#1065629 --- ...purious-page-breaks-at-doc-end-relat.patch | 357 +++++++++ ...vage-a-field-parameter-parsing-train.patch | 676 ++++++++++++++++++ libreoffice.spec | 4 + 3 files changed, 1037 insertions(+) create mode 100644 0001-RTF-import-fix-spurious-page-breaks-at-doc-end-relat.patch create mode 100644 0001-writerfilter-salvage-a-field-parameter-parsing-train.patch diff --git a/0001-RTF-import-fix-spurious-page-breaks-at-doc-end-relat.patch b/0001-RTF-import-fix-spurious-page-breaks-at-doc-end-relat.patch new file mode 100644 index 0000000..655d7c0 --- /dev/null +++ b/0001-RTF-import-fix-spurious-page-breaks-at-doc-end-relat.patch @@ -0,0 +1,357 @@ +From 153993292cc9f11fed8fcba8de45b0c46d5e0ef2 Mon Sep 17 00:00:00 2001 +From: Michael Stahl +Date: Thu, 27 Feb 2014 23:48:59 +0100 +Subject: [PATCH] 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. + +(cherry picked from commit e3f254ab8211fbab7541cde2100a35c875b0c240) + +RTF import: add unit test for page break in continuous section + +(cherry picked from commit f03218f43e8c25c2e136d364455f3cdaf95362b6) + +RTF import: fix paragraphs in header/footer + +(cherry picked from commit 74b3f4f00766d199df3b017d056cf7a00ae52988) + +Change-Id: I3c1a3bceb2c8b75bbecdc748170562451ce5f5c3 +Reviewed-on: https://gerrit.libreoffice.org/8439 +Reviewed-by: Miklos Vajna +Tested-by: Miklos Vajna +--- + .../rtfimport/data/cont-section-pagebreak.rtf | 16 +++++ + sw/qa/extras/rtfimport/data/footer-para.rtf | 5 ++ + sw/qa/extras/rtfimport/rtfimport.cxx | 42 +++++++++++++ + writerfilter/source/rtftok/rtfdocumentimpl.cxx | 71 +++++++++++++++------- + writerfilter/source/rtftok/rtfdocumentimpl.hxx | 3 + + 5 files changed, 116 insertions(+), 21 deletions(-) + create mode 100644 sw/qa/extras/rtfimport/data/cont-section-pagebreak.rtf + create mode 100644 sw/qa/extras/rtfimport/data/footer-para.rtf + +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/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 9504027..73bf749 100644 +--- a/sw/qa/extras/rtfimport/rtfimport.cxx ++++ b/sw/qa/extras/rtfimport/rtfimport.cxx +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1413,6 +1414,47 @@ DECLARE_RTFIMPORT_TEST(testNestedTable, "rhbz1065629.rtf") + getProperty(xCell, "RightBorder")); + CPPUNIT_ASSERT_EQUAL(static_cast(0xffffffff), + getProperty(xCell, "BackColor")); ++ ++ // \sect at the end resulted in spurious page break ++ CPPUNIT_ASSERT_EQUAL(1, getPages()); ++} ++ ++DECLARE_RTFIMPORT_TEST(testContSectionPageBreak, "cont-section-pagebreak.rtf") ++{ ++ uno::Reference xParaSecond = getParagraph(2); ++ CPPUNIT_ASSERT_EQUAL(OUString("SECOND"), xParaSecond->getString()); ++ CPPUNIT_ASSERT_EQUAL(style::BreakType_NONE, ++ getProperty(xParaSecond, "BreakType")); ++ CPPUNIT_ASSERT_EQUAL(OUString(""), ++ getProperty(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 xParaNext = getParagraph(3); ++ CPPUNIT_ASSERT_EQUAL(OUString(""), xParaNext->getString()); ++ CPPUNIT_ASSERT_EQUAL(OUString("Converted1"), ++ getProperty(xParaNext, "PageDescName")); ++ uno::Reference xParaThird = getParagraph(4); ++ CPPUNIT_ASSERT_EQUAL(OUString("THIRD"), xParaThird->getString()); ++ CPPUNIT_ASSERT_EQUAL(style::BreakType_NONE, ++ getProperty(xParaThird, "BreakType")); ++ CPPUNIT_ASSERT_EQUAL(OUString(""), ++ getProperty(xParaThird, "PageDescName")); ++ ++ CPPUNIT_ASSERT_EQUAL(2, getPages()); ++} ++ ++DECLARE_RTFIMPORT_TEST(testFooterPara, "footer-para.rtf") ++{ ++ // check that paragraph properties in footer are imported ++ uno::Reference xFooterText = ++ getProperty< uno::Reference >( ++ getStyles("PageStyles")->getByName("First Page"), "FooterText"); ++ uno::Reference xParagraph = ++ getParagraphOrTable(1, xFooterText); ++ CPPUNIT_ASSERT_EQUAL(OUString("All Rights Reserved."), ++ uno::Reference(xParagraph, uno::UNO_QUERY)->getString()); ++ CPPUNIT_ASSERT_EQUAL((sal_Int16)style::ParagraphAdjust_CENTER, ++ getProperty(xParagraph, "ParaAdjust")); + } + + DECLARE_RTFIMPORT_TEST(testCp1000016, "hello.rtf") +diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx +index d5e7843..0dc20b0 100644 +--- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx ++++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx +@@ -279,7 +279,8 @@ RTFDocumentImpl::RTFDocumentImpl(uno::Reference const& x + m_aHexBuffer(), + m_bMathNor(false), + m_bIgnoreNextContSectBreak(false), +- m_bNeedSect(true), ++ m_nResetBreakOnSectBreak(static_cast(-1)), ++ m_bNeedSect(false), // done by checkFirstRun + m_bWasInFrame(false), + m_bHadPicture(false), + m_bHadSect(false), +@@ -392,16 +393,15 @@ void RTFDocumentImpl::checkFirstRun() + writerfilter::Reference::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; + } + } + +@@ -417,7 +417,22 @@ 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(); ++ } ++ // set flag in substream too - otherwise multiple startParagraphGroup ++ m_bNeedSect = bNeedSect; ++ Mapper().startParagraphGroup(); ++ setNeedPar(true); ++ } ++ else if (m_bNeedSect && !bNeedSect) ++ { ++ m_bNeedSect = bNeedSect; ++ } + } + + writerfilter::Reference::Pointer_t RTFDocumentImpl::getProperties(RTFSprms& rAttributes, RTFSprms& rSprms) +@@ -542,6 +557,7 @@ void RTFDocumentImpl::sectBreak(bool bFinal = false) + { + dispatchFlag(RTF_PARD); + dispatchSymbol(RTF_PAR); ++ m_bNeedSect = bNeedSect; + } + while (!m_nHeaderFooterPositions.empty()) + { +@@ -572,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; + } + +@@ -1345,8 +1356,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) + { +@@ -1867,11 +1878,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) +@@ -1944,7 +1955,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(-1); ++ m_bNeedSect = false; // dispatchSymbol set it ++ } ++ } + } + break; + case RTF_NOBREAK: +@@ -2116,19 +2135,24 @@ int RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword) + RTFValue::Pointer_t pBreak = m_aStates.top().aSectionSprms.find(NS_sprm::LN_SBkc); + // 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 + { +@@ -2162,8 +2186,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; +@@ -2295,6 +2319,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_sprm::LN_SBkc, pValue); + return 0; +@@ -2902,8 +2930,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)); +@@ -3869,8 +3897,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(!bParam || nParam != 0)); +@@ -4721,7 +4749,8 @@ int RTFDocumentImpl::popState() + // not in case of other substreams, like headers. + if (m_bNeedCr && !(m_nStreamType == NS_rtf::LN_footnote || m_nStreamType == NS_rtf::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 932ec46..fa1f3d6 100644 +--- a/writerfilter/source/rtftok/rtfdocumentimpl.hxx ++++ b/writerfilter/source/rtftok/rtfdocumentimpl.hxx +@@ -524,6 +524,9 @@ namespace writerfilter { + bool m_bMathNor; + /// If the next continous 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. +-- +1.8.3.1 + diff --git a/0001-writerfilter-salvage-a-field-parameter-parsing-train.patch b/0001-writerfilter-salvage-a-field-parameter-parsing-train.patch new file mode 100644 index 0000000..93e380b --- /dev/null +++ b/0001-writerfilter-salvage-a-field-parameter-parsing-train.patch @@ -0,0 +1,676 @@ +From 50945ba67bf6dec66b1ed33011316e0fc9197572 Mon Sep 17 00:00:00 2001 +From: Michael Stahl +Date: Sat, 1 Mar 2014 22:05:51 +0100 +Subject: [PATCH] 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). + +(cherry picked from commit 3dc548476c7e88f7a67cc38daf622631a34e34dd) + +Conflicts: + writerfilter/source/dmapper/DomainMapper_Impl.cxx + +Change-Id: I9b2e32c3c7264be0fc1077cb8fb3f1bc5c1955bb +Reviewed-on: https://gerrit.libreoffice.org/8440 +Reviewed-by: Miklos Vajna +Tested-by: Miklos Vajna +--- + writerfilter/CppunitTest_writerfilter_misc.mk | 36 ++++ + writerfilter/Module_writerfilter.mk | 1 + + writerfilter/qa/cppunittests/misc/misc.cxx | 162 ++++++++++++++++ + writerfilter/source/dmapper/DomainMapper_Impl.cxx | 224 +++++++++++++++------- + writerfilter/source/dmapper/DomainMapper_Impl.hxx | 3 +- + 5 files changed, 356 insertions(+), 70 deletions(-) + create mode 100644 writerfilter/CppunitTest_writerfilter_misc.mk + create mode 100644 writerfilter/qa/cppunittests/misc/misc.cxx + +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 ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#include ++ ++ ++using namespace std; ++ ++ ++namespace writerfilter { namespace dmapper { ++ ++SAL_DLLPUBLIC_IMPORT // export just for test ++boost::tuple, vector > ++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, vector > 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 6b9562b..c7302cf 100644 +--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx ++++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx +@@ -66,6 +66,7 @@ + #include + + #include ++#include + + #include + #include +@@ -2003,37 +2004,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) + { +- nStartIndex = nQuoteIndex + 1; +- nEndIndex = rCommand.indexOf( '\"', nStartIndex + 1) - 1; ++ 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) ++ { ++ 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, vector > ++lcl_SplitFieldCommand(const OUString& rCommand) ++{ ++ OUString sType; ++ vector arguments; ++ vector 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 ) +@@ -2480,7 +2577,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, +@@ -2490,19 +2587,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 ), +@@ -2513,16 +2598,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 +@@ -2562,7 +2645,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 +@@ -2589,7 +2672,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)) +@@ -2889,13 +2972,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, vector > 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()) + { + uno::Reference< beans::XPropertySet > xFieldProperties; +@@ -2937,7 +3021,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 ); + } +@@ -2975,7 +3060,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()) +@@ -3014,14 +3101,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; +@@ -3047,7 +3134,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")) +@@ -3178,8 +3265,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 )); +@@ -3209,10 +3295,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", +@@ -3243,21 +3328,21 @@ void DomainMapper_Impl::CloseFieldCommand() + if (xFieldProperties.is()) + { + 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 )) +@@ -3287,7 +3372,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)); + } + } +@@ -3354,8 +3441,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 )); +@@ -3370,8 +3456,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 )); +@@ -3391,10 +3476,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 1ebf067..4686baa 100644 +--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx ++++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx +@@ -576,13 +576,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); +-- +1.8.3.1 + diff --git a/libreoffice.spec b/libreoffice.spec index af85ce2..a2f6b19 100644 --- a/libreoffice.spec +++ b/libreoffice.spec @@ -289,6 +289,8 @@ Patch26: 0002-rhbz-988516-DOCX-import-fix-context-stack-when-impor.patch Patch27: 0001-fdo-74787-rhbz-1072553-Fix-deselection-problems-of-t.patch Patch28: 0001-rhbz-1072607-hopefully-fix-crash-in-SvxRuler-MouseMo.patch Patch29: 0001-rhbz-1043551-sw-avoid-division-by-0-in-Text-Grid-pai.patch +Patch30: 0001-writerfilter-salvage-a-field-parameter-parsing-train.patch +Patch31: 0001-RTF-import-fix-spurious-page-breaks-at-doc-end-relat.patch %define instdir %{_libdir} %define baseinstdir %{instdir}/libreoffice @@ -2188,6 +2190,8 @@ update-desktop-database %{_datadir}/applications &> /dev/null || : - Resolves: rhbz#1072553: Fix deselection problems of template view - Resolves: rhbz#1072607: fix crash in SvxRuler::MouseMove() - Resolves: rhbz#1043551: sw: avoid division-by-0 in Text Grid painting code +- RTF import: import field parameters +- RTF import: fix spurious page breaks at doc end * Tue Mar 04 2014 Caolán McNamara - 1:4.2.2.1-5 - Related: rhbz#1065807 wizards should find the right wizards subdir