sc/qa/unit/data/xls/tdf128976.xls     |binary
 sc/qa/unit/subsequent_export-test.cxx |   23 +++++++++++++++++++++++
 sc/source/filter/excel/excdoc.cxx     |    6 +++---
 sc/source/filter/excel/xetable.cxx    |   29 +++++++++++++++++++++++------
 sc/source/filter/inc/xetable.hxx      |    6 +++---
 5 files changed, 52 insertions(+), 12 deletions(-)

New commits:
commit 90349075a0058623f3171acea54badcf7e3c972f
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Sun Nov 24 19:16:36 2019 +0300
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Mon Nov 25 13:45:42 2019 +0100

    tdf#128976: properly calculate default value when writing XLS
    
    XLS stores default column width as 16-bit unsigned integer in the range
    from 0 to 255 inclusive ([MS-XLS] 2.4.89 DefColWidth), unlike in OOXML
    where any floating-point value is valid. Additionally, some correction
    is used, introduced in commit 555d702903fb0857122024e1ab78a72d122d3f16
    (basis of empirical formula in XclTools::GetXclDefColWidthCorrection
    is unclear). So in XLS, when the default is calculated, we need to take
    into account if the resulting stored value will actually represent our
    calculated value. If not, then just ignore the calculated value, and
    use arbitrary 8 as the default.
    
    With that, following IsDefWidth will correctly check if passed width
    is represented by the stored default or not. All widths that can't be
    represented as integral count chars in DefColWidth will have fUserSet
    set in corresponding ColInfo records, thus correctly keeping widths.
    
    Change-Id: Iedcc5583c861f5b18a422a9b279c48cff729cbc5
    Reviewed-on: https://gerrit.libreoffice.org/83613
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/83641
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>

diff --git a/sc/qa/unit/data/xls/tdf128976.xls 
b/sc/qa/unit/data/xls/tdf128976.xls
new file mode 100644
index 000000000000..7cd7b748aec3
Binary files /dev/null and b/sc/qa/unit/data/xls/tdf128976.xls differ
diff --git a/sc/qa/unit/subsequent_export-test.cxx 
b/sc/qa/unit/subsequent_export-test.cxx
index 0f278b84de57..c448b3cf54a7 100644
--- a/sc/qa/unit/subsequent_export-test.cxx
+++ b/sc/qa/unit/subsequent_export-test.cxx
@@ -224,6 +224,7 @@ public:
     void testTdf121612();
     void testPivotCacheAfterExportXLSX();
     void testTdf114969XLSX();
+    void testTdf128976();
 
     CPPUNIT_TEST_SUITE(ScExportTest);
     CPPUNIT_TEST(test);
@@ -343,6 +344,7 @@ public:
     CPPUNIT_TEST(testTdf121612);
     CPPUNIT_TEST(testPivotCacheAfterExportXLSX);
     CPPUNIT_TEST(testTdf114969XLSX);
+    CPPUNIT_TEST(testTdf128976);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -4319,6 +4321,27 @@ void ScExportTest::testTdf114969XLSX()
     assertXPath(pDoc, "/x:worksheet/x:hyperlinks/x:hyperlink[2]", "location", 
"'1.1.1.1'!C2");
 }
 
+void ScExportTest::testTdf128976()
+{
+    ScDocShellRef xShell = loadDoc("tdf128976.", FORMAT_XLS);
+    CPPUNIT_ASSERT(xShell.is());
+
+    ScDocShellRef xDocSh = saveAndReload(xShell.get(), FORMAT_XLS);
+    xShell->DoClose();
+    CPPUNIT_ASSERT(xDocSh.is());
+
+    ScDocument& rDoc = xDocSh->GetDocument();
+
+    // Trying to save very small fractional default column width to XLS (where 
only integer values
+    // between 0 and 255 are allowed as default) resulted in negative (-1) 
value after correction,
+    // and was written as 65535 (invalid default width). As the result, all 
columns had large width
+    // when reopened: 28415 (and Excel warned about invalid format).
+    const sal_uInt16 nColumn0Width = rDoc.GetColWidth(SCCOL(0), SCTAB(0), 
false);
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(45), nColumn0Width);
+
+    xDocSh->DoClose();
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(ScExportTest);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sc/source/filter/excel/excdoc.cxx 
b/sc/source/filter/excel/excdoc.cxx
index ebe83426995e..8c490fe6aa68 100644
--- a/sc/source/filter/excel/excdoc.cxx
+++ b/sc/source/filter/excel/excdoc.cxx
@@ -692,7 +692,7 @@ void ExcTable::Write( XclExpStream& rStrm )
 {
     SetCurrScTab( mnScTab );
     if( mxCellTable.get() )
-        mxCellTable->Finalize();
+        mxCellTable->Finalize(true);
     aRecList.Save( rStrm );
 }
 
@@ -703,7 +703,7 @@ void ExcTable::WriteXml( XclExpXmlStream& rStrm )
         // header export.
         SetCurrScTab(mnScTab);
         if (mxCellTable)
-            mxCellTable->Finalize();
+            mxCellTable->Finalize(false);
         aRecList.SaveXml(rStrm);
 
         return;
@@ -723,7 +723,7 @@ void ExcTable::WriteXml( XclExpXmlStream& rStrm )
 
     SetCurrScTab( mnScTab );
     if (mxCellTable)
-        mxCellTable->Finalize();
+        mxCellTable->Finalize(false);
     aRecList.SaveXml( rStrm );
 
     XclExpXmlPivotTables* pPT = 
GetXmlPivotTableManager().GetTablesBySheet(mnScTab);
diff --git a/sc/source/filter/excel/xetable.cxx 
b/sc/source/filter/excel/xetable.cxx
index 7185d2b67f4c..30e755067df1 100644
--- a/sc/source/filter/excel/xetable.cxx
+++ b/sc/source/filter/excel/xetable.cxx
@@ -1573,9 +1573,26 @@ bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 
nXclColWidth ) const
     return std::abs(defaultColumnWidth - nXclColWidth) < 256.0 * 1.0 / 16.0;
 }
 
-void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth )
+void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth, bool bXLS )
 {
-    SetValue(nXclColWidth / 256.0);
+    double fCCh = nXclColWidth / 256.0;
+    if (bXLS)
+    {
+        const double fCorrection = lclGetCChCorrection(GetRoot());
+        const double fCorrectedCCh = fCCh - fCorrection;
+        // Now get the value which would be stored in XLS DefColWidth struct
+        double fCChRound = std::round(fCorrectedCCh);
+        // If default width was set to a value that is not representable as 
integral CCh between 0
+        // and 255, then just ignore that value, and use an arbitrary default. 
That way, the stored
+        // default might not represent the most used column width (or any used 
column width), but
+        // that's OK, and it just means that those columns will explicitly 
store their width in
+        // 1/256ths of char, and have fUserSet in their ColInfo records.
+        if (fCChRound < 0.0 || fCChRound > 255.0 || std::abs(fCChRound - 
fCorrectedCCh) > 1.0 / 512)
+            fCChRound = 8.0;
+        fCCh = fCChRound + fCorrection;
+    }
+
+    SetValue(fCCh);
 }
 
 void XclExpDefcolwidth::Save(XclExpStream& rStrm)
@@ -1717,7 +1734,7 @@ void XclExpColinfoBuffer::Initialize( SCROW nLastScRow )
     }
 }
 
-void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes )
+void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes, bool bXLS )
 {
     rXFIndexes.clear();
     rXFIndexes.reserve( maColInfos.GetSize() );
@@ -1763,7 +1780,7 @@ void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& 
rXFIndexes )
             nMaxUsedWidth = nWidth;
         }
     }
-    maDefcolwidth.SetDefWidth( nMaxUsedWidth );
+    maDefcolwidth.SetDefWidth( nMaxUsedWidth, bXLS );
 
     // remove all default COLINFO records
     nPos = 0;
@@ -2645,7 +2662,7 @@ XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot 
) :
     maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, 
nFirstUngroupedScRow ) );
 }
 
-void XclExpCellTable::Finalize()
+void XclExpCellTable::Finalize(bool bXLS)
 {
     // Finalize multiple operations.
     maTableopBfr.Finalize();
@@ -2653,7 +2670,7 @@ void XclExpCellTable::Finalize()
     /*  Finalize column buffer. This calculates column default XF indexes from
         the XF identifiers and fills a vector with these XF indexes. */
     ScfUInt16Vec aColXFIndexes;
-    maColInfoBfr.Finalize( aColXFIndexes );
+    maColInfoBfr.Finalize( aColXFIndexes, bXLS );
 
     /*  Finalize row buffer. This calculates all cell XF indexes from the XF
         identifiers. Then the XF index vector aColXFIndexes (filled above) is
diff --git a/sc/source/filter/inc/xetable.hxx b/sc/source/filter/inc/xetable.hxx
index fe22d555b80a..a90691384203 100644
--- a/sc/source/filter/inc/xetable.hxx
+++ b/sc/source/filter/inc/xetable.hxx
@@ -685,7 +685,7 @@ public:
     bool                IsDefWidth( sal_uInt16 nXclColWidth ) const;
 
     /** Sets the passed column width (in 1/256 character width) as default 
width. */
-    void                SetDefWidth( sal_uInt16 nXclColWidth );
+    void                SetDefWidth( sal_uInt16 nXclColWidth, bool bXLS );
 
     virtual void        Save(XclExpStream& rStrm) override;
 };
@@ -756,7 +756,7 @@ public:
     void                Initialize( SCROW nLastScRow );
     /** Converts the XF identifiers into the Excel XF indexes and merges equal 
columns.
         @param rXFIndexes  Returns the final XF indexes of all columns. */
-    void                Finalize( ScfUInt16Vec& rXFIndexes );
+    void                Finalize( ScfUInt16Vec& rXFIndexes, bool bXLS );
 
     /** Writes all COLINFO records of this buffer. */
     virtual void        Save( XclExpStream& rStrm ) override;
@@ -978,7 +978,7 @@ public:
     explicit            XclExpCellTable( const XclExpRoot& rRoot );
 
     /** Converts all XF identifiers into the Excel XF indexes and calculates 
default formats. */
-    void                Finalize();
+    void                Finalize(bool bXLS);
 
     /** Returns the reference to an internal record specified by the passed 
record id.
         @param nRecId  The record identifier that specifies which record is
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to