summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--filter/source/msfilter/util.cxx19
-rw-r--r--include/filter/msfilter/util.hxx7
-rw-r--r--sw/CppunitTest_sw_ooxmlexport.mk1
-rw-r--r--sw/qa/extras/ooxmlexport/data/bnc834035.odtbin0 -> 18557 bytes
-rw-r--r--sw/qa/extras/ooxmlexport/ooxmlexport.cxx63
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.cxx32
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.hxx7
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.cxx28
8 files changed, 133 insertions, 24 deletions
diff --git a/filter/source/msfilter/util.cxx b/filter/source/msfilter/util.cxx
index bf612c33c169..a10e4381d510 100644
--- a/filter/source/msfilter/util.cxx
+++ b/filter/source/msfilter/util.cxx
@@ -427,6 +427,25 @@ const ApiPaperSize& PaperSizeConv::getApiSizeForMSPaperSizeIndex( sal_Int32 nMSO
return spPaperSizeTable[ nMSOPaperIndex ];
}
+OUString findQuotedText( const OUString& rCommand,
+ const sal_Char* cStartQuote, const sal_Unicode uEndQuote )
+{
+ OUString sRet;
+ OUString sStartQuote( OUString::createFromAscii(cStartQuote) );
+ sal_Int32 nStartIndex = rCommand.indexOf( sStartQuote );
+ if( nStartIndex >= 0 )
+ {
+ sal_Int32 nStartLength = sStartQuote.getLength();
+ sal_Int32 nEndIndex = rCommand.indexOf( uEndQuote, nStartIndex + nStartLength);
+ if( nEndIndex > nStartIndex )
+ {
+ sRet = rCommand.copy( nStartIndex + nStartLength, nEndIndex - nStartIndex - nStartLength);
+ }
+ }
+ return sRet;
+
+}
+
}
}
diff --git a/include/filter/msfilter/util.hxx b/include/filter/msfilter/util.hxx
index e13419905a0f..38cdf56e7df2 100644
--- a/include/filter/msfilter/util.hxx
+++ b/include/filter/msfilter/util.hxx
@@ -88,6 +88,13 @@ public:
static sal_Int32 getMSPaperSizeIndex( const com::sun::star::awt::Size& rSize );
static const ApiPaperSize& getApiSizeForMSPaperSizeIndex( sal_Int32 nMSOPaperIndex );
};
+
+/**
+ * Finds the quoted text in a field instruction text.
+ *
+ * Example: SEQ "Figure" \someoption -> "Figure"
+ */
+MSFILTER_DLLPUBLIC OUString findQuotedText( const OUString& rCommand, const sal_Char* cStartQuote, const sal_Unicode uEndQuote );
}
}
diff --git a/sw/CppunitTest_sw_ooxmlexport.mk b/sw/CppunitTest_sw_ooxmlexport.mk
index e96961fade5b..2e90beca897c 100644
--- a/sw/CppunitTest_sw_ooxmlexport.mk
+++ b/sw/CppunitTest_sw_ooxmlexport.mk
@@ -76,6 +76,7 @@ $(eval $(call gb_CppunitTest_use_components,sw_ooxmlexport,\
ucb/source/ucp/file/ucpfile1 \
unotools/util/utl \
unoxml/source/service/unoxml \
+ uui/util/uui \
writerfilter/util/writerfilter \
xmloff/util/xo \
))
diff --git a/sw/qa/extras/ooxmlexport/data/bnc834035.odt b/sw/qa/extras/ooxmlexport/data/bnc834035.odt
new file mode 100644
index 000000000000..393c960dea63
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/bnc834035.odt
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
index 2ed291fbe183..fd1c30aea46b 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
@@ -10,6 +10,7 @@
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
#include <com/sun/star/style/TabStop.hpp>
#include <com/sun/star/view/XViewSettingsSupplier.hpp>
#include <com/sun/star/text/XTextFrame.hpp>
@@ -73,6 +74,7 @@ public:
void testN822175();
void testFdo58577();
void testFdo60990();
+ void testBnc834035();
CPPUNIT_TEST_SUITE(Test);
#if !defined(MACOSX) && !defined(WNT)
@@ -82,6 +84,14 @@ public:
private:
void run();
+ /**
+ * Given that some problem doesn't affect the result in the importer, we
+ * test the resulting file directly, by opening the zip file, parsing an
+ * xml stream, and asserting an XPath expression. This method returns the
+ * xml stream, so that you can do the asserting.
+ */
+ xmlDocPtr parseExport();
+ void assertXPath(xmlDocPtr pXmlDoc, OString aXPath, OString aAttribute = OString(), OUString aExpectedValue = OUString());
};
void Test::run()
@@ -123,6 +133,7 @@ void Test::run()
{"n822175.odt", &Test::testN822175},
{"fdo58577.odt", &Test::testFdo58577},
{"fdo60990.odt", &Test::testFdo60990},
+ {"bnc834035.odt", &Test::testBnc834035},
};
// Don't test the first import of these, for some reason those tests fail
const char* aBlacklist[] = {
@@ -144,6 +155,46 @@ void Test::run()
}
}
+xmlDocPtr Test::parseExport()
+{
+ // Create the zip file.
+ utl::TempFile aTempFile;
+ save("Office Open XML Text", aTempFile);
+
+ // Read the XML stream we're interested in.
+ uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), aTempFile.GetURL());
+ uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName("word/document.xml"), uno::UNO_QUERY);
+ boost::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, sal_True));
+ pStream->Seek(STREAM_SEEK_TO_END);
+ sal_Size nSize = pStream->Tell();
+ pStream->Seek(0);
+ OStringBuffer aDocument(nSize);
+ char ch;
+ for (sal_Size i = 0; i < nSize; ++i)
+ {
+ *pStream >> ch;
+ aDocument.append(ch);
+ }
+
+ // Parse the XML.
+ return xmlParseMemory((const char*)aDocument.getStr(), aDocument.getLength());
+}
+
+void Test::assertXPath(xmlDocPtr pXmlDoc, OString aXPath, OString aAttribute, OUString aExpectedValue)
+{
+ xmlXPathContextPtr pXmlXpathCtx = xmlXPathNewContext(pXmlDoc);
+ xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("w"), BAD_CAST("http://schemas.openxmlformats.org/wordprocessingml/2006/main"));
+ xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("v"), BAD_CAST("urn:schemas-microsoft-com:vml"));
+ xmlXPathObjectPtr pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(aXPath.getStr()), pXmlXpathCtx);
+ xmlNodeSetPtr pXmlNodes = pXmlXpathObj->nodesetval;
+ CPPUNIT_ASSERT_EQUAL(1, xmlXPathNodeSetGetLength(pXmlNodes));
+ if (aAttribute.isEmpty())
+ return;
+ xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
+ OUString aValue = OUString::createFromAscii((const char*)xmlGetProp(pXmlNode, BAD_CAST(aAttribute.getStr())));
+ CPPUNIT_ASSERT_EQUAL(aExpectedValue, aValue);
+}
+
void Test::testZoom()
{
uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
@@ -709,6 +760,18 @@ void Test::testFdo60990()
CPPUNIT_ASSERT_EQUAL(sal_Int32(0x00FF00), getProperty<sal_Int32>(getRun(xParagraph, 1), "CharColor"));
}
+void Test::testBnc834035()
+{
+ // This is tricky, when saving manually, there are 2 hyperlinks, here only
+ // one, no idea why. That one still shows that we're not using bookmarks, though.
+
+ // Illustration index had wrong hyperlinks: anchor was using Writer's
+ // <seqname>!<index>|sequence syntax, not a bookmark name.
+ xmlDocPtr pXmlDoc = parseExport();
+ // This was Figure!1|sequence.
+ assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:hyperlink", "anchor", "_Toc363553908");
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(Test);
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 08d79f02118b..0e2ee5e8969e 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -699,6 +699,7 @@ void DocxAttributeOutput::DoWriteBookmarks()
FSNS( XML_w, XML_id ), OString::valueOf( sal_Int32( nId ) ).getStr( ),
FSNS( XML_w, XML_name ), rName.getStr(),
FSEND );
+ m_sLastOpenedMark = rName;
}
m_rMarksStart.clear();
@@ -828,6 +829,12 @@ void DocxAttributeOutput::StartField_Impl( FieldInfos& rInfos, bool bWriteRun )
void DocxAttributeOutput::DoWriteCmd( String& rCmd )
{
+ OUString sCmd = OUString(rCmd).trim();
+ if (sCmd.startsWith("SEQ"))
+ {
+ OUString sSeqName = msfilter::util::findQuotedText(sCmd, "SEQ ", '\\').trim();
+ m_aSeqMarksNames[sSeqName].push_back(m_sLastOpenedMark);
+ }
// Write the Field command
m_pSerializer->startElementNS( XML_w, XML_instrText, FSEND );
m_pSerializer->writeEscaped( OUString( rCmd ) );
@@ -1313,8 +1320,33 @@ bool DocxAttributeOutput::StartURL( const String& rUrl, const String& rTarget )
m_pHyperlinkAttrList->add( FSNS( XML_r, XML_id), sId.getStr());
}
else
+ {
+ // Is this a link to a sequence? Then try to replace that with a
+ // normal bookmark, as Word won't understand our special
+ // <seqname>!<index>|sequence syntax.
+ OUString aMark(sMark);
+ if (aMark.endsWith("|sequence"))
+ {
+ sal_Int32 nPos = aMark.indexOf('!');
+ if (nPos != -1)
+ {
+ // Extract <seqname>, the field instruction text has the name quoted.
+ OUString aSequenceName = OUString('"') + aMark.copy(0, nPos) + OUString('"');
+ // Extract <index>.
+ sal_uInt32 nIndex = aMark.copy(nPos + 1, aMark.getLength() - nPos - sizeof("|sequence")).toInt32();
+ std::map<OUString, std::vector<OString> >::iterator it = m_aSeqMarksNames.find(aSequenceName);
+ if (it != m_aSeqMarksNames.end())
+ {
+ std::vector<OString>& rNames = it->second;
+ if (rNames.size() > nIndex)
+ // We know the bookmark name for this sequence and this index, do the replacement.
+ sMark = OStringToOUString(rNames[nIndex], RTL_TEXTENCODING_UTF8);
+ }
+ }
+ }
m_pHyperlinkAttrList->add( FSNS( XML_w, XML_anchor ),
OUStringToOString( OUString( sMark ), RTL_TEXTENCODING_UTF8 ).getStr( ) );
+ }
OUString sTarget( rTarget );
if ( !sTarget.isEmpty() )
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index 0854e46ea662..85014b9ca97f 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -603,6 +603,13 @@ private:
/// Maps of the bookmarks ids
std::map<OString, sal_uInt16> m_rOpenedMarksIds;
+ /// Name of the last opened bookmark.
+ OString m_sLastOpenedMark;
+
+ /// If there are bookmarks around sequence fields, this map contains the
+ /// names of these bookmarks for each sequence.
+ std::map<OUString, std::vector<OString> > m_aSeqMarksNames;
+
/// The current table helper
SwWriteTable *m_pTableWrt;
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 467e587616af..6319c7983dec 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -71,6 +71,7 @@
#include <comphelper/stlunosequence.hxx>
#include <vcl/svapp.hxx>
#include <vcl/outdev.hxx>
+#include <filter/msfilter/util.hxx>
using namespace ::com::sun::star;
using namespace ::rtl;
@@ -1797,33 +1798,12 @@ void DomainMapper_Impl::PopShapeContext()
}
}
-
-OUString lcl_FindQuotedText( const OUString& rCommand,
- const sal_Char* cStartQuote, const sal_Unicode uEndQuote )
-{
- OUString sRet;
- OUString sStartQuote( OUString::createFromAscii(cStartQuote) );
- sal_Int32 nStartIndex = rCommand.indexOf( sStartQuote );
- if( nStartIndex >= 0 )
- {
- sal_Int32 nStartLength = sStartQuote.getLength();
- sal_Int32 nEndIndex = rCommand.indexOf( uEndQuote, nStartIndex + nStartLength);
- if( nEndIndex > nStartIndex )
- {
- sRet = rCommand.copy( nStartIndex + nStartLength, nEndIndex - nStartIndex - nStartLength);
- }
- }
- return sRet;
-
-}
-
-
sal_Int16 lcl_ParseNumberingType( const OUString& rCommand )
{
sal_Int16 nRet = style::NumberingType::PAGE_DESCRIPTOR;
// The command looks like: " PAGE \* Arabic "
- OUString sNumber = lcl_FindQuotedText(rCommand, "\\* ", ' ');
+ OUString sNumber = msfilter::util::findQuotedText(rCommand, "\\* ", ' ');
if( !sNumber.isEmpty() )
{
@@ -1918,7 +1898,7 @@ style::NumberingType::
OUString lcl_ParseFormat( const OUString& rCommand )
{
// The command looks like: " DATE \@ "dd MMMM yyyy"
- return lcl_FindQuotedText(rCommand, "\\@ \"", '\"');
+ return msfilter::util::findQuotedText(rCommand, "\\@ \"", '\"');
}
/*-------------------------------------------------------------------------
extract a parameter (with or without quotes) between the command and the following backslash
@@ -3213,7 +3193,7 @@ void DomainMapper_Impl::CloseFieldCommand()
// command looks like: " SEQ Table \* ARABIC "
OUString sCmd(pContext->GetCommand());
// find the sequence name, e.g. "SEQ"
- OUString sSeqName = lcl_FindQuotedText(sCmd, "SEQ ", '\\');
+ OUString sSeqName = msfilter::util::findQuotedText(sCmd, "SEQ ", '\\');
sSeqName = sSeqName.trim();
// create a sequence field master using the sequence name