sc/qa/unit/subsequent_export_test3.cxx | 11 ++++++++++- sc/source/filter/excel/xehelper.cxx | 5 +++++ sc/source/filter/excel/xestring.cxx | 13 +++++++------ sc/source/filter/excel/xetable.cxx | 12 ++++++++---- sc/source/filter/inc/xestring.hxx | 10 ++++++++-- 5 files changed, 38 insertions(+), 13 deletions(-)
New commits: commit 424329bb0b71fdb46b56be5638d5304dbef9e718 Author: Justin Luth <justin.l...@collabora.com> AuthorDate: Thu Oct 3 14:03:55 2024 -0400 Commit: Justin Luth <jl...@mail.com> CommitDate: Sat Oct 19 17:30:10 2024 +0200 tdf#158460 xls/x export: don't force wrap-text for imported single-line Starting in LO 24.2, we started displaying imported XLS/X files that had contents-with-newlines on a single line if the cell did not have the wrap-text property - just like buggy Excel. So, now we need to round-trip that without setting wrap-text, so that we can continue to exhibit buggy behaviour (instead of fixing the document so that the newlines aren't ignored). A previous attempt to do this was reverted (for many reasons), significantly because it failed to handle the situation where a user entered new newline content (without forcing wrap-text). So in LO the new content DISPLAYS on multiple lines, but after a round-trip it was all squashed together. So it is important to keep the traditional behaviour of forcing wrap-text, and ONLY avoiding it when round-tripping imported content. It also preserves ODS -> XLS/X conversions. I also took the opportunity to rename mbWrapped. make CppunitTest_sc_subsequent_export_test3 \ CPPUNIT_TEST_NAME=testPreserveTextWhitespace2XLSX Change-Id: Ia35b0679946b51626fabd4043779c1b43cc1ae37 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/174436 Reviewed-by: Justin Luth <jl...@mail.com> Tested-by: Jenkins diff --git a/sc/qa/unit/subsequent_export_test3.cxx b/sc/qa/unit/subsequent_export_test3.cxx index 5070b5ebe7af..959b73e21d33 100644 --- a/sc/qa/unit/subsequent_export_test3.cxx +++ b/sc/qa/unit/subsequent_export_test3.cxx @@ -26,6 +26,7 @@ #include <unotools/useroptions.hxx> #include <sfx2/docfile.hxx> #include <tools/datetime.hxx> +#include <tools/UnitConversion.hxx> #include <com/sun/star/drawing/XDrawPageSupplier.hpp> #include <com/sun/star/awt/XBitmap.hpp> @@ -1423,11 +1424,19 @@ CPPUNIT_TEST_FIXTURE(ScExportTest3, testPreserveTextWhitespace2XLSX) { createScDoc("xlsx/preserve_space.xlsx"); - save(u"Calc Office Open XML"_ustr); + saveAndReload(u"Calc Office Open XML"_ustr); xmlDocUniquePtr pDoc = parseExport(u"xl/sharedStrings.xml"_ustr); CPPUNIT_ASSERT(pDoc); assertXPath(pDoc, "/x:sst/x:si[1]/x:t", "space", u"preserve"); assertXPath(pDoc, "/x:sst/x:si[2]/x:t", "space", u"preserve"); + + // tdf#158460: ensure B1 is NOT set to wrap text, so Excel keeps displaying as single line + SCTAB nTab = 0; + SCROW nRow = 0; + CPPUNIT_ASSERT(!getScDoc()->GetAttr(1, nRow, nTab, ATTR_LINEBREAK)->GetValue()); + // Without the fix, this wrapped to two lines high (841). It should be 1 line high (529). + int nHeight = convertTwipToMm100(getScDoc()->GetRowHeight(nRow, nTab, false)); + CPPUNIT_ASSERT_LESS(600, nHeight); } CPPUNIT_TEST_FIXTURE(ScExportTest3, testHiddenShapeXLS) diff --git a/sc/source/filter/excel/xehelper.cxx b/sc/source/filter/excel/xehelper.cxx index 0b3835527576..f910aa4aaad5 100644 --- a/sc/source/filter/excel/xehelper.cxx +++ b/sc/source/filter/excel/xehelper.cxx @@ -531,6 +531,11 @@ XclExpStringRef lclCreateFormattedString( XclExpStringHelper::AppendChar( *xString, rRoot, ' ' ); } + if (xString->HasNewline() && nParaCount == 1) + { + // Found buggy Excel behaviour: although the content has newlines, it has not been wrapped. + xString->SetSingleLineForMultipleParagraphs(true); + } return xString; } diff --git a/sc/source/filter/excel/xestring.cxx b/sc/source/filter/excel/xestring.cxx index 295f37709594..5715227cf127 100644 --- a/sc/source/filter/excel/xestring.cxx +++ b/sc/source/filter/excel/xestring.cxx @@ -205,7 +205,7 @@ bool XclExpString::IsEqual( const XclExpString& rCmp ) const (mnLen == rCmp.mnLen) && (mbIsBiff8 == rCmp.mbIsBiff8) && (mbIsUnicode == rCmp.mbIsUnicode) && - (mbWrapped == rCmp.mbWrapped) && + (mbHasNewline == rCmp.mbHasNewline) && ( ( mbIsBiff8 && (maUniBuffer == rCmp.maUniBuffer)) || (!mbIsBiff8 && (maCharBuffer == rCmp.maCharBuffer)) @@ -471,8 +471,8 @@ void XclExpString::CharsToBuffer( const sal_Unicode* pcSource, sal_Int32 nBegin, if( *aIt & 0xFF00 ) mbIsUnicode = true; } - if( !mbWrapped ) - mbWrapped = ::std::find( aBeg, aEnd, EXC_LF ) != aEnd; + if (!mbHasNewline) + mbHasNewline = std::find(aBeg, aEnd, EXC_LF) != aEnd; } void XclExpString::CharsToBuffer( const char* pcSource, sal_Int32 nBegin, sal_Int32 nLen ) @@ -485,8 +485,8 @@ void XclExpString::CharsToBuffer( const char* pcSource, sal_Int32 nBegin, sal_In for( ScfUInt8Vec::iterator aIt = aBeg; aIt != aEnd; ++aIt, ++pcSrcChar ) *aIt = static_cast< sal_uInt8 >( *pcSrcChar ); mbIsUnicode = false; - if( !mbWrapped ) - mbWrapped = ::std::find( aBeg, aEnd, EXC_LF_C ) != aEnd; + if (!mbHasNewline) + mbHasNewline = std::find(aBeg, aEnd, EXC_LF_C) != aEnd; } void XclExpString::Init( sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen, bool bBiff8 ) @@ -496,7 +496,8 @@ void XclExpString::Init( sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMax mb8BitLen = bool( nFlags & XclStrFlags::EightBitLength ); mbSmartFlags = bBiff8 && ( nFlags & XclStrFlags::SmartFlags ); mbSkipFormats = bool( nFlags & XclStrFlags::SeparateFormats ); - mbWrapped = false; + mbHasNewline = false; + mbSingleLineForMultipleParagraphs = false; mbSkipHeader = bool( nFlags & XclStrFlags::NoHeader ); mnMaxLen = nMaxLen; SetStrLen( nCurrLen ); diff --git a/sc/source/filter/excel/xetable.cxx b/sc/source/filter/excel/xetable.cxx index a5cddfc86ff1..a466bfa7ca81 100644 --- a/sc/source/filter/excel/xetable.cxx +++ b/sc/source/filter/excel/xetable.cxx @@ -722,7 +722,7 @@ XclExpLabelCell::XclExpLabelCell( bool XclExpLabelCell::IsMultiLineText() const { - return mbLineBreak || mxText->IsWrapped(); + return mbLineBreak || mxText->HasNewline(); } void XclExpLabelCell::Init( const XclExpRoot& rRoot, @@ -744,9 +744,13 @@ void XclExpLabelCell::Init( const XclExpRoot& rRoot, // create cell format if( GetXFId() == EXC_XFID_NOTFOUND ) { - OSL_ENSURE( nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found" ); - bool bForceLineBreak = mxText->IsWrapped(); - SetXFId( rRoot.GetXFBuffer().InsertWithFont( pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak ) ); + OSL_ENSURE(nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found"); + + // Buggy Excel behaviour - newlines are ignored unless wrap-text is enabled, + // so always force text-wrapping (unless it was imported that way and not modified). + bool bForceLineBreak = mxText->HasNewline() && !mxText->IsSingleLineForMultipleParagraphs(); + SetXFId(rRoot.GetXFBuffer().InsertWithFont( + pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak)); } // get auto-wrap attribute from cell format diff --git a/sc/source/filter/inc/xestring.hxx b/sc/source/filter/inc/xestring.hxx index a61e563f01b9..0b62d1a98bfb 100644 --- a/sc/source/filter/inc/xestring.hxx +++ b/sc/source/filter/inc/xestring.hxx @@ -119,7 +119,12 @@ public: /** Returns true, if the string is empty. */ bool IsEmpty() const { return mnLen == 0; } /** Returns true, if the string contains line breaks. */ - bool IsWrapped() const { return mbWrapped; } + bool HasNewline() const { return mbHasNewline; } + + /** Returns true, if multiple paragraphs are displayed without any wrapping **/ + bool IsSingleLineForMultipleParagraphs() const { return mbSingleLineForMultipleParagraphs; } + void SetSingleLineForMultipleParagraphs(bool bSet) { mbSingleLineForMultipleParagraphs = bSet; } + /** Returns true, if this string is equal to the passed string. */ bool IsEqual( const XclExpString& rCmp ) const; /** Returns true, if this string is less than the passed string. */ @@ -236,7 +241,8 @@ private: bool mb8BitLen; /// true = write 8-bit string length; false = 16-bit. bool mbSmartFlags; /// true = omit flags on empty string; false = always write flags. bool mbSkipFormats; /// true = skip formats on export; false = write complete formatted string. - bool mbWrapped; /// true = text contains several paragraphs. + bool mbHasNewline; /// true = text contains several paragraphs. + bool mbSingleLineForMultipleParagraphs; /// true = several paragraphs displayed all on one line bool mbSkipHeader; /// true = skip length and flags when writing string bytes. };