sc/qa/unit/subsequent_export_test4.cxx |   76 +++++++++++++++++++++++++++++++++
 sc/source/filter/excel/xecontent.cxx   |    5 +-
 2 files changed, 80 insertions(+), 1 deletion(-)

New commits:
commit f41c58ad4172729a2be261f0ccea01d741d61a2d
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Fri Aug 22 21:36:55 2025 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Fri Aug 22 21:30:47 2025 +0200

    tdf#150229: increase maximum size of URLs for Excel file formats export
    
    The values (8192 characters for XLSX, 2083 characters for XLS) are from
    experiments. While 8192 looks legit, 2083 seems odd - but when I export
    an XLSX with 2084-character hyperlink to XLS, the end result misses the
    hyperlink completely.
    
    Change-Id: Iaf607063b624af93393609eb42feba62910d5254
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190078
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Tested-by: Jenkins

diff --git a/sc/qa/unit/subsequent_export_test4.cxx 
b/sc/qa/unit/subsequent_export_test4.cxx
index b1a90b9f7517..9c0b90124e34 100644
--- a/sc/qa/unit/subsequent_export_test4.cxx
+++ b/sc/qa/unit/subsequent_export_test4.cxx
@@ -52,8 +52,12 @@
 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
 #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
 #include <com/sun/star/sheet/GlobalSheetSettings.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/text/XText.hpp>
 #include <com/sun/star/text/XTextColumns.hpp>
+#include <com/sun/star/text/XTextFieldsSupplier.hpp>
 
 using namespace ::com::sun::star;
 using namespace ::com::sun::star::uno;
@@ -2244,6 +2248,78 @@ CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf108244)
     CPPUNIT_ASSERT_EQUAL(u"1"_ustr, getXPathContent(pXmlDoc, 
"count(//office:annotation)"));
 }
 
+CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf150229)
+{
+    // Create a long URL: longer than Excel allows
+    OUString longUrl = []() {
+        OUStringBuffer buf("https://www.example.org/query?foo&bar&baz=";);
+        while (buf.getLength() < 8200)
+            buf.append("0123456789");
+        return buf.makeStringAndClear();
+    }();
+
+    // Create a document, and put that URL as hyperlink into a cell
+
+    createScDoc();
+
+    {
+        auto xDoc = mxComponent.queryThrow<sheet::XSpreadsheetDocument>();
+        auto xSheets = xDoc->getSheets().queryThrow<container::XIndexAccess>();
+        auto xSheet = xSheets->getByIndex(0).queryThrow<sheet::XSpreadsheet>();
+        auto xCell = xSheet->getCellByPosition(0, 0).queryThrow<text::XText>();
+
+        auto xFactory = mxComponent.queryThrow<lang::XMultiServiceFactory>();
+        auto xField = 
xFactory->createInstance(u"com.sun.star.text.TextField.URL"_ustr)
+                          .queryThrow<beans::XPropertySet>();
+        xField->setPropertyValue(u"URL"_ustr, uno::Any(longUrl));
+        xField->setPropertyValue(u"Representation"_ustr, 
uno::Any(u"hyperlink"_ustr));
+
+        xCell->insertTextContent(xCell->createTextCursor(), 
xField.queryThrow<text::XTextContent>(),
+                                 false);
+    }
+
+    // Test XLSX export: the hyperlink must truncate at 8192 character boundary
+
+    saveAndReload(u"Calc Office Open XML"_ustr);
+
+    {
+        auto xDoc = mxComponent.queryThrow<sheet::XSpreadsheetDocument>();
+        auto xSheets = xDoc->getSheets().queryThrow<container::XIndexAccess>();
+        auto xSheet = xSheets->getByIndex(0).queryThrow<sheet::XSpreadsheet>();
+        auto xCell = xSheet->getCellByPosition(0, 0).queryThrow<text::XText>();
+        CPPUNIT_ASSERT_EQUAL(u"hyperlink"_ustr, xCell->getString());
+        auto xCellFieldsSupplier = 
xCell.queryThrow<text::XTextFieldsSupplier>();
+        auto xFields = 
xCellFieldsSupplier->getTextFields()->createEnumeration();
+        auto xField = xFields->nextElement().queryThrow<beans::XPropertySet>();
+        CPPUNIT_ASSERT_EQUAL(longUrl.copy(0, 8192),
+                             
xField->getPropertyValue(u"URL"_ustr).get<OUString>());
+    }
+
+    // Test the respective OOXML markyp
+
+    xmlDocUniquePtr pXml = 
parseExport(u"xl/worksheets/_rels/sheet1.xml.rels"_ustr);
+    CPPUNIT_ASSERT(pXml);
+    assertXPath(pXml, "//rels:Relationship", "Target", longUrl.subView(0, 
8192));
+
+    // Test XLS export: the hyperlink must truncate at 2083 character boundary 
(that's the limit
+    // I found experimentally)
+
+    saveAndReload(u"MS Excel 97"_ustr);
+
+    {
+        auto xDoc = mxComponent.queryThrow<sheet::XSpreadsheetDocument>();
+        auto xSheets = xDoc->getSheets().queryThrow<container::XIndexAccess>();
+        auto xSheet = xSheets->getByIndex(0).queryThrow<sheet::XSpreadsheet>();
+        auto xCell = xSheet->getCellByPosition(0, 0).queryThrow<text::XText>();
+        CPPUNIT_ASSERT_EQUAL(u"hyperlink"_ustr, xCell->getString());
+        auto xCellFieldsSupplier = 
xCell.queryThrow<text::XTextFieldsSupplier>();
+        auto xFields = 
xCellFieldsSupplier->getTextFields()->createEnumeration();
+        auto xField = xFields->nextElement().queryThrow<beans::XPropertySet>();
+        CPPUNIT_ASSERT_EQUAL(longUrl.copy(0, 2083),
+                             
xField->getPropertyValue(u"URL"_ustr).get<OUString>());
+    }
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xecontent.cxx 
b/sc/source/filter/excel/xecontent.cxx
index 413094f44adb..c2da396ea972 100644
--- a/sc/source/filter/excel/xecontent.cxx
+++ b/sc/source/filter/excel/xecontent.cxx
@@ -406,7 +406,10 @@ XclExpHyperlink::XclExpHyperlink( const XclExpRoot& rRoot, 
const SvxURLField& rU
     }
     else if( eProtocol != INetProtocol::NotValid )
     {
-        XclExpString aUrl( aUrlObj.GetURLNoMark(), XclStrFlags::ForceUnicode, 
255 );
+        // There is an undocumented limitation of 8192 characters for XLSX 
hyperlink length; and
+        // one more for XLS format, which is a strange number 2083; both found 
by experimentation:
+        const sal_uInt16 nMaxLen = rRoot.GetOutput() == EXC_OUTPUT_XML_2007 ? 
8192 : 2083;
+        XclExpString aUrl(aUrlObj.GetURLNoMark(), XclStrFlags::ForceUnicode, 
nMaxLen);
         aXclStrm    << XclTools::maGuidUrlMoniker
                     << sal_uInt32( aUrl.GetBufferSize() + 2 );  // byte count 
+ 1 trailing zero word
         aUrl.WriteBuffer( aXclStrm );                           // NO flags

Reply via email to