Commits

Anonymous committed 818a29d

dr81: #i117446# rework import of cell formatting, better handling of attribute default values

  • Participants
  • Parent commits 134d714

Comments (0)

Files changed (16)

oox/inc/oox/helper/containerhelper.hxx

 
 // ============================================================================
 
-/** A range of signed 32-bit integer values. */
-struct ValueRange
-{
-    sal_Int32           mnFirst;
-    sal_Int32           mnLast;
-
-    inline explicit     ValueRange( sal_Int32 nValue = 0 ) : mnFirst( nValue ), mnLast( nValue ) {}
-    inline explicit     ValueRange( sal_Int32 nFirst, sal_Int32 nLast ) : mnFirst( nFirst ), mnLast( nLast ) {}
-
-    inline bool         operator==( const ValueRange& rRange ) const { return (mnFirst == rRange.mnFirst) && (mnLast == rRange.mnLast); }
-    inline bool         operator!=( const ValueRange& rRange ) const { return !(*this == rRange); }
-    inline bool         contains( sal_Int32 nValue ) const { return (mnFirst <= nValue) && (nValue <= mnLast); }
-    inline bool         contains( const ValueRange& rRange ) const { return (mnFirst <= rRange.mnFirst) && (rRange.mnLast <= mnLast); }
-    inline bool         intersects( const ValueRange& rRange ) const { return (mnFirst <= rRange.mnLast) && (rRange.mnFirst <= mnLast); }
-};
-
-// ----------------------------------------------------------------------------
-
-typedef ::std::vector< ValueRange > ValueRangeVector;
-
-// ----------------------------------------------------------------------------
-
-/** An ordered list of value ranges. The insertion operation will merge
-    consecutive value ranges.
- */
-class ValueRangeSet
-{
-public:
-    inline explicit     ValueRangeSet() {}
-
-    /** Inserts the passed value into the range list. */
-    inline void         insert( sal_Int32 nValue ) { insert( ValueRange( nValue ) ); }
-    /** Inserts the passed value range into the range list. */
-    void                insert( const ValueRange& rRange );
-
-    /** Returns the ordered list of all value ranges. */
-    inline const ValueRangeVector& getRanges() const { return maRanges; }
-    /** Returns an intersection of the range list and the passed range. */
-    ValueRangeVector    getIntersection( const ValueRange& rRange ) const;
-
-private:
-    ValueRangeVector    maRanges;
-};
-
-// ============================================================================
-
 /** Template for a 2-dimensional array of objects.
 
     This class template provides a similar interface to the ::std::vector

oox/inc/oox/xls/addressconverter.hxx

 #include <vector>
 #include <com/sun/star/table/CellAddress.hpp>
 #include <com/sun/star/table/CellRangeAddress.hpp>
+#include <o3tl/interval_set.hxx>
 #include "oox/xls/workbookhelper.hxx"
 
 namespace oox {
 // ============================================================================
 // ============================================================================
 
+typedef ::std::pair< sal_Int32, sal_Int32 > ColRowRange;
+
+/** A set taking column or row indexes. The columns or rows are stored in
+    ranges of consectutive indexes, the ranges are ordered.
+ */
+typedef ::o3tl::interval_set< sal_Int32 > ColRowSet;
+
+// ============================================================================
+
 /** A vector of com.sun.star.table.CellRangeAddress elements and additional
     functionality. */
 class ApiCellRangeList : public ::std::vector< ::com::sun::star::table::CellRangeAddress >
 
     // ------------------------------------------------------------------------
 
+    /** Checks the passed column range, may try to fit it to current sheet
+        limits.
+
+        First, this function reorders the column indexes so that the starting
+        indexes are less than or equal to the end indexes. Then, depending on
+        the parameter bAllowOverflow, the column range is just checked or
+        cropped to the current sheet limits.
+
+        @param orColRange  (in-out-parameter) Converts the passed column range
+            into a valid column range. If the passed range contains columns
+            outside the currently supported spreadsheet limits, and the
+            parameter bAllowOverflow is true, it will be cropped to these limits.
+        @param bAllowOverflow  true = Allow column ranges that start inside the
+            supported sheet limits but may end outside of these limits. The
+            column range returned in orRange will be cropped to these limits.
+            false = Do not allow column ranges that overflow the supported
+            limits. The function will return false when the range overflows the
+            sheet limits.
+        @param bTrackOverflow  true = Update the internal overflow flags, if
+            the original range contains columns outside of the supported sheet
+            limits.
+        @return  true = Converted column range is valid. This function returns
+            also true, if overflowing column ranges are allowed via parameter
+            bAllowOverflow and the range has been cropped, but still contains
+            columns inside the current sheet limits. Returns false, if the
+            entire range is outside the sheet limits or overflowing ranges are
+            not allowed via parameter bAllowOverflow.
+     */
+    bool                validateColumnRange(
+                            ColRowRange& orColRange,
+                            bool bAllowOverflow, bool bTrackOverflow );
+
+    /** Checks the passed row range, may try to fit it to current sheet limits.
+
+        First, this function reorders the row indexes so that the starting
+        indexes are less than or equal to the end indexes. Then, depending on
+        the parameter bAllowOverflow, the row range is just checked or cropped
+        to the current sheet limits.
+
+        @param orRowRange  (in-out-parameter) Converts the passed row range
+            into a valid row range. If the passed range contains rows outside
+            the currently supported spreadsheet limits, and the parameter
+            bAllowOverflow is true, it will be cropped to these limits.
+        @param bAllowOverflow  true = Allow row ranges that start inside the
+            supported sheet limits but may end outside of these limits. The
+            row range returned in orRange will be cropped to these limits.
+            false = Do not allow row ranges that overflow the supported limits.
+            The function will return false when the range overflows the sheet
+            limits.
+        @param bTrackOverflow  true = Update the internal overflow flags, if
+            the original range contains rows outside of the supported sheet
+            limits.
+        @return  true = Converted row range is valid. This function returns
+            also true, if overflowing row ranges are allowed via parameter
+            bAllowOverflow and the range has been cropped, but still contains
+            rows inside the current sheet limits. Returns false, if the entire
+            range is outside the sheet limits or overflowing ranges are not
+            allowed via parameter bAllowOverflow.
+     */
+    bool                validateRowRange(
+                            ColRowRange& orRowRange,
+                            bool bAllowOverflow, bool bTrackOverflow );
+
+    // ------------------------------------------------------------------------
+
     /** Checks the passed cell address if it fits into the spreadsheet limits.
 
         @param rAddress  The cell address to be checked.

oox/inc/oox/xls/pivotcachebuffer.hxx

 #include <com/sun/star/table/CellAddress.hpp>
 #include <com/sun/star/table/CellRangeAddress.hpp>
 #include <com/sun/star/util/DateTime.hpp>
-#include "oox/helper/containerhelper.hxx"
 #include "oox/helper/refvector.hxx"
+#include "oox/xls/addressconverter.hxx"
 #include "oox/xls/workbookhelper.hxx"
 
 namespace com { namespace sun { namespace star {
     PCDefinitionModel   maDefModel;         /// Global pivot cache settings.
     PCSourceModel       maSourceModel;      /// Pivot cache source settings.
     PCWorksheetSourceModel maSheetSrcModel; /// Sheet source data if cache type is sheet.
-    ValueRangeSet       maColSpans;         /// Column spans used by SheetDataBuffer for optimized cell import.
+    ColRowSet           maColSpans;         /// Column spans used by SheetDataBuffer for optimized cell import.
     ::rtl::OUString     maTargetUrl;        /// URL of an external source document.
     mutable sal_Int32   mnCurrRow;          /// Current row index in dummy sheet.
     bool                mbValidSource;      /// True = pivot cache is based on supported data source.

oox/inc/oox/xls/sheetdatabuffer.hxx

 
 #include <list>
 #include <map>
+#include <o3tl/interval_map.hxx>
+#include "oox/xls/addressconverter.hxx"
 #include "oox/xls/richstring.hxx"
 #include "oox/xls/worksheethelper.hxx"
 
 class CellBlock : public WorksheetHelper
 {
 public:
-    explicit            CellBlock( const WorksheetHelper& rHelper, const ValueRange& rColSpan, sal_Int32 nRow );
+    explicit            CellBlock( const WorksheetHelper& rHelper, const ColRowRange& rColSpan, sal_Int32 nRow );
 
     /** Returns true, if the end index of the passed colspan is greater than
         the own column end index, or if the passed range has the same end index
         but the start indexes do not match. */
-    bool                isBefore( const ValueRange& rColSpan ) const;
+    bool                isBefore( const ColRowRange& rColSpan ) const;
     /** Returns true, if the cell block can be expanded with the passed colspan. */
-    bool                isExpandable( const ValueRange& rColSpan ) const;
+    bool                isExpandable( const ColRowRange& rColSpan ) const;
     /** Returns true, if the own colspan contains the passed column. */
     bool                contains( sal_Int32 nCol ) const;
 
     explicit            CellBlockBuffer( const WorksheetHelper& rHelper );
 
     /** Sets column span information for a row. */
-    void                setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans );
+    void                setColSpans( sal_Int32 nRow, const ColRowSet& rColSpans );
 
     /** Tries to find a cell block. Recalculates the map of cell blocks, if the
         passed cell address is located in another row than the last cell. */
     void                finalizeImport();
 
 private:
-    typedef ::std::map< sal_Int32, ValueRangeVector >   ColSpanVectorMap;
-    typedef RefMap< sal_Int32, CellBlock >              CellBlockMap;
+    typedef ::std::map< sal_Int32, ColRowSet >  ColSpanMap;
+    typedef RefMap< sal_Int32, CellBlock >      CellBlockMap;
 
-    ColSpanVectorMap    maColSpans;             /// Buffereed column spans, mapped by row index.
+    ColSpanMap          maColSpans;             /// Buffered column spans, mapped by row index.
     CellBlockMap        maCellBlocks;           /// All open cell blocks, mapped by last (!) column of the block span.
     CellBlockMap::iterator maCellBlockIt;       /// Pointer to cell block currently in use.
     sal_Int32           mnCurrRow;              /// Current row index used for buffered cell import.
 
 // ============================================================================
 
+/** Manages the formatting of all cells in this sheet. */
+class CellFormatBuffer : public WorksheetHelper
+{
+public:
+    explicit            CellFormatBuffer( const WorksheetHelper& rHelper );
+
+    /** Sets default cell formatting for a range of columns. */
+    void                setDefaultColumnFormat( const ColRowRange& rColRange, sal_Int32 nXfId );
+    /** Sets default cell formatting for a row. */
+    void                setDefaultRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat );
+
+    /** Processes the cell formatting data of the passed cell.
+        @param nNumFmtId  If set, overrides number format of the cell XF. */
+    void                setCellFormat( const CellModel& rModel, sal_Int32 nNumFmtId = -1 );
+    /** Merges the cells in the passed cell range. */
+    void                setMergedRange( const ::com::sun::star::table::CellRangeAddress& rRange );
+
+    /** Initializes default sheet formatting. */
+    void                initializeImport();
+    /** Inserts all cells of all open cell blocks into the Calc document. */
+    void                finalizeImport();
+
+private:
+    /*  Merges ranges of maCurrRowXfIds with ranges in maOpenFormatRanges. */
+    void                mergeFormatRanges( bool bFlushOpenRanges );
+    /** Writes all buffered cell formatting to the spreadsheet. */
+    void                flushFormatRanges();
+
+    /** Merges the passed merged range and updates right/bottom cell borders. */
+    void                finalizeMergedRange( const ::com::sun::star::table::CellRangeAddress& rRange );
+
+private:
+    typedef ::std::vector< sal_Int32 > XfIdVector;
+    typedef ::o3tl::interval_map< sal_Int32, sal_Int32 > XfIdMap;
+
+    /** Stores information about cell formatting (XF identifier and custom number format). */
+    struct CellFormat
+    {
+        sal_Int32           mnXfId;             /// XF identifier.
+        sal_Int32           mnNumFmtId;         /// Number format overriding the XF.
+
+        explicit            CellFormat( sal_Int32 nXfId, sal_Int32 nNumFmtId );
+        bool                operator==( const CellFormat& rCellFormat ) const;
+    };
+    typedef ::o3tl::interval_map< sal_Int32, CellFormat > CellFormatMap;
+
+    /** Stores information about a range of cells with equal formatting. */
+    struct FormatRange
+    {
+        ::com::sun::star::table::CellRangeAddress
+                            maRange;            /// The formatted cell range.
+        CellFormat          maCellFormat;       /// Formatting of the range.
+
+        explicit            FormatRange( const ::com::sun::star::table::CellRangeAddress& rRange, const CellFormat& rCellFormat );
+    };
+    typedef ::std::list< FormatRange > FormatRangeList;
+
+    /** Stores information about a merged cell range. */
+    struct MergedRange
+    {
+        ::com::sun::star::table::CellRangeAddress
+                            maRange;            /// The formatted cell range.
+        sal_Int32           mnHorAlign;         /// Horizontal alignment in the range.
+
+        explicit            MergedRange( const ::com::sun::star::table::CellRangeAddress& rRange );
+        explicit            MergedRange( const ::com::sun::star::table::CellAddress& rAddress, sal_Int32 nHorAlign );
+        bool                tryExpand( const ::com::sun::star::table::CellAddress& rAddress, sal_Int32 nHorAlign );
+    };
+    typedef ::std::list< MergedRange > MergedRangeList;
+
+    AddressConverter&   mrAddressConv;          /// The address converter.
+    XfIdVector          maDefColXfIds;          /// Default column XF identifiers for this sheet.
+    XfIdMap             maDefRowXfIds;          /// Default row XF identifiers for this sheet.
+    CellFormatMap       maCurrRowXfIds;         /// Collected cell XF identifiers in the current row.
+    FormatRangeList     maOpenFormatRanges;     /// All formatted ranges able to merge with current row.
+    FormatRangeList     maFinalFormatRanges;    /// All formatted ranges not able to merge with current row.
+    MergedRangeList     maMergedRanges;         /// Merged cell ranges.
+    MergedRangeList     maCenterFillRanges;     /// Merged cell ranges from 'center across' or 'fill' alignment.
+    const sal_Int32     mnDefCellXfId;          /// XF identifier for default cell formatting.
+    sal_Int32           mnFirstRow;             /// Index of the first row used by the cells in maOpenFormatRanges or maFinalFormatRanges.
+    sal_Int32           mnCurrRow;              /// Index of the row used by the cells in maCurrRowXfIds.
+    bool                mbCustomColumnFormat;   /// True = any column has a non-default cell formatting.
+};
+
+// ============================================================================
+
 /** Manages the cell contents and cell formatting of a sheet.
  */
 class SheetDataBuffer : public WorksheetHelper
 public:
     explicit            SheetDataBuffer( const WorksheetHelper& rHelper );
 
-    /** Sets column span information for a row. */
-    void                setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans );
+    /** Sets default cell formatting for a range of columns. */
+    void                setDefaultColumnFormat( const ColRowRange& rColRange, sal_Int32 nXfId );
+    /** Sets default cell formatting for a row. */
+    void                setDefaultRowFormat( sal_Int32 nRow, const ColRowSet& rColSpans, sal_Int32 nXfId, bool bCustomFormat );
 
     /** Inserts a blank cell (with formatting) into the sheet. */
     void                setBlankCell( const CellModel& rModel );
                             const ::com::sun::star::table::CellAddress& rCellAddr,
                             const ApiTokenSequence& rTokens );
 
-    /** Sets default cell formatting for the specified range of rows. */
-    void                setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat );
     /** Merges the cells in the passed cell range. */
     void                setMergedRange( const ::com::sun::star::table::CellRangeAddress& rRange );
+
     /** Sets a standard number format (constant from com.sun.star.util.NumberFormat) to the specified cell. */
     void                setStandardNumFmt(
                             const ::com::sun::star::table::CellAddress& rCellAddr,
                             sal_Int16 nStdNumFmt );
 
+    /** Initial processing before the sheet will be imported. */
+    void                initializeImport();
     /** Final processing after the sheet has been imported. */
     void                finalizeImport();
     
 private:
-    struct XfIdRowRange;
-    struct XfIdRange;
-
     /** Sets the passed formula token array into a cell. */
     void                setCellFormula(
                             const ::com::sun::star::table::CellAddress& rCellAddr,
                             const ::com::sun::star::table::CellRangeAddress& rRange,
                             const DataTableModel& rModel ) const;
 
-    /** Processes the cell formatting data of the passed cell.
-        @param nNumFmtId  If set, overrides number format of the cell XF. */
-    void                setCellFormat( const CellModel& rModel, sal_Int32 nNumFmtId = -1 );
-
-    /** Writes all cell formatting attributes to the passed row range. */
-    void                writeXfIdRowRangeProperties( const XfIdRowRange& rXfIdRowRange ) const;
-    /** Writes all cell formatting attributes to the passed cell range. */
-    void                writeXfIdRangeProperties( const XfIdRange& rXfIdRange ) const;
-    /** Tries to merge the ranges last inserted in maXfIdRanges with existing ranges. */
-    void                mergeXfIdRanges();
-
-    /** Merges the passed merged range and updates right/bottom cell borders. */
-    void                finalizeMergedRange( const ::com::sun::star::table::CellRangeAddress& rRange );
-
 private:
     /** Stores cell range address and formula token array of an array formula. */
     typedef ::std::pair< ::com::sun::star::table::CellRangeAddress, ApiTokenSequence > ArrayFormula;
 
     typedef ::std::map< BinAddress, sal_Int32 > SharedFormulaMap;
 
-    /** Stores information about a range of rows with equal cell formatting. */
-    struct XfIdRowRange
-    {
-        ValueRange          maRowRange;         /// Indexes of first and last row.
-        sal_Int32           mnXfId;             /// XF identifier for the row range.
-
-        explicit            XfIdRowRange();
-        bool                intersects( const ::com::sun::star::table::CellRangeAddress& rRange ) const;
-        void                set( sal_Int32 nRow, sal_Int32 nXfId );
-        bool                tryExpand( sal_Int32 nRow, sal_Int32 nXfId );
-    };
-
-    /** Stores information about a range of cells with equal formatting. */
-    struct XfIdRange
-    {
-        ::com::sun::star::table::CellRangeAddress
-                            maRange;            /// The formatted cell range.
-        sal_Int32           mnXfId;             /// XF identifier for the range.
-        sal_Int32           mnNumFmtId;         /// Number format overriding the XF.
-
-        void                set( const ::com::sun::star::table::CellAddress& rCellAddr, sal_Int32 nXfId, sal_Int32 nNumFmtId );
-        bool                tryExpand( const ::com::sun::star::table::CellAddress& rCellAddr, sal_Int32 nXfId, sal_Int32 nNumFmtId );
-        bool                tryMerge( const XfIdRange& rXfIdRange );
-    };
-    typedef ::std::map< BinAddress, XfIdRange > XfIdRangeMap;
-
-    /** Stores information about a merged cell range. */
-    struct MergedRange
-    {
-        ::com::sun::star::table::CellRangeAddress
-                            maRange;            /// The formatted cell range.
-        sal_Int32           mnHorAlign;         /// Horizontal alignment in the range.
-
-        explicit            MergedRange( const ::com::sun::star::table::CellRangeAddress& rRange );
-        explicit            MergedRange( const ::com::sun::star::table::CellAddress& rAddress, sal_Int32 nHorAlign );
-        bool                tryExpand( const ::com::sun::star::table::CellAddress& rAddress, sal_Int32 nHorAlign );
-    };
-    typedef ::std::list< MergedRange > MergedRangeList;
-
     CellBlockBuffer     maCellBlocks;           /// Manages all open cell blocks.
+    CellFormatBuffer    maCellFormats;          /// Manages all cell formatting.
     ArrayFormulaList    maArrayFormulas;        /// All array formulas in the sheet.
     TableOperationList  maTableOperations;      /// All table operations in the sheet.
     SharedFormulaMap    maSharedFormulas;       /// Maps shared formula base address to defined name token index.
     ::com::sun::star::table::CellAddress
                         maSharedFmlaAddr;       /// Address of a cell containing a pending shared formula.
     BinAddress          maSharedBaseAddr;       /// Base address of the pending shared formula.
-    XfIdRowRange        maXfIdRowRange;         /// Cached XF identifier for a range of rows.
-    XfIdRangeMap        maXfIdRanges;           /// Collected XF identifiers for cell ranges.
-    MergedRangeList     maMergedRanges;         /// Merged cell ranges.
-    MergedRangeList     maCenterFillRanges;     /// Merged cell ranges from 'center across' or 'fill' alignment.
     bool                mbPendingSharedFmla;    /// True = maSharedFmlaAddr and maSharedBaseAddr are valid.
 };
 

oox/inc/oox/xls/stylesbuffer.hxx

     void                finalizeImport();
 
     /** Returns the XF identifier associated to the default cell style. */
-    sal_Int32           getDefaultXfId() const;
+    sal_Int32           getDefaultStyleXfId() const;
     /** Returns the default style sheet for unused cells. */
     ::rtl::OUString     getDefaultStyleName() const;
     /** Creates the style sheet described by the style XF with the passed identifier. */
     /** Returns true, if the specified fills are equal. */
     bool                equalFills( sal_Int32 nFillId1, sal_Int32 nFillId2 ) const;
 
+    /** Returns the default cell XF identifier used for all unspecified cells. */
+    inline sal_Int32    getDefaultCellXfId() const { return mnDefCellXfId; }
     /** Returns the default style sheet for unused cells. */
     ::rtl::OUString     getDefaultStyleName() const;
     /** Creates the style sheet described by the style XF with the passed identifier. */
     CellStyleBuffer     maCellStyles;       /// All built-in and user defined cell styles.
     DxfVector           maDxfs;             /// List of differential cell styles.
     mutable DxfStyleMap maDxfStyles;        /// Maps DXF identifiers to Calc style sheet names.
+    sal_Int32           mnDefCellXfId;      /// XF index of default cell formatting.
 };
 
 // ============================================================================

oox/inc/oox/xls/worksheethelper.hxx

 #ifndef OOX_XLS_WORKSHEETHELPER_HXX
 #define OOX_XLS_WORKSHEETHELPER_HXX
 
-#include "oox/helper/containerhelper.hxx"
 #include "oox/helper/progressbar.hxx"
 #include "oox/ole/olehelper.hxx"
 #include "oox/xls/addressconverter.hxx"
 /** Stores settings and formatting data about a range of sheet columns. */
 struct ColumnModel
 {
-    ValueRange          maRange;            /// 1-based (!) range of the described columns.
+    ColRowRange         maRange;            /// 1-based (!) range of the described columns.
     double              mfWidth;            /// Column width in number of characters.
     sal_Int32           mnXfId;             /// Column default formatting.
     sal_Int32           mnLevel;            /// Column outline level.
     bool                mbCollapsed;        /// True = column outline is collapsed.
 
     explicit            ColumnModel();
-
-    /** Returns true, if this entry can be merged with the passed column range (column settings are equal). */
-    bool                isMergeable( const ColumnModel& rModel ) const;
 };
 
 // ----------------------------------------------------------------------------
 struct RowModel
 {
     sal_Int32           mnRow;              /// 1-based (!) index of the described row.
-    ValueRangeSet       maColSpans;         /// 0-based (!) column ranges of used cells.
+    ColRowSet           maColSpans;         /// 0-based (!) column ranges of used cells.
     double              mfHeight;           /// Row height in points.
     sal_Int32           mnXfId;             /// Row default formatting (see mbIsFormatted).
     sal_Int32           mnLevel;            /// Row outline level.
     explicit            RowModel();
 
     /** Inserts the passed column span into the row model. */
-    void                insertColSpan( const ValueRange& rColSpan );
-    /** Returns true, if this entry can be merged with the passed row range (row settings are equal). */
-    bool                isMergeable( const RowModel& rModel ) const;
+    void                insertColSpan( sal_Int32 nFirstCol, sal_Int32 nLastCol );
 };
 
 // ----------------------------------------------------------------------------
 
     /** Returns the XTableColumns interface for a range of columns. */
     ::com::sun::star::uno::Reference< ::com::sun::star::table::XTableColumns >
-                        getColumns( const ValueRange& rColRange ) const;
+                        getColumns( const ColRowRange& rColRange ) const;
     /** Returns the XTableRows interface for a range of rows. */
     ::com::sun::star::uno::Reference< ::com::sun::star::table::XTableRows >
-                        getRows( const ValueRange& rRowRange ) const;
+                        getRows( const ColRowRange& rRowRange ) const;
 
     /** Returns the XDrawPage interface of the draw page of the current sheet. */
     ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XDrawPage >
     /** Sets default width for all columns. This function overrides the base
         width set with the setBaseColumnWidth() function. */
     void                setDefaultColumnWidth( double fWidth );
-    /** Converts default cell formatting for a range of columns. */
-    void                setDefaultColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId );
     /** Sets column settings for a specific range of columns.
         @descr  Column default formatting is converted directly, other settings
         are cached and converted in the finalizeWorksheetImport() call. */

oox/prj/build.lst

-oox	oox : vos cppu cppuhelper comphelper sal offapi sax basegfx xmlscript tools vcl BOOST:boost OPENSSL:openssl LIBXSLT:libxslt NULL
+oox	oox : vos cppu cppuhelper comphelper sal offapi sax basegfx xmlscript tools vcl o3tl BOOST:boost OPENSSL:openssl LIBXSLT:libxslt NULL
 oox	oox				usr1	-   all	oox_mkout NULL
 oox	oox\prj				get	-   all	oox_prj NULL
 oox	oox\source\token		nmake	-   all	oox_token NULL

oox/source/helper/containerhelper.cxx

 
 // ============================================================================
 
-namespace {
-
-struct ValueRangeComp
-{
-    inline bool         operator()( const ValueRange& rRange, sal_Int32 nValue ) const { return rRange.mnLast < nValue; }
-};
-
-} // namespace
-
-// ----------------------------------------------------------------------------
-
-void ValueRangeSet::insert( const ValueRange& rRange )
-{
-    // find the first range that contains or follows the starting point of the passed range
-    ValueRangeVector::iterator aBeg = maRanges.begin();
-    ValueRangeVector::iterator aEnd = maRanges.end();
-    ValueRangeVector::iterator aIt = ::std::lower_bound( aBeg, aEnd, rRange.mnFirst, ValueRangeComp() );
-    // nothing to do if found range contains passed range
-    if( (aIt != aEnd) && aIt->contains( rRange ) ) return;
-    // check if previous range can be used to merge with the passed range
-    if( (aIt != aBeg) && ((aIt - 1)->mnLast + 1 == rRange.mnFirst) ) --aIt;
-    // check if current range (aIt) can be used to merge with passed range
-    if( (aIt != aEnd) && aIt->intersects( rRange ) )
-    {
-        // set new start value to existing range
-        aIt->mnFirst = ::std::min( aIt->mnFirst, rRange.mnFirst );
-        // search first range that cannot be merged anymore (aNext)
-        ValueRangeVector::iterator aNext = aIt + 1;
-        while( (aNext != aEnd) && aNext->intersects( rRange ) ) ++aNext;
-        // set new end value to existing range
-        aIt->mnLast = ::std::max( (aNext - 1)->mnLast, rRange.mnLast );
-        // remove ranges covered by new existing range (aIt)
-        maRanges.erase( aIt + 1, aNext );
-    }
-    else
-    {
-        // merging not possible: insert new range
-        maRanges.insert( aIt, rRange );
-    }
-}
-
-ValueRangeVector ValueRangeSet::getIntersection( const ValueRange& rRange ) const
-{
-    ValueRangeVector aRanges;
-    // find the range that contains nFirst or the first range that follows nFirst
-    ValueRangeVector::const_iterator aIt = ::std::lower_bound( maRanges.begin(), maRanges.end(), rRange.mnFirst, ValueRangeComp() );
-    for( ValueRangeVector::const_iterator aEnd = maRanges.end(); (aIt != aEnd) && (aIt->mnFirst <= rRange.mnLast); ++aIt )
-        aRanges.push_back( ValueRange( ::std::max( aIt->mnFirst, rRange.mnFirst ), ::std::min( aIt->mnLast, rRange.mnLast ) ) );
-    return aRanges;
-}
-
-// ============================================================================
-
 Reference< XIndexContainer > ContainerHelper::createIndexContainer( const Reference< XComponentContext >& rxContext )
 {
     Reference< XIndexContainer > xContainer;

oox/source/xls/addressconverter.cxx

 
 // ----------------------------------------------------------------------------
 
+bool AddressConverter::validateColumnRange( ColRowRange& orColRange, bool bAllowOverflow, bool bTrackOverflow )
+{
+    if( orColRange.first > orColRange.second )
+        ::std::swap( orColRange.first, orColRange.second );
+    if( !checkCol( orColRange.first, bTrackOverflow ) )
+        return false;
+    if( checkCol( orColRange.second, bTrackOverflow ) )
+        return true;
+    if( bAllowOverflow )
+        orColRange.second = maMaxPos.Column;
+    return bAllowOverflow;
+}
+
+bool AddressConverter::validateRowRange( ColRowRange& orRowRange, bool bAllowOverflow, bool bTrackOverflow )
+{
+    if( orRowRange.first > orRowRange.second )
+        ::std::swap( orRowRange.first, orRowRange.second );
+    if( !checkRow( orRowRange.first, bTrackOverflow ) )
+        return false;
+    if( checkRow( orRowRange.second, bTrackOverflow ) )
+        return true;
+    if( bAllowOverflow )
+        orRowRange.second = maMaxPos.Column;
+    return bAllowOverflow;
+}
+
+// ----------------------------------------------------------------------------
+
 bool AddressConverter::checkCellAddress( const CellAddress& rAddress, bool bTrackOverflow )
 {
     return

oox/source/xls/pivotcachebuffer.cxx

     // check range location, do not allow ranges that overflow the sheet partly
     if( getAddressConverter().checkCellRange( rRange, false, true ) )
     {
-        maColSpans.insert( ValueRange( rRange.StartColumn, rRange.EndColumn ) );
+        maColSpans.insert( ColRowRange( rRange.StartColumn, rRange.EndColumn ) );
         OUString aSheetName = CREATE_OUSTRING( "DPCache_" ) + maSheetSrcModel.maSheet;
         rRange.Sheet = getWorksheets().insertEmptySheet( aSheetName, false );
         mbValidSource = mbDummySheet = rRange.Sheet >= 0;
 {
     if( mnCurrRow != nRow )
     {
-        rSheetHelper.getSheetData().setColSpans( nRow, maColSpans );
+        rSheetHelper.getSheetData().setDefaultRowFormat( nRow, maColSpans, -1, false );
         mnCurrRow = nRow;
     }
 }

oox/source/xls/sheetdatabuffer.cxx

 
 // ============================================================================
 
+namespace {
+
+const sal_Int32 CELLBLOCK_MAXROWS   = 32;   /// Maximum number of rows in a cell block.
+const sal_Int32 CELLFORMAT_MAXROWS  = 128;  /// Maximum number of rows in the cell formatting buffer.
+
+} // namespace
+
+// ============================================================================
+
 CellModel::CellModel() :
     mnCellType( XML_TOKEN_INVALID ),
-    mnXfId( -1 ),
+    mnXfId( 0 ),
     mbShowPhonetic( false )
 {
 }
 
 // ============================================================================
 
-namespace {
-
-const sal_Int32 CELLBLOCK_MAXROWS  = 16;    /// Number of rows in a cell block.
-
-} // namespace
-
-CellBlock::CellBlock( const WorksheetHelper& rHelper, const ValueRange& rColSpan, sal_Int32 nRow ) :
+CellBlock::CellBlock( const WorksheetHelper& rHelper, const ColRowRange& rColSpan, sal_Int32 nRow ) :
     WorksheetHelper( rHelper ),
-    maRange( rHelper.getSheetIndex(), rColSpan.mnFirst, nRow, rColSpan.mnLast, nRow ),
-    mnRowLength( rColSpan.mnLast - rColSpan.mnFirst + 1 ),
+    maRange( rHelper.getSheetIndex(), rColSpan.first, nRow, rColSpan.second, nRow ),
+    mnRowLength( rColSpan.second - rColSpan.first + 1 ),
     mnFirstFreeIndex( 0 )
 {
     maCellArray.realloc( 1 );
     mpCurrCellRow = maCellArray[ 0 ].getArray();
 }
 
-bool CellBlock::isExpandable( const ValueRange& rColSpan ) const
+bool CellBlock::isExpandable( const ColRowRange& rColSpan ) const
 {
-    return (maRange.StartColumn == rColSpan.mnFirst) && (maRange.EndColumn == rColSpan.mnLast);
+    return (maRange.StartColumn == rColSpan.first) && (maRange.EndColumn == rColSpan.second);
 }
 
-bool CellBlock::isBefore( const ValueRange& rColSpan ) const
+bool CellBlock::isBefore( const ColRowRange& rColSpan ) const
 {
-    return (maRange.EndColumn < rColSpan.mnLast) ||
-        ((maRange.EndColumn == rColSpan.mnLast) && (maRange.StartColumn != rColSpan.mnFirst));
+    return (maRange.EndColumn < rColSpan.second) ||
+        ((maRange.EndColumn == rColSpan.second) && (maRange.StartColumn != rColSpan.first));
 }
 
 bool CellBlock::contains( sal_Int32 nCol ) const
     maCellBlockIt = maCellBlocks.end();
 }
 
-void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
+void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ColRowSet& rColSpans )
 {
     OSL_ENSURE( maColSpans.count( nRow ) == 0, "CellBlockBuffer::setColSpans - multiple column spans for the same row" );
     OSL_ENSURE( (mnCurrRow < nRow) && (maColSpans.empty() || (maColSpans.rbegin()->first < nRow)), "CellBlockBuffer::setColSpans - rows are unsorted" );
     if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) )
-        maColSpans[ nRow ] = rColSpans.getRanges();
+        maColSpans[ nRow ] = rColSpans;
 }
 
 CellBlock* CellBlockBuffer::getCellBlock( const CellAddress& rCellAddr )
     if( rCellAddr.Row != mnCurrRow )
     {
         // find colspans for the new row
-        ColSpanVectorMap::iterator aIt = maColSpans.find( rCellAddr.Row );
+        ColSpanMap::iterator aIt = maColSpans.find( rCellAddr.Row );
 
         /*  Gap between rows, or rows out of order, or no colspan
             information for the new row found: flush all open cell blocks. */
                 as the cell block map. In the folloing, this vector and the
                 list of cell blocks can be iterated simultanously. */
             CellBlockMap::iterator aMIt = maCellBlocks.begin();
-            const ValueRangeVector& rColRanges = aIt->second;
-            for( ValueRangeVector::const_iterator aVIt = rColRanges.begin(), aVEnd = rColRanges.end(); aVIt != aVEnd; ++aVIt, ++aMIt )
+            for( ColRowSet::const_iterator aVIt = aIt->second.begin(), aVEnd = aIt->second.end(); aVIt != aVEnd; ++aVIt, ++aMIt )
             {
-                const ValueRange& rColSpan = *aVIt;
+                const ColRowRange& rColSpan = *aVIt;
                 /*  Finalize and remove all cell blocks up to end of the column
                     range (cell blocks are keyed by end column index).
                     CellBlock::isBefore() returns true, if the end index of the
                 if( (aMIt != maCellBlocks.end()) && aMIt->second->isExpandable( rColSpan ) )
                     aMIt->second->startNextRow();
                 else
-                    aMIt = maCellBlocks.insert( aMIt, CellBlockMap::value_type( rColSpan.mnLast,
+                    aMIt = maCellBlocks.insert( aMIt, CellBlockMap::value_type( rColSpan.second,
                         CellBlockMap::mapped_type( new CellBlock( *this, rColSpan, rCellAddr.Row ) ) ) );
             }
             // finalize and remove all remaining cell blocks
 
 // ============================================================================
 
+CellFormatBuffer::CellFormatBuffer( const WorksheetHelper& rHelper ) :
+    WorksheetHelper( rHelper ),
+    mrAddressConv( rHelper.getAddressConverter() ),
+    mnDefCellXfId( rHelper.getStyles().getDefaultCellXfId() ),
+    mnCurrRow( -1 ),    // indicates that no cell has been inserted yet
+    mnFirstRow( 0 ),    // used as start index for first conversion of row default formatting
+    mbCustomColumnFormat( false )
+{
+    maDefColXfIds.resize( static_cast< size_t >( mrAddressConv.getMaxApiAddress().Column + 1 ), mnDefCellXfId );
+}
+
+void CellFormatBuffer::setDefaultColumnFormat( const ColRowRange& rColRange, sal_Int32 nXfId )
+{
+    OSL_ENSURE( maDefRowXfIds.empty() && (mnCurrRow < 0), "CellFormatBuffer::setDefaultColumnFormat - must be called before setting row/cell formatting" );
+    CellRangeAddress aRange( getSheetIndex(), rColRange.first, 0, rColRange.second, mrAddressConv.getMaxApiAddress().Row );
+    if( (nXfId >= 0) && mrAddressConv.validateCellRange( aRange, true, true ) )
+    {
+        // write column formatting directly
+        PropertySet aPropSet( getCellRange( aRange ) );
+        getStyles().writeCellXfToPropertySet( aPropSet, nXfId );
+        // store default column XF identifier for later use
+        ::std::fill( maDefColXfIds.begin() + aRange.StartColumn, maDefColXfIds.begin() + aRange.EndColumn + 1, nXfId );
+        mbCustomColumnFormat = nXfId != mnDefCellXfId;
+    }
+}
+
+void CellFormatBuffer::setDefaultRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
+{
+    OSL_ENSURE( maDefRowXfIds.empty() || (maDefRowXfIds.back().first.second < nRow), "CellFormatBuffer::setDefaultRowFormat - rows are unsorted" );
+    /*  Do not insert row formatting, if row format is default cell format AND
+        there are no formatted columns (otherwise default row format will
+        overwrite the formatted columns). */
+    if( (nXfId >= 0) && bCustomFormat && mrAddressConv.checkRow( nRow, true ) && (mbCustomColumnFormat || (nXfId != mnDefCellXfId)) )
+        maDefRowXfIds.insert( nRow, nXfId );
+}
+
+void CellFormatBuffer::setCellFormat( const CellModel& rModel, sal_Int32 nNumFmtId )
+{
+    // first call: initialize row indexes
+    if( mnCurrRow < 0 )
+    {
+        mnCurrRow = rModel.maCellAddr.Row;
+    }
+    // update format buffers if current row changes
+    else if( mnCurrRow != rModel.maCellAddr.Row )
+    {
+        OSL_ENSURE( mnCurrRow < rModel.maCellAddr.Row, "CellFormatBuffer::setCellFormat - cells are unordered" );
+
+        /*  Merge ranges of (old) current row contained in maCurrRowXfIds
+            with ranges in maOpenFormatRanges and move finished ranges from
+            maOpenFormatRanges to maFinalFormatRanges. */
+        bool bFlushOpenRanges = mnCurrRow + 1 != rModel.maCellAddr.Row;
+        mergeFormatRanges( bFlushOpenRanges );
+
+        /*  Write all final formatting attributes to the spreadsheet, if a
+            specific number of rows is buffered, or if rows are unordered. */
+        if( (mnCurrRow - mnFirstRow >= CELLFORMAT_MAXROWS) || (rModel.maCellAddr.Row < mnCurrRow) )
+            flushFormatRanges();
+
+        // update row indexes
+        mnCurrRow = rModel.maCellAddr.Row;
+    }
+
+    // check if XF needs to be written (compare to row and column default formatting)
+    CellFormat aCellFormat( rModel.mnXfId, nNumFmtId );
+    if( aCellFormat.mnXfId >= 0 )
+    {
+        // find entry in row formatting array
+        XfIdMap::const_iterator aIt = maDefRowXfIds.find( rModel.maCellAddr.Row );
+        // if default row formatting is not set, check default column formatting
+        sal_Int32 nDefXfId = (aIt == maDefRowXfIds.end()) ? maDefColXfIds[ rModel.maCellAddr.Column ] : aIt->second;
+        // if cell format equals default format, do not set it again
+        if( nDefXfId == aCellFormat.mnXfId )
+            aCellFormat.mnXfId = -1;
+    }
+
+    // insert new cell into maCurrRowXfIds
+    if( (aCellFormat.mnXfId >= 0) || (aCellFormat.mnNumFmtId >= 0) )
+        maCurrRowXfIds.insert( rModel.maCellAddr.Column, aCellFormat );
+
+    // update merged ranges for 'center across selection' and 'fill' (use original XF identifier)
+    if( const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get() )
+    {
+        sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
+        if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
+        {
+            /*  start new merged range, if cell is not empty (#108781#),
+                or try to expand last range with empty cell */
+            if( rModel.mnCellType != XML_TOKEN_INVALID )
+                maCenterFillRanges.push_back( MergedRange( rModel.maCellAddr, nHorAlign ) );
+            else if( !maCenterFillRanges.empty() )
+                maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
+        }
+    }
+}
+
+void CellFormatBuffer::setMergedRange( const CellRangeAddress& rRange )
+{
+    maMergedRanges.push_back( MergedRange( rRange ) );
+}
+
+void CellFormatBuffer::initializeImport()
+{
+    // initialize default column XF indexes for entire sheet
+    PropertySet aPropSet( getSheet() );
+    if( mnDefCellXfId >= 0 )
+    {
+        getStyles().writeCellXfToPropertySet( aPropSet, mnDefCellXfId );
+    }
+    else
+    {
+        // no default cell XF found, try to set a customized default cell style
+        OUString aDefStyle = getStyles().getDefaultStyleName();
+        if( aDefStyle.getLength() > 0 )
+            aPropSet.setProperty( PROP_CellStyle, aDefStyle );
+    }
+}
+
+void CellFormatBuffer::finalizeImport()
+{
+    // write remaining buffered row and cell formatting to the spreadsheet
+    mergeFormatRanges( true );
+    mnCurrRow = mrAddressConv.getMaxApiAddress().Row;
+    flushFormatRanges();
+
+    // merge all cached merged ranges and update right/bottom cell borders
+    for( MergedRangeList::iterator aIt = maMergedRanges.begin(), aEnd = maMergedRanges.end(); aIt != aEnd; ++aIt )
+        finalizeMergedRange( aIt->maRange );
+    for( MergedRangeList::iterator aIt = maCenterFillRanges.begin(), aEnd = maCenterFillRanges.end(); aIt != aEnd; ++aIt )
+        finalizeMergedRange( aIt->maRange );
+}
+
+// private --------------------------------------------------------------------
+
+CellFormatBuffer::CellFormat::CellFormat( sal_Int32 nXfId, sal_Int32 nNumFmtId ) :
+    mnXfId( nXfId ),
+    mnNumFmtId( nNumFmtId )
+{
+}
+
+bool CellFormatBuffer::CellFormat::operator==( const CellFormat& rCellFormat ) const
+{
+    return (mnXfId == rCellFormat.mnXfId) && (mnNumFmtId == rCellFormat.mnNumFmtId);
+}
+
+CellFormatBuffer::FormatRange::FormatRange( const CellRangeAddress& rRange, const CellFormat& rCellFormat ) :
+    maRange( rRange ),
+    maCellFormat( rCellFormat )
+{
+}
+
+CellFormatBuffer::MergedRange::MergedRange( const CellRangeAddress& rRange ) :
+    maRange( rRange ),
+    mnHorAlign( XML_TOKEN_INVALID )
+{
+}
+
+CellFormatBuffer::MergedRange::MergedRange( const CellAddress& rAddress, sal_Int32 nHorAlign ) :
+    maRange( rAddress.Sheet, rAddress.Column, rAddress.Row, rAddress.Column, rAddress.Row ),
+    mnHorAlign( nHorAlign )
+{
+}
+
+bool CellFormatBuffer::MergedRange::tryExpand( const CellAddress& rAddress, sal_Int32 nHorAlign )
+{
+    if( (mnHorAlign == nHorAlign) && (maRange.StartRow == rAddress.Row) &&
+        (maRange.EndRow == rAddress.Row) && (maRange.EndColumn + 1 == rAddress.Column) )
+    {
+        ++maRange.EndColumn;
+        return true;
+    }
+    return false;
+}
+
+// ----------------------------------------------------------------------------
+
+void CellFormatBuffer::mergeFormatRanges( bool bFlushOpenRanges )
+{
+    /*  Merge or insert all ranges of the current row (maCurrRowXfIds) into
+        maOpenFormatRanges whose ranges all end at mnCurrRow-1. Move finished
+        (not expandable) ranges from maOpenFormatRanges to maFinalFormatRanges.
+        Do it all in one pass by iterating maCurrRowXfIds and
+        maOpenFormatRanges simultaneously. */
+    FormatRangeList::iterator aOIt = maOpenFormatRanges.begin();
+    for( CellFormatMap::const_iterator aIt = maCurrRowXfIds.begin(), aEnd = maCurrRowXfIds.end(); aIt != aEnd; ++aIt )
+    {
+        const CellFormatMap::key_type& rRange = aIt->first;
+
+        /*  Move all ranges in maOpenFormatRanges to maFinalFormatRanges that
+            start before the begin of the range currently processed (aIt). */
+        FormatRangeList::iterator aOIt2 = aOIt;
+        while( (aOIt2 != maOpenFormatRanges.end()) && (aOIt2->maRange.StartColumn < rRange.first) ) ++aOIt2;
+        // move elements in range [aOIt,aOIt2) from maOpenFormatRanges to maFinalFormatRanges
+        maFinalFormatRanges.splice( maFinalFormatRanges.end(), maOpenFormatRanges, aOIt, aOIt2 );
+        // aOIt2 is still valid and points to first element still in maOpenFormatRanges
+        aOIt = aOIt2;
+        
+        // now, aOIt may point to a range that can be merged with aIt
+        if( (aOIt != maOpenFormatRanges.end()) && (aOIt->maRange.StartColumn == rRange.first) && (aOIt->maRange.EndColumn == rRange.second) && (aOIt->maCellFormat == aIt->second) )
+            ++aOIt->maRange.EndRow;
+        else
+            aOIt = maOpenFormatRanges.insert( aOIt, FormatRange( CellRangeAddress( getSheetIndex(), rRange.first, mnCurrRow, rRange.second, mnCurrRow ), aIt->second ) );
+        // aOIt points to updated or new range, go to next element if open ranges will not flushed
+        if( !bFlushOpenRanges )
+            ++aOIt;
+    }
+    maCurrRowXfIds.clear();
+
+    // move remaining ranges from maOpenFormatRanges to maFinalFormatRanges
+    maFinalFormatRanges.splice( maFinalFormatRanges.end(), maOpenFormatRanges, aOIt, maOpenFormatRanges.end() );
+}
+
+void CellFormatBuffer::flushFormatRanges()
+{
+    StylesBuffer& rStyles = getStyles();
+
+    // write row default formatting in the current row range
+    if( mnFirstRow <= mnCurrRow )
+    {
+        ColRowRange aRowRange( mnFirstRow, mnCurrRow );
+        XfIdMap aDefRowXfIds = maDefRowXfIds.make_intersection( aRowRange );
+        for( XfIdMap::const_iterator aIt = aDefRowXfIds.begin(), aEnd = aDefRowXfIds.end(); aIt != aEnd; ++aIt )
+        {
+            CellRangeAddress aRange( getSheetIndex(), 0, aIt->first.first, mrAddressConv.getMaxApiAddress().Column, aIt->first.second );
+            PropertySet aPropSet( getCellRange( aRange ) );
+            rStyles.writeCellXfToPropertySet( aPropSet, aIt->second );
+        }
+        maDefRowXfIds.erase( aRowRange );
+        mnFirstRow = mnCurrRow + 1;
+    }
+
+    // move all remaining entries from maOpenFormatRanges to maFinalFormatRanges
+    maFinalFormatRanges.splice( maFinalFormatRanges.end(), maOpenFormatRanges );
+
+    // write cell formatting
+    for( FormatRangeList::const_iterator aIt = maFinalFormatRanges.begin(), aEnd = maFinalFormatRanges.end(); aIt != aEnd; ++aIt )
+    {
+        PropertyMap aPropMap;
+        if( aIt->maCellFormat.mnXfId >= 0 )
+            rStyles.writeCellXfToPropertyMap( aPropMap, aIt->maCellFormat.mnXfId );
+        if( aIt->maCellFormat.mnNumFmtId >= 0 )
+            rStyles.writeNumFmtToPropertyMap( aPropMap, aIt->maCellFormat.mnNumFmtId );
+        PropertySet aPropSet( getCellRange( aIt->maRange ) );
+        aPropSet.setProperties( aPropMap );
+    }
+    maFinalFormatRanges.clear();
+}
+
+void CellFormatBuffer::finalizeMergedRange( const CellRangeAddress& rRange )
+{
+    bool bMultiCol = rRange.StartColumn < rRange.EndColumn;
+    bool bMultiRow = rRange.StartRow < rRange.EndRow;
+
+    if( bMultiCol || bMultiRow ) try
+    {
+        // merge the cell range
+        Reference< XMergeable > xMerge( getCellRange( rRange ), UNO_QUERY_THROW );
+        xMerge->merge( sal_True );
+
+        // if merging this range worked (no overlapping merged ranges), update cell borders
+        Reference< XCell > xTopLeft( getCell( CellAddress( getSheetIndex(), rRange.StartColumn, rRange.StartRow ) ), UNO_SET_THROW );
+        PropertySet aTopLeftProp( xTopLeft );
+
+        // copy right border of top-right cell to right border of top-left cell
+        if( bMultiCol )
+        {
+            PropertySet aTopRightProp( getCell( CellAddress( getSheetIndex(), rRange.EndColumn, rRange.StartRow ) ) );
+            BorderLine aLine;
+            if( aTopRightProp.getProperty( aLine, PROP_RightBorder ) )
+                aTopLeftProp.setProperty( PROP_RightBorder, aLine );
+        }
+
+        // copy bottom border of bottom-left cell to bottom border of top-left cell
+        if( bMultiRow )
+        {
+            PropertySet aBottomLeftProp( getCell( CellAddress( getSheetIndex(), rRange.StartColumn, rRange.EndRow ) ) );
+            BorderLine aLine;
+            if( aBottomLeftProp.getProperty( aLine, PROP_BottomBorder ) )
+                aTopLeftProp.setProperty( PROP_BottomBorder, aLine );
+        }
+
+        // #i93609# merged range in a single row: test if manual row height is needed
+        if( !bMultiRow )
+        {
+            bool bTextWrap = aTopLeftProp.getBoolProperty( PROP_IsTextWrapped );
+            if( !bTextWrap && (xTopLeft->getType() == CellContentType_TEXT) )
+            {
+                Reference< XText > xText( xTopLeft, UNO_QUERY );
+                bTextWrap = xText.is() && (xText->getString().indexOf( '\x0A' ) >= 0);
+            }
+            if( bTextWrap )
+                setManualRowHeight( rRange.StartRow );
+        }
+    }
+    catch( Exception& )
+    {
+    }
+}
+
+// ============================================================================
+
 SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
     WorksheetHelper( rHelper ),
     maCellBlocks( rHelper ),
+    maCellFormats( rHelper ),
     mbPendingSharedFmla( false )
 {
 }
 
-void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
+void SheetDataBuffer::setDefaultColumnFormat( const ColRowRange& rColRange, sal_Int32 nXfId )
+{
+    maCellFormats.setDefaultColumnFormat( rColRange, nXfId );
+}
+
+void SheetDataBuffer::setDefaultRowFormat( sal_Int32 nRow, const ColRowSet& rColSpans, sal_Int32 nXfId, bool bCustomFormat )
 {
     maCellBlocks.setColSpans( nRow, rColSpans );
+    maCellFormats.setDefaultRowFormat( nRow, nXfId, bCustomFormat );
 }
 
 void SheetDataBuffer::setBlankCell( const CellModel& rModel )
 {
-    setCellFormat( rModel );
+    maCellFormats.setCellFormat( rModel );
 }
 
 void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
         pCellBlock->getCellAny( rModel.maCellAddr.Column ) <<= fValue;
     else
         putValue( rModel.maCellAddr, fValue );
-    setCellFormat( rModel );
+    maCellFormats.setCellFormat( rModel );
 }
 
 void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
         pCellBlock->getCellAny( rModel.maCellAddr.Column ) <<= rText;
     else
         putString( rModel.maCellAddr, rText );
-    setCellFormat( rModel );
+    maCellFormats.setCellFormat( rModel );
 }
 
 void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
             pCellBlock->insertRichString( rModel.maCellAddr, rxString, pFirstPortionFont );
         else
             putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
-        setCellFormat( rModel );
+        maCellFormats.setCellFormat( rModel );
     }
 }
 
 {
     setCellFormula( rModel.maCellAddr, getFormulaParser().convertBoolToFormula( bValue ) );
     // #108770# set 'Standard' number format for all Boolean cells
-    setCellFormat( rModel, 0 );
+    maCellFormats.setCellFormat( rModel, 0 );
 }
 
 void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
 void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
 {
     setCellFormula( rModel.maCellAddr, getFormulaParser().convertErrorToFormula( nErrorCode ) );
-    setCellFormat( rModel );
+    maCellFormats.setCellFormat( rModel );
 }
 
 void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
     }
 
     setCellFormula( rModel.maCellAddr, aTokens );
-    setCellFormat( rModel );
+    maCellFormats.setCellFormat( rModel );
 }
 
 void SheetDataBuffer::setFormulaCell( const CellModel& rModel, sal_Int32 nSharedId )
 {
     setCellFormula( rModel.maCellAddr, resolveSharedFormula( BinAddress( nSharedId, 0 ) ) );
-    setCellFormat( rModel );
+    maCellFormats.setCellFormat( rModel );
 }
 
 void SheetDataBuffer::createArrayFormula( const CellRangeAddress& rRange, const ApiTokenSequence& rTokens )
     createSharedFormula( BinAddress( rCellAddr ), rTokens );
 }
 
-void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
-{
-    // set row formatting
-    if( bCustomFormat )
-    {
-        // try to expand cached row range, if formatting is equal
-        if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
-        {
-            writeXfIdRowRangeProperties( maXfIdRowRange );
-            maXfIdRowRange.set( nRow, nXfId );
-        }
-    }
-    else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
-    {
-        // finish last cached row range
-        writeXfIdRowRangeProperties( maXfIdRowRange );
-        maXfIdRowRange.set( -1, -1 );
-    }
-}
-
 void SheetDataBuffer::setMergedRange( const CellRangeAddress& rRange )
 {
-    maMergedRanges.push_back( MergedRange( rRange ) );
+    maCellFormats.setMergedRange( rRange );
 }
 
 void SheetDataBuffer::setStandardNumFmt( const CellAddress& rCellAddr, sal_Int16 nStdNumFmt )
     }
 }
 
+void SheetDataBuffer::initializeImport()
+{
+    maCellFormats.initializeImport();
+}
+
 void SheetDataBuffer::finalizeImport()
 {
     // insert all cells of all open cell blocks
     for( TableOperationList::iterator aIt = maTableOperations.begin(), aEnd = maTableOperations.end(); aIt != aEnd; ++aIt )
         finalizeTableOperation( aIt->first, aIt->second );
 
-    // write default formatting of remaining row range
-    writeXfIdRowRangeProperties( maXfIdRowRange );
-
-    // try to merge remaining inserted ranges
-    mergeXfIdRanges();
-    // write all formatting
-    for( XfIdRangeMap::const_iterator aIt = maXfIdRanges.begin(), aEnd = maXfIdRanges.end(); aIt != aEnd; ++aIt )
-        writeXfIdRangeProperties( aIt->second );
-
-    // merge all cached merged ranges and update right/bottom cell borders
-    for( MergedRangeList::iterator aIt = maMergedRanges.begin(), aEnd = maMergedRanges.end(); aIt != aEnd; ++aIt )
-        finalizeMergedRange( aIt->maRange );
-    for( MergedRangeList::iterator aIt = maCenterFillRanges.begin(), aEnd = maCenterFillRanges.end(); aIt != aEnd; ++aIt )
-        finalizeMergedRange( aIt->maRange );
+    // finalize all cell formatting
+    maCellFormats.finalizeImport();
 }
 
 // private --------------------------------------------------------------------
 
-SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
-    maRowRange( -1 ),
-    mnXfId( -1 )
-{
-}
-
-bool SheetDataBuffer::XfIdRowRange::intersects( const CellRangeAddress& rRange ) const
-{
-    return (rRange.StartRow <= maRowRange.mnLast) && (maRowRange.mnFirst <= rRange.EndRow);
-}
-
-void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
-{
-    maRowRange = ValueRange( nRow );
-    mnXfId = nXfId;
-}
-
-bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
-{
-    if( mnXfId == nXfId )
-    {
-        if( maRowRange.mnLast + 1 == nRow )
-        {
-            ++maRowRange.mnLast;
-            return true;
-        }
-        if( maRowRange.mnFirst == nRow + 1 )
-        {
-            --maRowRange.mnFirst;
-            return true;
-        }
-    }
-    return false;
-}
-
-void SheetDataBuffer::XfIdRange::set( const CellAddress& rCellAddr, sal_Int32 nXfId, sal_Int32 nNumFmtId )
-{
-    maRange.Sheet = rCellAddr.Sheet;
-    maRange.StartColumn = maRange.EndColumn = rCellAddr.Column;
-    maRange.StartRow = maRange.EndRow = rCellAddr.Row;
-    mnXfId = nXfId;
-    mnNumFmtId = nNumFmtId;
-}
-
-bool SheetDataBuffer::XfIdRange::tryExpand( const CellAddress& rCellAddr, sal_Int32 nXfId, sal_Int32 nNumFmtId )
-{
-    if( (mnXfId == nXfId) && (mnNumFmtId == nNumFmtId) &&
-        (maRange.StartRow == rCellAddr.Row) &&
-        (maRange.EndRow == rCellAddr.Row) &&
-        (maRange.EndColumn + 1 == rCellAddr.Column) )
-    {
-        ++maRange.EndColumn;
-        return true;
-    }
-    return false;
-}
-
-bool SheetDataBuffer::XfIdRange::tryMerge( const XfIdRange& rXfIdRange )
-{
-    if( (mnXfId == rXfIdRange.mnXfId) &&
-        (mnNumFmtId == rXfIdRange.mnNumFmtId) &&
-        (maRange.EndRow + 1 == rXfIdRange.maRange.StartRow) &&
-        (maRange.StartColumn == rXfIdRange.maRange.StartColumn) &&
-        (maRange.EndColumn == rXfIdRange.maRange.EndColumn) )
-    {
-        maRange.EndRow = rXfIdRange.maRange.EndRow;
-        return true;
-    }
-    return false;
-}
-
-
-SheetDataBuffer::MergedRange::MergedRange( const CellRangeAddress& rRange ) :
-    maRange( rRange ),
-    mnHorAlign( XML_TOKEN_INVALID )
-{
-}
-
-SheetDataBuffer::MergedRange::MergedRange( const CellAddress& rAddress, sal_Int32 nHorAlign ) :
-    maRange( rAddress.Sheet, rAddress.Column, rAddress.Row, rAddress.Column, rAddress.Row ),
-    mnHorAlign( nHorAlign )
-{
-}
-
-bool SheetDataBuffer::MergedRange::tryExpand( const CellAddress& rAddress, sal_Int32 nHorAlign )
-{
-    if( (mnHorAlign == nHorAlign) && (maRange.StartRow == rAddress.Row) &&
-        (maRange.EndRow == rAddress.Row) && (maRange.EndColumn + 1 == rAddress.Column) )
-    {
-        ++maRange.EndColumn;
-        return true;
-    }
-    return false;
-}
-
-// ----------------------------------------------------------------------------
-
 void SheetDataBuffer::setCellFormula( const CellAddress& rCellAddr, const ApiTokenSequence& rTokens )
 {
     if( rTokens.hasElements() )
     }
 }
 
-void SheetDataBuffer::setCellFormat( const CellModel& rModel, sal_Int32 nNumFmtId )
-{
-    if( (rModel.mnXfId >= 0) || (nNumFmtId >= 0) )
-    {
-        // try to merge existing ranges and to write some formatting properties
-        if( !maXfIdRanges.empty() )
-        {
-            // get row index of last inserted cell
-            sal_Int32 nLastRow = maXfIdRanges.rbegin()->second.maRange.StartRow;
-            // row changed - try to merge ranges of last row with existing ranges
-            if( rModel.maCellAddr.Row != nLastRow )
-            {
-                mergeXfIdRanges();
-                // write format properties of all ranges above last row and remove them
-                XfIdRangeMap::iterator aIt = maXfIdRanges.begin(), aEnd = maXfIdRanges.end();
-                while( aIt != aEnd )
-                {
-                    // check that range cannot be merged with current row, and that range is not in cached row range
-                    if( (aIt->second.maRange.EndRow < nLastRow) && !maXfIdRowRange.intersects( aIt->second.maRange ) )
-                    {
-                        writeXfIdRangeProperties( aIt->second );
-                        maXfIdRanges.erase( aIt++ );
-                    }
-                    else
-                        ++aIt;
-                }
-            }
-        }
-
-        // try to expand last existing range, or create new range entry
-        if( maXfIdRanges.empty() || !maXfIdRanges.rbegin()->second.tryExpand( rModel.maCellAddr, rModel.mnXfId, nNumFmtId ) )
-            maXfIdRanges[ BinAddress( rModel.maCellAddr ) ].set( rModel.maCellAddr, rModel.mnXfId, nNumFmtId );
-
-        // update merged ranges for 'center across selection' and 'fill'
-        if( const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get() )
-        {
-            sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
-            if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
-            {
-                /*  start new merged range, if cell is not empty (#108781#),
-                    or try to expand last range with empty cell */
-                if( rModel.mnCellType != XML_TOKEN_INVALID )
-                    maCenterFillRanges.push_back( MergedRange( rModel.maCellAddr, nHorAlign ) );
-                else if( !maCenterFillRanges.empty() )
-                    maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
-            }
-        }
-    }
-}
-
-void SheetDataBuffer::writeXfIdRowRangeProperties( const XfIdRowRange& rXfIdRowRange ) const
-{
-    if( (rXfIdRowRange.maRowRange.mnLast >= 0) && (rXfIdRowRange.mnXfId >= 0) )
-    {
-        AddressConverter& rAddrConv = getAddressConverter();
-        CellRangeAddress aRange( getSheetIndex(), 0, rXfIdRowRange.maRowRange.mnFirst, rAddrConv.getMaxApiAddress().Column, rXfIdRowRange.maRowRange.mnLast );
-        if( rAddrConv.validateCellRange( aRange, true, false ) )
-        {
-            PropertySet aPropSet( getCellRange( aRange ) );
-            getStyles().writeCellXfToPropertySet( aPropSet, rXfIdRowRange.mnXfId );
-        }
-    }
-}
-
-void SheetDataBuffer::writeXfIdRangeProperties( const XfIdRange& rXfIdRange ) const
-{
-    StylesBuffer& rStyles = getStyles();
-    PropertyMap aPropMap;
-    if( rXfIdRange.mnXfId >= 0 )
-        rStyles.writeCellXfToPropertyMap( aPropMap, rXfIdRange.mnXfId );
-    if( rXfIdRange.mnNumFmtId >= 0 )
-        rStyles.writeNumFmtToPropertyMap( aPropMap, rXfIdRange.mnNumFmtId );
-    PropertySet aPropSet( getCellRange( rXfIdRange.maRange ) );
-    aPropSet.setProperties( aPropMap );
-}
-
-void SheetDataBuffer::mergeXfIdRanges()
-{
-    if( !maXfIdRanges.empty() )
-    {
-        // get row index of last range
-        sal_Int32 nLastRow = maXfIdRanges.rbegin()->second.maRange.StartRow;
-        // process all ranges located in the same row of the last range
-        XfIdRangeMap::iterator aMergeIt = maXfIdRanges.end();
-        while( (aMergeIt != maXfIdRanges.begin()) && ((--aMergeIt)->second.maRange.StartRow == nLastRow) )
-        {
-            const XfIdRange& rMergeXfIdRange = aMergeIt->second;
-            // try to find a range that can be merged with rMergeRange
-            bool bFound = false;
-            for( XfIdRangeMap::iterator aIt = maXfIdRanges.begin(); !bFound && (aIt != aMergeIt); ++aIt )
-                if( (bFound = aIt->second.tryMerge( rMergeXfIdRange )) == true )
-                    maXfIdRanges.erase( aMergeIt++ );
-        }
-    }
-}
-
-void SheetDataBuffer::finalizeMergedRange( const CellRangeAddress& rRange )
-{
-    bool bMultiCol = rRange.StartColumn < rRange.EndColumn;
-    bool bMultiRow = rRange.StartRow < rRange.EndRow;
-
-    if( bMultiCol || bMultiRow ) try
-    {
-        // merge the cell range
-        Reference< XMergeable > xMerge( getCellRange( rRange ), UNO_QUERY_THROW );
-        xMerge->merge( sal_True );
-
-        // if merging this range worked (no overlapping merged ranges), update cell borders
-        Reference< XCell > xTopLeft( getCell( CellAddress( getSheetIndex(), rRange.StartColumn, rRange.StartRow ) ), UNO_SET_THROW );
-        PropertySet aTopLeftProp( xTopLeft );
-
-        // copy right border of top-right cell to right border of top-left cell
-        if( bMultiCol )
-        {
-            PropertySet aTopRightProp( getCell( CellAddress( getSheetIndex(), rRange.EndColumn, rRange.StartRow ) ) );
-            BorderLine aLine;
-            if( aTopRightProp.getProperty( aLine, PROP_RightBorder ) )
-                aTopLeftProp.setProperty( PROP_RightBorder, aLine );
-        }
-
-        // copy bottom border of bottom-left cell to bottom border of top-left cell
-        if( bMultiRow )
-        {
-            PropertySet aBottomLeftProp( getCell( CellAddress( getSheetIndex(), rRange.StartColumn, rRange.EndRow ) ) );
-            BorderLine aLine;
-            if( aBottomLeftProp.getProperty( aLine, PROP_BottomBorder ) )
-                aTopLeftProp.setProperty( PROP_BottomBorder, aLine );
-        }
-
-        // #i93609# merged range in a single row: test if manual row height is needed
-        if( !bMultiRow )
-        {
-            bool bTextWrap = aTopLeftProp.getBoolProperty( PROP_IsTextWrapped );
-            if( !bTextWrap && (xTopLeft->getType() == CellContentType_TEXT) )
-            {
-                Reference< XText > xText( xTopLeft, UNO_QUERY );
-                bTextWrap = xText.is() && (xText->getString().indexOf( '\x0A' ) >= 0);
-            }
-            if( bTextWrap )
-                setManualRowHeight( rRange.StartRow );
-        }
-    }
-    catch( Exception& )
-    {
-    }
-}
-
 // ============================================================================
 
 } // namespace xls

oox/source/xls/sheetdatacontext.cxx

     RowModel aModel;
     aModel.mnRow          = rAttribs.getInteger( XML_r, -1 );
     aModel.mfHeight       = rAttribs.getDouble( XML_ht, -1.0 );
-    aModel.mnXfId         = rAttribs.getInteger( XML_s, -1 );
+    aModel.mnXfId         = rAttribs.getInteger( XML_s, 0 );
     aModel.mnLevel        = rAttribs.getInteger( XML_outlineLevel, 0 );
     aModel.mbCustomHeight = rAttribs.getBool( XML_customHeight, false );
     aModel.mbCustomFormat = rAttribs.getBool( XML_customFormat, false );
         {
             // OOXML uses 1-based integer column indexes, row model expects 0-based colspans
             sal_Int32 nLastCol = ::std::min( aColSpanToken.copy( nSepPos + 1 ).toInt32() - 1, nMaxCol );
-            aModel.insertColSpan( ValueRange( aColSpanToken.copy( 0, nSepPos ).toInt32() - 1, nLastCol ) );
+            aModel.insertColSpan( aColSpanToken.copy( 0, nSepPos ).toInt32() - 1, nLastCol );
         }
     }
 
     if( bValidAddr )
     {
         maCellData.mnCellType     = rAttribs.getToken( XML_t, XML_n );
-        maCellData.mnXfId         = rAttribs.getInteger( XML_s, -1 );
+        maCellData.mnXfId         = rAttribs.getInteger( XML_s, 0 );
         maCellData.mbShowPhonetic = rAttribs.getBool( XML_ph, false );
 
         // reset cell value, formula settings, and inline string
     {
         sal_Int32 nFirstCol, nLastCol;
         rStrm >> nFirstCol >> nLastCol;
-        aModel.insertColSpan( ValueRange( nFirstCol, ::std::min( nLastCol, nMaxCol ) ) );
+        aModel.insertColSpan( nFirstCol, ::std::min( nLastCol, nMaxCol ) );
     }
 
     // set row properties in the current sheet
     if( nFirstUsedCol < nFirstFreeCol )
     {
         sal_Int32 nLastCol = ::std::min< sal_Int32 >( nFirstFreeCol - 1, mrAddressConv.getMaxApiAddress().Column );
-        aModel.insertColSpan( ValueRange( nFirstUsedCol, nLastCol ) );
+        aModel.insertColSpan( nFirstUsedCol, nLastCol );
     }
 
     // set row properties in the current sheet

oox/source/xls/stylesbuffer.cxx

         PropertySet aPropSet( xStyle );
         getStyles().writeStyleXfToPropertySet( aPropSet, maModel.mnXfId );
         if( !maModel.isDefaultStyle() )
-            xStyle->setParentStyle( getStyles().getDefaultStyleName() );
+        {
+            OUString aDefStyle = getStyles().getDefaultStyleName();
+            if( aDefStyle.getLength() > 0 )
+                xStyle->setParentStyle( aDefStyle );
+        }
     }
     catch( Exception& )
     {
 void CellStyle::finalizeImport( const OUString& rFinalName )
 {
     maFinalName = rFinalName;
-    if( !maModel.isBuiltin() || maModel.mbCustom )
+    if( !maModel.isBuiltin() || maModel.isDefaultStyle() || maModel.mbCustom )
         createCellStyle();
 }
 
     typedef RefMap< OUString, CellStyle, IgnoreCaseCompare > CellStyleNameMap;
     CellStyleNameMap aCellStyles;
     CellStyleVector aConflictNameStyles;
+    OUString aDefStyleName;
 
     /*  First, reserve style names that are built-in in Calc. This causes that
         imported cell styles get different unused names and thus do not try to
             aConflictNameStyles.push_back( *aIt );
         else
             aCellStyles[ aStyleName ] = *aIt;
+        if( rModel.isDefaultStyle() )
+            aDefStyleName = aStyleName;
     }
 
     /*  Calculate names of user defined styles. Store styles with reserved
         }
         while( aCellStyles.count( aUnusedName ) > 0 );
         aCellStyles[ aUnusedName ] = *aIt;
+        if( rModel.isDefaultStyle() )
+            aDefStyleName = aUnusedName;
     }
 
+    // first set final name of the default style (its final name is needed by other styles)
+    if( mxDefStyle.get() )
+        mxDefStyle->finalizeImport( aDefStyleName );
+    aCellStyles.erase( aDefStyleName );
     // set final names and create user-defined and modified built-in cell styles
     aCellStyles.forEachMemWithKey( &CellStyle::finalizeImport );
 }
 
-sal_Int32 CellStyleBuffer::getDefaultXfId() const
+sal_Int32 CellStyleBuffer::getDefaultStyleXfId() const
 {
     return mxDefStyle.get() ? mxDefStyle->getModel().mnXfId : -1;
 }
 
 OUString CellStyleBuffer::getDefaultStyleName() const
 {
-    return createCellStyle( mxDefStyle );
+    return mxDefStyle.get() ? mxDefStyle->getFinalStyleName() : OUString();
 }
 
 OUString CellStyleBuffer::createCellStyle( sal_Int32 nXfId ) const
     WorkbookHelper( rHelper ),
     maPalette( rHelper ),
     maNumFmts( rHelper ),
-    maCellStyles( rHelper )
+    maCellStyles( rHelper ),
+    mnDefCellXfId( -1 )
 {
 }
 
 
 XfRef StylesBuffer::createCellXf( sal_Int32* opnXfId )
 {
-    if( opnXfId ) *opnXfId = static_cast< sal_Int32 >( maCellXfs.size() );
+    sal_Int32 nXfId = static_cast< sal_Int32 >( maCellXfs.size() );
+    if( opnXfId ) *opnXfId = nXfId;
+    if( mnDefCellXfId < 0 ) mnDefCellXfId = nXfId;
     XfRef xXf( new Xf( *this ) );
     maCellXfs.push_back( xXf );
     return xXf;
 FontRef StylesBuffer::getDefaultFont() const
 {
     FontRef xDefFont;
-    if( const Xf* pXf = getStyleXf( maCellStyles.getDefaultXfId() ).get() )
+    if( const Xf* pXf = getStyleXf( maCellStyles.getDefaultStyleXfId() ).get() )
         xDefFont = pXf->getFont();
     // no font from styles - try first loaded font (e.g. BIFF2)
     if( !xDefFont )
 
 OUString StylesBuffer::createDxfStyle( sal_Int32 nDxfId ) const
 {
+    DxfStyleMap::iterator aIt = maDxfStyles.find( nDxfId );
+    if( aIt != maDxfStyles.end() )
+        return aIt->second;
+
     OUString& rStyleName = maDxfStyles[ nDxfId ];
-    if( rStyleName.getLength() == 0 )
+    if( Dxf* pDxf = maDxfs.get( nDxfId ).get() )
     {
-        if( Dxf* pDxf = maDxfs.get( nDxfId ).get() )
-        {
-            rStyleName = OUStringBuffer( CREATE_OUSTRING( "ConditionalStyle_" ) ).append( nDxfId + 1 ).makeStringAndClear();
-            // create the style sheet (this may change rStyleName if such a style already exists)
-            Reference< XStyle > xStyle = createStyleObject( rStyleName, false );
-            // write style formatting properties
-            PropertySet aPropSet( xStyle );
-            pDxf->writeToPropertySet( aPropSet );
-        }
-        // on error: fallback to default style
-        if( rStyleName.getLength() == 0 )
-            rStyleName = maCellStyles.getDefaultStyleName();
+        rStyleName = OUStringBuffer( CREATE_OUSTRING( "ConditionalStyle_" ) ).append( nDxfId + 1 ).makeStringAndClear();
+        // create the style sheet (this may change rStyleName if such a style already exists)
+        Reference< XStyle > xStyle = createStyleObject( rStyleName, false );
+        // write style formatting properties
+        PropertySet aPropSet( xStyle );
+        pDxf->writeToPropertySet( aPropSet );
     }
     return rStyleName;
 }

oox/source/xls/workbookhelper.cxx

 
 #include "oox/xls/workbookhelper.hxx"
 
+#include <stdio.h>
 #include <com/sun/star/container/XIndexAccess.hpp>
 #include <com/sun/star/container/XNameContainer.hpp>
 #include <com/sun/star/document/XActionLockable.hpp>

oox/source/xls/worksheetfragment.cxx

 void WorksheetFragment::importCol( const AttributeList& rAttribs )
 {
     ColumnModel aModel;
-    aModel.maRange.mnFirst = rAttribs.getInteger( XML_min, -1 );
-    aModel.maRange.mnLast  = rAttribs.getInteger( XML_max, -1 );
+    aModel.maRange.first   = rAttribs.getInteger( XML_min, -1 );
+    aModel.maRange.second  = rAttribs.getInteger( XML_max, -1 );
     aModel.mfWidth         = rAttribs.getDouble( XML_width, 0.0 );
     aModel.mnXfId          = rAttribs.getInteger( XML_style, -1 );
     aModel.mnLevel         = rAttribs.getInteger( XML_outlineLevel, 0 );
 
     sal_Int32 nWidth;
     sal_uInt16 nFlags;
-    rStrm >> aModel.maRange.mnFirst >> aModel.maRange.mnLast >> nWidth >> aModel.mnXfId >> nFlags;
+    rStrm >> aModel.maRange.first >> aModel.maRange.second >> nWidth >> aModel.mnXfId >> nFlags;
 
     // column indexes are 0-based in BIFF12, but ColumnModel expects 1-based
-    ++aModel.maRange.mnFirst;
-    ++aModel.maRange.mnLast;
+    ++aModel.maRange.first;
+    ++aModel.maRange.second;
     // width is stored as 1/256th of a character in BIFF12, convert to entire character
     aModel.mfWidth        = static_cast< double >( nWidth ) / 256.0;
     // equal flags in all BIFFs
 
     ColumnModel aModel;
     // column indexes are 0-based in BIFF, but ColumnModel expects 1-based
-    aModel.maRange.mnFirst = static_cast< sal_Int32 >( nFirstCol ) + 1;
-    aModel.maRange.mnLast  = static_cast< sal_Int32 >( nLastCol ) + 1;
+    aModel.maRange.first   = static_cast< sal_Int32 >( nFirstCol ) + 1;
+    aModel.maRange.second  = static_cast< sal_Int32 >( nLastCol ) + 1;
     // width is stored as 1/256th of a character in BIFF, convert to entire character
     aModel.mfWidth         = static_cast< double >( nWidth ) / 256.0;
     aModel.mnXfId          = nXfId;
 {
     sal_uInt16 nFirstCol, nLastCol, nXfId;
     rStrm >> nFirstCol >> nLastCol >> nXfId;
-    setDefaultColumnFormat( nFirstCol, nLastCol, nXfId );
+    getSheetData().setDefaultColumnFormat( ColRowRange( nFirstCol, nLastCol ), nXfId );
 }
 
 void BiffWorksheetFragment::importColWidth( BiffInputStream& rStrm )
 
     ColumnModel aModel;
     // column indexes are 0-based in BIFF, but ColumnModel expects 1-based
-    aModel.maRange.mnFirst = static_cast< sal_Int32 >( nFirstCol ) + 1;
-    aModel.maRange.mnLast = static_cast< sal_Int32 >( nLastCol ) + 1;
+    aModel.maRange.first  = static_cast< sal_Int32 >( nFirstCol ) + 1;
+    aModel.maRange.second = static_cast< sal_Int32 >( nLastCol ) + 1;
     // width is stored as 1/256th of a character in BIFF, convert to entire character
     aModel.mfWidth = static_cast< double >( nWidth ) / 256.0;
     // set column properties in the current sheet

oox/source/xls/worksheethelper.cxx

 #include <com/sun/star/text/XText.hpp>
 #include <rtl/ustrbuf.hxx>
 #include "oox/core/filterbase.hxx"
+#include "oox/helper/containerhelper.hxx"
 #include "oox/helper/propertyset.hxx"
 #include "oox/xls/addressconverter.hxx"
 #include "oox/xls/autofilterbuffer.hxx"
         rxProgressBar->setPosition( fPosition );
 }
 
+// ----------------------------------------------------------------------------
+
+/** Functor comparing two column models for equality, ignores the column range
+    and column default formatting.
+ */
+struct ColumnModelComp
+{
+    bool                operator()( const ColumnModel& rModel1, const ColumnModel& rModel2 ) const;
+};
+
+bool ColumnModelComp::operator()( const ColumnModel& rModel1, const ColumnModel& rModel2 ) const
+{
+    return
+        // ignore maRange
+        // ignore mnXfId, mbShowPhonetic; cell formatting is set seperately
+        (rModel1.mfWidth                == rModel2.mfWidth) &&
+        (rModel1.mnLevel                == rModel2.mnLevel) &&
+        (rModel1.mbHidden               == rModel2.mbHidden) &&
+        (rModel1.mbCollapsed            == rModel2.mbCollapsed);
+}
+
+// ----------------------------------------------------------------------------
+
+/** Functor comparing two row models for equality, ignores the row index and
+    row default formatting.
+ */
+struct RowModelComp
+{
+    bool                operator()( const RowModel& rModel1, const RowModel& rModel2 ) const;
+};
+
+bool RowModelComp::operator()( const RowModel& rModel1, const RowModel& rModel2 ) const
+{
+    return
+        // ignore mnRow
+        // ignore maColSpans; handled separately in SheetDataBuffer class
+        // ignore mnXfId, mbCustomFormat, mbShowPhonetic; cell formatting is set seperately
+        (rModel1.mfHeight       == rModel2.mfHeight) &&
+        (rModel1.mnLevel        == rModel2.mnLevel) &&
+        (rModel1.mbCustomHeight == rModel2.mbCustomHeight) &&
+        (rModel1.mbHidden       == rModel2.mbHidden) &&
+        (rModel1.mbCollapsed    == rModel2.mbCollapsed);
+        // TODO: mbThickTop, mbThickBottom; not used yet
+}
+
 } // namespace
 
 // ============================================================================
 // ============================================================================
 
 ColumnModel::ColumnModel() :
-    maRange( -1 ),
+    maRange( -1, -1 ),
     mfWidth( 0.0 ),
     mnXfId( -1 ),
     mnLevel( 0 ),
 {
 }
 
-bool ColumnModel::isMergeable( const ColumnModel& rModel ) const
-{
-    return
-        (maRange.mnFirst        <= rModel.maRange.mnFirst) &&
-        (rModel.maRange.mnFirst <= maRange.mnLast + 1) &&
-        (mfWidth                == rModel.mfWidth) &&
-        // ignore mnXfId, cell formatting is always set directly
-        (mnLevel                == rModel.mnLevel) &&
-        (mbHidden               == rModel.mbHidden) &&
-        (mbCollapsed            == rModel.mbCollapsed);
-}
-
 // ----------------------------------------------------------------------------
 
 RowModel::RowModel() :
 {
 }
 
-void RowModel::insertColSpan( const ValueRange& rColSpan )
+void RowModel::insertColSpan( sal_Int32 nFirstCol, sal_Int32 nLastCol )
 {
-    if( (0 <= rColSpan.mnFirst) && (rColSpan.mnFirst <= rColSpan.mnLast) )
-        maColSpans.insert( rColSpan );
-}
-
-bool RowModel::isMergeable( const RowModel& rModel ) const
-{
-    return
-        // ignore maColSpans - is handled separately in SheetDataBuffer class
-        (mfHeight       == rModel.mfHeight) &&
-        // ignore mnXfId, mbCustomFormat, mbShowPhonetic - cell formatting is always set directly
-        (mnLevel        == rModel.mnLevel) &&
-        (mbCustomHeight == rModel.mbCustomHeight) &&
-        (mbHidden       == rModel.mbHidden) &&
-        (mbCollapsed    == rModel.mbCollapsed);
+    if( (0 <= nFirstCol) && (nFirstCol <= nLastCol) )
+        maColSpans.insert( ColRowRange( nFirstCol, nLastCol ) );
 }
 
 // ----------------------------------------------------------------------------
     Reference< XCellRange > getRow( sal_Int32 nRow ) const;
 
     /** Returns the XTableColumns interface for a range of columns. */
-    Reference< XTableColumns > getColumns( const ValueRange& rColRange ) const;
+    Reference< XTableColumns > getColumns( const ColRowRange& rColRange ) const;
     /** Returns the XTableRows interface for a range of rows. */
-    Reference< XTableRows > getRows( const ValueRange& rRowRange ) const;
+    Reference< XTableRows > getRows( const ColRowRange& rRowRange ) const;
 
     /** Returns the XDrawPage interface of the draw page of the current sheet. */
     Reference< XDrawPage > getDrawPage() const;
         @descr  Column default formatting is converted directly, other settings
         are cached and converted in the finalizeImport() call. */
     void                setColumnModel( const ColumnModel& rModel );
-    /** Converts column default cell formatting. */
-    void                convertColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId ) const;
 
     /** Sets default height and hidden state for all unused rows in the sheet. */
     void                setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom );
     void                finalizeWorksheetImport();
 
 private:
-    typedef ::std::vector< sal_Int32 >                  OutlineLevelVec;
-    typedef ::std::pair< ColumnModel, sal_Int32 >       ColumnModelRange;
-    typedef ::std::map< sal_Int32, ColumnModelRange >   ColumnModelRangeMap;
-    typedef ::std::pair< RowModel, sal_Int32 >          RowModelRange;
-    typedef ::std::map< sal_Int32, RowModelRange >      RowModelRangeMap;
-    typedef ::std::list< HyperlinkModel >               HyperlinkModelList;
-    typedef ::std::list< ValidationModel >              ValidationModelList;
+    typedef ::std::vector< sal_Int32 >                                      OutlineLevelVector;
+    typedef ::o3tl::interval_map< sal_Int32, ColumnModel, ColumnModelComp > ColumnModelMap;
+    typedef ::o3tl::interval_map< sal_Int32, RowModel, RowModelComp >       RowModelMap;
+    typedef ::std::list< HyperlinkModel >                                   HyperlinkModelList;
+    typedef ::std::list< ValidationModel >                                  ValidationModelList;
 
     /** Inserts all imported hyperlinks into their cell ranges. */
     void                finalizeHyperlinkRanges() const;
     /** Converts column properties for all columns in the sheet. */
     void                convertColumns();
     /** Converts column properties. */
-    void                convertColumns( OutlineLevelVec& orColLevels, const ValueRange& rColRange, const ColumnModel& rModel );
+    void                convertColumns( OutlineLevelVector& orColLevels, const ColRowRange& rColRange, const ColumnModel& rModel );
 
     /** Converts row properties for all rows in the sheet. */
     void                convertRows();
     /** Converts row properties. */
-    void                convertRows( OutlineLevelVec& orRowLevels, const ValueRange& rRowRange, const RowModel& rModel, double fDefHeight = -1.0 );
+    void                convertRows( OutlineLevelVector& orRowLevels, const ColRowRange& rRowRange, const RowModel& rModel, double fDefHeight = -1.0 );
 
     /** Converts outline grouping for the passed column or row. */
-    void                convertOutlines( OutlineLevelVec& orLevels, sal_Int32 nColRow, sal_Int32 nLevel, bool bCollapsed, bool bRows );
+    void                convertOutlines( OutlineLevelVector& orLevels, sal_Int32 nColRow, sal_Int32 nLevel, bool bCollapsed, bool bRows );
     /** Groups columns or rows for the given range. */
     void                groupColumnsOrRows( sal_Int32 nFirstColRow, sal_Int32 nLastColRow, bool bCollapsed, bool bRows );
 
     const CellAddress&  mrMaxApiPos;        /// Reference to maximum Calc cell address from address converter.
     CellRangeAddress    maUsedArea;         /// Used area of the sheet, and sheet index of the sheet.
     ColumnModel         maDefColModel;      /// Default column formatting.
-    ColumnModelRangeMap maColModels;        /// Ranges of columns sorted by first column index.
+    ColumnModelMap      maColModels;        /// Ranges of column models sorted by column index.
     RowModel            maDefRowModel;      /// Default row formatting.
-    RowModelRangeMap    maRowModels;        /// Ranges of rows sorted by first row index.
+    RowModelMap         maRowModels;        /// Ranges of row models sorted by row index.
     HyperlinkModelList  maHyperlinks;       /// Cell ranges containing hyperlinks.
     ValidationModelList maValidations;      /// Cell ranges containing data validation settings.
-    ValueRangeSet       maManualRowHeights; /// Rows that need manual height independent from own settings.
+    ColRowSet           maManualRowHeights; /// Rows that need manual height independent from own settings.
     SheetDataBuffer     maSheetData;        /// Buffer for cell contents and cell formatting.
     CondFormatBuffer    maCondFormats;      /// Buffer for conditional formattings.
     CommentsBuffer      maComments;         /// Buffer for all cell comments in this sheet.
     return xRow;
 }
 
-Reference< XTableColumns > WorksheetGlobals::getColumns( const ValueRange& rColRange ) const
+Reference< XTableColumns > WorksheetGlobals::getColumns( const ColRowRange& rColRange ) const
 {
     Reference< XTableColumns > xColumns;
-    sal_Int32 nLastCol = ::std::min( rColRange.mnLast, mrMaxApiPos.Column );
-    if( (0 <= rColRange.mnFirst) && (rColRange.mnFirst <= nLastCol) )
+    sal_Int32 nLastCol = ::std::min( rColRange.second, mrMaxApiPos.Column );
+    if( (0 <= rColRange.first) && (rColRange.first <= nLastCol) )
     {
-        Reference< XColumnRowRange > xRange( getCellRange( CellRangeAddress( getSheetIndex(), rColRange.mnFirst, 0, nLastCol, 0 ) ), UNO_QUERY );
+        Reference< XColumnRowRange > xRange( getCellRange( CellRangeAddress( getSheetIndex(), rColRange.first, 0, nLastCol, 0 ) ), UNO_QUERY );
         if( xRange.is() )
             xColumns = xRange->getColumns();
     }
     return xColumns;
 }
 
-Reference< XTableRows > WorksheetGlobals::getRows( const ValueRange& rRowRange ) const
+Reference< XTableRows > WorksheetGlobals::getRows( const ColRowRange& rRowRange ) const
 {
     Reference< XTableRows > xRows;
-    sal_Int32 nLastRow = ::std::min( rRowRange.mnLast, mrMaxApiPos.Row );