parent
583d3ad9dd
commit
5a3da6f4a3
@ -0,0 +1,357 @@
|
|||||||
|
From 153993292cc9f11fed8fcba8de45b0c46d5e0ef2 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Michael Stahl <mstahl@redhat.com>
|
||||||
|
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 <vmiklos@collabora.co.uk>
|
||||||
|
Tested-by: Miklos Vajna <vmiklos@collabora.co.uk>
|
||||||
|
---
|
||||||
|
.../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 <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>
|
||||||
|
@@ -1413,6 +1414,47 @@ 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(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(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")
|
||||||
|
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<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),
|
||||||
|
@@ -392,16 +393,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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -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<Properties>::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<RTFKeyword>(-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
|
||||||
|
|
@ -0,0 +1,676 @@
|
|||||||
|
From 50945ba67bf6dec66b1ed33011316e0fc9197572 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Michael Stahl <mstahl@redhat.com>
|
||||||
|
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 <vmiklos@collabora.co.uk>
|
||||||
|
Tested-by: Miklos Vajna <vmiklos@collabora.co.uk>
|
||||||
|
---
|
||||||
|
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 <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 6b9562b..c7302cf 100644
|
||||||
|
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
|
||||||
|
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
|
||||||
|
@@ -66,6 +66,7 @@
|
||||||
|
#include <ooxml/OOXMLFastTokens.hxx>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
+#include <boost/tuple/tuple.hpp>
|
||||||
|
|
||||||
|
#include <vcl/svapp.hxx>
|
||||||
|
#include <vcl/outdev.hxx>
|
||||||
|
@@ -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<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 )
|
||||||
|
@@ -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<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())
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
Loading…
Reference in new issue