diff options
author | Szymon Kłos <szymon.klos@collabora.com> | 2017-11-10 19:04:35 +0100 |
---|---|---|
committer | Szymon Kłos <szymon.klos@collabora.com> | 2017-11-19 21:15:54 +0100 |
commit | 2b2f1352c72280dd25ed3bef090a3c708ee4b964 (patch) | |
tree | 077d196bd438cf4e7b0f9bed466acbe14ae59d5c | |
parent | 40acf8d6447065077acba9e800c56239f58c8262 (diff) |
tdf#86087 Save relative links in DOCX
Save links depending on preferences set
Options -> Load/Save -> General -> Save URLs relative to ...
Change-Id: I96d06cfdc405d1e1254515106926374aee279f6c
Reviewed-on: https://gerrit.libreoffice.org/44785
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Szymon Kłos <szymon.klos@collabora.com>
-rwxr-xr-x | sw/CppunitTest_sw_ooxmllinks.mk | 45 | ||||
-rw-r--r-- | sw/Module_sw.mk | 1 | ||||
-rwxr-xr-x | sw/qa/extras/ooxmlexport/data/absolute-link.docx | bin | 0 -> 13233 bytes | |||
-rwxr-xr-x | sw/qa/extras/ooxmlexport/data/relative-link.docx | bin | 0 -> 13415 bytes | |||
-rwxr-xr-x | sw/qa/extras/ooxmlexport/ooxmllinks.cxx | 197 | ||||
-rw-r--r-- | sw/source/filter/ww8/attributeoutputbase.hxx | 6 | ||||
-rw-r--r-- | sw/source/filter/ww8/wrtw8nds.cxx | 68 |
7 files changed, 317 insertions, 0 deletions
diff --git a/sw/CppunitTest_sw_ooxmllinks.mk b/sw/CppunitTest_sw_ooxmllinks.mk new file mode 100755 index 000000000000..78b81d4a14b4 --- /dev/null +++ b/sw/CppunitTest_sw_ooxmllinks.mk @@ -0,0 +1,45 @@ +# -*- 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,sw_ooxmllinks)) + +$(eval $(call gb_CppunitTest_add_exception_objects,sw_ooxmllinks, \ + sw/qa/extras/ooxmlexport/ooxmllinks \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,sw_ooxmllinks, \ + $(sw_ooxmlexport_libraries) \ +)) + +$(eval $(call gb_CppunitTest_use_externals,sw_ooxmllinks,\ + boost_headers \ + libxml2 \ +)) + +$(eval $(call gb_CppunitTest_set_include,sw_ooxmllinks,\ + -I$(SRCDIR)/sw/inc \ + -I$(SRCDIR)/sw/source/core/inc \ + -I$(SRCDIR)/sw/qa/extras/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,sw_ooxmllinks)) + +$(eval $(call gb_CppunitTest_use_ure,sw_ooxmllinks)) +$(eval $(call gb_CppunitTest_use_vcl,sw_ooxmllinks)) + +$(eval $(call gb_CppunitTest_use_components,sw_ooxmllinks,\ + $(sw_ooxmlexport_components) \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,sw_ooxmllinks)) + +# vim: set noet sw=4 ts=4: diff --git a/sw/Module_sw.mk b/sw/Module_sw.mk index f15d928b0c46..39772b8b930e 100644 --- a/sw/Module_sw.mk +++ b/sw/Module_sw.mk @@ -67,6 +67,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sw,\ CppunitTest_sw_ooxmlexport10 \ CppunitTest_sw_ooxmlexport11 \ CppunitTest_sw_ooxmlfieldexport \ + CppunitTest_sw_ooxmllinks \ CppunitTest_sw_ooxmlw14export \ CppunitTest_sw_ooxmlencryption \ CppunitTest_sw_ooxmlimport \ diff --git a/sw/qa/extras/ooxmlexport/data/absolute-link.docx b/sw/qa/extras/ooxmlexport/data/absolute-link.docx Binary files differnew file mode 100755 index 000000000000..34480d0a2b94 --- /dev/null +++ b/sw/qa/extras/ooxmlexport/data/absolute-link.docx diff --git a/sw/qa/extras/ooxmlexport/data/relative-link.docx b/sw/qa/extras/ooxmlexport/data/relative-link.docx Binary files differnew file mode 100755 index 000000000000..c3688f3e8726 --- /dev/null +++ b/sw/qa/extras/ooxmlexport/data/relative-link.docx diff --git a/sw/qa/extras/ooxmlexport/ooxmllinks.cxx b/sw/qa/extras/ooxmlexport/ooxmllinks.cxx new file mode 100755 index 000000000000..2765e8f0c5fe --- /dev/null +++ b/sw/qa/extras/ooxmlexport/ooxmllinks.cxx @@ -0,0 +1,197 @@ +/* -*- 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 <swmodeltestbase.hxx> +#include <unotools/tempfile.hxx> +#include <tools/urlobj.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <unotools/saveopt.hxx> + + // This file contains tests to check relative/absolute hyperlinks handling + +#define USE_TEMP_DIR true +#define DONT_MODIFY_LINK false + +#define USE_ABSOLUTE true +#define USE_RELATIVE false + +// bAbsolute - decide if output link should be converted to absolute +// bUseTempDir - decide if link should be modified to be placed in temp dir - for testing relative links +#define DECLARE_LINKS_EXPORT_TEST(TestName, FileName, bAbsolute, bUseTempDir) \ +class TestName : public Test { \ +protected: \ + virtual OUString getTestName() override { return OUString(#TestName); } \ + virtual void postLoad(const char*) override \ + { \ + if(!bUseTempDir) return; \ + \ + uno::Reference<text::XTextRange> xParagraph = getParagraph(1); \ + /* can be changed only after import */ \ + uno::Reference<text::XTextRange> xText = getRun(xParagraph, 1); \ + \ + /* Get original link */ \ + OUString sOriginalFileName = getProperty<OUString>(xText, "HyperLinkURL"); \ + INetURLObject aOriginalURL; \ + aOriginalURL.setFSysPath(sOriginalFileName, FSysStyle::Detect); \ + OUString sFileName = aOriginalURL.GetName().isEmpty() ? sOriginalFileName : aOriginalURL.GetName(); \ + \ + /* Get temp path */ \ + OUString sTempDir = utl::TempFile::CreateTempName(); \ + INetURLObject aTempURL; \ + aTempURL.setFSysPath(sTempDir, FSysStyle::Detect); \ + /* remove file name */ \ + aTempURL.removeSegment(); \ + sTempDir = INetURLObject::GetScheme(aTempURL.GetProtocol()) + aTempURL.GetURLPath(); \ + \ + /* Create & apply new URL */ \ + OUString sOriginalFileInTempDir = sTempDir + sFileName; \ + uno::Reference<beans::XPropertySet> xPropertySet(xText, css::uno::UNO_QUERY); \ + xPropertySet->setPropertyValue("HyperLinkURL", css::uno::makeAny(sOriginalFileInTempDir)); \ + } \ +public: \ + CPPUNIT_TEST_SUITE(TestName); \ + CPPUNIT_TEST(Import_Export_Import); \ + CPPUNIT_TEST_SUITE_END(); \ + void Import_Export_Import() \ + { \ + SvtSaveOptions aOpt; \ + if (bAbsolute) { \ + aOpt.SetSaveRelFSys(false); \ + CPPUNIT_ASSERT(!aOpt.IsSaveRelFSys()); \ + } else { \ + aOpt.SetSaveRelFSys(true); \ + CPPUNIT_ASSERT(aOpt.IsSaveRelFSys()); \ + } \ + executeImportExportImportTest(FileName); \ + } \ + void verify() override; \ +}; \ +CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \ +void TestName::verify() + +// bAbsolute - decide if relative link should be converted to absolute on import +#define DECLARE_LINKS_IMPORT_TEST(TestName, FileName, bAbsolute) \ +class TestName : public Test { \ +protected: \ + virtual OUString getTestName() override { return OUString(#TestName); } \ +public: \ + CPPUNIT_TEST_SUITE(TestName); \ + CPPUNIT_TEST(Import); \ + CPPUNIT_TEST_SUITE_END(); \ + void Import() \ + { \ + SvtSaveOptions aOpt; \ + if (bAbsolute) { \ + aOpt.SetSaveRelFSys(false); \ + CPPUNIT_ASSERT(!aOpt.IsSaveRelFSys()); \ + } else { \ + aOpt.SetSaveRelFSys(true); \ + CPPUNIT_ASSERT(aOpt.IsSaveRelFSys()); \ + } \ + executeImportTest(FileName); \ + } \ + void verify() override; \ +}; \ +CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \ +void TestName::verify() + +class Test : public SwModelTestBase +{ +public: + Test() : SwModelTestBase("/sw/qa/extras/ooxmlexport/data/", "Office Open XML Text") {} + +protected: + /** + * Blacklist handling + */ + bool mustTestImportOf(const char* filename) const override { + // If the testcase is stored in some other format, it's pointless to test. + return OString(filename).endsWith(".docx"); + } +}; + +/* IMPORT */ + +DECLARE_LINKS_IMPORT_TEST(testRelativeToRelativeImport, "relative-link.docx", USE_RELATIVE) +{ + uno::Reference<text::XTextRange> xParagraph = getParagraph(1); + uno::Reference<text::XTextRange> xText = getRun(xParagraph, 1); + CPPUNIT_ASSERT_EQUAL(OUString("relative.docx"), getProperty<OUString>(xText, "HyperLinkURL")); +} + +DECLARE_LINKS_IMPORT_TEST(testRelativeToAbsoluteImport, "relative-link.docx", USE_ABSOLUTE) +{ + uno::Reference<text::XTextRange> xParagraph = getParagraph(1); + uno::Reference<text::XTextRange> xText = getRun(xParagraph, 1); + OUString sTarget = getProperty<OUString>(xText, "HyperLinkURL"); + CPPUNIT_ASSERT(sTarget.startsWith("file:///")); + CPPUNIT_ASSERT(sTarget.endsWith("relative.docx")); +} + +DECLARE_LINKS_IMPORT_TEST(testAbsoluteToAbsoluteImport, "absolute-link.docx", USE_ABSOLUTE) +{ + uno::Reference<text::XTextRange> xParagraph = getParagraph(1); + uno::Reference<text::XTextRange> xText = getRun(xParagraph, 1); + CPPUNIT_ASSERT_EQUAL(OUString("file:///B:\\Users\\user\\Desktop\\test.docx"), getProperty<OUString>(xText, "HyperLinkURL")); +} + +DECLARE_LINKS_IMPORT_TEST(testAbsoluteToRelativeImport, "absolute-link.docx", USE_RELATIVE) +{ + uno::Reference<text::XTextRange> xParagraph = getParagraph(1); + uno::Reference<text::XTextRange> xText = getRun(xParagraph, 1); + // when target file (B:\\...) & document with link (temp dir) are placed on different partitions, absolute path will be loaded + CPPUNIT_ASSERT_EQUAL(OUString("file:///B:\\Users\\user\\Desktop\\test.docx"), getProperty<OUString>(xText, "HyperLinkURL")); +} + +/* EXPORT */ + +DECLARE_LINKS_EXPORT_TEST(testRelativeToRelativeExport, "relative-link.docx", USE_RELATIVE, DONT_MODIFY_LINK) +{ + xmlDocPtr pXmlDoc = parseExport("word/_rels/document.xml.rels"); + if (!pXmlDoc) + return; + + assertXPath(pXmlDoc, "/rels:Relationships/rels:Relationship[2]", "Target", "relative.docx"); +} + +DECLARE_LINKS_EXPORT_TEST(testRelativeToAbsoluteExport, "relative-link.docx", USE_ABSOLUTE, DONT_MODIFY_LINK) +{ + xmlDocPtr pXmlDoc = parseExport("word/_rels/document.xml.rels"); + if (!pXmlDoc) + return; + + OUString sTarget = getXPath(pXmlDoc, "/rels:Relationships/rels:Relationship[2]", "Target"); + CPPUNIT_ASSERT(sTarget.startsWith("file:///")); + CPPUNIT_ASSERT(sTarget.endsWith("relative.docx")); +} + +DECLARE_LINKS_EXPORT_TEST(testAbsoluteToRelativeExport, "absolute-link.docx", USE_RELATIVE, USE_TEMP_DIR) +{ + xmlDocPtr pXmlDoc = parseExport("word/_rels/document.xml.rels"); + if (!pXmlDoc) + return; + + assertXPath(pXmlDoc, "/rels:Relationships/rels:Relationship[2]", "Target", "test.docx"); +} + +DECLARE_LINKS_EXPORT_TEST(testAbsoluteToAbsoluteExport, "absolute-link.docx", USE_ABSOLUTE, DONT_MODIFY_LINK) +{ + xmlDocPtr pXmlDoc = parseExport("word/_rels/document.xml.rels"); + if (!pXmlDoc) + return; + + OUString sTarget = getXPath(pXmlDoc, "/rels:Relationships/rels:Relationship[2]", "Target"); + CPPUNIT_ASSERT(sTarget.startsWith("file:///")); + CPPUNIT_ASSERT(sTarget.endsWith("test.docx")); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx b/sw/source/filter/ww8/attributeoutputbase.hxx index 94ed269fa453..028961cd1d8b 100644 --- a/sw/source/filter/ww8/attributeoutputbase.hxx +++ b/sw/source/filter/ww8/attributeoutputbase.hxx @@ -34,6 +34,7 @@ #include <wrtswtbl.hxx> #include <fldbas.hxx> #include <IDocumentRedlineAccess.hxx> +#include <unotools/saveopt.hxx> #include <vector> @@ -146,6 +147,11 @@ enum StyleType class AttributeOutputBase { +private: + SvtSaveOptions m_aSaveOpt; + + OUString ConvertURL( const OUString& rUrl, bool bAbsoluteOut ); + public: /// Export the state of RTL/CJK. virtual void RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript ) = 0; diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx index a9d5146d7dac..281232ca5bfd 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -23,6 +23,10 @@ #include <algorithm> #include <iostream> +#include <oox/core/filterbase.hxx> +#include "docxexport.hxx" +#include "docxexportfilter.hxx" + #include <i18nlangtag/mslangid.hxx> #include <hintids.hxx> #include <comphelper/string.hxx> @@ -939,6 +943,62 @@ OUString &TruncateBookmark( OUString &rRet ) return rRet; } +OUString AttributeOutputBase::ConvertURL( const OUString& rUrl, bool bAbsoluteOut ) +{ + OUString sURL = rUrl; + OUString sExportedDocumentURL = ""; + { + DocxExport* pDocxExport = dynamic_cast<DocxExport*>(&GetExport()); + if ( pDocxExport ) + { + // DOCX + DocxExportFilter& rFilter = pDocxExport->GetFilter(); + sExportedDocumentURL = rFilter.getFileUrl(); + } + else + { + // DOC + WW8Export* pWW8Export = dynamic_cast<WW8Export*>(&GetExport()); + if ( pWW8Export ) + { + SwWW8Writer& rWriter = pWW8Export->GetWriter(); + sExportedDocumentURL = rWriter.GetMedia()->GetURLObject().GetPath(); + } + } + } + + INetURLObject anAbsoluteParent( sExportedDocumentURL ); + if ( anAbsoluteParent.GetURLPath().isEmpty() ) + { + // DOC filter returns system path (without file:///) + anAbsoluteParent.setFSysPath( sExportedDocumentURL, FSysStyle::Detect ); + anAbsoluteParent.setFinalSlash(); + } + OUString sConvertedParent = INetURLObject::GetScheme( anAbsoluteParent.GetProtocol() ) + anAbsoluteParent.GetURLPath(); + OUString sParentPath = sConvertedParent.isEmpty() ? sExportedDocumentURL : sConvertedParent; + + if ( bAbsoluteOut ) + { + INetURLObject anAbsoluteNew; + + if ( anAbsoluteParent.GetNewAbsURL( rUrl, &anAbsoluteNew ) ) + sURL = anAbsoluteNew.GetMainURL( INetURLObject::DecodeMechanism::WithCharset ); + else + sURL = rUrl; + } + else + { + OUString sToConvert = rUrl.replaceAll( "\\", "/" ); + INetURLObject aURL( sToConvert ); + sToConvert = INetURLObject::GetScheme( aURL.GetProtocol() ) + aURL.GetURLPath(); + OUString sRelative = INetURLObject::GetRelURL( sParentPath, sToConvert, INetURLObject::EncodeMechanism::WasEncoded, INetURLObject::DecodeMechanism::WithCharset ); + if ( !sRelative.isEmpty() ) + sURL = sRelative; + } + + return sURL; +} + bool AttributeOutputBase::AnalyzeURL( const OUString& rUrl, const OUString& /*rTarget*/, OUString* pLinkURL, OUString* pMark ) { bool bBookMarkOnly = false; @@ -975,6 +1035,14 @@ bool AttributeOutputBase::AnalyzeURL( const OUString& rUrl, const OUString& /*rT INetURLObject aURL( rUrl, INetProtocol::NotValid ); sURL = aURL.GetURLNoMark( INetURLObject::DecodeMechanism::Unambiguous ); sMark = aURL.GetMark( INetURLObject::DecodeMechanism::Unambiguous ); + INetProtocol aProtocol = aURL.GetProtocol(); + + if ( aProtocol == INetProtocol::File || aProtocol == INetProtocol::NotValid ) + { + // INetProtocol::NotValid - may be a relative link + bool bExportRelative = m_aSaveOpt.IsSaveRelFSys(); + sURL = ConvertURL( rUrl, !bExportRelative ); + } } if ( !sMark.isEmpty() && sURL.isEmpty() ) |