diff options
author | Łukasz Leszko <leszko.lucas@gmail.com> | 2022-03-17 17:13:24 +0100 |
---|---|---|
committer | Mike Kaganski <mike.kaganski@collabora.com> | 2022-08-30 06:34:06 +0200 |
commit | 59ddcb0445da12f2b912f1a8a876117fec112c41 (patch) | |
tree | e6862534ab6c9d81edd7dc271311c82718bb766f /sc | |
parent | a606244d9aa0054988caf1859efb7f26d38748cf (diff) |
tdf#90299 Fix saving external links as relative
In current build some links to external xls files are not saved as relative even if
"Save URLs relative to file system" option is checked. This patch aims to
solve this issue.
Change-Id: I6d0984bdcdeef57b227c8ab1353e002fa4355fc9
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131711
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Diffstat (limited to 'sc')
-rw-r--r-- | sc/qa/unit/data/xls/tdf90299.xls | bin | 0 -> 5632 bytes | |||
-rw-r--r-- | sc/qa/unit/subsequent_export_test.cxx | 77 | ||||
-rw-r--r-- | sc/source/filter/excel/xehelper.cxx | 76 |
3 files changed, 129 insertions, 24 deletions
diff --git a/sc/qa/unit/data/xls/tdf90299.xls b/sc/qa/unit/data/xls/tdf90299.xls Binary files differnew file mode 100644 index 000000000000..0028e11cfd51 --- /dev/null +++ b/sc/qa/unit/data/xls/tdf90299.xls diff --git a/sc/qa/unit/subsequent_export_test.cxx b/sc/qa/unit/subsequent_export_test.cxx index 15155636d2a1..b460319bd0a3 100644 --- a/sc/qa/unit/subsequent_export_test.cxx +++ b/sc/qa/unit/subsequent_export_test.cxx @@ -44,6 +44,10 @@ #include <editeng/fhgtitem.hxx> #include <editeng/udlnitem.hxx> #include <editeng/colritem.hxx> +#include <osl/file.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <unotools/tempfile.hxx> #include <unotools/useroptions.hxx> #include <tools/datetime.hxx> @@ -182,6 +186,7 @@ public: void testPreserveTextWhitespace2XLSX(); void testTdf113646(); void testDateStandardfilterXLSX(); + void testTdf90299(); CPPUNIT_TEST_SUITE(ScExportTest); CPPUNIT_TEST(test); @@ -287,6 +292,7 @@ public: CPPUNIT_TEST(testMoveCellAnchoredShapesODS); CPPUNIT_TEST(testTdf113646); CPPUNIT_TEST(testDateStandardfilterXLSX); + CPPUNIT_TEST(testTdf90299); CPPUNIT_TEST_SUITE_END(); private: @@ -4519,6 +4525,77 @@ void ScExportTest::testDateStandardfilterXLSX() xDocSh->DoClose(); } +void ScExportTest::testTdf90299() +{ + utl::TempFile aTmpDirectory1(nullptr, true); + utl::TempFile aTmpDirectory2(nullptr, true); + utl::TempFile aSavedFile(&aTmpDirectory1.GetURL()); + + struct + { + void checkFormula(ScDocShellRef xShell, OUString aExpectedFormula) + { + CPPUNIT_ASSERT(xShell.is()); + xShell->ReloadAllLinks(); + + ScDocument& rDoc = xShell->GetDocument(); + + ScAddress aPos(0, 0, 0); + ScTokenArray* pCode = getTokens(rDoc, aPos); + CPPUNIT_ASSERT_MESSAGE("empty token array", pCode); + + OUString aFormula = toString(rDoc, aPos, *pCode, rDoc.GetGrammar()); + + CPPUNIT_ASSERT_EQUAL(aExpectedFormula, aFormula); + } + + } aCheckShell; + + OUString aReferencedFileURL; + OUString aReferencingFileURL; + createFileURL(u"tdf90299.", u"xls", aReferencingFileURL); + + auto eError = osl::File::copy(aReferencingFileURL, aTmpDirectory1.GetURL() + "/tdf90299.xls"); + CPPUNIT_ASSERT_EQUAL(osl::File::E_None, eError); + + aReferencingFileURL = aTmpDirectory1.GetURL() + "/tdf90299.xls"; + aReferencedFileURL = aTmpDirectory1.GetURL() + "/dummy.xls"; + + ScDocShellRef xShell = load(aReferencingFileURL, FORMAT_XLS); + aCheckShell.checkFormula(xShell, "'" + aReferencedFileURL + "'#$Sheet1.A1"); + + aReferencingFileURL = aSavedFile.GetURL(); + + FileFormat afilterFormat = ScBootstrapFixture::getFileFormats()[FORMAT_XLS]; + OUString aFilterName(afilterFormat.pFilterName, strlen(afilterFormat.pFilterName), + RTL_TEXTENCODING_UTF8); + OUString aFilterType(afilterFormat.pTypeName, strlen(afilterFormat.pTypeName), + RTL_TEXTENCODING_UTF8); + + SfxMedium aStoreMedium(aReferencingFileURL, StreamMode::STD_WRITE); + + auto pExportFilter = std::make_shared<SfxFilter>( + aFilterName, OUString(), afilterFormat.nFormatType, SotClipboardFormatId::NONE, aFilterType, + OUString(), OUString(), "private:factory/scalc*"); + pExportFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT); + + aStoreMedium.SetFilter(pExportFilter); + + xShell->DoSaveAs(aStoreMedium); + xShell->DoClose(); + + eError = osl::File::copy(aReferencingFileURL, aTmpDirectory2.GetURL() + "/tdf90299.xls"); + CPPUNIT_ASSERT_EQUAL(osl::File::E_None, eError); + + aReferencingFileURL = aTmpDirectory2.GetURL() + "/tdf90299.xls"; + aReferencedFileURL = aTmpDirectory2.GetURL() + "/dummy.xls"; + + xShell = load(aReferencingFileURL, FORMAT_XLS); + aCheckShell.checkFormula(xShell, "'" + aReferencedFileURL + "'#$Sheet1.A1"); + + xShell->DoClose(); +} + CPPUNIT_TEST_SUITE_REGISTRATION(ScExportTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sc/source/filter/excel/xehelper.cxx b/sc/source/filter/excel/xehelper.cxx index 2e5ebba684c4..2025e65a7bd2 100644 --- a/sc/source/filter/excel/xehelper.cxx +++ b/sc/source/filter/excel/xehelper.cxx @@ -23,6 +23,10 @@ #include <com/sun/star/i18n/XBreakIterator.hpp> #include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/XUriReferenceFactory.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <comphelper/processfactory.hxx> #include <o3tl/string_view.hxx> #include <sfx2/objsh.hxx> #include <vcl/font.hxx> @@ -40,6 +44,7 @@ #include <editeng/escapementitem.hxx> #include <editeng/svxfont.hxx> #include <editeng/editids.hrc> +#include <osl/file.hxx> #include <document.hxx> #include <docpool.hxx> @@ -887,34 +892,26 @@ namespace { /** Encodes special parts of the path, i.e. directory separators and volume names. @param pTableName Pointer to a table name to be encoded in this path, or 0. */ OUString lclEncodeDosPath( - XclBiff eBiff, std::u16string_view path, std::u16string_view rBase, const OUString* pTableName) + XclBiff eBiff, std::u16string_view path, bool bIsRel, const OUString* pTableName) { OUStringBuffer aBuf; if (!path.empty()) { - std::u16string_view aOldPath = path; aBuf.append(EXC_URLSTART_ENCODED); - if ( aOldPath.length() > 2 && o3tl::starts_with(aOldPath, u"\\\\") ) + if ( path.length() > 2 && o3tl::starts_with(path, u"\\\\") ) { // UNC aBuf.append(EXC_URL_DOSDRIVE).append('@'); - aOldPath = aOldPath.substr(2); + path = path.substr(2); } - else if ( aOldPath.length() > 2 && o3tl::starts_with(aOldPath.substr(1), u":\\") ) + else if ( path.length() > 2 && o3tl::starts_with(path.substr(1), u":\\") ) { - // drive letter - sal_Unicode cThisDrive = rBase.empty() ? ' ' : rBase[0]; - sal_Unicode cDrive = aOldPath[0]; - if (cThisDrive == cDrive) - // This document and the referenced document are under the same drive. - aBuf.append(EXC_URL_DRIVEROOT); - else - aBuf.append(EXC_URL_DOSDRIVE).append(cDrive); - aOldPath = aOldPath.substr(3); + aBuf.append(EXC_URL_DOSDRIVE).append(path[0]); + path = path.substr(3); } - else + else if ( !bIsRel ) { // URL probably points to a document on a Unix-like file system aBuf.append(EXC_URL_DRIVEROOT); @@ -922,23 +919,23 @@ OUString lclEncodeDosPath( // directories auto nPos = std::u16string_view::npos; - while((nPos = aOldPath.find('\\')) != std::u16string_view::npos) + while((nPos = path.find('\\')) != std::u16string_view::npos) { - if ( o3tl::starts_with(aOldPath, u"..") ) + if ( o3tl::starts_with(path, u"..") ) // parent dir (NOTE: the MS-XLS spec doesn't mention this, and // Excel seems confused by this token). aBuf.append(EXC_URL_PARENTDIR); else - aBuf.append(aOldPath.substr(0,nPos)).append(EXC_URL_SUBDIR); + aBuf.append(path.substr(0,nPos)).append(EXC_URL_SUBDIR); - aOldPath = aOldPath.substr(nPos + 1); + path = path.substr(nPos + 1); } // file name if (pTableName) // enclose file name in brackets if table name follows - aBuf.append('[').append(aOldPath).append(']'); + aBuf.append('[').append(path).append(']'); else - aBuf.append(aOldPath); + aBuf.append(path); } else // empty URL -> self reference { @@ -968,13 +965,44 @@ OUString lclEncodeDosPath( return aBuf.makeStringAndClear(); } +bool isUrlRelative(const OUString& aUrl) +{ + css::uno::Reference<css::uri::XUriReferenceFactory> xUriFactory( + css::uri::UriReferenceFactory::create( + comphelper::getProcessComponentContext())); + css::uno::Reference<css::uri::XUriReference> xUri(xUriFactory->parse(aUrl)); + + return !xUri->isAbsolute(); +} + } // namespace OUString XclExpUrlHelper::EncodeUrl( const XclExpRoot& rRoot, std::u16string_view rAbsUrl, const OUString* pTableName ) { - OUString aDosPath = INetURLObject(rAbsUrl).getFSysPath(FSysStyle::Dos); - OUString aDosBase = INetURLObject(rRoot.GetBasePath()).getFSysPath(FSysStyle::Dos); - return lclEncodeDosPath(rRoot.GetBiff(), aDosPath, aDosBase, pTableName); + OUString aDosPath; + bool bIsRel = false; + + if (rRoot.IsRelUrl()) + { + OUString aUrlPath = INetURLObject::GetRelURL( + rRoot.GetBasePath(), OUString(rAbsUrl), + INetURLObject::EncodeMechanism::All, + INetURLObject::DecodeMechanism::NONE, + RTL_TEXTENCODING_UTF8, FSysStyle::Detect + ); + + if (isUrlRelative(aUrlPath)) + { + bIsRel = true; + osl::FileBase::getSystemPathFromFileURL(aUrlPath, aDosPath); + aDosPath = aDosPath.replaceAll(u"/", u"\\"); + } + } + + if (!bIsRel) + aDosPath = INetURLObject(rAbsUrl).getFSysPath(FSysStyle::Dos); + + return lclEncodeDosPath(rRoot.GetBiff(), aDosPath, bIsRel, pTableName); } OUString XclExpUrlHelper::EncodeDde( std::u16string_view rApplic, std::u16string_view rTopic ) |