filter/source/msfilter/util.cxx | 19 ++++++ include/filter/msfilter/util.hxx | 7 ++ sw/CppunitTest_sw_ooxmlexport.mk | 1 sw/qa/extras/ooxmlexport/data/bnc834035.odt |binary sw/qa/extras/ooxmlexport/ooxmlexport.cxx | 63 ++++++++++++++++++++++ sw/source/filter/ww8/docxattributeoutput.cxx | 32 +++++++++++ sw/source/filter/ww8/docxattributeoutput.hxx | 7 ++ writerfilter/source/dmapper/DomainMapper_Impl.cxx | 28 +-------- 8 files changed, 133 insertions(+), 24 deletions(-)
New commits: commit dbce1c855a9a8a61f1636b1e37bd83e176eb8e2a Author: Miklos Vajna <[email protected]> Date: Mon Aug 26 11:05:04 2013 +0200 bnc#834035 DOCX export: fix hyperlinks of illustration index We used to export raw Writer bookmarks, but that's not valid in OOXML. Instead, it has normal bookmarks around the sequence fields, so use them if they are available. Conflicts: sw/qa/extras/ooxmlexport/ooxmlexport.cxx sw/source/filter/ww8/docxattributeoutput.cxx (cherry picked from commits 750f0ebf97d19d1cf305dabe72d52ad6e90adf70, e9275c08acc2f4f1c925f78b56a1089515cd9a37 and 8f146a8042dd8f3c88f39f8317372cd9c02ce4ad) Change-Id: I0ef2ff7967c2802b53752c9505ef6db4cc2b8265 Reviewed-on: https://gerrit.libreoffice.org/5635 Reviewed-by: Jan Holesovsky <[email protected]> Tested-by: Jan Holesovsky <[email protected]> diff --git a/filter/source/msfilter/util.cxx b/filter/source/msfilter/util.cxx index bf612c3..a10e438 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 e134199..38cdf56 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 e96961f..2e90bec 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 0000000..393c960 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/bnc834035.odt differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx index 2ed291f..fd1c30a 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 08d79f0..0e2ee5e8 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 0854e46..85014b9 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 467e587..6319c79 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 _______________________________________________ Libreoffice-commits mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
