sc/qa/unit/data/xls/tdf90299.xls |binary sc/qa/unit/subsequent_export_test.cxx | 77 ++++++++++++++++++++++++++++++++++ sc/source/filter/excel/xehelper.cxx | 76 ++++++++++++++++++++++----------- 3 files changed, 129 insertions(+), 24 deletions(-)
New commits: commit 59ddcb0445da12f2b912f1a8a876117fec112c41 Author: Łukasz Leszko <leszko.lu...@gmail.com> AuthorDate: Thu Mar 17 17:13:24 2022 +0100 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Tue Aug 30 06:34:06 2022 +0200 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.kagan...@collabora.com> diff --git a/sc/qa/unit/data/xls/tdf90299.xls b/sc/qa/unit/data/xls/tdf90299.xls new file mode 100644 index 000000000000..0028e11cfd51 Binary files /dev/null and b/sc/qa/unit/data/xls/tdf90299.xls differ 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 )