[Libreoffice-commits] core.git: sw/CppunitTest_sw_ooxmllinks.mk sw/Module_sw.mk sw/qa sw/source

Szymon Kłos szymon.klos at collabora.com
Sun Nov 19 20:16:19 UTC 2017


 sw/CppunitTest_sw_ooxmllinks.mk                  |   45 +++++
 sw/Module_sw.mk                                  |    1 
 sw/qa/extras/ooxmlexport/data/absolute-link.docx |binary
 sw/qa/extras/ooxmlexport/data/relative-link.docx |binary
 sw/qa/extras/ooxmlexport/ooxmllinks.cxx          |  197 +++++++++++++++++++++++
 sw/source/filter/ww8/attributeoutputbase.hxx     |    6 
 sw/source/filter/ww8/wrtw8nds.cxx                |   68 +++++++
 7 files changed, 317 insertions(+)

New commits:
commit 2b2f1352c72280dd25ed3bef090a3c708ee4b964
Author: Szymon Kłos <szymon.klos at collabora.com>
Date:   Fri Nov 10 19:04:35 2017 +0100

    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 at libreoffice.org>
    Reviewed-by: Szymon Kłos <szymon.klos at collabora.com>

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
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/absolute-link.docx 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
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/relative-link.docx 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() )


More information about the Libreoffice-commits mailing list