summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSzymon Kłos <szymon.klos@collabora.com>2017-11-10 19:04:35 +0100
committerSzymon Kłos <szymon.klos@collabora.com>2017-11-19 21:15:54 +0100
commit2b2f1352c72280dd25ed3bef090a3c708ee4b964 (patch)
tree077d196bd438cf4e7b0f9bed466acbe14ae59d5c
parenttdf#86087 DOCX Import link as relative if preferred (diff)
downloadcore-2b2f1352c72280dd25ed3bef090a3c708ee4b964.tar.gz
core-2b2f1352c72280dd25ed3bef090a3c708ee4b964.zip
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-xsw/CppunitTest_sw_ooxmllinks.mk45
-rw-r--r--sw/Module_sw.mk1
-rwxr-xr-xsw/qa/extras/ooxmlexport/data/absolute-link.docxbin0 -> 13233 bytes
-rwxr-xr-xsw/qa/extras/ooxmlexport/data/relative-link.docxbin0 -> 13415 bytes
-rwxr-xr-xsw/qa/extras/ooxmlexport/ooxmllinks.cxx197
-rw-r--r--sw/source/filter/ww8/attributeoutputbase.hxx6
-rw-r--r--sw/source/filter/ww8/wrtw8nds.cxx68
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
new file mode 100755
index 000000000000..34480d0a2b94
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/absolute-link.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/data/relative-link.docx b/sw/qa/extras/ooxmlexport/data/relative-link.docx
new file mode 100755
index 000000000000..c3688f3e8726
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/relative-link.docx
Binary files differ
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() )