summaryrefslogtreecommitdiffstats
path: root/sc/source/filter/excel
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sc/source/filter/excel
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sc/source/filter/excel')
-rw-r--r--sc/source/filter/excel/colrowst.cxx359
-rw-r--r--sc/source/filter/excel/excdoc.cxx905
-rw-r--r--sc/source/filter/excel/excel.cxx494
-rw-r--r--sc/source/filter/excel/excform.cxx1911
-rw-r--r--sc/source/filter/excel/excform8.cxx1671
-rw-r--r--sc/source/filter/excel/excimp8.cxx817
-rw-r--r--sc/source/filter/excel/excrecds.cxx1177
-rw-r--r--sc/source/filter/excel/exctools.cxx245
-rw-r--r--sc/source/filter/excel/expop2.cxx150
-rw-r--r--sc/source/filter/excel/export/SparklineExt.cxx243
-rw-r--r--sc/source/filter/excel/fontbuff.cxx130
-rw-r--r--sc/source/filter/excel/frmbase.cxx209
-rw-r--r--sc/source/filter/excel/impop.cxx1414
-rw-r--r--sc/source/filter/excel/namebuff.cxx187
-rw-r--r--sc/source/filter/excel/ooxml-export-TODO.txt164
-rw-r--r--sc/source/filter/excel/read.cxx1314
-rw-r--r--sc/source/filter/excel/tokstack.cxx752
-rw-r--r--sc/source/filter/excel/xechart.cxx3475
-rw-r--r--sc/source/filter/excel/xecontent.cxx2211
-rw-r--r--sc/source/filter/excel/xedbdata.cxx261
-rw-r--r--sc/source/filter/excel/xeescher.cxx2046
-rw-r--r--sc/source/filter/excel/xeextlst.cxx652
-rw-r--r--sc/source/filter/excel/xeformula.cxx2709
-rw-r--r--sc/source/filter/excel/xehelper.cxx1071
-rw-r--r--sc/source/filter/excel/xelink.cxx2660
-rw-r--r--sc/source/filter/excel/xename.cxx870
-rw-r--r--sc/source/filter/excel/xepage.cxx512
-rw-r--r--sc/source/filter/excel/xepivot.cxx1702
-rw-r--r--sc/source/filter/excel/xepivotxml.cxx1187
-rw-r--r--sc/source/filter/excel/xerecord.cxx264
-rw-r--r--sc/source/filter/excel/xeroot.cxx360
-rw-r--r--sc/source/filter/excel/xestream.cxx1259
-rw-r--r--sc/source/filter/excel/xestring.cxx565
-rw-r--r--sc/source/filter/excel/xestyle.cxx3306
-rw-r--r--sc/source/filter/excel/xetable.cxx2841
-rw-r--r--sc/source/filter/excel/xeview.cxx537
-rw-r--r--sc/source/filter/excel/xichart.cxx4415
-rw-r--r--sc/source/filter/excel/xicontent.cxx1442
-rw-r--r--sc/source/filter/excel/xiescher.cxx4453
-rw-r--r--sc/source/filter/excel/xiformula.cxx107
-rw-r--r--sc/source/filter/excel/xihelper.cxx896
-rw-r--r--sc/source/filter/excel/xilink.cxx962
-rw-r--r--sc/source/filter/excel/xiname.cxx322
-rw-r--r--sc/source/filter/excel/xipage.cxx401
-rw-r--r--sc/source/filter/excel/xipivot.cxx1737
-rw-r--r--sc/source/filter/excel/xiroot.cxx298
-rw-r--r--sc/source/filter/excel/xistream.cxx1084
-rw-r--r--sc/source/filter/excel/xistring.cxx212
-rw-r--r--sc/source/filter/excel/xistyle.cxx2084
-rw-r--r--sc/source/filter/excel/xiview.cxx300
-rw-r--r--sc/source/filter/excel/xladdress.cxx161
-rw-r--r--sc/source/filter/excel/xlchart.cxx1283
-rw-r--r--sc/source/filter/excel/xlescher.cxx335
-rw-r--r--sc/source/filter/excel/xlformula.cxx1022
-rw-r--r--sc/source/filter/excel/xlpage.cxx262
-rw-r--r--sc/source/filter/excel/xlpivot.cxx1043
-rw-r--r--sc/source/filter/excel/xlroot.cxx438
-rw-r--r--sc/source/filter/excel/xlstyle.cxx1744
-rw-r--r--sc/source/filter/excel/xltoolbar.cxx439
-rw-r--r--sc/source/filter/excel/xltoolbar.hxx106
-rw-r--r--sc/source/filter/excel/xltools.cxx744
-rw-r--r--sc/source/filter/excel/xltracer.cxx133
-rw-r--r--sc/source/filter/excel/xlview.cxx103
63 files changed, 67156 insertions, 0 deletions
diff --git a/sc/source/filter/excel/colrowst.cxx b/sc/source/filter/excel/colrowst.cxx
new file mode 100644
index 000000000..e194b7309
--- /dev/null
+++ b/sc/source/filter/excel/colrowst.cxx
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <colrowst.hxx>
+
+#include <document.hxx>
+#include <ftools.hxx>
+#include <xltable.hxx>
+#include <xistyle.hxx>
+#include <excimp8.hxx>
+#include <table.hxx>
+
+XclImpColRowSettings::XclImpColRowSettings( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maColWidths(0, rRoot.GetDoc().GetSheetLimits().GetMaxColCount(), 0),
+ maColFlags(0, rRoot.GetDoc().GetSheetLimits().GetMaxColCount(), ExcColRowFlags::NONE),
+ maRowHeights(0, rRoot.GetDoc().GetSheetLimits().GetMaxRowCount(), 0),
+ maRowFlags(0, rRoot.GetDoc().GetSheetLimits().GetMaxRowCount(), ExcColRowFlags::NONE),
+ maHiddenRows(0, rRoot.GetDoc().GetSheetLimits().GetMaxRowCount(), false),
+ mnLastScRow( -1 ),
+ mnDefWidth( STD_COL_WIDTH ),
+ mnDefHeight( ScGlobal::nStdRowHeight ),
+ mnDefRowFlags( EXC_DEFROW_DEFAULTFLAGS ),
+ mbHasStdWidthRec( false ),
+ mbHasDefHeight( false ),
+ mbDirty( true )
+{
+}
+
+XclImpColRowSettings::~XclImpColRowSettings()
+{
+}
+
+void XclImpColRowSettings::SetDefWidth( sal_uInt16 nDefWidth, bool bStdWidthRec )
+{
+ if( bStdWidthRec )
+ {
+ // STANDARDWIDTH record overrides DEFCOLWIDTH record
+ mnDefWidth = nDefWidth;
+ mbHasStdWidthRec = true;
+ }
+ else if( !mbHasStdWidthRec )
+ {
+ // use DEFCOLWIDTH record only, if no STANDARDWIDTH record exists
+ mnDefWidth = nDefWidth;
+ }
+}
+
+void XclImpColRowSettings::SetWidthRange( SCCOL nCol1, SCCOL nCol2, sal_uInt16 nWidth )
+{
+ ScDocument& rDoc = GetDoc();
+ nCol2 = ::std::min( nCol2, rDoc.MaxCol() );
+ if (nCol2 == 256)
+ // In BIFF8, the column range is 0-255, and the use of 256 probably
+ // means the range should extend to the max column if the loading app
+ // support columns beyond 255.
+ nCol2 = rDoc.MaxCol();
+
+ nCol1 = ::std::min( nCol1, nCol2 );
+ maColWidths.insert_back(nCol1, nCol2+1, nWidth);
+
+ // We need to apply flag values individually since all flag values are aggregated for each column.
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ ApplyColFlag(nCol, ExcColRowFlags::Used);
+}
+
+void XclImpColRowSettings::HideCol( SCCOL nCol )
+{
+ if (!GetDoc().ValidCol(nCol))
+ return;
+
+ ApplyColFlag(nCol, ExcColRowFlags::Hidden);
+}
+
+void XclImpColRowSettings::HideColRange( SCCOL nCol1, SCCOL nCol2 )
+{
+ nCol2 = ::std::min( nCol2, GetDoc().MaxCol() );
+ nCol1 = ::std::min( nCol1, nCol2 );
+
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ ApplyColFlag(nCol, ExcColRowFlags::Hidden);
+}
+
+void XclImpColRowSettings::SetDefHeight( sal_uInt16 nDefHeight, sal_uInt16 nFlags )
+{
+ mnDefHeight = nDefHeight;
+ mnDefRowFlags = nFlags;
+ if( mnDefHeight == 0 )
+ {
+ mnDefHeight = ScGlobal::nStdRowHeight;
+ ::set_flag( mnDefRowFlags, EXC_DEFROW_HIDDEN );
+ }
+ mbHasDefHeight = true;
+}
+
+void XclImpColRowSettings::SetHeight( SCROW nScRow, sal_uInt16 nHeight )
+{
+ if (!GetDoc().ValidRow(nScRow))
+ return;
+
+ sal_uInt16 nRawHeight = nHeight & EXC_ROW_HEIGHTMASK;
+ bool bDefHeight = ::get_flag( nHeight, EXC_ROW_FLAGDEFHEIGHT ) || (nRawHeight == 0);
+ maRowHeights.insert_back(nScRow, nScRow+1, nRawHeight);
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ if (!maRowFlags.search(nScRow, nFlagVal).second)
+ return;
+
+ ::set_flag(nFlagVal, ExcColRowFlags::Used);
+ ::set_flag(nFlagVal, ExcColRowFlags::Default, bDefHeight);
+
+ maRowFlags.insert_back(nScRow, nScRow+1, nFlagVal);
+
+ if (nScRow > mnLastScRow)
+ mnLastScRow = nScRow;
+}
+
+void XclImpColRowSettings::SetRowSettings( SCROW nScRow, sal_uInt16 nHeight, sal_uInt16 nFlags )
+{
+ if (!GetDoc().ValidRow(nScRow))
+ return;
+
+ SetHeight(nScRow, nHeight);
+
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ if (!maRowFlags.search(nScRow, nFlagVal).second)
+ return;
+
+ if (::get_flag(nFlags, EXC_ROW_UNSYNCED))
+ ::set_flag(nFlagVal, ExcColRowFlags::Man);
+
+ maRowFlags.insert_back(nScRow, nScRow+1, nFlagVal);
+
+ if (::get_flag(nFlags, EXC_ROW_HIDDEN))
+ maHiddenRows.insert_back(nScRow, nScRow+1, true);
+}
+
+void XclImpColRowSettings::SetManualRowHeight( SCROW nScRow )
+{
+ if (!GetDoc().ValidRow(nScRow))
+ return;
+
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ if (!maRowFlags.search(nScRow, nFlagVal).second)
+ return;
+
+ nFlagVal |= ExcColRowFlags::Man;
+ maRowFlags.insert_back(nScRow, nScRow+1, nFlagVal);
+}
+
+void XclImpColRowSettings::SetDefaultXF( SCCOL nCol1, SCCOL nCol2, sal_uInt16 nXFIndex )
+{
+ /* assign the default column formatting here to ensure that
+ explicit cell formatting is not overwritten. */
+ OSL_ENSURE( (nCol1 <= nCol2) && GetDoc().ValidCol( nCol2 ), "XclImpColRowSettings::SetDefaultXF - invalid column index" );
+ nCol2 = ::std::min( nCol2, GetDoc().MaxCol() );
+ nCol1 = ::std::min( nCol1, nCol2 );
+ XclImpXFRangeBuffer& rXFRangeBuffer = GetXFRangeBuffer();
+ for( SCCOL nCol = nCol1; nCol <= nCol2; ++nCol )
+ rXFRangeBuffer.SetColumnDefXF( nCol, nXFIndex );
+}
+
+void XclImpColRowSettings::Convert( SCTAB nScTab )
+{
+ if( !mbDirty )
+ return;
+
+ ScDocument& rDoc = GetDoc();
+
+ // column widths ----------------------------------------------------------
+
+ maColWidths.build_tree();
+ for (SCCOL nCol = 0; nCol <= rDoc.MaxCol(); ++nCol)
+ {
+ sal_uInt16 nWidth = mnDefWidth;
+ if (GetColFlag(nCol, ExcColRowFlags::Used))
+ {
+ sal_uInt16 nTmp;
+ if (maColWidths.search_tree(nCol, nTmp).second)
+ nWidth = nTmp;
+ }
+
+ /* Hidden columns: remember hidden state, but do not set hidden state
+ in document here. Needed for #i11776#, no HIDDEN flags in the
+ document, until filters and outlines are inserted. */
+ if( nWidth == 0 )
+ {
+ ApplyColFlag(nCol, ExcColRowFlags::Hidden);
+ nWidth = mnDefWidth;
+ }
+ rDoc.SetColWidthOnly( nCol, nScTab, nWidth );
+ }
+
+ // row heights ------------------------------------------------------------
+
+ // #i54252# set default row height
+ rDoc.SetRowHeightOnly( 0, rDoc.MaxRow(), nScTab, mnDefHeight );
+ if( ::get_flag( mnDefRowFlags, EXC_DEFROW_UNSYNCED ) )
+ // first access to row flags, do not ask for old flags
+ rDoc.SetRowFlags( 0, rDoc.MaxRow(), nScTab, CRFlags::ManualSize );
+
+ maRowHeights.build_tree();
+ if (!maRowHeights.is_tree_valid())
+ return;
+
+ SCROW nPrevRow = -1;
+ ExcColRowFlags nPrevFlags = ExcColRowFlags::NONE;
+ for (const auto& [nRow, nFlags] : maRowFlags)
+ {
+ if (nPrevRow >= 0)
+ {
+ sal_uInt16 nHeight = 0;
+
+ if (nPrevFlags & ExcColRowFlags::Used)
+ {
+ if (nPrevFlags & ExcColRowFlags::Default)
+ {
+ nHeight = mnDefHeight;
+ rDoc.SetRowHeightOnly(nPrevRow, nRow-1, nScTab, nHeight);
+ }
+ else
+ {
+ for (SCROW i = nPrevRow; i <= nRow - 1; ++i)
+ {
+ SCROW nLast;
+ if (!maRowHeights.search_tree(i, nHeight, nullptr, &nLast).second)
+ {
+ // search failed for some reason
+ return;
+ }
+
+ if (nLast > nRow)
+ nLast = nRow;
+
+ rDoc.SetRowHeightOnly(i, nLast-1, nScTab, nHeight);
+ i = nLast-1;
+ }
+ }
+
+ if (nPrevFlags & ExcColRowFlags::Man)
+ rDoc.SetManualHeight(nPrevRow, nRow-1, nScTab, true);
+ }
+ else
+ {
+ nHeight = mnDefHeight;
+ rDoc.SetRowHeightOnly(nPrevRow, nRow-1, nScTab, nHeight);
+ }
+ }
+
+ nPrevRow = nRow;
+ nPrevFlags = nFlags;
+ }
+
+ mbDirty = false;
+}
+
+void XclImpColRowSettings::ConvertHiddenFlags( SCTAB nScTab )
+{
+ ScDocument& rDoc = GetDoc();
+
+ // hide the columns
+ for( SCCOL nCol : rDoc.GetColumnsRange(nScTab, 0, rDoc.MaxCol()) )
+ if (GetColFlag(nCol, ExcColRowFlags::Hidden))
+ rDoc.ShowCol( nCol, nScTab, false );
+
+ // #i38093# rows hidden by filter need extra flag
+ SCROW nFirstFilterScRow = SCROW_MAX;
+ SCROW nLastFilterScRow = SCROW_MAX;
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ const XclImpAutoFilterData* pFilter = GetFilterManager().GetByTab( nScTab );
+ // #i70026# use IsFiltered() to set the CRFlags::Filtered flag for active filters only
+ if( pFilter && pFilter->IsActive() && pFilter->IsFiltered() )
+ {
+ nFirstFilterScRow = pFilter->StartRow();
+ nLastFilterScRow = pFilter->EndRow();
+ }
+ }
+
+ // In case the excel row limit is lower than calc's, use the visibility of
+ // the last row and extend it to calc's last row.
+ SCROW nLastXLRow = GetRoot().GetXclMaxPos().Row();
+ if (nLastXLRow < rDoc.MaxRow())
+ {
+ bool bHidden = false;
+ if (!maHiddenRows.search(nLastXLRow, bHidden).second)
+ return;
+
+ maHiddenRows.insert_back(nLastXLRow, GetDoc().GetSheetLimits().GetMaxRowCount(), bHidden);
+ }
+
+ SCROW nPrevRow = -1;
+ bool bPrevHidden = false;
+ for (const auto& [nRow, bHidden] : maHiddenRows)
+ {
+ if (nPrevRow >= 0)
+ {
+ if (bPrevHidden)
+ {
+ rDoc.SetRowHidden(nPrevRow, nRow-1, nScTab, true);
+ // #i38093# rows hidden by filter need extra flag
+ if (nFirstFilterScRow <= nPrevRow && nPrevRow <= nLastFilterScRow)
+ {
+ SCROW nLast = ::std::min(nRow-1, nLastFilterScRow);
+ rDoc.SetRowFiltered(nPrevRow, nLast, nScTab, true);
+ }
+ }
+ }
+
+ nPrevRow = nRow;
+ bPrevHidden = bHidden;
+ }
+
+ // #i47438# if default row format is hidden, hide remaining rows
+ if( ::get_flag( mnDefRowFlags, EXC_DEFROW_HIDDEN ) && (mnLastScRow < rDoc.MaxRow()) )
+ rDoc.ShowRows( mnLastScRow + 1, rDoc.MaxRow(), nScTab, false );
+}
+
+void XclImpColRowSettings::ApplyColFlag(SCCOL nCol, ExcColRowFlags nNewVal)
+{
+ // Get the original flag value.
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ std::pair<ColRowFlagsType::const_iterator,bool> r = maColFlags.search(nCol, nFlagVal);
+ if (!r.second)
+ // Search failed.
+ return;
+
+ ::set_flag(nFlagVal, nNewVal);
+
+ // Re-insert the flag value.
+ maColFlags.insert(r.first, nCol, nCol+1, nFlagVal);
+}
+
+bool XclImpColRowSettings::GetColFlag(SCCOL nCol, ExcColRowFlags nMask) const
+{
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ if (!maColFlags.search(nCol, nFlagVal).second)
+ return false;
+ // Search failed.
+
+ return bool(nFlagVal & nMask);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excdoc.cxx b/sc/source/filter/excel/excdoc.cxx
new file mode 100644
index 000000000..c01dde329
--- /dev/null
+++ b/sc/source/filter/excel/excdoc.cxx
@@ -0,0 +1,905 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/objsh.hxx>
+#include <rtl/ustring.hxx>
+
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <docoptio.hxx>
+#include <tabprotection.hxx>
+#include <postit.hxx>
+#include <root.hxx>
+
+#include <excdoc.hxx>
+#include <xeextlst.hxx>
+#include <biffhelper.hxx>
+
+#include <xcl97rec.hxx>
+#include <xetable.hxx>
+#include <xelink.hxx>
+#include <xepage.hxx>
+#include <xeview.hxx>
+#include <xecontent.hxx>
+#include <xeescher.hxx>
+#include <xepivot.hxx>
+#include <export/SparklineExt.hxx>
+#include <XclExpChangeTrack.hxx>
+#include <xepivotxml.hxx>
+#include <xedbdata.hxx>
+#include <xlcontent.hxx>
+#include <xlname.hxx>
+#include <xllink.hxx>
+#include <xltools.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <o3tl/safeint.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/namespaces.hxx>
+#include <memory>
+
+using namespace oox;
+
+static OUString lcl_GetVbaTabName( SCTAB n )
+{
+ OUString aRet = "__VBA__" + OUString::number( static_cast<sal_uInt16>(n) );
+ return aRet;
+}
+
+static void lcl_AddBookviews( XclExpRecordList<>& aRecList, const ExcTable& self )
+{
+ aRecList.AppendNewRecord( new XclExpXmlStartElementRecord( XML_bookViews ) );
+ aRecList.AppendNewRecord( new XclExpWindow1( self.GetRoot() ) );
+ aRecList.AppendNewRecord( new XclExpXmlEndElementRecord( XML_bookViews ) );
+}
+
+static void lcl_AddCalcPr( XclExpRecordList<>& aRecList, const ExcTable& self )
+{
+ ScDocument& rDoc = self.GetDoc();
+
+ aRecList.AppendNewRecord( new XclExpXmlStartSingleElementRecord( XML_calcPr ) );
+ // OOXTODO: calcCompleted, calcId, calcMode, calcOnSave,
+ // concurrentCalc, concurrentManualCount,
+ // forceFullCalc, fullCalcOnLoad, fullPrecision
+ aRecList.AppendNewRecord( new XclCalccount( rDoc ) );
+ aRecList.AppendNewRecord( new XclRefmode( rDoc ) );
+ aRecList.AppendNewRecord( new XclIteration( rDoc ) );
+ aRecList.AppendNewRecord( new XclDelta( rDoc ) );
+ aRecList.AppendNewRecord( new XclExpBoolRecord(oox::xls::BIFF_ID_SAVERECALC, true) );
+ aRecList.AppendNewRecord( new XclExpXmlEndSingleElementRecord() ); // XML_calcPr
+}
+
+static void lcl_AddWorkbookProtection( XclExpRecordList<>& aRecList, const ExcTable& self )
+{
+ aRecList.AppendNewRecord( new XclExpXmlStartSingleElementRecord( XML_workbookProtection ) );
+
+ const ScDocProtection* pProtect = self.GetDoc().GetDocProtection();
+ if (pProtect && pProtect->isProtected())
+ {
+ aRecList.AppendNewRecord( new XclExpWindowProtection(pProtect->isOptionEnabled(ScDocProtection::WINDOWS)) );
+ aRecList.AppendNewRecord( new XclExpProtection(pProtect->isOptionEnabled(ScDocProtection::STRUCTURE)) );
+ aRecList.AppendNewRecord( new XclExpPassHash(pProtect->getPasswordHash(PASSHASH_XL)) );
+ }
+
+ aRecList.AppendNewRecord( new XclExpXmlEndSingleElementRecord() ); // XML_workbookProtection
+}
+
+static void lcl_AddScenariosAndFilters( XclExpRecordList<>& aRecList, const XclExpRoot& rRoot, SCTAB nScTab )
+{
+ // Scenarios
+ aRecList.AppendNewRecord( new ExcEScenarioManager( rRoot, nScTab ) );
+ // filter
+ aRecList.AppendRecord( rRoot.GetFilterManager().CreateRecord( nScTab ) );
+}
+
+ExcTable::ExcTable( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnScTab( SCTAB_GLOBAL ),
+ nExcTab( EXC_NOTAB ),
+ mxNoteList( new XclExpNoteList )
+{
+}
+
+ExcTable::ExcTable( const XclExpRoot& rRoot, SCTAB nScTab ) :
+ XclExpRoot( rRoot ),
+ mnScTab( nScTab ),
+ nExcTab( rRoot.GetTabInfo().GetXclTab( nScTab ) ),
+ mxNoteList( new XclExpNoteList )
+{
+}
+
+ExcTable::~ExcTable()
+{
+}
+
+void ExcTable::Add( XclExpRecordBase* pRec )
+{
+ OSL_ENSURE( pRec, "-ExcTable::Add(): pRec is NULL!" );
+ aRecList.AppendNewRecord( pRec );
+}
+
+void ExcTable::FillAsHeaderBinary( ExcBoundsheetList& rBoundsheetList )
+{
+ InitializeGlobals();
+
+ RootData& rR = GetOldRoot();
+ ScDocument& rDoc = GetDoc();
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+
+ if ( GetBiff() <= EXC_BIFF5 )
+ Add( new ExcBofW );
+ else
+ Add( new ExcBofW8 );
+
+ sal_uInt16 nExcTabCount = rTabInfo.GetXclTabCount();
+ sal_uInt16 nCodenames = static_cast< sal_uInt16 >( GetExtDocOptions().GetCodeNameCount() );
+
+ SfxObjectShell* pShell = GetDocShell();
+ sal_uInt16 nWriteProtHash = pShell ? pShell->GetModifyPasswordHash() : 0;
+ bool bRecommendReadOnly = pShell && pShell->IsLoadReadonly();
+
+ if( (nWriteProtHash > 0) || bRecommendReadOnly )
+ Add( new XclExpEmptyRecord( EXC_ID_WRITEPROT ) );
+
+ // TODO: correct codepage for BIFF5?
+ sal_uInt16 nCodePage = XclTools::GetXclCodePage( (GetBiff() <= EXC_BIFF5) ? RTL_TEXTENCODING_MS_1252 : RTL_TEXTENCODING_UNICODE );
+
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ Add( new XclExpEmptyRecord( EXC_ID_INTERFACEHDR ) );
+ Add( new XclExpUInt16Record( EXC_ID_MMS, 0 ) );
+ Add( new XclExpEmptyRecord( EXC_ID_TOOLBARHDR ) );
+ Add( new XclExpEmptyRecord( EXC_ID_TOOLBAREND ) );
+ Add( new XclExpEmptyRecord( EXC_ID_INTERFACEEND ) );
+ Add( new ExcDummy_00 );
+ }
+ else
+ {
+ if( IsDocumentEncrypted() )
+ Add( new XclExpFileEncryption( GetRoot() ) );
+ Add( new XclExpInterfaceHdr( nCodePage ) );
+ Add( new XclExpUInt16Record( EXC_ID_MMS, 0 ) );
+ Add( new XclExpInterfaceEnd );
+ Add( new XclExpWriteAccess );
+ }
+
+ Add( new XclExpFileSharing( GetRoot(), nWriteProtHash, bRecommendReadOnly ) );
+ Add( new XclExpUInt16Record( EXC_ID_CODEPAGE, nCodePage ) );
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ Add( new XclExpBoolRecord( EXC_ID_DSF, false ) );
+ Add( new XclExpEmptyRecord( EXC_ID_XL9FILE ) );
+ rR.pTabId = new XclExpChTrTabId( std::max( nExcTabCount, nCodenames ) );
+ Add( rR.pTabId );
+ if( HasVbaStorage() )
+ {
+ Add( new XclObproj );
+ const OUString& rCodeName = GetExtDocOptions().GetDocSettings().maGlobCodeName;
+ if( !rCodeName.isEmpty() )
+ Add( new XclCodename( rCodeName ) );
+ }
+ }
+
+ Add( new XclExpUInt16Record( EXC_ID_FNGROUPCOUNT, 14 ) );
+
+ if ( GetBiff() <= EXC_BIFF5 )
+ {
+ // global link table: EXTERNCOUNT, EXTERNSHEET, NAME
+ aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_NAME ) );
+ }
+
+ // document protection options
+ lcl_AddWorkbookProtection( aRecList, *this );
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ Add( new XclExpProt4Rev );
+ Add( new XclExpProt4RevPass );
+ }
+
+ lcl_AddBookviews( aRecList, *this );
+
+ Add( new XclExpXmlStartSingleElementRecord( XML_workbookPr ) );
+ if ( GetBiff() == EXC_BIFF8 && GetOutput() != EXC_OUTPUT_BINARY )
+ {
+ Add( new XclExpBoolRecord(0x0040, false, XML_backupFile ) ); // BACKUP
+ Add( new XclExpBoolRecord(0x008D, false, XML_showObjects ) ); // HIDEOBJ
+ }
+
+ if ( GetBiff() == EXC_BIFF8 )
+ {
+ Add( new XclExpBoolRecord(0x0040, false) ); // BACKUP
+ Add( new XclExpBoolRecord(0x008D, false) ); // HIDEOBJ
+ }
+
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ Add( new ExcDummy_040 );
+ Add( new Exc1904( rDoc ) );
+ Add( new ExcDummy_041 );
+ }
+ else
+ {
+ // BIFF8
+ Add( new Exc1904( rDoc ) );
+ Add( new XclExpBoolRecord( 0x000E, !rDoc.GetDocOptions().IsCalcAsShown() ) );
+ Add( new XclExpBoolRecord(0x01B7, false) ); // REFRESHALL
+ Add( new XclExpBoolRecord(0x00DA, false) ); // BOOKBOOL
+ }
+
+ // Formatting: FONT, FORMAT, XF, STYLE, PALETTE
+ aRecList.AppendRecord( CreateRecord( EXC_ID_FONTLIST ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_FORMATLIST ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_XFLIST ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_PALETTE ) );
+
+ SCTAB nC;
+ SCTAB nScTabCount = rTabInfo.GetScTabCount();
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ // Bundlesheet
+ for( nC = 0 ; nC < nScTabCount ; nC++ )
+ if( rTabInfo.IsExportTab( nC ) )
+ {
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet( rR, nC );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+ }
+ else
+ {
+ // Pivot Cache
+ GetPivotTableManager().CreatePivotTables();
+ aRecList.AppendRecord( GetPivotTableManager().CreatePivotCachesRecord() );
+
+ // Change tracking
+ if( rDoc.GetChangeTrack() )
+ {
+ rR.pUserBViewList = new XclExpUserBViewList( *rDoc.GetChangeTrack() );
+ Add( rR.pUserBViewList );
+ }
+
+ // Natural Language Formulas Flag
+ aRecList.AppendNewRecord( new XclExpBoolRecord( EXC_ID_USESELFS, GetDoc().GetDocOptions().IsLookUpColRowNames() ) );
+
+ // Bundlesheet
+ for( nC = 0 ; nC < nScTabCount ; nC++ )
+ if( rTabInfo.IsExportTab( nC ) )
+ {
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet8( rR, nC );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+
+ OUString aTmpString;
+ for( SCTAB nAdd = 0; nC < static_cast<SCTAB>(nCodenames) ; nC++, nAdd++ )
+ {
+ aTmpString = lcl_GetVbaTabName( nAdd );
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet8( aTmpString );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+
+ // COUNTRY - in BIFF8 in workbook globals
+ Add( new XclExpCountry( GetRoot() ) );
+
+ // link table: SUPBOOK, XCT, CRN, EXTERNNAME, EXTERNSHEET, NAME
+ aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_NAME ) );
+
+ Add( new XclExpRecalcId );
+
+ // MSODRAWINGGROUP per-document data
+ aRecList.AppendRecord( GetObjectManager().CreateDrawingGroup() );
+ // Shared string table: SST, EXTSST
+ aRecList.AppendRecord( CreateRecord( EXC_ID_SST ) );
+
+ Add( new XclExpBookExt );
+ }
+
+ Add( new ExcEof );
+}
+
+void ExcTable::FillAsHeaderXml( ExcBoundsheetList& rBoundsheetList )
+{
+ InitializeGlobals();
+
+ RootData& rR = GetOldRoot();
+ ScDocument& rDoc = GetDoc();
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+
+ sal_uInt16 nExcTabCount = rTabInfo.GetXclTabCount();
+ sal_uInt16 nCodenames = static_cast< sal_uInt16 >( GetExtDocOptions().GetCodeNameCount() );
+
+ rR.pTabId = new XclExpChTrTabId( std::max( nExcTabCount, nCodenames ) );
+ Add( rR.pTabId );
+
+ Add( new XclExpXmlStartSingleElementRecord( XML_workbookPr ) );
+ Add( new XclExpBoolRecord(0x0040, false, XML_backupFile ) ); // BACKUP
+ Add( new XclExpBoolRecord(0x008D, false, XML_showObjects ) ); // HIDEOBJ
+
+ Add( new Exc1904( rDoc ) );
+ // OOXTODO: The following /workbook/workbookPr attributes are mapped
+ // to various BIFF records that are not currently supported:
+ //
+ // XML_allowRefreshQuery: QSISTAG 802h: fEnableRefresh
+ // XML_autoCompressPictures: COMPRESSPICTURES 89Bh: fAutoCompressPictures
+ // XML_checkCompatibility: COMPAT12 88Ch: fNoCompatChk
+ // XML_codeName: "Calc"
+ // XML_defaultThemeVersion: ???
+ // XML_filterPrivacy: BOOKEXT 863h: fFilterPrivacy
+ // XML_hidePivotFieldList: BOOKBOOL DAh: fHidePivotTableFList
+ // XML_promptedSolutions: BOOKEXT 863h: fBuggedUserAboutSolution
+ // XML_publishItems: NAMEPUBLISH 893h: fPublished
+ // XML_saveExternalLinkValues: BOOKBOOL DAh: fNoSavSupp
+ // XML_showBorderUnselectedTables: BOOKBOOL DAh: fHideBorderUnsels
+ // XML_showInkAnnotation: BOOKEXT 863h: fShowInkAnnotation
+ // XML_showPivotChart: PIVOTCHARTBITS 859h: fGXHide??
+ // XML_updateLinks: BOOKBOOL DAh: grbitUpdateLinks
+ Add( new XclExpXmlEndSingleElementRecord() ); // XML_workbookPr
+
+ // Formatting: FONT, FORMAT, XF, STYLE, PALETTE
+ aRecList.AppendNewRecord( new XclExpXmlStyleSheet( *this ) );
+
+ // Change tracking
+ if( rDoc.GetChangeTrack() )
+ {
+ rR.pUserBViewList = new XclExpUserBViewList( *rDoc.GetChangeTrack() );
+ Add( rR.pUserBViewList );
+ }
+
+ lcl_AddWorkbookProtection( aRecList, *this );
+ lcl_AddBookviews( aRecList, *this );
+
+ // Bundlesheet
+ SCTAB nC;
+ SCTAB nScTabCount = rTabInfo.GetScTabCount();
+ aRecList.AppendNewRecord( new XclExpXmlStartElementRecord( XML_sheets ) );
+ for( nC = 0 ; nC < nScTabCount ; nC++ )
+ if( rTabInfo.IsExportTab( nC ) )
+ {
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet8( rR, nC );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+ aRecList.AppendNewRecord( new XclExpXmlEndElementRecord( XML_sheets ) );
+
+ OUString aTmpString;
+ for( SCTAB nAdd = 0; nC < static_cast<SCTAB>(nCodenames) ; nC++, nAdd++ )
+ {
+ aTmpString = lcl_GetVbaTabName( nAdd );
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet8( aTmpString );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+
+ // link table: SUPBOOK, XCT, CRN, EXTERNNAME, EXTERNSHEET, NAME
+ aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_NAME ) );
+
+ lcl_AddCalcPr( aRecList, *this );
+
+ // MSODRAWINGGROUP per-document data
+ aRecList.AppendRecord( GetObjectManager().CreateDrawingGroup() );
+ // Shared string table: SST, EXTSST
+ aRecList.AppendRecord( CreateRecord( EXC_ID_SST ) );
+}
+
+void ExcTable::FillAsTableBinary( SCTAB nCodeNameIdx )
+{
+ InitializeTable( mnScTab );
+
+ RootData& rR = GetOldRoot();
+ XclBiff eBiff = GetBiff();
+ ScDocument& rDoc = GetDoc();
+
+ OSL_ENSURE( (mnScTab >= 0) && (mnScTab <= MAXTAB), "-ExcTable::Table(): mnScTab - no ordinary table!" );
+ OSL_ENSURE( nExcTab <= o3tl::make_unsigned(MAXTAB), "-ExcTable::Table(): nExcTab - no ordinary table!" );
+
+ // create a new OBJ list for this sheet (may be used by notes, autofilter, data validation)
+ if( eBiff == EXC_BIFF8 )
+ GetObjectManager().StartSheet();
+
+ // cell table: DEFROWHEIGHT, DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records
+ mxCellTable = new XclExpCellTable( GetRoot() );
+
+ //export cell notes
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetAllNoteEntries(aNotes);
+ for (const auto& rNote : aNotes)
+ {
+ if (rNote.maPos.Tab() != mnScTab)
+ continue;
+
+ mxNoteList->AppendNewRecord(new XclExpNote(GetRoot(), rNote.maPos, rNote.mpNote, u""));
+ }
+
+ // WSBOOL needs data from page settings, create it here, add it later
+ rtl::Reference<XclExpPageSettings> xPageSett = new XclExpPageSettings( GetRoot() );
+ bool bFitToPages = xPageSett->GetPageData().mbFitToPages;
+
+ if( eBiff <= EXC_BIFF5 )
+ {
+ Add( new ExcBof );
+ Add( new ExcDummy_02a );
+ }
+ else
+ {
+ Add( new ExcBof8 );
+ lcl_AddCalcPr( aRecList, *this );
+ }
+
+ // GUTS (count & size of outline icons)
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_GUTS ) );
+ // DEFROWHEIGHT, created by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID2_DEFROWHEIGHT ) );
+
+ // COUNTRY - in BIFF5/7 in every worksheet
+ if( eBiff <= EXC_BIFF5 )
+ Add( new XclExpCountry( GetRoot() ) );
+
+ Add( new XclExpWsbool( bFitToPages ) );
+
+ // page settings (SETUP and various other records)
+ aRecList.AppendRecord( xPageSett );
+
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnScTab);
+ if (pTabProtect && pTabProtect->isProtected())
+ {
+ Add( new XclExpProtection(true) );
+ Add( new XclExpBoolRecord(oox::xls::BIFF_ID_SCENPROTECT, pTabProtect->isOptionEnabled(ScTableProtection::SCENARIOS)) );
+ if (pTabProtect->isOptionEnabled(ScTableProtection::OBJECTS))
+ Add( new XclExpBoolRecord(oox::xls::BIFF_ID_OBJECTPROTECT, true ));
+ Add( new XclExpPassHash(pTabProtect->getPasswordHash(PASSHASH_XL)) );
+ }
+
+ // local link table: EXTERNCOUNT, EXTERNSHEET
+ if( eBiff <= EXC_BIFF5 )
+ aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) );
+
+ if ( eBiff == EXC_BIFF8 )
+ lcl_AddScenariosAndFilters( aRecList, GetRoot(), mnScTab );
+
+ // cell table: DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records
+ aRecList.AppendRecord( mxCellTable );
+
+ // MERGEDCELLS record, generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_MERGEDCELLS ) );
+ // label ranges
+ if( eBiff == EXC_BIFF8 )
+ Add( new XclExpLabelranges( GetRoot() ) );
+ // data validation (DVAL and list of DV records), generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_DVAL ) );
+
+ if( eBiff == EXC_BIFF8 )
+ {
+ // all MSODRAWING and OBJ stuff of this sheet goes here
+ aRecList.AppendRecord( GetObjectManager().ProcessDrawing( GetSdrPage( mnScTab ) ) );
+ // pivot tables
+ aRecList.AppendRecord( GetPivotTableManager().CreatePivotTablesRecord( mnScTab ) );
+ }
+
+ // list of NOTE records, generated by the cell table
+ aRecList.AppendRecord( mxNoteList );
+
+ // sheet view settings: WINDOW2, SCL, PANE, SELECTION
+ aRecList.AppendNewRecord( new XclExpTabViewSettings( GetRoot(), mnScTab ) );
+
+ if( eBiff == EXC_BIFF8 )
+ {
+ // sheet protection options
+ Add( new XclExpSheetProtectOptions( GetRoot(), mnScTab ) );
+
+ // enhanced protections if there are
+ if (pTabProtect)
+ {
+ const ::std::vector<ScEnhancedProtection>& rProts( pTabProtect->getEnhancedProtection());
+ for (const auto& rProt : rProts)
+ {
+ Add( new XclExpSheetEnhancedProtection( GetRoot(), rProt));
+ }
+ }
+
+ // web queries
+ Add( new XclExpWebQueryBuffer( GetRoot() ) );
+
+ // conditional formats
+ Add( new XclExpCondFormatBuffer( GetRoot(), XclExtLstRef() ) );
+
+ if( HasVbaStorage() )
+ if( nCodeNameIdx < GetExtDocOptions().GetCodeNameCount() )
+ Add( new XclCodename( GetExtDocOptions().GetCodeName( nCodeNameIdx ) ) );
+ }
+
+ // list of HLINK records, generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_HLINK ) );
+
+ // change tracking
+ if( rR.pUserBViewList )
+ {
+ XclExpUserBViewList::const_iterator iter;
+ for ( iter = rR.pUserBViewList->cbegin(); iter != rR.pUserBViewList->cend(); ++iter)
+ {
+ Add( new XclExpUsersViewBegin( (*iter).GetGUID(), nExcTab ) );
+ Add( new XclExpUsersViewEnd );
+ }
+ }
+
+ // EOF
+ Add( new ExcEof );
+}
+
+void ExcTable::FillAsTableXml()
+{
+ InitializeTable( mnScTab );
+
+ ScDocument& rDoc = GetDoc();
+
+ OSL_ENSURE( (mnScTab >= 0) && (mnScTab <= MAXTAB), "-ExcTable::Table(): mnScTab - no ordinary table!" );
+ OSL_ENSURE( nExcTab <= o3tl::make_unsigned(MAXTAB), "-ExcTable::Table(): nExcTab - no ordinary table!" );
+
+ // create a new OBJ list for this sheet (may be used by notes, autofilter, data validation)
+ GetObjectManager().StartSheet();
+
+ // cell table: DEFROWHEIGHT, DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records
+ mxCellTable = new XclExpCellTable( GetRoot() );
+
+ //export cell notes
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetAllNoteEntries(aNotes);
+ for (const auto& rNote : aNotes)
+ {
+ if (rNote.maPos.Tab() != mnScTab)
+ continue;
+
+ mxNoteList->AppendNewRecord(new XclExpNote(GetRoot(), rNote.maPos, rNote.mpNote, u""));
+ }
+
+ // WSBOOL needs data from page settings, create it here, add it later
+ rtl::Reference<XclExpPageSettings> xPageSett = new XclExpPageSettings( GetRoot() );
+ XclExtLstRef xExtLst = new XclExtLst( GetRoot() );
+ bool bFitToPages = xPageSett->GetPageData().mbFitToPages;
+
+ Color aTabColor = GetRoot().GetDoc().GetTabBgColor(mnScTab);
+ Add(new XclExpXmlSheetPr(bFitToPages, mnScTab, aTabColor, &GetFilterManager()));
+
+ // GUTS (count & size of outline icons)
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_GUTS ) );
+ // DEFROWHEIGHT, created by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID2_DEFROWHEIGHT ) );
+
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID3_DIMENSIONS ) );
+
+ // sheet view settings: WINDOW2, SCL, PANE, SELECTION
+ aRecList.AppendNewRecord( new XclExpTabViewSettings( GetRoot(), mnScTab ) );
+
+ // cell table: DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records
+ aRecList.AppendRecord( mxCellTable );
+
+ // list of NOTE records, generated by the cell table
+ // not in the worksheet file
+ if( mxNoteList != nullptr && !mxNoteList->IsEmpty() )
+ aRecList.AppendNewRecord( new XclExpComments( mnScTab, *mxNoteList ) );
+
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnScTab);
+ if (pTabProtect && pTabProtect->isProtected())
+ Add( new XclExpSheetProtection(true, mnScTab) );
+
+ lcl_AddScenariosAndFilters( aRecList, GetRoot(), mnScTab );
+
+ // MERGEDCELLS record, generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_MERGEDCELLS ) );
+
+ // conditional formats
+ Add( new XclExpCondFormatBuffer( GetRoot(), xExtLst ) );
+
+ Add(new xcl::exp::SparklineBuffer(GetRoot(), xExtLst));
+
+ // data validation (DVAL and list of DV records), generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_DVAL ) );
+
+ // list of HLINK records, generated by the cell table
+ XclExpRecordRef xHyperlinks = mxCellTable->CreateRecord( EXC_ID_HLINK );
+ XclExpHyperlinkList* pHyperlinkList = dynamic_cast<XclExpHyperlinkList*>(xHyperlinks.get());
+ if( pHyperlinkList != nullptr && !pHyperlinkList->IsEmpty() )
+ {
+ aRecList.AppendNewRecord( new XclExpXmlStartElementRecord( XML_hyperlinks ) );
+ aRecList.AppendRecord( xHyperlinks );
+ aRecList.AppendNewRecord( new XclExpXmlEndElementRecord( XML_hyperlinks ) );
+ }
+
+ aRecList.AppendRecord( xPageSett );
+
+ // all MSODRAWING and OBJ stuff of this sheet goes here
+ aRecList.AppendRecord( GetObjectManager().ProcessDrawing( GetSdrPage( mnScTab ) ) );
+
+ XclExpImgData* pImgData = xPageSett->getGraphicExport();
+ if (pImgData)
+ aRecList.AppendRecord(pImgData);
+
+ // <tableParts> after <drawing> and before <extLst>
+ aRecList.AppendRecord( GetTablesManager().GetTablesBySheet( mnScTab));
+
+ aRecList.AppendRecord( xExtLst );
+}
+
+void ExcTable::FillAsEmptyTable( SCTAB nCodeNameIdx )
+{
+ InitializeTable( mnScTab );
+
+ if( !(HasVbaStorage() && (nCodeNameIdx < GetExtDocOptions().GetCodeNameCount())) )
+ return;
+
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ Add( new ExcBof );
+ }
+ else
+ {
+ Add( new ExcBof8 );
+ Add( new XclCodename( GetExtDocOptions().GetCodeName( nCodeNameIdx ) ) );
+ }
+ // sheet view settings: WINDOW2, SCL, PANE, SELECTION
+ aRecList.AppendNewRecord( new XclExpTabViewSettings( GetRoot(), mnScTab ) );
+ Add( new ExcEof );
+}
+
+void ExcTable::Write( XclExpStream& rStrm )
+{
+ SetCurrScTab( mnScTab );
+ if( mxCellTable )
+ mxCellTable->Finalize(true);
+ aRecList.Save( rStrm );
+}
+
+void ExcTable::WriteXml( XclExpXmlStream& rStrm )
+{
+ if (!GetTabInfo().IsExportTab(mnScTab))
+ {
+ // header export.
+ SetCurrScTab(mnScTab);
+ if (mxCellTable)
+ mxCellTable->Finalize(false);
+ aRecList.SaveXml(rStrm);
+
+ return;
+ }
+
+ // worksheet export
+ OUString sSheetName = XclXmlUtils::GetStreamName( "xl/", "worksheets/sheet", mnScTab+1 );
+
+ sax_fastparser::FSHelperPtr pWorksheet = rStrm.GetStreamForPath( sSheetName );
+
+ rStrm.PushStream( pWorksheet );
+
+ pWorksheet->startElement( XML_worksheet,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ FSNS(XML_xmlns, XML_xdr), "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing", // rStrm.getNamespaceURL(OOX_NS(xm)).toUtf8() -> "http://schemas.microsoft.com/office/excel/2006/main",
+ FSNS(XML_xmlns, XML_x14), rStrm.getNamespaceURL(OOX_NS(xls14Lst)).toUtf8(),
+ FSNS(XML_xmlns, XML_xr2), rStrm.getNamespaceURL(OOX_NS(xr2)).toUtf8(),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)).toUtf8());
+
+ SetCurrScTab( mnScTab );
+ if (mxCellTable)
+ mxCellTable->Finalize(false);
+ aRecList.SaveXml( rStrm );
+
+ XclExpXmlPivotTables* pPT = GetXmlPivotTableManager().GetTablesBySheet(mnScTab);
+ if (pPT)
+ pPT->SaveXml(rStrm);
+
+ rStrm.GetCurrentStream()->endElement( XML_worksheet );
+ rStrm.PopStream();
+}
+
+ExcDocument::ExcDocument( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ aHeader( rRoot )
+{
+}
+
+ExcDocument::~ExcDocument()
+{
+ maTableList.RemoveAllRecords(); // for the following assertion!
+}
+
+void ExcDocument::ReadDoc()
+{
+ InitializeConvert();
+
+ if (GetOutput() == EXC_OUTPUT_BINARY)
+ aHeader.FillAsHeaderBinary(maBoundsheetList);
+ else
+ {
+ aHeader.FillAsHeaderXml(maBoundsheetList);
+ GetXmlPivotTableManager().Initialize();
+ GetTablesManager().Initialize(); // Move outside conditions if we wanted to support BIFF.
+ }
+
+ SCTAB nScTab = 0, nScTabCount = GetTabInfo().GetScTabCount();
+ SCTAB nCodeNameIdx = 0, nCodeNameCount = GetExtDocOptions().GetCodeNameCount();
+
+ for( ; nScTab < nScTabCount; ++nScTab )
+ {
+ if( GetTabInfo().IsExportTab( nScTab ) )
+ {
+ ExcTableList::RecordRefType xTab = new ExcTable( GetRoot(), nScTab );
+ maTableList.AppendRecord( xTab );
+ if (GetOutput() == EXC_OUTPUT_BINARY)
+ xTab->FillAsTableBinary(nCodeNameIdx);
+ else
+ xTab->FillAsTableXml();
+
+ ++nCodeNameIdx;
+ }
+ }
+ for( ; nCodeNameIdx < nCodeNameCount; ++nScTab, ++nCodeNameIdx )
+ {
+ ExcTableList::RecordRefType xTab = new ExcTable( GetRoot(), nScTab );
+ maTableList.AppendRecord( xTab );
+ xTab->FillAsEmptyTable( nCodeNameIdx );
+ }
+
+ if ( GetBiff() == EXC_BIFF8 )
+ {
+ // complete temporary Escher stream
+ GetObjectManager().EndDocument();
+
+ // change tracking
+ if ( GetDoc().GetChangeTrack() )
+ m_xExpChangeTrack.reset(new XclExpChangeTrack( GetRoot() ));
+ }
+}
+
+void ExcDocument::Write( SvStream& rSvStrm )
+{
+ if( !maTableList.IsEmpty() )
+ {
+ InitializeSave();
+
+ XclExpStream aXclStrm( rSvStrm, GetRoot() );
+
+ aHeader.Write( aXclStrm );
+
+ OSL_ENSURE( maTableList.GetSize() == maBoundsheetList.GetSize(),
+ "ExcDocument::Write - different number of sheets and BOUNDSHEET records" );
+
+ for( size_t nTab = 0, nTabCount = maTableList.GetSize(); nTab < nTabCount; ++nTab )
+ {
+ // set current stream position in BOUNDSHEET record
+ ExcBoundsheetRef xBoundsheet = maBoundsheetList.GetRecord( nTab );
+ if( xBoundsheet )
+ xBoundsheet->SetStreamPos( aXclStrm.GetSvStreamPos() );
+ // write the table
+ maTableList.GetRecord( nTab )->Write( aXclStrm );
+ }
+
+ // write the table stream positions into the BOUNDSHEET records
+ for( size_t nBSheet = 0, nBSheetCount = maBoundsheetList.GetSize(); nBSheet < nBSheetCount; ++nBSheet )
+ maBoundsheetList.GetRecord( nBSheet )->UpdateStreamPos( aXclStrm );
+ }
+ if( m_xExpChangeTrack )
+ m_xExpChangeTrack->Write();
+}
+
+void ExcDocument::WriteXml( XclExpXmlStream& rStrm )
+{
+ SfxObjectShell* pDocShell = GetDocShell();
+
+ using namespace ::com::sun::star;
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS( pDocShell->GetModel(), uno::UNO_QUERY_THROW );
+ uno::Reference<document::XDocumentProperties> xDocProps = xDPS->getDocumentProperties();
+
+ OUString sUserName = GetUserName();
+ sal_uInt32 nWriteProtHash = pDocShell->GetModifyPasswordHash();
+ bool bHasPasswordHash = nWriteProtHash && !sUserName.isEmpty();
+ const uno::Sequence<beans::PropertyValue> aInfo = pDocShell->GetModifyPasswordInfo();
+ OUString sAlgorithm, sSalt, sHash;
+ sal_Int32 nCount = 0;
+ for (const auto& prop : aInfo)
+ {
+ if (prop.Name == "algorithm-name")
+ prop.Value >>= sAlgorithm;
+ else if (prop.Name == "salt")
+ prop.Value >>= sSalt;
+ else if (prop.Name == "iteration-count")
+ prop.Value >>= nCount;
+ else if (prop.Name == "hash")
+ prop.Value >>= sHash;
+ }
+ bool bHasPasswordInfo
+ = sAlgorithm != "PBKDF2" && !sSalt.isEmpty() && !sHash.isEmpty() && !sUserName.isEmpty();
+ rStrm.exportDocumentProperties(xDocProps, pDocShell->IsSecurityOptOpenReadOnly()
+ && !bHasPasswordHash && !bHasPasswordInfo);
+ rStrm.exportCustomFragments();
+
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement( XML_workbook,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8() );
+ rWorkbook->singleElement( XML_fileVersion,
+ XML_appName, "Calc"
+ // OOXTODO: XML_codeName
+ // OOXTODO: XML_lastEdited
+ // OOXTODO: XML_lowestEdited
+ // OOXTODO: XML_rupBuild
+ );
+
+ if (bHasPasswordHash)
+ rWorkbook->singleElement(XML_fileSharing,
+ XML_userName, sUserName,
+ XML_reservationPassword, OString::number(nWriteProtHash, 16).getStr());
+ else if (bHasPasswordInfo)
+ rWorkbook->singleElement(XML_fileSharing,
+ XML_userName, sUserName,
+ XML_algorithmName, sAlgorithm.toUtf8().getStr(),
+ XML_hashValue, sHash.toUtf8().getStr(),
+ XML_saltValue, sSalt.toUtf8().getStr(),
+ XML_spinCount, OString::number(nCount).getStr());
+
+ if( !maTableList.IsEmpty() )
+ {
+ InitializeSave();
+
+ aHeader.WriteXml( rStrm );
+
+ for( size_t nTab = 0, nTabCount = maTableList.GetSize(); nTab < nTabCount; ++nTab )
+ {
+ // write the table
+ maTableList.GetRecord( nTab )->WriteXml( rStrm );
+ }
+ }
+
+ if( m_xExpChangeTrack )
+ m_xExpChangeTrack->WriteXml( rStrm );
+
+ XclExpXmlPivotCaches& rCaches = GetXmlPivotTableManager().GetCaches();
+ if (rCaches.HasCaches())
+ rCaches.SaveXml(rStrm);
+
+ const ScCalcConfig& rCalcConfig = GetDoc().GetCalcConfig();
+ formula::FormulaGrammar::AddressConvention eConv = rCalcConfig.meStringRefAddressSyntax;
+
+ // don't save "unspecified" string ref syntax ... query formula grammar
+ // and save that instead
+ if( eConv == formula::FormulaGrammar::CONV_UNSPECIFIED)
+ {
+ eConv = GetDoc().GetAddressConvention();
+ }
+
+ // write if it has been read|imported or explicitly changed
+ // or if ref syntax isn't what would be native for our file format
+ // i.e. ExcelA1 in this case
+ if ( rCalcConfig.mbHasStringRefSyntax ||
+ (eConv != formula::FormulaGrammar::CONV_XL_A1) )
+ {
+ XclExtLstRef xExtLst = new XclExtLst( GetRoot() );
+ xExtLst->AddRecord( new XclExpExtCalcPr( GetRoot(), eConv ) );
+ xExtLst->SaveXml(rStrm);
+ }
+
+ rWorkbook->endElement( XML_workbook );
+ rWorkbook.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excel.cxx b/sc/source/filter/excel/excel.cxx
new file mode 100644
index 000000000..edc60721a
--- /dev/null
+++ b/sc/source/filter/excel/excel.cxx
@@ -0,0 +1,494 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sot/storage.hxx>
+#include <sot/exchange.hxx>
+#include <filter/msfilter/classids.hxx>
+#include <tools/globname.hxx>
+#include <com/sun/star/packages/XPackageEncryption.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <unotools/streamwrap.hxx>
+#include <unotools/defaultencoding.hxx>
+#include <unotools/wincodepage.hxx>
+#include <osl/diagnose.h>
+#include <filter.hxx>
+#include <document.hxx>
+#include <xistream.hxx>
+#include <xltools.hxx>
+#include <docoptio.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <docsh.hxx>
+#include <scerrors.hxx>
+#include <imp_op.hxx>
+#include <excimp8.hxx>
+#include <exp_op.hxx>
+#include <scdll.hxx>
+
+#include <memory>
+
+using namespace css;
+
+static void lcl_getListOfStreams(SotStorage * pStorage, comphelper::SequenceAsHashMap& aStreamsData, const OUString& sPrefix)
+{
+ SvStorageInfoList aElements;
+ pStorage->FillInfoList(&aElements);
+ for (const auto & aElement : aElements)
+ {
+ OUString sStreamFullName = sPrefix.getLength() ? sPrefix + "/" + aElement.GetName() : aElement.GetName();
+ if (aElement.IsStorage())
+ {
+ tools::SvRef<SotStorage> xSubStorage = pStorage->OpenSotStorage(aElement.GetName(), StreamMode::STD_READ | StreamMode::SHARE_DENYALL);
+ lcl_getListOfStreams(xSubStorage.get(), aStreamsData, sStreamFullName);
+ }
+ else
+ {
+ // Read stream
+ tools::SvRef<SotStorageStream> rStream = pStorage->OpenSotStream(aElement.GetName(), StreamMode::READ | StreamMode::SHARE_DENYALL);
+ if (rStream.is())
+ {
+ sal_Int32 nStreamSize = rStream->GetSize();
+ uno::Sequence< sal_Int8 > oData;
+ oData.realloc(nStreamSize);
+ sal_Int32 nReadBytes = rStream->ReadBytes(oData.getArray(), nStreamSize);
+ if (nStreamSize == nReadBytes)
+ aStreamsData[sStreamFullName] <<= oData;
+ }
+ }
+ }
+}
+
+static tools::SvRef<SotStorage> lcl_DRMDecrypt(const SfxMedium& rMedium, const tools::SvRef<SotStorage>& rStorage, std::shared_ptr<SvStream>& rNewStorageStrm)
+{
+ tools::SvRef<SotStorage> aNewStorage;
+
+ // We have DRM encrypted storage. We should try to decrypt it first, if we can
+ uno::Sequence< uno::Any > aArguments;
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference< packages::XPackageEncryption > xPackageEncryption(
+ xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.oox.crypto.DRMDataSpace", aArguments, xComponentContext), uno::UNO_QUERY);
+
+ if (!xPackageEncryption.is())
+ {
+ // We do not know how to decrypt this
+ return aNewStorage;
+ }
+
+ comphelper::SequenceAsHashMap aStreamsData;
+ lcl_getListOfStreams(rStorage.get(), aStreamsData, "");
+
+ try {
+ uno::Sequence<beans::NamedValue> aStreams = aStreamsData.getAsConstNamedValueList();
+ if (!xPackageEncryption->readEncryptionInfo(aStreams))
+ {
+ // We failed with decryption
+ return aNewStorage;
+ }
+
+ tools::SvRef<SotStorageStream> rContentStream = rStorage->OpenSotStream("\011DRMContent", StreamMode::READ | StreamMode::SHARE_DENYALL);
+ if (!rContentStream.is())
+ {
+ return aNewStorage;
+ }
+
+ rNewStorageStrm = std::make_shared<SvMemoryStream>();
+
+ uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(rContentStream.get(), false));
+ uno::Reference<io::XOutputStream > xDecryptedStream(new utl::OSeekableOutputStreamWrapper(*rNewStorageStrm));
+
+ if (!xPackageEncryption->decrypt(xInputStream, xDecryptedStream))
+ {
+ // We failed with decryption
+ return aNewStorage;
+ }
+
+ rNewStorageStrm->Seek(0);
+
+ // Further reading is done from new document
+ aNewStorage = new SotStorage(*rNewStorageStrm);
+
+ // Set the media descriptor data
+ uno::Sequence<beans::NamedValue> aEncryptionData = xPackageEncryption->createEncryptionData("");
+ rMedium.GetItemSet()->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
+ }
+ catch (const std::exception&)
+ {
+ return aNewStorage;
+ }
+
+ return aNewStorage;
+}
+
+ErrCode ScFormatFilterPluginImpl::ScImportExcel( SfxMedium& rMedium, ScDocument* pDocument, const EXCIMPFORMAT eFormat )
+{
+ // check the passed Calc document
+ OSL_ENSURE( pDocument, "::ScImportExcel - no document" );
+ if( !pDocument ) return SCERR_IMPORT_INTERNAL; // should not happen
+
+ /* Import all BIFF versions regardless on eFormat, needed for import of
+ external cells (file type detection returns Excel4.0). */
+ if( (eFormat != EIF_AUTO) && (eFormat != EIF_BIFF_LE4) && (eFormat != EIF_BIFF5) && (eFormat != EIF_BIFF8) )
+ {
+ OSL_FAIL( "::ScImportExcel - wrong file format specification" );
+ return SCERR_IMPORT_FORMAT;
+ }
+
+ // check the input stream from medium
+ SvStream* pMedStrm = rMedium.GetInStream();
+ OSL_ENSURE( pMedStrm, "::ScImportExcel - medium without input stream" );
+ if( !pMedStrm ) return SCERR_IMPORT_OPEN; // should not happen
+
+ SvStream* pBookStrm = nullptr; // The "Book"/"Workbook" stream containing main data.
+ XclBiff eBiff = EXC_BIFF_UNKNOWN; // The BIFF version of the main stream.
+
+ // try to open an OLE storage
+ tools::SvRef<SotStorage> xRootStrg;
+ tools::SvRef<SotStorageStream> xStrgStrm;
+ std::shared_ptr<SvStream> aNewStorageStrm;
+ if( SotStorage::IsStorageFile( pMedStrm ) )
+ {
+ xRootStrg = new SotStorage( pMedStrm, false );
+ if( xRootStrg->GetError() )
+ xRootStrg = nullptr;
+ }
+
+ // try to open "Book" or "Workbook" stream in OLE storage
+ if( xRootStrg.is() )
+ {
+ // Check if there is DRM encryption in storage
+ tools::SvRef<SotStorageStream> xDRMStrm = ScfTools::OpenStorageStreamRead(xRootStrg, "\011DRMContent");
+ if (xDRMStrm.is())
+ {
+ xRootStrg = lcl_DRMDecrypt(rMedium, xRootStrg, aNewStorageStrm);
+ }
+
+ // try to open the "Book" stream
+ tools::SvRef<SotStorageStream> xBookStrm = ScfTools::OpenStorageStreamRead( xRootStrg, EXC_STREAM_BOOK );
+ XclBiff eBookBiff = xBookStrm.is() ? XclImpStream::DetectBiffVersion( *xBookStrm ) : EXC_BIFF_UNKNOWN;
+
+ // try to open the "Workbook" stream
+ tools::SvRef<SotStorageStream> xWorkbookStrm = ScfTools::OpenStorageStreamRead( xRootStrg, EXC_STREAM_WORKBOOK );
+ XclBiff eWorkbookBiff = xWorkbookStrm.is() ? XclImpStream::DetectBiffVersion( *xWorkbookStrm ) : EXC_BIFF_UNKNOWN;
+
+ // decide which stream to use
+ if( (eWorkbookBiff != EXC_BIFF_UNKNOWN) && ((eBookBiff == EXC_BIFF_UNKNOWN) || (eWorkbookBiff > eBookBiff)) )
+ {
+ /* Only "Workbook" stream exists; or both streams exist,
+ and "Workbook" has higher BIFF version than "Book" stream. */
+ xStrgStrm = xWorkbookStrm;
+ eBiff = eWorkbookBiff;
+ }
+ else if( eBookBiff != EXC_BIFF_UNKNOWN )
+ {
+ /* Only "Book" stream exists; or both streams exist,
+ and "Book" has higher BIFF version than "Workbook" stream. */
+ xStrgStrm = xBookStrm;
+ eBiff = eBookBiff;
+ }
+
+ pBookStrm = xStrgStrm.get();
+ }
+
+ // no "Book" or "Workbook" stream found, try plain input stream from medium (even for BIFF5+)
+ if( !pBookStrm )
+ {
+ eBiff = XclImpStream::DetectBiffVersion( *pMedStrm );
+ if( eBiff != EXC_BIFF_UNKNOWN )
+ pBookStrm = pMedStrm;
+ }
+
+ // try to import the file
+ ErrCode eRet = SCERR_IMPORT_UNKNOWN_BIFF;
+ if( pBookStrm )
+ {
+ pBookStrm->SetBufferSize( 0x8000 ); // still needed?
+
+ XclImpRootData aImpData(
+ eBiff, rMedium, xRootStrg, *pDocument,
+ utl_getWinTextEncodingFromLangStr(utl_getLocaleForGlobalDefaultEncoding()));
+ std::unique_ptr< ImportExcel > xFilter;
+ switch( eBiff )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ xFilter.reset( new ImportExcel( aImpData, *pBookStrm ) );
+ break;
+ case EXC_BIFF8:
+ xFilter.reset( new ImportExcel8( aImpData, *pBookStrm ) );
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+
+ eRet = xFilter ? xFilter->Read() : SCERR_IMPORT_INTERNAL;
+ }
+
+ return eRet;
+}
+
+static ErrCode lcl_ExportExcelBiff( SfxMedium& rMedium, ScDocument *pDocument,
+ SvStream* pMedStrm, bool bBiff8, rtl_TextEncoding eNach )
+{
+ uno::Reference< packages::XPackageEncryption > xPackageEncryption;
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(rMedium.GetItemSet(), SID_ENCRYPTIONDATA, false);
+ SvStream* pOriginalMediaStrm = pMedStrm;
+ std::shared_ptr<SvStream> pMediaStrm;
+ if (pEncryptionDataItem && (pEncryptionDataItem->GetValue() >>= aEncryptionData))
+ {
+ ::comphelper::SequenceAsHashMap aHashData(aEncryptionData);
+ OUString sCryptoType = aHashData.getUnpackedValueOrDefault("CryptoType", OUString());
+
+ if (sCryptoType.getLength())
+ {
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Sequence<uno::Any> aArguments{
+ uno::Any(beans::NamedValue("Binary", uno::Any(true))) };
+ xPackageEncryption.set(
+ xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.oox.crypto." + sCryptoType, aArguments, xComponentContext), uno::UNO_QUERY);
+
+ if (xPackageEncryption.is())
+ {
+ // We have an encryptor. Export document into memory stream and encrypt it later
+ pMediaStrm = std::make_shared<SvMemoryStream>();
+ pMedStrm = pMediaStrm.get();
+
+ // Temp removal of EncryptionData to avoid password protection triggering
+ rMedium.GetItemSet()->ClearItem(SID_ENCRYPTIONDATA);
+ }
+ }
+ }
+
+ // try to open an OLE storage
+ tools::SvRef<SotStorage> xRootStrg = new SotStorage( pMedStrm, false );
+ if( xRootStrg->GetError() ) return SCERR_IMPORT_OPEN;
+
+ // create BIFF dependent strings
+ OUString aStrmName, aClipName, aClassName;
+ if( bBiff8 )
+ {
+ aStrmName = EXC_STREAM_WORKBOOK;
+ aClipName = "Biff8";
+ aClassName = "Microsoft Excel 97-Tabelle";
+ }
+ else
+ {
+ aStrmName = EXC_STREAM_BOOK;
+ aClipName = "Biff5";
+ aClassName = "Microsoft Excel 5.0-Tabelle";
+ }
+
+ // open the "Book"/"Workbook" stream
+ tools::SvRef<SotStorageStream> xStrgStrm = ScfTools::OpenStorageStreamWrite( xRootStrg, aStrmName );
+ if( !xStrgStrm.is() || xStrgStrm->GetError() ) return SCERR_IMPORT_OPEN;
+
+ xStrgStrm->SetBufferSize( 0x8000 ); // still needed?
+
+ ErrCode eRet = SCERR_IMPORT_UNKNOWN_BIFF;
+ XclExpRootData aExpData( bBiff8 ? EXC_BIFF8 : EXC_BIFF5, rMedium, xRootStrg, *pDocument, eNach );
+ if ( bBiff8 )
+ {
+ ExportBiff8 aFilter( aExpData, *xStrgStrm );
+ eRet = aFilter.Write();
+ }
+ else
+ {
+ ExportBiff5 aFilter( aExpData, *xStrgStrm );
+ eRet = aFilter.Write();
+ }
+
+ if( eRet == SCWARN_IMPORT_RANGE_OVERFLOW )
+ eRet = SCWARN_EXPORT_MAXROW;
+
+ SvGlobalName aGlobName(MSO_EXCEL5_CLASSID);
+ SotClipboardFormatId nClip = SotExchange::RegisterFormatName( aClipName );
+ xRootStrg->SetClass( aGlobName, nClip, aClassName );
+
+ xStrgStrm->Commit();
+ xRootStrg->Commit();
+
+ if (xPackageEncryption.is())
+ {
+ // Perform DRM encryption
+ pMedStrm->Seek(0);
+
+ xPackageEncryption->setupEncryption(aEncryptionData);
+
+ uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(pMedStrm, false));
+ uno::Sequence<beans::NamedValue> aStreams = xPackageEncryption->encrypt(xInputStream);
+
+ tools::SvRef<SotStorage> xEncryptedRootStrg = new SotStorage(pOriginalMediaStrm, false);
+ for (const beans::NamedValue & aStreamData : std::as_const(aStreams))
+ {
+ // To avoid long paths split and open substorages recursively
+ // Splitting paths manually, since comphelper::string::split is trimming special characters like \0x01, \0x09
+ tools::SvRef<SotStorage> pStorage = xEncryptedRootStrg.get();
+ OUString sFileName;
+ sal_Int32 idx = 0;
+ do
+ {
+ OUString sPathElem = aStreamData.Name.getToken(0, L'/', idx);
+ if (!sPathElem.isEmpty())
+ {
+ if (idx < 0)
+ {
+ sFileName = sPathElem;
+ }
+ else
+ {
+ pStorage = pStorage->OpenSotStorage(sPathElem);
+ }
+ }
+ } while (pStorage && idx >= 0);
+
+ if (!pStorage)
+ {
+ eRet = ERRCODE_IO_GENERAL;
+ break;
+ }
+
+ tools::SvRef<SotStorageStream> pStream = pStorage->OpenSotStream(sFileName);
+ if (!pStream)
+ {
+ eRet = ERRCODE_IO_GENERAL;
+ break;
+ }
+ uno::Sequence<sal_Int8> aStreamContent;
+ aStreamData.Value >>= aStreamContent;
+ size_t nBytesWritten = pStream->WriteBytes(aStreamContent.getConstArray(), aStreamContent.getLength());
+ if (nBytesWritten != static_cast<size_t>(aStreamContent.getLength()))
+ {
+ eRet = ERRCODE_IO_CANTWRITE;
+ break;
+ }
+ }
+ xEncryptedRootStrg->Commit();
+
+ // Restore encryption data
+ rMedium.GetItemSet()->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
+ }
+
+ return eRet;
+}
+
+ErrCode ScFormatFilterPluginImpl::ScExportExcel5( SfxMedium& rMedium, ScDocument *pDocument,
+ ExportFormatExcel eFormat, rtl_TextEncoding eNach )
+{
+ if( eFormat != ExpBiff5 && eFormat != ExpBiff8 )
+ return SCERR_IMPORT_NI;
+
+ // check the passed Calc document
+ OSL_ENSURE( pDocument, "::ScExportExcel5 - no document" );
+ if( !pDocument ) return SCERR_IMPORT_INTERNAL; // should not happen
+
+ // check the output stream from medium
+ SvStream* pMedStrm = rMedium.GetOutStream();
+ OSL_ENSURE( pMedStrm, "::ScExportExcel5 - medium without output stream" );
+ if( !pMedStrm ) return SCERR_IMPORT_OPEN; // should not happen
+
+ ErrCode eRet = lcl_ExportExcelBiff(rMedium, pDocument, pMedStrm, eFormat == ExpBiff8, eNach);
+ return eRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportCalcRTF(SvStream &rStream)
+{
+ ScDLL::Init();
+ ScDocument aDocument;
+ ScDocOptions aDocOpt = aDocument.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ aDocument.SetDocOptions(aDocOpt);
+ aDocument.MakeTable(0);
+ aDocument.EnableExecuteLink(false);
+ aDocument.SetInsertingFromOtherDoc(true);
+ ScRange aRange;
+
+ bool bRet;
+
+ try
+ {
+ bRet = ScFormatFilter::Get().ScImportRTF(rStream, OUString(), &aDocument, aRange) == ERRCODE_NONE;
+ }
+ catch (const std::range_error&)
+ {
+ return false;
+ }
+
+ return bRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportXLS(SvStream& rStream)
+{
+ ScDLL::Init();
+ SfxMedium aMedium;
+ css::uno::Reference<css::io::XInputStream> xStm(new utl::OInputStreamWrapper(rStream));
+ aMedium.GetItemSet()->Put(SfxUnoAnyItem(SID_INPUTSTREAM, css::uno::Any(xStm)));
+
+ ScDocShellRef xDocShell = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT |
+ SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS |
+ SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
+
+ xDocShell->DoInitNew();
+
+ ScDocument& rDoc = xDocShell->GetDocument();
+
+ ScDocOptions aDocOpt = rDoc.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ rDoc.SetDocOptions(aDocOpt);
+ rDoc.MakeTable(0);
+ rDoc.EnableExecuteLink(false);
+ rDoc.SetInsertingFromOtherDoc(true);
+ rDoc.InitDrawLayer(xDocShell.get());
+ bool bRet(false);
+ try
+ {
+ bRet = ScFormatFilter::Get().ScImportExcel(aMedium, &rDoc, EIF_AUTO) == ERRCODE_NONE;
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ }
+ catch (const std::out_of_range&)
+ {
+ }
+ xDocShell->DoClose();
+ xDocShell.clear();
+ return bRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportDIF(SvStream &rStream)
+{
+ ScDLL::Init();
+ ScDocument aDocument;
+ ScDocOptions aDocOpt = aDocument.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ aDocument.SetDocOptions(aDocOpt);
+ aDocument.MakeTable(0);
+ aDocument.EnableExecuteLink(false);
+ aDocument.SetInsertingFromOtherDoc(true);
+ return ScFormatFilter::Get().ScImportDif(rStream, &aDocument, ScAddress(0, 0, 0), RTL_TEXTENCODING_IBM_850) == ERRCODE_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excform.cxx b/sc/source/filter/excel/excform.cxx
new file mode 100644
index 000000000..d217c9622
--- /dev/null
+++ b/sc/source/filter/excel/excform.cxx
@@ -0,0 +1,1911 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excform.hxx>
+
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <scmatrix.hxx>
+
+#include <formula/errorcodes.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+
+#include <imp_op.hxx>
+#include <namebuff.hxx>
+#include <root.hxx>
+#include <xltracer.hxx>
+#include <xihelper.hxx>
+#include <xiname.hxx>
+#include <xistyle.hxx>
+#include <documentimport.hxx>
+
+using ::std::vector;
+
+const sal_uInt16 ExcelToSc::nRowMask = 0x3FFF;
+
+void ImportExcel::Formula25()
+{
+ XclAddress aXclPos;
+ sal_uInt16 nXF = 0, nFormLen;
+ double fCurVal;
+ bool bShrFmla;
+
+ aIn >> aXclPos;
+
+ if( GetBiff() == EXC_BIFF2 )
+ {// BIFF2
+ aIn.Ignore( 3 );
+
+ fCurVal = aIn.ReadDouble();
+ aIn.Ignore( 1 );
+ nFormLen = aIn.ReaduInt8();
+ bShrFmla = false;
+ }
+ else
+ {// BIFF5
+ sal_uInt8 nFlag0;
+ nXF = aIn.ReaduInt16();
+ fCurVal = aIn.ReadDouble();
+ nFlag0 = aIn.ReaduInt8();
+ aIn.Ignore( 5 );
+
+ nFormLen = aIn.ReaduInt16();
+
+ bShrFmla = nFlag0 & 0x08; // shared or not shared
+ }
+
+ Formula( aXclPos, nXF, nFormLen, fCurVal, bShrFmla );
+}
+
+void ImportExcel::Formula3()
+{
+ Formula4();
+}
+
+void ImportExcel::Formula4()
+{
+ XclAddress aXclPos;
+
+ aIn >> aXclPos;
+ sal_uInt16 nXF = aIn.ReaduInt16();
+ double fCurVal = aIn.ReadDouble();
+ aIn.Ignore( 2 );
+ sal_uInt16 nFormLen = aIn.ReaduInt16();
+
+ Formula( aXclPos, nXF, nFormLen, fCurVal, false );
+}
+
+void ImportExcel::Formula(
+ const XclAddress& rXclPos, sal_uInt16 nXF, sal_uInt16 nFormLen, double fCurVal, bool bShrFmla)
+{
+ if (!nFormLen)
+ return;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if (!GetAddressConverter().ConvertAddress(aScPos, rXclPos, GetCurrScTab(), true))
+ // Conversion failed.
+ return;
+
+ // Formula will be read next, length in nFormLen
+ std::unique_ptr<ScTokenArray> pResult;
+
+ pFormConv->Reset( aScPos );
+ ScDocumentImport& rDoc = GetDocImport();
+
+ if (bShrFmla)
+ {
+ // This is a shared formula. Get the token array from the shared formula pool.
+ SCCOL nSharedCol;
+ SCROW nSharedRow;
+ if (ExcelToSc::ReadSharedFormulaPosition(maStrm, nSharedCol, nSharedRow))
+ {
+ ScAddress aRefPos(nSharedCol, nSharedRow, GetCurrScTab());
+ const ScTokenArray* pSharedCode = pFormConv->GetSharedFormula(aRefPos);
+ if (pSharedCode)
+ {
+ ScFormulaCell* pCell;
+ pCell = new ScFormulaCell(rD, aScPos, pSharedCode->Clone());
+ pCell->GetCode()->WrapReference(aScPos, EXC_MAXCOL8, EXC_MAXROW8);
+ rDoc.getDoc().EnsureTable(aScPos.Tab());
+ rDoc.setFormulaCell(aScPos, pCell);
+ pCell->SetNeedNumberFormat(false);
+ if (std::isfinite(fCurVal))
+ pCell->SetResultDouble(fCurVal);
+
+ GetXFRangeBuffer().SetXF(aScPos, nXF);
+ SetLastFormula(aScPos.Col(), aScPos.Row(), fCurVal, nXF, pCell);
+ }
+ else
+ {
+ // Shared formula not found even though it's clearly a shared formula.
+ // The cell will be created in the following shared formula
+ // record.
+ SetLastFormula(aScPos.Col(), aScPos.Row(), fCurVal, nXF, nullptr);
+ }
+ return;
+ }
+ }
+
+ ConvErr eErr = pFormConv->Convert( pResult, maStrm, nFormLen, true );
+
+ ScFormulaCell* pCell = nullptr;
+
+ if (pResult)
+ {
+ pCell = new ScFormulaCell(rDoc.getDoc(), aScPos, std::move(pResult));
+ pCell->GetCode()->WrapReference(aScPos, EXC_MAXCOL8, EXC_MAXROW8);
+ rDoc.getDoc().CheckLinkFormulaNeedingCheck( *pCell->GetCode());
+ rDoc.getDoc().EnsureTable(aScPos.Tab());
+ rDoc.setFormulaCell(aScPos, pCell);
+ SetLastFormula(aScPos.Col(), aScPos.Row(), fCurVal, nXF, pCell);
+ }
+ else
+ {
+ pCell = rDoc.getDoc().GetFormulaCell(aScPos);
+ if (pCell)
+ pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE );
+ }
+
+ if (pCell)
+ {
+ pCell->SetNeedNumberFormat(false);
+ if( eErr != ConvErr::OK )
+ ExcelToSc::SetError( *pCell, eErr );
+
+ if (std::isfinite(fCurVal))
+ pCell->SetResultDouble(fCurVal);
+ }
+
+ GetXFRangeBuffer().SetXF(aScPos, nXF);
+}
+
+ExcelToSc::ExcelToSc( XclImpRoot& rRoot ) :
+ ExcelConverterBase(rRoot.GetDocImport().getDoc().GetSharedStringPool()),
+ XclImpRoot( rRoot ),
+ maFuncProv( rRoot ),
+ meBiff( rRoot.GetBiff() )
+{
+}
+
+ExcelToSc::~ExcelToSc()
+{
+}
+
+std::unique_ptr<ScTokenArray> ExcelToSc::GetDummy()
+{
+ aPool.Store( "Dummy()" );
+ aPool >> aStack;
+ return aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+}
+
+// if bAllowArrays is false stream seeks to first byte after <nFormulaLen>
+// otherwise it will seek to the first byte after the additional content (eg
+// inline arrays) following <nFormulaLen>
+ConvErr ExcelToSc::Convert( std::unique_ptr<ScTokenArray>& pResult, XclImpStream& aIn, std::size_t nFormulaLen, bool bAllowArrays, const FORMULA_TYPE eFT )
+{
+ RootData& rR = GetOldRoot();
+ sal_uInt8 nOp, nLen;
+ bool bError = false;
+ bool bArrayFormula = false;
+ TokenId nBuf0;
+ const bool bRangeName = eFT == FT_RangeName;
+ const bool bSharedFormula = eFT == FT_SharedFormula;
+ const bool bConditional = eFT == FT_CondFormat;
+ const bool bRNorSF = bRangeName || bSharedFormula || bConditional;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+ ExtensionTypeVec aExtensions;
+
+ if( nFormulaLen == 0 )
+ {
+ aPool.Store( "-/-" );
+ aPool >> aStack;
+ pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ return ConvErr::OK;
+ }
+
+ std::size_t nEndPos = aIn.GetRecPos() + nFormulaLen;
+
+ while( (aIn.GetRecPos() < nEndPos) && !bError )
+ {
+ nOp = aIn.ReaduInt8();
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp ) // book page:
+ { // SDK4 SDK5
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ case 0x02: // Data Table [325 277]
+ {
+ sal_uInt16 nUINT16 = 3;
+
+ if( meBiff != EXC_BIFF2 )
+ nUINT16++;
+
+ aIn.Ignore( nUINT16 );
+
+ bArrayFormula = true;
+ break;
+ }
+ case 0x03: // Addition [312 264]
+ aStack >> nBuf0;
+ aPool << aStack << ocAdd << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x04: // Subtraction [313 264]
+ // SECOND-TOP minus TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocSub << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x05: // Multiplication [313 264]
+ aStack >> nBuf0;
+ aPool << aStack << ocMul << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x06: // Division [313 264]
+ // divide TOP by SECOND-TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocDiv << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x07: // Exponentiation [313 265]
+ // raise SECOND-TOP to power of TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocPow << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x08: // Concatenation [313 265]
+ // append TOP to SECOND-TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocAmpersand << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x09: // Less Than [313 265]
+ // SECOND-TOP < TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocLess << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0A: // Less Than or Equal [313 265]
+ // SECOND-TOP <= TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocLessEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0B: // Equal [313 265]
+ // SECOND-TOP == TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0C: // Greater Than or Equal [313 265]
+ // SECOND-TOP >= TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocGreaterEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0D: // Greater Than [313 265]
+ // SECOND-TOP > TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocGreater << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0E: // Not Equal [313 265]
+ // SECOND-TOP != TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocNotEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0F: // Intersection [314 265]
+ aStack >> nBuf0;
+ aPool << aStack << ocIntersect << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x10: // Union [314 265]
+ // ocSep instead of 'ocUnion'
+ aStack >> nBuf0;
+ aPool << aStack << ocSep << nBuf0;
+ // doesn't fit exactly, but is more Excel-like
+ aPool >> aStack;
+ break;
+ case 0x11: // Range [314 265]
+ aStack >> nBuf0;
+ aPool << aStack << ocRange << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x12: // Unary Plus [312 264]
+ aPool << ocAdd << aStack;
+ aPool >> aStack;
+ break;
+ case 0x13: // Unary Minus [312 264]
+ aPool << ocNegSub << aStack;
+ aPool >> aStack;
+ break;
+ case 0x14: // Percent Sign [312 264]
+ aPool << aStack << ocPercentSign;
+ aPool >> aStack;
+ break;
+ case 0x15: // Parenthesis [326 278]
+ aPool << ocOpen << aStack << ocClose;
+ aPool >> aStack;
+ break;
+ case 0x16: // Missing Argument [314 266]
+ aPool << ocMissing;
+ aPool >> aStack;
+ GetTracer().TraceFormulaMissingArg();
+ break;
+ case 0x17: // String Constant [314 266]
+ {
+ nLen = aIn.ReaduInt8();
+ OUString aString = aIn.ReadRawByteString( nLen );
+
+ aStack << aPool.Store( aString );
+ break;
+ }
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData(0), nFactor(0);
+
+ sal_uInt8 nOpt = aIn.ReaduInt8();
+
+ if( meBiff == EXC_BIFF2 )
+ {
+ nData = aIn.ReaduInt8();
+ nFactor = 1;
+ }
+ else
+ {
+ nData = aIn.ReaduInt16();
+ nFactor = 2;
+ }
+
+ if( nOpt & 0x04 )
+ {
+ // nFactor -> skip bytes or words AttrChoose
+ ++nData;
+ aIn.Ignore(static_cast<std::size_t>(nData) * nFactor);
+ }
+ else if( nOpt & 0x10 ) // AttrSum
+ DoMulArgs( ocSum, 1 );
+ }
+ break;
+ case 0x1A: // External Reference [330 ]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: aIn.Ignore( 7 ); break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: aIn.Ignore( 10 ); break;
+ case EXC_BIFF5:
+ SAL_INFO( "sc", "-ExcelToSc::Convert(): 0x1A does not exist in Biff5!" );
+ [[fallthrough]];
+ default:
+ SAL_INFO( "sc", "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x1B: // End External Reference [330 ]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: aIn.Ignore( 3 ); break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: aIn.Ignore( 4 ); break;
+ case EXC_BIFF5:
+ SAL_INFO( "sc", "-ExcelToSc::Convert(): 0x1B does not exist in Biff5!" );
+ [[fallthrough]];
+ default:
+ SAL_INFO( "sc", "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x1C: // Error Value [314 266]
+ {
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ DefTokenId eOc;
+ switch( nByte )
+ {
+ case EXC_ERR_NULL:
+ case EXC_ERR_DIV0:
+ case EXC_ERR_VALUE:
+ case EXC_ERR_REF:
+ case EXC_ERR_NAME:
+ case EXC_ERR_NUM: eOc = ocStop; break;
+ case EXC_ERR_NA: eOc = ocNotAvail; break;
+ default: eOc = ocNoName;
+ }
+ aPool << eOc;
+ if( eOc != ocStop )
+ aPool << ocOpen << ocClose;
+ aPool >> aStack;
+ break;
+ }
+ case 0x1D: // Boolean [315 266]
+ {
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ if( nByte == 0 )
+ aPool << ocFalse << ocOpen << ocClose;
+ else
+ aPool << ocTrue << ocOpen << ocClose;
+ aPool >> aStack;
+ break;
+ }
+ case 0x1E: // Integer [315 266]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ aStack << aPool.Store( static_cast<double>(nUINT16) );
+ break;
+ }
+ case 0x1F: // Number [315 266]
+ {
+ double fDouble = aIn.ReadDouble();
+ aStack << aPool.Store( fDouble );
+ break;
+ }
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ {
+ aIn.Ignore( (meBiff == EXC_BIFF2) ? 6 : 7 );
+ if( bAllowArrays )
+ {
+ aStack << aPool.StoreMatrix();
+ aExtensions.push_back( EXTENSION_ARRAY );
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ break;
+ }
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ {
+ sal_uInt16 nXclFunc;
+ if( meBiff <= EXC_BIFF3 )
+ nXclFunc = aIn.ReaduInt8();
+ else
+ nXclFunc = aIn.ReaduInt16();
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) )
+ DoMulArgs( pFuncInfo->meOpCode, pFuncInfo->mnMaxParamCount );
+ else
+ DoMulArgs( ocNoName, 0 );
+ }
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ {
+ sal_uInt16 nXclFunc;
+ sal_uInt8 nParamCount;
+ nParamCount = aIn.ReaduInt8();
+ nParamCount &= 0x7F;
+ if( meBiff <= EXC_BIFF3 )
+ nXclFunc = aIn.ReaduInt8();
+ else
+ nXclFunc = aIn.ReaduInt16();
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) )
+ DoMulArgs( pFuncInfo->meOpCode, nParamCount );
+ else
+ DoMulArgs( ocNoName, 0 );
+ }
+ break;
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ switch( meBiff )
+ {
+ case EXC_BIFF2: aIn.Ignore( 5 ); break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: aIn.Ignore( 8 ); break;
+ case EXC_BIFF5: aIn.Ignore( 12 ); break;
+ default:
+ OSL_FAIL(
+ "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ const XclImpName* pName = GetNameManager().GetName( nUINT16 );
+ if(pName && !pName->GetScRangeData())
+ aStack << aPool.Store( ocMacro, pName->GetXclName() );
+ else
+ aStack << aPool.StoreName(nUINT16, -1);
+ }
+ break;
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ aSRD.SetAbsCol(static_cast<SCCOL>(nByte));
+ aSRD.SetAbsRow(nUINT16 & 0x3FFF);
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nUINT16, nByte, aSRD, bRangeName );
+
+ switch ( nOp )
+ {
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ // no information which part is deleted, set both
+ aSRD.SetColDeleted( true );
+ aSRD.SetRowDeleted( true );
+ }
+
+ aStack << aPool.Store( aSRD );
+ break;
+ }
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ {
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt8 nColFirst, nColLast;
+ ScSingleRefData& rSRef1 = aCRD.Ref1;
+ ScSingleRefData& rSRef2 = aCRD.Ref2;
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ rSRef1.SetRelTab(0);
+ rSRef2.SetRelTab(0);
+ rSRef1.SetFlag3D( bRangeName );
+ rSRef2.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ switch ( nOp )
+ {
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ // no information which part is deleted, set all
+ rSRef1.SetColDeleted( true );
+ rSRef1.SetRowDeleted( true );
+ rSRef2.SetColDeleted( true );
+ rSRef2.SetRowDeleted( true );
+ }
+
+ aStack << aPool.Store( aCRD );
+ }
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ aExtensions.push_back( EXTENSION_MEMAREA );
+ [[fallthrough]];
+
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ aIn.Ignore( (meBiff == EXC_BIFF2) ? 4 : 6 );
+ break;
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ sal_uInt8 nByte = aIn.ReaduInt8(); // >> Attribute, Row >> Col
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nUINT16, nByte, aSRD, bRNorSF );
+
+ aStack << aPool.Store( aSRD );
+ break;
+ }
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ { // Area Reference Within a Shared Formula[ 274]
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt8 nColFirst, nColLast;
+
+ aCRD.Ref1.SetRelTab(0);
+ aCRD.Ref2.SetRelTab(0);
+ aCRD.Ref1.SetFlag3D( bRangeName );
+ aCRD.Ref2.SetFlag3D( bRangeName );
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8( );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRNorSF );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ aStack << aPool.Store( aCRD );
+ }
+ break;
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ aIn.Ignore( (meBiff == EXC_BIFF2) ? 1 : 2 );
+ break;
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ {
+ OUString aString = "COMM_EQU_FUNC";
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ aString += OUString::number( nByte );
+ nByte = aIn.ReaduInt8();
+ aStack << aPool.Store( aString );
+ DoMulArgs( ocPush, nByte + 1 );
+ break;
+ }
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ {
+ sal_Int16 nINT16 = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ if( nINT16 >= 0 )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ else
+ aStack << aPool.StoreName( nUINT16, -1 );
+ aIn.Ignore( 12 );
+ break;
+ }
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ {
+ sal_uInt16 nTabFirst, nTabLast, nRow;
+ sal_Int16 nExtSheet;
+ sal_uInt8 nCol;
+
+ nExtSheet = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ nTabFirst = aIn.ReaduInt16();
+ nTabLast = aIn.ReaduInt16();
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt8();
+
+ if( nExtSheet >= 0 )
+ { // from external
+ if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) )
+ {
+ nTabFirst = nTabLast;
+ nExtSheet = 0; // found
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ nExtSheet = 1; // don't create a SingleRef
+ }
+ }
+
+ if( nExtSheet <= 0 )
+ { // in current Workbook
+ aSRD.SetAbsTab(nTabFirst);
+ aSRD.SetFlag3D(true);
+
+ ExcRelToScRel( nRow, nCol, aSRD, bRangeName );
+
+ switch ( nOp )
+ {
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ // no information which part is deleted, set both
+ aSRD.SetColDeleted( true );
+ aSRD.SetRowDeleted( true );
+ }
+ if ( !ValidTab(static_cast<SCTAB>(nTabFirst)) )
+ aSRD.SetTabDeleted( true );
+
+ if( nTabLast != nTabFirst )
+ {
+ aCRD.Ref1 = aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(nTabLast);
+ aCRD.Ref2.SetTabDeleted( !ValidTab(static_cast<SCTAB>(nTabLast)) );
+ aStack << aPool.Store( aCRD );
+ }
+ else
+ aStack << aPool.Store( aSRD );
+ }
+ }
+
+ break;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ {
+ sal_uInt16 nTabFirst, nTabLast, nRowFirst, nRowLast;
+ sal_Int16 nExtSheet;
+ sal_uInt8 nColFirst, nColLast;
+
+ nExtSheet = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ nTabFirst = aIn.ReaduInt16();
+ nTabLast = aIn.ReaduInt16();
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ if( nExtSheet >= 0 )
+ // from external
+ {
+ if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) )
+ {
+ nTabFirst = nTabLast;
+ nExtSheet = 0; // found
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ nExtSheet = 1; // don't create a CompleteRef
+ }
+ }
+
+ if( nExtSheet <= 0 )
+ {// in current Workbook
+ // first part of range
+ ScSingleRefData& rR1 = aCRD.Ref1;
+ ScSingleRefData& rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nTabFirst);
+ rR2.SetAbsTab(nTabLast);
+ rR1.SetFlag3D(true);
+ rR2.SetFlag3D( nTabFirst != nTabLast );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ switch ( nOp )
+ {
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ // no information which part is deleted, set all
+ rR1.SetColDeleted( true );
+ rR1.SetRowDeleted( true );
+ rR2.SetColDeleted( true );
+ rR2.SetRowDeleted( true );
+ }
+ if ( !ValidTab(static_cast<SCTAB>(nTabFirst)) )
+ rR1.SetTabDeleted( true );
+ if ( !ValidTab(static_cast<SCTAB>(nTabLast)) )
+ rR2.SetTabDeleted( true );
+
+ aStack << aPool.Store( aCRD );
+ }//END in current Workbook
+ }
+ break;
+ default: bError = true;
+ }
+ bError |= !aIn.IsValid();
+ }
+
+ ConvErr eRet;
+
+ if( bError )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::Ni;
+ }
+ else if( aIn.GetRecPos() != nEndPos )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::Count;
+ }
+ else if( bArrayFormula )
+ {
+ pResult = nullptr;
+ eRet = ConvErr::OK;
+ }
+ else
+ {
+ pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::OK;
+ }
+
+ aIn.Seek( nEndPos );
+
+ if( eRet == ConvErr::OK )
+ ReadExtensions( aExtensions, aIn );
+
+ return eRet;
+}
+
+// stream seeks to first byte after <nFormulaLen>
+ConvErr ExcelToSc::Convert( ScRangeListTabs& rRangeList, XclImpStream& aIn, std::size_t nFormulaLen,
+ SCTAB nTab, const FORMULA_TYPE eFT )
+{
+ RootData& rR = GetOldRoot();
+ sal_uInt8 nOp, nLen;
+ bool bError = false;
+ const bool bRangeName = eFT == FT_RangeName;
+ const bool bSharedFormula = eFT == FT_SharedFormula;
+ const bool bRNorSF = bRangeName || bSharedFormula;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+ aCRD.Ref1.SetAbsTab(aEingPos.Tab());
+ aCRD.Ref2.SetAbsTab(aEingPos.Tab());
+
+ if( nFormulaLen == 0 )
+ return ConvErr::OK;
+
+ std::size_t nEndPos = aIn.GetRecPos() + nFormulaLen;
+
+ while( (aIn.GetRecPos() < nEndPos) && !bError )
+ {
+ nOp = aIn.ReaduInt8();
+ std::size_t nIgnore = 0;
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp ) // book page:
+ { // SDK4 SDK5
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ nIgnore = (meBiff == EXC_BIFF2) ? 3 : 4;
+ break;
+ case 0x02: // Data Table [325 277]
+ nIgnore = (meBiff == EXC_BIFF2) ? 3 : 4;
+ break;
+ case 0x03: // Addition [312 264]
+ case 0x04: // Subtraction [313 264]
+ case 0x05: // Multiplication [313 264]
+ case 0x06: // Division [313 264]
+ case 0x07: // Exponetiation [313 265]
+ case 0x08: // Concatenation [313 265]
+ case 0x09: // Less Than [313 265]
+ case 0x0A: // Less Than or Equal [313 265]
+ case 0x0B: // Equal [313 265]
+ case 0x0C: // Greater Than or Equal [313 265]
+ case 0x0D: // Greater Than [313 265]
+ case 0x0E: // Not Equal [313 265]
+ case 0x0F: // Intersection [314 265]
+ case 0x10: // Union [314 265]
+ case 0x11: // Range [314 265]
+ case 0x12: // Unary Plus [312 264]
+ case 0x13: // Unary Minus [312 264]
+ case 0x14: // Percent Sign [312 264]
+ case 0x15: // Parenthesis [326 278]
+ case 0x16: // Missing Argument [314 266]
+ break;
+ case 0x17: // String Constant [314 266]
+ nLen = aIn.ReaduInt8();
+ nIgnore = nLen;
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData(0), nFactor(0);
+
+ sal_uInt8 nOpt = aIn.ReaduInt8();
+
+ if( meBiff == EXC_BIFF2 )
+ {
+ nData = aIn.ReaduInt8();
+ nFactor = 1;
+ }
+ else
+ {
+ nData = aIn.ReaduInt16();
+ nFactor = 2;
+ }
+
+ if( nOpt & 0x04 )
+ {
+ // nFactor -> skip bytes or words AttrChoose
+ ++nData;
+ aIn.Ignore(static_cast<std::size_t>(nData) * nFactor);
+ }
+ }
+ break;
+ case 0x1A: // External Reference [330 ]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: nIgnore = 7; break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: nIgnore = 10; break;
+ case EXC_BIFF5: SAL_INFO( "sc", "-ExcelToSc::Convert(): 0x1A does not exist in Biff5!" );
+ [[fallthrough]];
+ default: SAL_INFO( "sc", "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x1B: // End External Reference [330 ]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: nIgnore = 3; break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: nIgnore = 4; break;
+ case EXC_BIFF5: SAL_INFO( "sc", "-ExcelToSc::Convert(): 0x1B does not exist in Biff5!" );
+ [[fallthrough]];
+ default: SAL_INFO( "sc", "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x1C: // Error Value [314 266]
+ case 0x1D: // Boolean [315 266]
+ nIgnore = 1;
+ break;
+ case 0x1E: // Integer [315 266]
+ nIgnore = 2;
+ break;
+ case 0x1F: // Number [315 266]
+ nIgnore = 8;
+ break;
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ nIgnore = (meBiff == EXC_BIFF2) ? 6 : 7;
+ break;
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ nIgnore = (meBiff <= EXC_BIFF3) ? 1 : 2;
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ nIgnore = (meBiff <= EXC_BIFF3) ? 2 : 3;
+ break;
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: nIgnore = 7; break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: nIgnore = 10; break;
+ case EXC_BIFF5: nIgnore = 14; break;
+ default: OSL_FAIL( "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ aSRD.SetAbsCol(static_cast<SCCOL>(nByte));
+ aSRD.SetAbsRow(nUINT16 & 0x3FFF);
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nUINT16, nByte, aSRD, bRangeName );
+
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ break;
+ }
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ {
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt8 nColFirst, nColLast;
+ ScSingleRefData &rSRef1 = aCRD.Ref1;
+ ScSingleRefData &rSRef2 = aCRD.Ref2;
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ rSRef1.SetRelTab(0);
+ rSRef2.SetRelTab(0);
+ rSRef1.SetFlag3D( bRangeName );
+ rSRef2.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ nIgnore = (meBiff == EXC_BIFF2) ? 4 : 6;
+ break;
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ nIgnore = 3;
+ break;
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ nIgnore = 6;
+ break;
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ sal_uInt8 nByte = aIn.ReaduInt8(); // >> Attribute, Row >> Col
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nUINT16, nByte, aSRD, bRNorSF );
+
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ break;
+ }
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ { // Area Reference Within a Shared Formula[ 274]
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt8 nColFirst, nColLast;
+
+ aCRD.Ref1.SetRelTab(0);
+ aCRD.Ref2.SetRelTab(0);
+ aCRD.Ref1.SetFlag3D( bRangeName );
+ aCRD.Ref2.SetFlag3D( bRangeName );
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRNorSF );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ nIgnore = (meBiff == EXC_BIFF2) ? 1 : 2;
+ break;
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ nIgnore = 2;
+ break;
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ nIgnore = 24;
+ break;
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ {
+ sal_uInt16 nTabFirst, nTabLast, nRow;
+ sal_Int16 nExtSheet;
+ sal_uInt8 nCol;
+
+ nExtSheet = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ nTabFirst = aIn.ReaduInt16();
+ nTabLast = aIn.ReaduInt16();
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt8();
+
+ if( nExtSheet >= 0 )
+ // from external
+ {
+ if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) )
+ {
+ nTabFirst = nTabLast;
+ nExtSheet = 0; // found
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ nExtSheet = 1; // don't create a SingleRef
+ }
+ }
+
+ if( nExtSheet <= 0 )
+ {// in current Workbook
+ bool b3D = ( static_cast<SCTAB>(nTabFirst) != aEingPos.Tab() ) || bRangeName;
+ aSRD.SetAbsTab(nTabFirst);
+ aSRD.SetFlag3D( b3D );
+
+ ExcRelToScRel( nRow, nCol, aSRD, bRangeName );
+
+ if( nTabLast != nTabFirst )
+ {
+ aCRD.Ref1 = aSRD;
+ aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(static_cast<SCTAB>(nTabLast));
+ b3D = ( static_cast<SCTAB>(nTabLast) != aEingPos.Tab() );
+ aCRD.Ref2.SetFlag3D( b3D );
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ else
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ }
+
+ break;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ {
+ sal_uInt16 nTabFirst, nTabLast, nRowFirst, nRowLast;
+ sal_Int16 nExtSheet;
+ sal_uInt8 nColFirst, nColLast;
+
+ nExtSheet = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ nTabFirst = aIn.ReaduInt16();
+ nTabLast = aIn.ReaduInt16();
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ if( nExtSheet >= 0 )
+ // from external
+ {
+ if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) )
+ {
+ nTabFirst = nTabLast;
+ nExtSheet = 0; // found
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ nExtSheet = 1; // don't create a CompleteRef
+ }
+ }
+
+ if( nExtSheet <= 0 )
+ {// in current Workbook
+ // first part of range
+ ScSingleRefData &rR1 = aCRD.Ref1;
+ ScSingleRefData &rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nTabFirst);
+ rR2.SetAbsTab(nTabLast);
+ rR1.SetFlag3D( ( static_cast<SCTAB>(nTabFirst) != aEingPos.Tab() ) || bRangeName );
+ rR2.SetFlag3D( ( static_cast<SCTAB>(nTabLast) != aEingPos.Tab() ) || bRangeName );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }//END in current Workbook
+ }
+ break;
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ nIgnore = 17;
+ break;
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ nIgnore = 20;
+ break;
+ default: bError = true;
+ }
+ bError |= !aIn.IsValid();
+
+ aIn.Ignore( nIgnore );
+ }
+
+ ConvErr eRet;
+
+ if( bError )
+ eRet = ConvErr::Ni;
+ else if( aIn.GetRecPos() != nEndPos )
+ eRet = ConvErr::Count;
+ else
+ eRet = ConvErr::OK;
+
+ aIn.Seek( nEndPos );
+ return eRet;
+}
+
+void ExcelToSc::ConvertExternName( std::unique_ptr<ScTokenArray>& /*rpArray*/, XclImpStream& /*rStrm*/, std::size_t /*nFormulaLen*/,
+ const OUString& /*rUrl*/, const vector<OUString>& /*rTabNames*/ )
+{
+}
+
+void ExcelToSc::GetAbsRefs( ScRangeList& rRangeList, XclImpStream& rStrm, std::size_t nLen )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF5 );
+ if( GetBiff() != EXC_BIFF5 )
+ return;
+
+ sal_uInt8 nOp;
+ sal_uInt16 nRow1, nRow2;
+ sal_uInt8 nCol1, nCol2;
+ SCTAB nTab1, nTab2;
+ sal_uInt16 nTabFirst, nTabLast;
+ sal_Int16 nRefIdx;
+
+ std::size_t nSeek;
+ std::size_t nEndPos = rStrm.GetRecPos() + nLen;
+
+ while( rStrm.IsValid() && (rStrm.GetRecPos() < nEndPos) )
+ {
+ nOp = rStrm.ReaduInt8();
+ nSeek = 0;
+
+ switch( nOp )
+ {
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ nRow1 = rStrm.ReaduInt16();
+ nCol1 = rStrm.ReaduInt8();
+
+ nRow2 = nRow1;
+ nCol2 = nCol1;
+ nTab1 = nTab2 = GetCurrScTab();
+ goto _common;
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ // Area Reference Within a Shared Formula[ 274]
+ nRow1 = rStrm.ReaduInt16();
+ nRow2 = rStrm.ReaduInt16();
+ nCol1 = rStrm.ReaduInt8();
+ nCol2 = rStrm.ReaduInt8();
+
+ nTab1 = nTab2 = GetCurrScTab();
+ goto _common;
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ nRefIdx = rStrm.ReadInt16();
+ rStrm.Ignore( 8 );
+ nTabFirst = rStrm.ReaduInt16();
+ nTabLast = rStrm.ReaduInt16();
+ nRow1 = rStrm.ReaduInt16();
+ nCol1 = rStrm.ReaduInt8();
+
+ nRow2 = nRow1;
+ nCol2 = nCol1;
+
+ goto _3d_common;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ nRefIdx = rStrm.ReadInt16();
+ rStrm.Ignore( 8 );
+ nTabFirst = rStrm.ReaduInt16();
+ nTabLast = rStrm.ReaduInt16();
+ nRow1 = rStrm.ReaduInt16();
+ nRow2 = rStrm.ReaduInt16();
+ nCol1 = rStrm.ReaduInt8();
+ nCol2 = rStrm.ReaduInt8();
+
+ _3d_common:
+ nTab1 = static_cast< SCTAB >( nTabFirst );
+ nTab2 = static_cast< SCTAB >( nTabLast );
+
+ // skip references to deleted sheets
+ if( (nRefIdx >= 0) || !ValidTab( nTab1 ) || (nTab1 != nTab2) )
+ break;
+
+ goto _common;
+ _common:
+ // do not check abs/rel flags, linked controls have set them!
+ {
+ ScRange aScRange;
+ nRow1 &= 0x3FFF;
+ nRow2 &= 0x3FFF;
+ if( GetAddressConverter().ConvertRange( aScRange, XclRange( nCol1, nRow1, nCol2, nRow2 ), nTab1, nTab2, true ) )
+ rRangeList.push_back( aScRange );
+ }
+ break;
+
+ case 0x03: // Addition [312 264]
+ case 0x04: // Subtraction [313 264]
+ case 0x05: // Multiplication [313 264]
+ case 0x06: // Division [313 264]
+ case 0x07: // Exponetiation [313 265]
+ case 0x08: // Concatenation [313 265]
+ case 0x09: // Less Than [313 265]
+ case 0x0A: // Less Than or Equal [313 265]
+ case 0x0B: // Equal [313 265]
+ case 0x0C: // Greater Than or Equal [313 265]
+ case 0x0D: // Greater Than [313 265]
+ case 0x0E: // Not Equal [313 265]
+ case 0x0F: // Intersection [314 265]
+ case 0x10: // Union [314 265]
+ case 0x11: // Range [314 265]
+ case 0x12: // Unary Plus [312 264]
+ case 0x13: // Unary Minus [312 264]
+ case 0x14: // Percent Sign [312 264]
+ case 0x15: // Parenthesis [326 278]
+ case 0x16: // Missing Argument [314 266]
+ break;
+ case 0x1C: // Error Value [314 266]
+ case 0x1D: // Boolean [315 266]
+ nSeek = 1;
+ break;
+ case 0x1E: // Integer [315 266]
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ nSeek = 2;
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ nSeek = 3;
+ break;
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ case 0x02: // Data Table [325 277]
+ nSeek = 4;
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ nSeek = 6;
+ break;
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ nSeek = 7;
+ break;
+ case 0x1F: // Number [315 266]
+ nSeek = 8;
+ break;
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ nSeek = 14;
+ break;
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ nSeek = 17;
+ break;
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ nSeek = 20;
+ break;
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ nSeek = 24;
+ break;
+ case 0x17: // String Constant [314 266]
+ nSeek = rStrm.ReaduInt8();
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt8 nOpt;
+ sal_uInt16 nData;
+ nOpt = rStrm.ReaduInt8();
+ nData = rStrm.ReaduInt16();
+ if( nOpt & 0x04 )
+ nSeek = nData * 2 + 2;
+ }
+ break;
+ }
+
+ rStrm.Ignore( nSeek );
+ }
+ rStrm.Seek( nEndPos );
+}
+
+void ExcelToSc::DoMulArgs( DefTokenId eId, sal_uInt8 nCnt )
+{
+ TokenId eParam[ 256 ];
+ sal_Int32 nPass;
+
+ if( eId == ocCeil || eId == ocFloor )
+ {
+ aStack << aPool.Store( 1.0 ); // default, because not present in Excel
+ nCnt++;
+ }
+
+ for( nPass = 0; aStack.HasMoreTokens() && (nPass < nCnt); nPass++ )
+ aStack >> eParam[ nPass ];
+ // #i70925# reduce parameter count, if no more tokens available on token stack
+ if( nPass < nCnt )
+ nCnt = static_cast< sal_uInt8 >( nPass );
+
+ if( nCnt > 0 && eId == ocExternal )
+ {
+ TokenId n = eParam[ nCnt - 1 ];
+//##### ADJUST STUPIDITY FOR BASIC-FUNCS!
+ if( const OUString* pExt = aPool.GetExternal( n ) )
+ {
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclMacroName( *pExt ) )
+ aPool << pFuncInfo->meOpCode;
+ else
+ aPool << n;
+ nCnt--;
+ }
+ else
+ aPool << eId;
+ }
+ else
+ aPool << eId;
+
+ aPool << ocOpen;
+
+ if( nCnt > 0 )
+ {
+ // attention: 0 = last parameter, nCnt-1 = first parameter
+ sal_Int16 nSkipEnd = -1; // skip all parameters <= nSkipEnd
+
+ sal_Int16 nLast = nCnt - 1;
+
+ // functions for which parameters have to be skipped
+ if( eId == ocPercentrank && nCnt == 3 )
+ nSkipEnd = 0; // skip last parameter if necessary
+
+ // Joost special cases
+ else if( eId == ocIf )
+ {
+ sal_uInt16 nNullParam = 0;
+ for( nPass = 0 ; nPass < nCnt ; nPass++ )
+ {
+ if( aPool.IsSingleOp( eParam[ nPass ], ocMissing ) )
+ {
+ if( !nNullParam )
+ nNullParam = static_cast<sal_uInt16>(aPool.Store( 0.0 ));
+ eParam[ nPass ] = nNullParam;
+ }
+ }
+ }
+
+ // [Parameter{;Parameter}]
+ if( nLast > nSkipEnd )
+ {
+ // nSkipEnd is either 0 or -1 => nLast >= 0
+ aPool << eParam[ nLast ];
+ for( nPass = nLast - 1 ; nPass > nSkipEnd ; nPass-- )
+ {
+ // nPass > nSkipEnd => nPass >= 0
+ aPool << ocSep << eParam[nPass];
+ }
+ }
+ }
+ aPool << ocClose;
+
+ aPool >> aStack;
+}
+
+void ExcelToSc::ExcRelToScRel( sal_uInt16 nRow, sal_uInt8 nCol, ScSingleRefData &rSRD, const bool bName )
+{
+ if( bName )
+ {
+ // C O L
+ if( nRow & 0x4000 )
+ rSRD.SetRelCol(nCol);
+ else
+ rSRD.SetAbsCol(nCol);
+
+ // R O W
+ if( nRow & 0x8000 )
+ {// rel Row
+ if( nRow & 0x2000 ) // Bit 13 set?
+ // Row negative
+ rSRD.SetRelRow(nRow | 0xC000);
+ else
+ // Row positive
+ rSRD.SetRelRow(nRow & nRowMask);
+ }
+ else
+ {// abs Row
+ rSRD.SetAbsRow(nRow & nRowMask);
+ }
+
+ // T A B
+ // abs needed if rel in shared formula for ScCompiler UpdateNameReference
+ if ( rSRD.IsTabRel() && !rSRD.IsFlag3D() )
+ rSRD.SetAbsTab(GetCurrScTab());
+ }
+ else
+ {
+ bool bColRel = (nRow & 0x4000) > 0;
+ bool bRowRel = (nRow & 0x8000) > 0;
+
+ if (bColRel)
+ rSRD.SetRelCol(nCol - aEingPos.Col());
+ else
+ rSRD.SetAbsCol(nCol);
+
+ rSRD.SetAbsRow(nRow & nRowMask);
+ if (bRowRel)
+ rSRD.SetRelRow(rSRD.Row() - aEingPos.Row());
+
+ // T A B
+ // #i10184# abs needed if rel in shared formula for ScCompiler UpdateNameReference
+ if ( rSRD.IsTabRel() && !rSRD.IsFlag3D() )
+ rSRD.SetAbsTab(GetCurrScTab() + rSRD.Tab());
+ }
+}
+
+std::unique_ptr<ScTokenArray> ExcelToSc::GetBoolErr( XclBoolError eType )
+{
+ FormulaError nError;
+ aPool.Reset();
+ aStack.Reset();
+
+ DefTokenId eOc;
+
+ switch( eType )
+ {
+ case xlErrNull: eOc = ocStop; nError = FormulaError::NoCode; break;
+ case xlErrDiv0: eOc = ocStop; nError = FormulaError::DivisionByZero; break;
+ case xlErrValue: eOc = ocStop; nError = FormulaError::NoValue; break;
+ case xlErrRef: eOc = ocStop; nError = FormulaError::NoRef; break;
+ case xlErrName: eOc = ocStop; nError = FormulaError::NoName; break;
+ case xlErrNum: eOc = ocStop; nError = FormulaError::IllegalFPOperation; break;
+ case xlErrNA: eOc = ocNotAvail; nError = FormulaError::NotAvailable; break;
+ case xlErrTrue: eOc = ocTrue; nError = FormulaError::NONE; break;
+ case xlErrFalse: eOc = ocFalse; nError = FormulaError::NONE; break;
+ case xlErrUnknown: eOc = ocStop; nError = FormulaError::UnknownState; break;
+ default:
+ OSL_FAIL( "ExcelToSc::GetBoolErr - wrong enum!" );
+ eOc = ocNoName;
+ nError = FormulaError::UnknownState;
+ }
+
+ aPool << eOc;
+ if( eOc != ocStop )
+ aPool << ocOpen << ocClose;
+
+ aPool >> aStack;
+
+ std::unique_ptr<ScTokenArray> pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ if( nError != FormulaError::NONE )
+ pResult->SetCodeError( nError );
+
+ pResult->SetExclusiveRecalcModeNormal();
+
+ return pResult;
+}
+
+bool ExcelToSc::ReadSharedFormulaPosition( XclImpStream& rStrm, SCCOL& rCol, SCROW& rRow )
+{
+ rStrm.PushPosition();
+
+ sal_uInt8 nOp;
+ nOp = rStrm.ReaduInt8();
+
+ if (nOp != 0x01) // must be PtgExp token.
+ {
+ rStrm.PopPosition();
+ return false;
+ }
+
+ sal_uInt16 nRow, nCol;
+ nRow = rStrm.ReaduInt16();
+ nCol = rStrm.ReaduInt16();
+ rStrm.PopPosition();
+ rCol = nCol;
+ rRow = nRow;
+ return true;
+}
+
+const ScTokenArray* ExcelToSc::GetSharedFormula( const ScAddress& rRefPos ) const
+{
+ return GetOldRoot().pShrfmlaBuff->Find(rRefPos);
+}
+
+void ExcelToSc::SetError( ScFormulaCell &rCell, const ConvErr eErr )
+{
+ FormulaError nInd;
+
+ switch( eErr )
+ {
+ case ConvErr::Ni: nInd = FormulaError::UnknownToken; break;
+ case ConvErr::Count: nInd = FormulaError::CodeOverflow; break;
+ default: nInd = FormulaError::NoCode; // I had no better idea
+ }
+
+ rCell.SetErrCode( nInd );
+}
+
+void ExcelToSc::SetComplCol( ScComplexRefData &rCRD )
+{
+ ScSingleRefData &rSRD = rCRD.Ref2;
+ ScDocument& rDoc = GetDocImport().getDoc();
+ if( rSRD.IsColRel() )
+ rSRD.SetRelCol(rDoc.MaxCol() - aEingPos.Col());
+ else
+ rSRD.SetAbsCol(rDoc.MaxCol());
+}
+
+void ExcelToSc::SetComplRow( ScComplexRefData &rCRD )
+{
+ ScSingleRefData &rSRD = rCRD.Ref2;
+ ScDocument& rDoc = GetDocImport().getDoc();
+ if( rSRD.IsRowRel() )
+ rSRD.SetRelRow(rDoc.MaxRow() - aEingPos.Row());
+ else
+ rSRD.SetAbsRow(rDoc.MaxRow());
+}
+
+void ExcelToSc::ReadExtensionArray( unsigned int n, XclImpStream& aIn )
+{
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+
+ SCSIZE nC, nCols;
+ SCSIZE nR, nRows;
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ nCols = nByte + 1;
+ nRows = nUINT16 + 1;
+ }
+ else
+ {
+ nCols = nByte ? nByte : 256;
+ nRows = nUINT16;
+ }
+
+ ScMatrix* pMatrix = aPool.GetMatrix( n );
+
+ if( nullptr != pMatrix )
+ {
+ pMatrix->Resize(nCols, nRows);
+ pMatrix->GetDimensions( nC, nR);
+ if( nC != nCols || nR != nRows )
+ {
+ OSL_FAIL( "ExcelToSc::ReadExtensionArray - matrix size mismatch" );
+ pMatrix = nullptr;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "ExcelToSc::ReadExtensionArray - missing matrix" );
+ }
+
+ //assuming worst case scenario of unknown types
+ const size_t nMinRecordSize = 1;
+ const size_t nMaxRows = aIn.GetRecLeft() / (nMinRecordSize * nCols);
+ if (nRows > nMaxRows)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRows <<
+ " max possible rows, but " << nRows << " claimed, truncating");
+ nRows = nMaxRows;
+ }
+
+ svl::SharedStringPool& rPool = GetDoc().GetSharedStringPool();
+ for( nR = 0 ; nR < nRows; nR++ )
+ {
+ for( nC = 0 ; nC < nCols; nC++ )
+ {
+ nByte = aIn.ReaduInt8();
+ switch( nByte )
+ {
+ case EXC_CACHEDVAL_EMPTY:
+ aIn.Ignore( 8 );
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutEmpty( nC, nR );
+ }
+ break;
+
+ case EXC_CACHEDVAL_DOUBLE:
+ {
+ double fDouble = aIn.ReadDouble();
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutDouble( fDouble, nC, nR );
+ }
+ break;
+ }
+ case EXC_CACHEDVAL_STRING:
+ {
+ OUString aString;
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ nUINT16 = aIn.ReaduInt16();
+ aString = aIn.ReadUniString( nUINT16 );
+ }
+ else
+ {
+ nByte = aIn.ReaduInt8();
+ aString = aIn.ReadRawByteString( nByte );
+ }
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutString(rPool.intern(aString), nC, nR);
+ }
+ break;
+ }
+ case EXC_CACHEDVAL_BOOL:
+ nByte = aIn.ReaduInt8();
+ aIn.Ignore( 7 );
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutBoolean( nByte != 0, nC, nR );
+ }
+ break;
+
+ case EXC_CACHEDVAL_ERROR:
+ nByte = aIn.ReaduInt8();
+ aIn.Ignore( 7 );
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutError( XclTools::GetScErrorCode( nByte ), nC, nR );
+ }
+ break;
+ }
+ }
+ }
+}
+
+void ExcelToSc::ReadExtensionNlr( XclImpStream& aIn )
+{
+ sal_uInt32 nFlags;
+ nFlags = aIn.ReaduInt32();
+
+ sal_uInt32 nCount = nFlags & EXC_TOK_NLR_ADDMASK;
+ aIn.Ignore( nCount * 4 ); // Drop the cell positions
+}
+
+void ExcelToSc::ReadExtensionMemArea( XclImpStream& aIn )
+{
+ sal_uInt16 nCount = aIn.ReaduInt16();
+
+ aIn.Ignore( static_cast<std::size_t>(nCount) * ((GetBiff() == EXC_BIFF8) ? 8 : 6) ); // drop the ranges
+}
+
+void ExcelToSc::ReadExtensions( const ExtensionTypeVec& rExtensions,
+ XclImpStream& aIn )
+{
+ unsigned int nArray = 0;
+
+ for(int eType : rExtensions)
+ {
+ switch( eType )
+ {
+ case EXTENSION_ARRAY:
+ ReadExtensionArray( nArray++, aIn );
+ break;
+
+ case EXTENSION_NLR:
+ ReadExtensionNlr( aIn );
+ break;
+
+ case EXTENSION_MEMAREA:
+ ReadExtensionMemArea( aIn );
+ break;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excform8.cxx b/sc/source/filter/excel/excform8.cxx
new file mode 100644
index 000000000..62e184204
--- /dev/null
+++ b/sc/source/filter/excel/excform8.cxx
@@ -0,0 +1,1671 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excform.hxx>
+
+#include <document.hxx>
+#include <documentimport.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xilink.hxx>
+#include <xiname.hxx>
+
+#include <externalrefmgr.hxx>
+
+#include <cstring>
+
+#include <o3tl/safeint.hxx>
+
+using ::std::vector;
+
+namespace {
+
+/**
+ * Extract a file path from OLE link path. An OLE link path is expected to
+ * be in the following format:
+ *
+ * Excel.Sheet.8 \3 [file path]
+ */
+bool extractFilePath(const OUString& rUrl, OUString& rPath)
+{
+ const char* prefix = "Excel.Sheet.8\3";
+ size_t nPrefixLen = ::std::strlen(prefix);
+
+ sal_Int32 n = rUrl.getLength();
+ if (n <= static_cast<sal_Int32>(nPrefixLen))
+ // needs to have the specified prefix.
+ return false;
+
+ OUStringBuffer aBuf;
+ const sal_Unicode* p = rUrl.getStr();
+ for (size_t i = 0; i < o3tl::make_unsigned(n); ++i, ++p)
+ {
+ if (i < nPrefixLen)
+ {
+ sal_Unicode pc = static_cast<sal_Unicode>(*prefix++);
+ if (pc != *p)
+ return false;
+
+ continue;
+ }
+ aBuf.append(*p);
+ }
+
+ rPath = aBuf.makeStringAndClear();
+ return true;
+}
+
+}
+
+ExcelToSc8::ExternalTabInfo::ExternalTabInfo() :
+ mnFileId(0), mbExternal(false)
+{
+}
+
+ExcelToSc8::ExcelToSc8( XclImpRoot& rRoot ) :
+ ExcelToSc( rRoot ),
+ rLinkMan( rRoot.GetLinkManager() )
+{
+}
+
+ExcelToSc8::~ExcelToSc8()
+{
+}
+
+bool ExcelToSc8::GetExternalFileIdFromXti( sal_uInt16 nIxti, sal_uInt16& rFileId ) const
+{
+ const OUString* pFileUrl = rLinkMan.GetSupbookUrl(nIxti);
+ if (!pFileUrl || pFileUrl->isEmpty() || !GetDocShell())
+ return false;
+
+ OUString aFileUrl = ScGlobal::GetAbsDocName(*pFileUrl, GetDocShell());
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ rFileId = pRefMgr->getExternalFileId(aFileUrl);
+
+ return true;
+}
+
+bool ExcelToSc8::Read3DTabReference( sal_uInt16 nIxti, SCTAB& rFirstTab, SCTAB& rLastTab, ExternalTabInfo& rExtInfo )
+{
+ rFirstTab = rLastTab = 0;
+ rExtInfo.mbExternal = !rLinkMan.IsSelfRef(nIxti);
+ bool bSuccess = rLinkMan.GetScTabRange(rFirstTab, rLastTab, nIxti);
+ if (!bSuccess)
+ return false;
+
+ if (!rExtInfo.mbExternal)
+ // This is internal reference. Stop here.
+ return true;
+
+ rExtInfo.maTabName = rLinkMan.GetSupbookTabName(nIxti, rFirstTab);
+ return GetExternalFileIdFromXti(nIxti, rExtInfo.mnFileId);
+}
+
+bool ExcelToSc8::HandleOleLink(sal_uInt16 nXtiIndex, const XclImpExtName& rExtName, ExternalTabInfo& rExtInfo)
+{
+ const OUString* pUrl = rLinkMan.GetSupbookUrl(nXtiIndex);
+ if (!pUrl)
+ return false;
+
+ OUString aPath;
+ if (!extractFilePath(*pUrl, aPath))
+ // file path extraction failed.
+ return false;
+
+ OUString aFileUrl = ScGlobal::GetAbsDocName(aPath, GetDocShell());
+ return rExtName.CreateOleData(GetDoc(), aFileUrl, rExtInfo.mnFileId, rExtInfo.maTabName, rExtInfo.maRange);
+}
+
+// if bAllowArrays is false stream seeks to first byte after <nFormulaLen>
+// otherwise it will seek to the first byte past additional content after <nFormulaLen>
+ConvErr ExcelToSc8::Convert( std::unique_ptr<ScTokenArray>& rpTokArray, XclImpStream& aIn, std::size_t nFormulaLen, bool bAllowArrays, const FORMULA_TYPE eFT )
+{
+ bool bError = false;
+ bool bArrayFormula = false;
+ TokenId nBuf0;
+ const bool bCondFormat = eFT == FT_CondFormat;
+ const bool bRangeName = eFT == FT_RangeName;
+ const bool bRangeNameOrCond = bRangeName || bCondFormat;
+ const bool bSharedFormula = eFT == FT_SharedFormula;
+ const bool bRNorSF = bRangeNameOrCond || bSharedFormula;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+ ExtensionTypeVec aExtensions;
+
+ if( nFormulaLen == 0 )
+ {
+ aPool.Store( "-/-" );
+ aPool >> aStack;
+ rpTokArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ return ConvErr::OK;
+ }
+
+ std::size_t nEndPos = aIn.GetRecPos() + nFormulaLen;
+
+ while( (aIn.GetRecPos() < nEndPos) && !bError )
+ {
+ sal_uInt8 nOp = aIn.ReaduInt8();
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp ) // book page:
+ { // SDK4 SDK5
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ case 0x02: // Data Table [325 277]
+ aIn.Ignore( 4 );
+
+ bArrayFormula = true;
+ break;
+ case 0x03: // Addition [312 264]
+ aStack >> nBuf0;
+ aPool << aStack << ocAdd << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x04: // Subtraction [313 264]
+ // SECOND-TOP minus TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocSub << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x05: // Multiplication [313 264]
+ aStack >> nBuf0;
+ aPool << aStack << ocMul << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x06: // Division [313 264]
+ // divide TOP by SECOND-TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocDiv << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x07: // Exponentiation [313 265]
+ // raise SECOND-TOP to power of TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocPow << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x08: // Concatenation [313 265]
+ // append TOP to SECOND-TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocAmpersand << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x09: // Less Than [313 265]
+ // SECOND-TOP < TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocLess << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0A: // Less Than or Equal [313 265]
+ // SECOND-TOP <= TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocLessEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0B: // Equal [313 265]
+ // SECOND-TOP == TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0C: // Greater Than or Equal [313 265]
+ // SECOND-TOP >= TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocGreaterEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0D: // Greater Than [313 265]
+ // SECOND-TOP > TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocGreater << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0E: // Not Equal [313 265]
+ // SECOND-TOP != TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocNotEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0F: // Intersection [314 265]
+ aStack >> nBuf0;
+ aPool << aStack << ocIntersect << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x10: // Union [314 265]
+ // ocSep instead of 'ocUnion'
+ aStack >> nBuf0;
+ aPool << aStack << ocSep << nBuf0;
+ // doesn't fit exactly, but is more Excel-like
+ aPool >> aStack;
+ break;
+ case 0x11: // Range [314 265]
+ aStack >> nBuf0;
+ aPool << aStack << ocRange << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x12: // Unary Plus [312 264]
+ aPool << ocAdd << aStack;
+ aPool >> aStack;
+ break;
+ case 0x13: // Unary Minus [312 264]
+ aPool << ocNegSub << aStack;
+ aPool >> aStack;
+ break;
+ case 0x14: // Percent Sign [312 264]
+ aPool << aStack << ocPercentSign;
+ aPool >> aStack;
+ break;
+ case 0x15: // Parenthesis [326 278]
+ aPool << ocOpen << aStack << ocClose;
+ aPool >> aStack;
+ break;
+ case 0x16: // Missing Argument [314 266]
+ aPool << ocMissing;
+ aPool >> aStack;
+ GetTracer().TraceFormulaMissingArg();
+ break;
+ case 0x17: // String Constant [314 266]
+ {
+ sal_uInt8 nLen = aIn.ReaduInt8(); // Why?
+ OUString aString = aIn.ReadUniString( nLen ); // reads Grbit even if nLen==0
+
+ aStack << aPool.Store( aString );
+ break;
+ }
+ case 0x18: // natural language formula
+ {
+ sal_uInt8 nEptg;
+ sal_uInt16 nCol, nRow;
+ nEptg = aIn.ReaduInt8();
+ switch( nEptg )
+ { // name size ext type
+ case 0x01: // Lel 4 - err
+ aIn.Ignore( 4 );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case 0x02: // Rw 4 - ref
+ case 0x03: // Col 4 - ref
+ case 0x06: // RwV 4 - val
+ case 0x07: // ColV 4 - val
+ {
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+ ScAddress aAddr(static_cast<SCCOL>(nCol & 0xFF), static_cast<SCROW>(nRow), aEingPos.Tab());
+ aSRD.InitAddress(aAddr);
+
+ if( nEptg == 0x02 || nEptg == 0x06 )
+ aSRD.SetRowRel(true);
+ else
+ aSRD.SetColRel(true);
+
+ aSRD.SetAddress(GetDocImport().getDoc().GetSheetLimits(), aAddr, aEingPos);
+
+ aStack << aPool.StoreNlf( aSRD );
+
+ break;
+ }
+ case 0x0A: // Radical 13 - ref
+ {
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+ aIn.Ignore( 9 );
+ ScAddress aAddr(static_cast<SCCOL>(nCol & 0xFF), static_cast<SCROW>(nRow), aEingPos.Tab());
+ aSRD.InitAddress(aAddr);
+ aSRD.SetColRel(true);
+ aSRD.SetAddress(GetDocImport().getDoc().GetSheetLimits(), aAddr, aEingPos);
+
+ aStack << aPool.StoreNlf( aSRD );
+
+ break;
+ }
+ case 0x0B: // RadicalS 13 x ref
+ aIn.Ignore( 13 );
+ aExtensions.push_back( EXTENSION_NLR );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case 0x0C: // RwS 4 x ref
+ case 0x0D: // ColS 4 x ref
+ case 0x0E: // RwSV 4 x val
+ case 0x0F: // ColSV 4 x val
+ aIn.Ignore( 4 );
+ aExtensions.push_back( EXTENSION_NLR );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case 0x10: // RadicalLel 4 - err
+ case 0x1D: // SxName 4 - val
+ aIn.Ignore( 4 );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ default:
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ }
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData(0), nFactor(0);
+
+ sal_uInt8 nOpt = aIn.ReaduInt8();
+ nData = aIn.ReaduInt16();
+ nFactor = 2;
+
+ if( nOpt & 0x04 )
+ {
+ // nFactor -> skip bytes or words AttrChoose
+ nData++;
+ aIn.Ignore(static_cast<std::size_t>(nData) * nFactor);
+ }
+ else if( nOpt & 0x10 ) // AttrSum
+ DoMulArgs( ocSum, 1 );
+ break;
+ }
+ case 0x1C: // Error Value [314 266]
+ {
+ sal_uInt8 nByte = aIn.ReaduInt8();
+
+ DefTokenId eOc;
+ switch( nByte )
+ {
+ case EXC_ERR_NULL:
+ case EXC_ERR_DIV0:
+ case EXC_ERR_VALUE:
+ case EXC_ERR_REF:
+ case EXC_ERR_NAME:
+ case EXC_ERR_NUM: eOc = ocStop; break;
+ case EXC_ERR_NA: eOc = ocNotAvail; break;
+ default: eOc = ocNoName;
+ }
+ aPool << eOc;
+ if( eOc != ocStop )
+ aPool << ocOpen << ocClose;
+ aPool >> aStack;
+
+ break;
+ }
+ case 0x1D: // Boolean [315 266]
+ {
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ if( nByte == 0 )
+ aPool << ocFalse << ocOpen << ocClose;
+ else
+ aPool << ocTrue << ocOpen << ocClose;
+ aPool >> aStack;
+ break;
+ }
+ case 0x1E: // Integer [315 266]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ aStack << aPool.Store( static_cast<double>(nUINT16) );
+ break;
+ }
+ case 0x1F: // Number [315 266]
+ {
+ double fDouble = aIn.ReadDouble();
+ aStack << aPool.Store( fDouble );
+ break;
+ }
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ {
+ aIn.Ignore( 7 );
+ if( bAllowArrays )
+ {
+ aStack << aPool.StoreMatrix();
+ aExtensions.push_back( EXTENSION_ARRAY );
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ break;
+ }
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ {
+ sal_uInt16 nXclFunc;
+ nXclFunc = aIn.ReaduInt16();
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) )
+ DoMulArgs( pFuncInfo->meOpCode, pFuncInfo->mnMaxParamCount );
+ else
+ DoMulArgs( ocNoName, 0 );
+ break;
+ }
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ {
+ sal_uInt16 nXclFunc;
+ sal_uInt8 nParamCount;
+ nParamCount = aIn.ReaduInt8();
+ nXclFunc = aIn.ReaduInt16();
+ nParamCount &= 0x7F;
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) )
+ DoMulArgs( pFuncInfo->meOpCode, nParamCount );
+ else
+ DoMulArgs( ocNoName, 0 );
+ break;
+ }
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ aIn.Ignore( 2 );
+ const XclImpName* pName = GetNameManager().GetName( nUINT16 );
+ if (pName)
+ {
+ if (pName->IsMacro())
+ // user-defined macro name.
+ aStack << aPool.Store(ocMacro, pName->GetXclName());
+ else
+ aStack << aPool.StoreName(nUINT16, pName->IsGlobal() ? -1 : pName->GetScTab());
+ }
+ break;
+ }
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ {
+ sal_uInt16 nCol, nRow;
+
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel8( nRow, nCol, aSRD, bRangeNameOrCond );
+
+ switch ( nOp )
+ {
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ // no information which part is deleted, set both
+ aSRD.SetColDeleted( true );
+ aSRD.SetRowDeleted( true );
+ }
+
+ aStack << aPool.Store( aSRD );
+ break;
+ }
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ {
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt16 nColFirst, nColLast;
+ ScSingleRefData &rSRef1 = aCRD.Ref1;
+ ScSingleRefData &rSRef2 = aCRD.Ref2;
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt16();
+ nColLast = aIn.ReaduInt16();
+
+ rSRef1.SetRelTab(0);
+ rSRef2.SetRelTab(0);
+ rSRef1.SetFlag3D( bRangeName );
+ rSRef2.SetFlag3D( bRangeName );
+
+ ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRangeNameOrCond );
+ ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRangeNameOrCond );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ switch ( nOp )
+ {
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ // no information which part is deleted, set all
+ rSRef1.SetColDeleted( true );
+ rSRef1.SetRowDeleted( true );
+ rSRef2.SetColDeleted( true );
+ rSRef2.SetRowDeleted( true );
+ }
+
+ aStack << aPool.Store( aCRD );
+ break;
+ }
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ aExtensions.push_back( EXTENSION_MEMAREA );
+ aIn.Ignore( 6 ); // There isn't any more
+ break;
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ aIn.Ignore( 6 ); // There isn't any more
+ break;
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ aIn.Ignore( 6 ); // There isn't any more
+ break;
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ aIn.Ignore( 2 ); // There isn't any more
+ break;
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ {
+ sal_uInt16 nRow, nCol;
+
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel8( nRow, nCol, aSRD, bRNorSF );
+
+ aStack << aPool.Store( aSRD );
+ break;
+ }
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ { // Area Reference Within a Shared Formula[ 274]
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt16 nColFirst, nColLast;
+
+ aCRD.Ref1.SetRelTab(0);
+ aCRD.Ref2.SetRelTab(0);
+ aCRD.Ref1.SetFlag3D( bRangeName );
+ aCRD.Ref2.SetFlag3D( bRangeName );
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt16();
+ nColLast = aIn.ReaduInt16();
+
+ ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF );
+ ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRNorSF );
+
+ bool bColRel = aCRD.Ref1.IsColRel() || aCRD.Ref2.IsColRel();
+ bool bRowRel = aCRD.Ref1.IsRowRel() || aCRD.Ref2.IsRowRel();
+
+ if( !bColRel && IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( !bRowRel && IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ aStack << aPool.Store( aCRD );
+ break;
+ }
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ aIn.Ignore( 2 ); // There isn't any more
+ break;
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ aIn.Ignore( 2 ); // There isn't any more
+ break;
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ {
+ OUString aString = "COMM_EQU_FUNC";
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ aString += OUString::number( nByte );
+ nByte = aIn.ReaduInt8();
+ aStack << aPool.Store( aString );
+ DoMulArgs( ocPush, nByte + 1 );
+ break;
+ }
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ {
+ sal_uInt16 nXtiIndex, nNameIdx;
+ nXtiIndex = aIn.ReaduInt16();
+ nNameIdx = aIn.ReaduInt16();
+ aIn.Ignore( 2 );
+
+ if( rLinkMan.IsSelfRef( nXtiIndex ) )
+ {
+ // internal defined name with explicit sheet, i.e.: =Sheet1!AnyName
+ const XclImpName* pName = GetNameManager().GetName( nNameIdx );
+ if (pName)
+ {
+ if (pName->GetScRangeData())
+ aStack << aPool.StoreName( nNameIdx, pName->IsGlobal() ? -1 : pName->GetScTab());
+ else
+ aStack << aPool.Store(ocMacro, pName->GetXclName());
+ }
+ }
+ else if( const XclImpExtName* pExtName = rLinkMan.GetExternName( nXtiIndex, nNameIdx ) )
+ {
+ switch( pExtName->GetType() )
+ {
+ case xlExtName:
+ {
+ /* FIXME: enable this code for #i4385# once
+ * external name reference can be stored in ODF,
+ * which remains to be done for #i3740#. Until then
+ * create a #NAME? token. */
+#if 1
+ sal_uInt16 nFileId;
+ if (!GetExternalFileIdFromXti(nXtiIndex, nFileId) || !pExtName->HasFormulaTokens())
+ {
+ aStack << aPool.Store(ocNoName, pExtName->GetName());
+ break;
+ }
+
+ aStack << aPool.StoreExtName(nFileId, pExtName->GetName());
+ pExtName->CreateExtNameData(GetDoc(), nFileId);
+#else
+ aStack << aPool.Store( ocNoName, pExtName->GetName() );
+#endif
+ }
+ break;
+
+ case xlExtAddIn:
+ {
+ aStack << aPool.Store( ocExternal, pExtName->GetName() );
+ }
+ break;
+
+ case xlExtDDE:
+ {
+ OUString aApplic, aTopic;
+ if( rLinkMan.GetLinkData( aApplic, aTopic, nXtiIndex ) )
+ {
+ TokenId nPar1 = aPool.Store( aApplic );
+ TokenId nPar2 = aPool.Store( aTopic );
+ nBuf0 = aPool.Store( pExtName->GetName() );
+ aPool << ocDde << ocOpen << nPar1 << ocSep << nPar2 << ocSep
+ << nBuf0 << ocClose;
+ aPool >> aStack;
+ pExtName->CreateDdeData( GetDoc(), aApplic, aTopic );
+ GetDoc().SetLinkFormulaNeedingCheck(true);
+ }
+ }
+ break;
+
+ case xlExtEuroConvert:
+ {
+ aStack << aPool.Store( ocEuroConvert, OUString() );
+ }
+ break;
+ case xlExtOLE:
+ {
+ ExternalTabInfo aExtInfo;
+ if (HandleOleLink(nXtiIndex, *pExtName, aExtInfo))
+ {
+ if (aExtInfo.maRange.aStart == aExtInfo.maRange.aEnd)
+ {
+ // single cell
+ aSRD.InitAddress(aExtInfo.maRange.aStart);
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aSRD);
+ }
+ else
+ {
+ // range
+ aCRD.InitRange(aExtInfo.maRange);
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aCRD);
+ }
+ }
+ else
+ aStack << aPool.Store(ocNoName, pExtName->GetName());
+ }
+ break;
+ default:
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ }
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ break;
+ }
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ {
+ sal_uInt16 nIxti, nRw, nGrbitCol;
+ SCTAB nTabFirst, nTabLast;
+
+ nIxti = aIn.ReaduInt16();
+ nRw = aIn.ReaduInt16();
+ nGrbitCol = aIn.ReaduInt16();
+
+ ExternalTabInfo aExtInfo;
+ if (!Read3DTabReference(nIxti, nTabFirst, nTabLast, aExtInfo))
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ }
+
+ aSRD.SetAbsTab(nTabFirst);
+ aSRD.SetFlag3D(true);
+
+ ExcRelToScRel8( nRw, nGrbitCol, aSRD, bRangeNameOrCond );
+
+ switch ( nOp )
+ {
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ // no information which part is deleted, set both
+ aSRD.SetColDeleted( true );
+ aSRD.SetRowDeleted( true );
+ }
+
+ if (aExtInfo.mbExternal)
+ {
+ // nTabFirst and nTabLast are the indices of the referenced
+ // sheets in the SUPBOOK record, hence do not represent
+ // the actual indices of the original sheets since the
+ // SUPBOOK record only stores referenced sheets and skips
+ // the ones that are not referenced.
+
+ if (nTabLast != nTabFirst)
+ {
+ aCRD.Ref1 = aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(nTabLast);
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aCRD);
+ }
+ else
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aSRD);
+ }
+ else
+ {
+ if ( !ValidTab(nTabFirst))
+ aSRD.SetTabDeleted( true );
+
+ if( nTabLast != nTabFirst )
+ {
+ aCRD.Ref1 = aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(nTabLast);
+ aCRD.Ref2.SetTabDeleted( !ValidTab(nTabLast) );
+ aStack << aPool.Store( aCRD );
+ }
+ else
+ aStack << aPool.Store( aSRD );
+ }
+ break;
+ }
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ {
+ sal_uInt16 nIxti, nRw1, nGrbitCol1, nRw2, nGrbitCol2;
+ SCTAB nTabFirst, nTabLast;
+ nIxti = aIn.ReaduInt16();
+ nRw1 = aIn.ReaduInt16();
+ nRw2 = aIn.ReaduInt16();
+ nGrbitCol1 = aIn.ReaduInt16();
+ nGrbitCol2 = aIn.ReaduInt16();
+
+ ExternalTabInfo aExtInfo;
+ if (!Read3DTabReference(nIxti, nTabFirst, nTabLast, aExtInfo))
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ }
+ ScSingleRefData &rR1 = aCRD.Ref1;
+ ScSingleRefData &rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nTabFirst);
+ rR2.SetAbsTab(nTabLast);
+ rR1.SetFlag3D(true);
+ rR2.SetFlag3D( nTabFirst != nTabLast );
+
+ ExcRelToScRel8( nRw1, nGrbitCol1, aCRD.Ref1, bRangeNameOrCond );
+ ExcRelToScRel8( nRw2, nGrbitCol2, aCRD.Ref2, bRangeNameOrCond );
+
+ if( IsComplColRange( nGrbitCol1, nGrbitCol2 ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRw1, nRw2 ) )
+ SetComplRow( aCRD );
+
+ switch ( nOp )
+ {
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ // no information which part is deleted, set all
+ rR1.SetColDeleted( true );
+ rR1.SetRowDeleted( true );
+ rR2.SetColDeleted( true );
+ rR2.SetRowDeleted( true );
+ }
+
+ if (aExtInfo.mbExternal)
+ {
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aCRD);
+ }
+ else
+ {
+ if ( !ValidTab(nTabFirst) )
+ rR1.SetTabDeleted( true );
+ if ( !ValidTab(nTabLast) )
+ rR2.SetTabDeleted( true );
+
+ aStack << aPool.Store( aCRD );
+ }
+ break;
+ }
+ default:
+ bError = true;
+ }
+ bError |= !aIn.IsValid();
+ }
+
+ ConvErr eRet;
+
+ if( bError )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ rpTokArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::Ni;
+ }
+ else if( aIn.GetRecPos() != nEndPos )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ rpTokArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::Count;
+ }
+ else if( bArrayFormula )
+ {
+ rpTokArray = nullptr;
+ eRet = ConvErr::OK;
+ }
+ else
+ {
+ rpTokArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::OK;
+ }
+
+ aIn.Seek( nEndPos );
+
+ if( eRet == ConvErr::OK)
+ ReadExtensions( aExtensions, aIn );
+
+ return eRet;
+}
+
+// stream seeks to first byte after <nFormulaLen>
+ConvErr ExcelToSc8::Convert( ScRangeListTabs& rRangeList, XclImpStream& aIn, std::size_t nFormulaLen,
+ SCTAB nTab, const FORMULA_TYPE eFT )
+{
+ sal_uInt8 nOp, nLen;
+ bool bError = false;
+ const bool bCondFormat = eFT == FT_CondFormat;
+ const bool bRangeName = eFT == FT_RangeName || bCondFormat;
+ const bool bSharedFormula = eFT == FT_SharedFormula;
+ const bool bRNorSF = bRangeName || bSharedFormula;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+
+ if( nFormulaLen == 0 )
+ return ConvErr::OK;
+
+ std::size_t nEndPos = aIn.GetRecPos() + nFormulaLen;
+
+ while( (aIn.GetRecPos() < nEndPos) && !bError )
+ {
+ nOp = aIn.ReaduInt8();
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp ) // book page:
+ { // SDK4 SDK5
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ aIn.Ignore( 4 );
+ break;
+ case 0x02: // Data Table [325 277]
+ aIn.Ignore( 4 );
+ break;
+ case 0x03: // Addition [312 264]
+ case 0x04: // Subtraction [313 264]
+ case 0x05: // Multiplication [313 264]
+ case 0x06: // Division [313 264]
+ case 0x07: // Exponetiation [313 265]
+ case 0x08: // Concatenation [313 265]
+ case 0x09: // Less Than [313 265]
+ case 0x0A: // Less Than or Equal [313 265]
+ case 0x0B: // Equal [313 265]
+ case 0x0C: // Greater Than or Equal [313 265]
+ case 0x0D: // Greater Than [313 265]
+ case 0x0E: // Not Equal [313 265]
+ case 0x0F: // Intersection [314 265]
+ case 0x10: // Union [314 265]
+ case 0x11: // Range [314 265]
+ case 0x12: // Unary Plus [312 264]
+ case 0x13: // Unary Minus [312 264]
+ case 0x14: // Percent Sign [312 264]
+ case 0x15: // Parenthesis [326 278]
+ case 0x16: // Missing Argument [314 266]
+ break;
+ case 0x17: // String Constant [314 266]
+ nLen = aIn.ReaduInt8(); // Why?
+
+ aIn.IgnoreUniString( nLen ); // reads Grbit even if nLen==0
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData(0), nFactor(0);
+
+ sal_uInt8 nOpt = aIn.ReaduInt8();
+ nData = aIn.ReaduInt16();
+ nFactor = 2;
+
+ if( nOpt & 0x04 )
+ {
+ // nFactor -> skip bytes or words AttrChoose
+ ++nData;
+ aIn.Ignore(static_cast<std::size_t>(nData) * nFactor);
+ }
+ }
+ break;
+ case 0x1C: // Error Value [314 266]
+ case 0x1D: // Boolean [315 266]
+ aIn.Ignore( 1 );
+ break;
+ case 0x1E: // Integer [315 266]
+ aIn.Ignore( 2 );
+ break;
+ case 0x1F: // Number [315 266]
+ aIn.Ignore( 8 );
+ break;
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ aIn.Ignore( 7 );
+ break;
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ aIn.Ignore( 2 );
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ aIn.Ignore( 3 );
+ break;
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ aIn.Ignore( 4 );
+ break;
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ {
+ sal_uInt16 nCol, nRow;
+
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName && !bCondFormat );
+
+ ExcRelToScRel8( nRow, nCol, aSRD, bRangeName );
+
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ {
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt16 nColFirst, nColLast;
+ ScSingleRefData &rSRef1 = aCRD.Ref1;
+ ScSingleRefData &rSRef2 = aCRD.Ref2;
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt16();
+ nColLast = aIn.ReaduInt16();
+
+ rSRef1.SetRelTab(0);
+ rSRef2.SetRelTab(0);
+ rSRef1.SetFlag3D( bRangeName && !bCondFormat );
+ rSRef2.SetFlag3D( bRangeName && !bCondFormat );
+
+ ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ aIn.Ignore( 6 ); // There isn't any more
+ break;
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ aIn.Ignore( 2 ); // There isn't any more
+ break;
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ aIn.Ignore( 3 );
+ break;
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ aIn.Ignore( 6 );
+ break;
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ {
+ sal_uInt16 nRow, nCol;
+
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel8( nRow, nCol, aSRD, bRNorSF );
+
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ { // Area Reference Within a Shared Formula[ 274]
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt16 nColFirst, nColLast;
+
+ aCRD.Ref1.SetRelTab(0);
+ aCRD.Ref2.SetRelTab(0);
+ aCRD.Ref1.SetFlag3D( bRangeName );
+ aCRD.Ref2.SetFlag3D( bRangeName );
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt16( );
+ nColLast = aIn.ReaduInt16();
+
+ ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF );
+ ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRNorSF );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ aIn.Ignore( 2 );
+ break;
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ aIn.Ignore( 24 );
+ break;
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ {
+ sal_uInt16 nIxti, nRw, nGrbitCol;
+
+ nIxti = aIn.ReaduInt16();
+ nRw = aIn.ReaduInt16();
+ nGrbitCol = aIn.ReaduInt16();
+
+ SCTAB nFirstScTab, nLastScTab;
+ if( rLinkMan.GetScTabRange( nFirstScTab, nLastScTab, nIxti ) )
+ {
+ aSRD.SetAbsTab(nFirstScTab);
+ aSRD.SetFlag3D(true);
+
+ ExcRelToScRel8( nRw, nGrbitCol, aSRD, bRangeName );
+
+ if( nFirstScTab != nLastScTab )
+ {
+ aCRD.Ref1 = aSRD;
+ aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(nLastScTab);
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ else
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ }
+ break;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ {
+ sal_uInt16 nIxti, nRw1, nGrbitCol1, nRw2, nGrbitCol2;
+
+ nIxti = aIn.ReaduInt16();
+ nRw1 = aIn.ReaduInt16();
+ nRw2 = aIn.ReaduInt16();
+ nGrbitCol1 = aIn.ReaduInt16();
+ nGrbitCol2 = aIn.ReaduInt16();
+
+ SCTAB nFirstScTab, nLastScTab;
+ if( rLinkMan.GetScTabRange( nFirstScTab, nLastScTab, nIxti ) )
+ {
+ ScSingleRefData &rR1 = aCRD.Ref1;
+ ScSingleRefData &rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nFirstScTab);
+ rR2.SetAbsTab(nLastScTab);
+ rR1.SetFlag3D(true);
+ rR2.SetFlag3D( nFirstScTab != nLastScTab );
+
+ ExcRelToScRel8( nRw1, nGrbitCol1, aCRD.Ref1, bRangeName );
+ ExcRelToScRel8( nRw2, nGrbitCol2, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nGrbitCol1, nGrbitCol2 ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRw1, nRw2 ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ }
+ break;
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ aIn.Ignore( 6 );
+ break;
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ aIn.Ignore( 10 );
+ break;
+ default:
+ bError = true;
+ }
+ bError |= !aIn.IsValid();
+ }
+
+ ConvErr eRet;
+
+ if( bError )
+ eRet = ConvErr::Ni;
+ else if( aIn.GetRecPos() != nEndPos )
+ eRet = ConvErr::Count;
+ else
+ eRet = ConvErr::OK;
+
+ aIn.Seek( nEndPos );
+ return eRet;
+}
+
+void ExcelToSc8::ConvertExternName( std::unique_ptr<ScTokenArray>& rpArray, XclImpStream& rStrm, std::size_t nFormulaLen,
+ const OUString& rUrl, const vector<OUString>& rTabNames )
+{
+ if( !GetDocShell() )
+ return;
+
+ OUString aFileUrl = ScGlobal::GetAbsDocName(rUrl, GetDocShell());
+
+ sal_uInt8 nOp, nByte;
+ bool bError = false;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+
+ if (nFormulaLen == 0)
+ {
+ aPool.Store("-/-");
+ aPool >> aStack;
+ rpArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ return;
+ }
+
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFileUrl);
+ sal_uInt16 nTabCount = static_cast< sal_uInt16 >( rTabNames.size() );
+
+ std::size_t nEndPos = rStrm.GetRecPos() + nFormulaLen;
+
+ while( (rStrm.GetRecPos() < nEndPos) && !bError )
+ {
+ nOp = rStrm.ReaduInt8();
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp )
+ {
+ case 0x1C: // Error Value
+ {
+ nByte = rStrm.ReaduInt8();
+ DefTokenId eOc;
+ switch( nByte )
+ {
+ case EXC_ERR_NULL:
+ case EXC_ERR_DIV0:
+ case EXC_ERR_VALUE:
+ case EXC_ERR_REF:
+ case EXC_ERR_NAME:
+ case EXC_ERR_NUM: eOc = ocStop; break;
+ case EXC_ERR_NA: eOc = ocNotAvail; break;
+ default: eOc = ocNoName;
+ }
+ aPool << eOc;
+ if( eOc != ocStop )
+ aPool << ocOpen << ocClose;
+ aPool >> aStack;
+ }
+ break;
+ case 0x3A:
+ {
+ // cell reference in external range name
+ sal_uInt16 nExtTab1, nExtTab2, nRow, nGrbitCol;
+ nExtTab1 = rStrm.ReaduInt16();
+ nExtTab2 = rStrm.ReaduInt16();
+ nRow = rStrm.ReaduInt16();
+ nGrbitCol = rStrm.ReaduInt16();
+ if (nExtTab1 >= nTabCount || nExtTab2 >= nTabCount)
+ {
+ bError = true;
+ break;
+ }
+
+ aSRD.SetAbsTab(nExtTab1);
+ aSRD.SetFlag3D(true);
+ ExcRelToScRel8(nRow, nGrbitCol, aSRD, true);
+ aCRD.Ref1 = aCRD.Ref2 = aSRD;
+ OUString aTabName = rTabNames[nExtTab1];
+
+ if (nExtTab1 == nExtTab2)
+ {
+ // single cell reference
+ aStack << aPool.StoreExtRef(nFileId, aTabName, aSRD);
+ }
+ else
+ {
+ // area reference
+ aCRD.Ref2.SetAbsTab(nExtTab2);
+ aStack << aPool.StoreExtRef(nFileId, aTabName, aCRD);
+ }
+ }
+ break;
+ case 0x3B:
+ {
+ // area reference
+ sal_uInt16 nExtTab1, nExtTab2, nRow1, nRow2, nGrbitCol1, nGrbitCol2;
+ nExtTab1 = rStrm.ReaduInt16();
+ nExtTab2 = rStrm.ReaduInt16();
+ nRow1 = rStrm.ReaduInt16();
+ nRow2 = rStrm.ReaduInt16();
+ nGrbitCol1 = rStrm.ReaduInt16();
+ nGrbitCol2 = rStrm.ReaduInt16();
+
+ if (nExtTab1 >= nTabCount || nExtTab2 >= nTabCount)
+ {
+ bError = true;
+ break;
+ }
+
+ ScSingleRefData& rR1 = aCRD.Ref1;
+ ScSingleRefData& rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nExtTab1);
+ rR1.SetFlag3D(true);
+ ExcRelToScRel8(nRow1, nGrbitCol1, rR1, true);
+
+ rR2.SetAbsTab(nExtTab2);
+ rR2.SetFlag3D(true);
+ ExcRelToScRel8(nRow2, nGrbitCol2, rR2, true);
+
+ OUString aTabName = rTabNames[nExtTab1];
+ aStack << aPool.StoreExtRef(nFileId, aTabName, aCRD);
+ }
+ break;
+ default:
+ bError = true;
+ }
+ bError |= !rStrm.IsValid();
+ }
+
+ if( bError )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ rpArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ }
+ else if( rStrm.GetRecPos() != nEndPos )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ rpArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ }
+ else
+ {
+ rpArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ }
+
+ rStrm.Seek(nEndPos);
+}
+
+void ExcelToSc8::ExcRelToScRel8( sal_uInt16 nRow, sal_uInt16 nC, ScSingleRefData &rSRD, const bool bName )
+{
+ const bool bColRel = ( nC & 0x4000 ) != 0;
+ const bool bRowRel = ( nC & 0x8000 ) != 0;
+ const sal_uInt8 nCol = static_cast<sal_uInt8>(nC);
+
+ if( bName )
+ {
+ // C O L
+ if( bColRel )
+ {
+ SCCOL nRelCol = static_cast<sal_Int8>(nC);
+ sal_Int16 nDiff = aEingPos.Col() + nRelCol;
+ if ( nDiff < 0)
+ {
+ // relative column references wrap around
+ nRelCol = static_cast<sal_Int16>(256 + static_cast<int>(nRelCol));
+ }
+ rSRD.SetRelCol(nRelCol);
+ }
+ else
+ rSRD.SetAbsCol(static_cast<SCCOL>(nCol));
+
+ // R O W
+ if( bRowRel )
+ {
+ SCROW nRelRow = static_cast<sal_Int16>(nRow);
+ sal_Int32 nDiff = aEingPos.Row() + nRelRow;
+ if (nDiff < 0)
+ {
+ // relative row references wrap around
+ nRelRow = 65536 + nRelRow;
+ }
+ rSRD.SetRelRow(nRelRow);
+ }
+ else
+ rSRD.SetAbsRow(std::min( static_cast<SCROW>(nRow), GetDoc().MaxRow()));
+ }
+ else
+ {
+ // C O L
+ if ( bColRel )
+ rSRD.SetRelCol(static_cast<SCCOL>(nCol) - aEingPos.Col());
+ else
+ rSRD.SetAbsCol(nCol);
+
+ // R O W
+ if ( bRowRel )
+ rSRD.SetRelRow(static_cast<SCROW>(nRow) - aEingPos.Row());
+ else
+ rSRD.SetAbsRow(nRow);
+ }
+}
+
+// stream seeks to first byte after <nLen>
+void ExcelToSc8::GetAbsRefs( ScRangeList& r, XclImpStream& aIn, std::size_t nLen )
+{
+ sal_uInt8 nOp;
+ sal_uInt16 nRow1, nRow2, nCol1, nCol2;
+ SCTAB nTab1, nTab2;
+ sal_uInt16 nIxti;
+
+ std::size_t nSeek;
+
+ std::size_t nEndPos = aIn.GetRecPos() + nLen;
+
+ while( aIn.IsValid() && (aIn.GetRecPos() < nEndPos) )
+ {
+ nOp = aIn.ReaduInt8();
+ nSeek = 0;
+
+ switch( nOp )
+ {
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ nRow1 = aIn.ReaduInt16();
+ nCol1 = aIn.ReaduInt16();
+
+ nRow2 = nRow1;
+ nCol2 = nCol1;
+ nTab1 = nTab2 = GetCurrScTab();
+ goto _common;
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ // Area Reference Within a Shared Formula[ 274]
+ nRow1 = aIn.ReaduInt16();
+ nRow2 = aIn.ReaduInt16();
+ nCol1 = aIn.ReaduInt16();
+ nCol2 = aIn.ReaduInt16();
+
+ nTab1 = nTab2 = GetCurrScTab();
+ goto _common;
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ nIxti = aIn.ReaduInt16();
+ nRow1 = aIn.ReaduInt16();
+ nCol1 = aIn.ReaduInt16();
+
+ nRow2 = nRow1;
+ nCol2 = nCol1;
+
+ goto _3d_common;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ nIxti = aIn.ReaduInt16();
+ nRow1 = aIn.ReaduInt16();
+ nRow2 = aIn.ReaduInt16();
+ nCol1 = aIn.ReaduInt16();
+ nCol2 = aIn.ReaduInt16();
+
+ _3d_common:
+ // skip references to deleted sheets
+ if( !rLinkMan.GetScTabRange( nTab1, nTab2, nIxti ) || !ValidTab( nTab1 ) || !ValidTab( nTab2 ) )
+ break;
+
+ goto _common;
+ _common:
+ // do not check abs/rel flags, linked controls have set them!
+ {
+ ScRange aScRange;
+ nCol1 &= 0x3FFF;
+ nCol2 &= 0x3FFF;
+ if( GetAddressConverter().ConvertRange( aScRange, XclRange( nCol1, nRow1, nCol2, nRow2 ), nTab1, nTab2, true ) )
+ r.push_back( aScRange );
+ }
+ break;
+ case 0x1C: // Error Value [314 266]
+ case 0x1D: // Boolean [315 266]
+ nSeek = 1;
+ break;
+ case 0x1E: // Integer [315 266]
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ nSeek = 2;
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ nSeek = 3;
+ break;
+ case 0x01: // Array Formula [325 ]
+ case 0x02: // Data Table [325 277]
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ nSeek = 4;
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ nSeek = 6;
+ break;
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ nSeek = 7;
+ break;
+ case 0x1F: // Number [315 266]
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ nSeek = 8;
+ break;
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ nSeek = 10;
+ break;
+ case 0x17: // String Constant [314 266]
+ {
+ sal_uInt8 nStrLen;
+ nStrLen = aIn.ReaduInt8();
+ aIn.IgnoreUniString( nStrLen ); // reads Grbit even if nLen==0
+ nSeek = 0;
+ }
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData;
+ sal_uInt8 nOpt;
+ nOpt = aIn.ReaduInt8();
+ nData = aIn.ReaduInt16();
+ if( nOpt & 0x04 )
+ {// nFactor -> skip bytes or words AttrChoose
+ nData++;
+ nSeek = nData * 2;
+ }
+ }
+ break;
+ }
+
+ aIn.Ignore( nSeek );
+ }
+ aIn.Seek( nEndPos );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excimp8.cxx b/sc/source/filter/excel/excimp8.cxx
new file mode 100644
index 000000000..d5db209a1
--- /dev/null
+++ b/sc/source/filter/excel/excimp8.cxx
@@ -0,0 +1,817 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <excimp8.hxx>
+
+#include <scitems.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <unotools/fltrcfg.hxx>
+
+#include <sfx2/docfile.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docinf.hxx>
+#include <sot/storage.hxx>
+#include <svl/sharedstringpool.hxx>
+
+#include <rtl/math.hxx>
+#include <rtl/ustring.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+#include <document.hxx>
+#include <attrib.hxx>
+#include <dbdata.hxx>
+#include <globalnames.hxx>
+#include <docoptio.hxx>
+#include <xihelper.hxx>
+#include <xicontent.hxx>
+#include <xilink.hxx>
+#include <xiescher.hxx>
+#include <xistyle.hxx>
+#include <excdefs.hxx>
+
+#include <excform.hxx>
+#include <queryentry.hxx>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <cppuhelper/implbase.hxx>
+#include "xltoolbar.hxx"
+#include <oox/ole/vbaproject.hxx>
+#include <oox/ole/olestorage.hxx>
+
+using namespace com::sun::star;
+using namespace ::comphelper;
+
+//OleNameOverrideContainer
+
+namespace {
+
+class OleNameOverrideContainer : public ::cppu::WeakImplHelper< container::XNameContainer >
+{
+private:
+ typedef std::unordered_map< OUString, uno::Reference< container::XIndexContainer > > NamedIndexToOleName;
+ NamedIndexToOleName IdToOleNameHash;
+ ::osl::Mutex m_aMutex;
+public:
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType( ) override { return cppu::UnoType<container::XIndexContainer>::get(); }
+ virtual sal_Bool SAL_CALL hasElements( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return ( !IdToOleNameHash.empty() );
+ }
+ // XNameAccess
+ virtual uno::Any SAL_CALL getByName( const OUString& aName ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !hasByName(aName) )
+ throw container::NoSuchElementException();
+ return uno::Any( IdToOleNameHash[ aName ] );
+ }
+ virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return comphelper::mapKeysToSequence( IdToOleNameHash);
+ }
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return ( IdToOleNameHash.find( aName ) != IdToOleNameHash.end() );
+ }
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( hasByName( aName ) )
+ throw container::ElementExistException();
+ uno::Reference< container::XIndexContainer > xElement;
+ if ( ! ( aElement >>= xElement ) )
+ throw lang::IllegalArgumentException();
+ IdToOleNameHash[ aName ] = xElement;
+ }
+ virtual void SAL_CALL removeByName( const OUString& aName ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( IdToOleNameHash.erase( aName ) == 0 )
+ throw container::NoSuchElementException();
+ }
+ virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !hasByName( aName ) )
+ throw container::NoSuchElementException();
+ uno::Reference< container::XIndexContainer > xElement;
+ if ( ! ( aElement >>= xElement ) )
+ throw lang::IllegalArgumentException();
+ IdToOleNameHash[ aName ] = xElement;
+ }
+};
+
+/** Future Record Type header.
+ @return whether read rt matches nRecordID
+ */
+bool readFrtHeader( XclImpStream& rStrm, sal_uInt16 nRecordID )
+{
+ sal_uInt16 nRt = rStrm.ReaduInt16();
+ rStrm.Ignore(10); // grbitFrt (2 bytes) and reserved (8 bytes)
+ return nRt == nRecordID;
+}
+
+}
+
+ImportExcel8::ImportExcel8( XclImpRootData& rImpData, SvStream& rStrm ) :
+ ImportExcel( rImpData, rStrm )
+{
+ // replace BIFF2-BIFF5 formula importer with BIFF8 formula importer
+ pFormConv.reset(new ExcelToSc8( GetRoot() ));
+ pExcRoot->pFmlaConverter = pFormConv.get();
+}
+
+ImportExcel8::~ImportExcel8()
+{
+}
+
+void ImportExcel8::Calccount()
+{
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetIterCount( aIn.ReaduInt16() );
+ rD.SetDocOptions( aOpt );
+}
+
+void ImportExcel8::Precision()
+{
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetCalcAsShown( aIn.ReaduInt16() == 0 );
+ rD.SetDocOptions( aOpt );
+}
+
+void ImportExcel8::Delta()
+{
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetIterEps( aIn.ReadDouble() );
+ rD.SetDocOptions( aOpt );
+}
+
+void ImportExcel8::Iteration()
+{
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetIter( aIn.ReaduInt16() == 1 );
+ rD.SetDocOptions( aOpt );
+}
+
+void ImportExcel8::Boundsheet()
+{
+ sal_uInt8 nLen;
+ sal_uInt16 nGrbit;
+
+ aIn.DisableDecryption();
+ maSheetOffsets.push_back( aIn.ReaduInt32() );
+ aIn.EnableDecryption();
+ nGrbit = aIn.ReaduInt16();
+ nLen = aIn.ReaduInt8();
+
+ OUString aName( aIn.ReadUniString( nLen ) );
+ GetTabInfo().AppendXclTabName( aName, nBdshtTab );
+
+ SCTAB nScTab = nBdshtTab;
+ if( nScTab > 0 )
+ {
+ OSL_ENSURE( !rD.HasTable( nScTab ), "ImportExcel8::Boundsheet - sheet exists already" );
+ rD.MakeTable( nScTab );
+ }
+
+ if( ( nGrbit & 0x0001 ) || ( nGrbit & 0x0002 ) )
+ rD.SetVisible( nScTab, false );
+
+ if( !rD.RenameTab( nScTab, aName ) )
+ {
+ rD.CreateValidTabName( aName );
+ rD.RenameTab( nScTab, aName );
+ }
+
+ nBdshtTab++;
+}
+
+void ImportExcel8::Scenman()
+{
+ sal_uInt16 nLastDispl;
+
+ aIn.Ignore( 4 );
+ nLastDispl = aIn.ReaduInt16();
+
+ maScenList.nLastScenario = nLastDispl;
+}
+
+void ImportExcel8::Scenario()
+{
+ maScenList.aEntries.push_back( std::make_unique<ExcScenario>( aIn, *pExcRoot ) );
+}
+
+void ImportExcel8::Labelsst()
+{
+ XclAddress aXclPos;
+ sal_uInt16 nXF;
+ sal_uInt32 nSst;
+
+ aIn >> aXclPos;
+ nXF = aIn.ReaduInt16();
+ nSst = aIn.ReaduInt32( );
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ GetXFRangeBuffer().SetXF( aScPos, nXF );
+ const XclImpString* pXclStr = GetSst().GetString(nSst);
+ if (pXclStr)
+ XclImpStringHelper::SetToDocument(GetDocImport(), aScPos, *this, *pXclStr, nXF);
+ }
+}
+
+void ImportExcel8::FeatHdr()
+{
+ if (!readFrtHeader( aIn, 0x0867))
+ return;
+
+ // Feature type (isf) can be EXC_ISFPROTECTION, EXC_ISFFEC2 or
+ // EXC_ISFFACTOID.
+ sal_uInt16 nFeatureType = aIn.ReaduInt16();
+ if (nFeatureType != EXC_ISFPROTECTION)
+ // We currently only support import of enhanced protection data.
+ return;
+
+ aIn.Ignore(1); // always 1
+
+ GetSheetProtectBuffer().ReadOptions( aIn, GetCurrScTab() );
+}
+
+void ImportExcel8::Feat()
+{
+ if (!readFrtHeader( aIn, 0x0868))
+ return;
+
+ // Feature type (isf) can be EXC_ISFPROTECTION, EXC_ISFFEC2 or
+ // EXC_ISFFACTOID.
+ sal_uInt16 nFeatureType = aIn.ReaduInt16();
+ if (nFeatureType != EXC_ISFPROTECTION)
+ // We currently only support import of enhanced protection data.
+ return;
+
+ aIn.Ignore(5); // reserved1 (1 byte) and reserved2 (4 bytes)
+
+ sal_uInt16 nCref = aIn.ReaduInt16(); // number of ref elements
+ aIn.Ignore(4); // size if EXC_ISFFEC2, else 0 and to be ignored
+ aIn.Ignore(2); // reserved3 (2 bytes)
+
+ ScEnhancedProtection aProt;
+ if (nCref)
+ {
+ XclRangeList aRefs;
+ aRefs.Read( aIn, true, nCref);
+ if (!aRefs.empty())
+ {
+ aProt.maRangeList = new ScRangeList;
+ GetAddressConverter().ConvertRangeList( *aProt.maRangeList, aRefs, GetCurrScTab(), false);
+ }
+ }
+
+ // FeatProtection structure follows in record.
+
+ aProt.mnAreserved = aIn.ReaduInt32();
+ aProt.mnPasswordVerifier = aIn.ReaduInt32();
+ aProt.maTitle = aIn.ReadUniString();
+ if ((aProt.mnAreserved & 0x00000001) == 0x00000001)
+ {
+ sal_uInt32 nCbSD = aIn.ReaduInt32();
+ // TODO: could here be some sanity check applied to not allocate 4GB?
+ aProt.maSecurityDescriptor.resize( nCbSD);
+ std::size_t nRead = aIn.Read(aProt.maSecurityDescriptor.data(), nCbSD);
+ if (nRead < nCbSD)
+ aProt.maSecurityDescriptor.resize( nRead);
+ }
+
+ GetSheetProtectBuffer().AppendEnhancedProtection( aProt, GetCurrScTab() );
+}
+
+void ImportExcel8::ReadBasic()
+{
+ SfxObjectShell* pShell = GetDocShell();
+ tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
+ const SvtFilterOptions& rFilterOpt = SvtFilterOptions::Get();
+ if( !pShell || !xRootStrg.is() )
+ return;
+
+ try
+ {
+ // #FIXME need to get rid of this, we can also do this from within oox
+ // via the "ooo.vba.VBAGlobals" service
+ if( ( rFilterOpt.IsLoadExcelBasicCode() ||
+ rFilterOpt.IsLoadExcelBasicStorage() ) &&
+ rFilterOpt.IsLoadExcelBasicExecutable() )
+ {
+ // see if we have the XCB stream
+ tools::SvRef<SotStorageStream> xXCB = xRootStrg->OpenSotStream( "XCB", StreamMode::STD_READ );
+ if ( xXCB.is()|| ERRCODE_NONE == xXCB->GetError() )
+ {
+ ScCTBWrapper wrapper;
+ if ( wrapper.Read( *xXCB ) )
+ {
+#ifdef DEBUG_SC_EXCEL
+ wrapper.Print( stderr );
+#endif
+ wrapper.ImportCustomToolBar( *pShell );
+ }
+ }
+ }
+ try
+ {
+ uno::Reference< uno::XComponentContext > aCtx( ::comphelper::getProcessComponentContext() );
+ SfxMedium& rMedium = GetMedium();
+ uno::Reference< io::XInputStream > xIn = rMedium.GetInputStream();
+ oox::ole::OleStorage root( aCtx, xIn, false );
+ oox::StorageRef vbaStg = root.openSubStorage( "_VBA_PROJECT_CUR", false );
+ if ( vbaStg )
+ {
+ oox::ole::VbaProject aVbaPrj( aCtx, pShell->GetModel(), u"Calc" );
+ // collect names of embedded form controls, as specified in the VBA project
+ uno::Reference< container::XNameContainer > xOleNameOverrideSink( new OleNameOverrideContainer );
+ aVbaPrj.setOleOverridesSink( xOleNameOverrideSink );
+ aVbaPrj.importVbaProject( *vbaStg );
+ GetObjectManager().SetOleNameOverrideInfo( xOleNameOverrideSink );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+void ImportExcel8::EndSheet()
+{
+ ImportExcel::EndSheet();
+ GetCondFormatManager().Apply();
+ GetValidationManager().Apply();
+}
+
+void ImportExcel8::PostDocLoad()
+{
+#if HAVE_FEATURE_SCRIPTING
+ // reading basic has been delayed until sheet objects (codenames etc.) are read
+ if( HasBasic() )
+ ReadBasic();
+#endif
+ // #i11776# filtered ranges before outlines and hidden rows
+ if( pExcRoot->pAutoFilterBuffer )
+ pExcRoot->pAutoFilterBuffer->Apply();
+
+ GetWebQueryBuffer().Apply(); //TODO: test if extant
+ GetSheetProtectBuffer().Apply();
+ GetDocProtectBuffer().Apply();
+
+ ImportExcel::PostDocLoad();
+
+ // check scenarios; Attention: This increases the table count of the document!!
+ if( !rD.IsClipboard() && !maScenList.aEntries.empty() )
+ {
+ rD.UpdateChartListenerCollection(); // references in charts must be updated
+
+ maScenList.Apply( GetRoot() );
+ }
+
+ // read doc info (no docshell while pasting from clipboard)
+ SfxObjectShell* pShell = GetDocShell();
+ if(!pShell)
+ return;
+
+ // BIFF5+ without storage is possible
+ tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
+ if( xRootStrg.is() ) try
+ {
+ uno::Reference< document::XDocumentPropertiesSupplier > xDPS( pShell->GetModel(), uno::UNO_QUERY_THROW );
+ uno::Reference< document::XDocumentProperties > xDocProps( xDPS->getDocumentProperties(), uno::UNO_SET_THROW );
+ sfx2::LoadOlePropertySet( xDocProps, xRootStrg.get() );
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ // #i45843# Pivot tables are now handled outside of PostDocLoad, so they are available
+ // when formula cells are calculated, for the GETPIVOTDATA function.
+}
+
+// autofilter
+
+void ImportExcel8::FilterMode()
+{
+ // The FilterMode record exists: if either the AutoFilter
+ // record exists or an Advanced Filter is saved and stored
+ // in the sheet. Thus if the FilterMode records only exists
+ // then the latter is true...
+ if( !pExcRoot->pAutoFilterBuffer ) return;
+
+ XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() );
+ if( pData )
+ pData->SetAutoOrAdvanced();
+}
+
+void ImportExcel8::AutoFilterInfo()
+{
+ if( !pExcRoot->pAutoFilterBuffer ) return;
+
+ XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() );
+ if( pData )
+ {
+ pData->SetAdvancedRange( nullptr );
+ pData->Activate();
+ }
+}
+
+void ImportExcel8::AutoFilter()
+{
+ if( !pExcRoot->pAutoFilterBuffer ) return;
+
+ XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() );
+ if( pData )
+ pData->ReadAutoFilter(aIn, GetDoc().GetSharedStringPool());
+}
+
+XclImpAutoFilterData::XclImpAutoFilterData( RootData* pRoot, const ScRange& rRange ) :
+ ExcRoot( pRoot ),
+ pCurrDBData(nullptr),
+ bActive( false ),
+ bCriteria( false ),
+ bAutoOrAdvanced(false)
+{
+ aParam.nCol1 = rRange.aStart.Col();
+ aParam.nRow1 = rRange.aStart.Row();
+ aParam.nTab = rRange.aStart.Tab();
+ aParam.nCol2 = rRange.aEnd.Col();
+ aParam.nRow2 = rRange.aEnd.Row();
+
+ aParam.bInplace = true;
+
+}
+
+namespace {
+
+OUString CreateFromDouble( double fVal )
+{
+ return rtl::math::doubleToUString(fVal,
+ rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+ ScGlobal::getLocaleData().getNumDecimalSep()[0], true);
+}
+
+}
+
+void XclImpAutoFilterData::SetCellAttribs()
+{
+ ScDocument& rDoc = pExcRoot->pIR->GetDoc();
+ for ( SCCOL nCol = StartCol(); nCol <= EndCol(); nCol++ )
+ {
+ ScMF nFlag = rDoc.GetAttr( nCol, StartRow(), Tab(), ATTR_MERGE_FLAG )->GetValue();
+ rDoc.ApplyAttr( nCol, StartRow(), Tab(), ScMergeFlagAttr( nFlag | ScMF::Auto) );
+ }
+}
+
+void XclImpAutoFilterData::InsertQueryParam()
+{
+ if (!pCurrDBData)
+ return;
+
+ ScRange aAdvRange;
+ bool bHasAdv = pCurrDBData->GetAdvancedQuerySource( aAdvRange );
+ if( bHasAdv )
+ pExcRoot->pIR->GetDoc().CreateQueryParam(aAdvRange, aParam);
+
+ pCurrDBData->SetQueryParam( aParam );
+ if( bHasAdv )
+ pCurrDBData->SetAdvancedQuerySource( &aAdvRange );
+ else
+ {
+ pCurrDBData->SetAutoFilter( true );
+ SetCellAttribs();
+ }
+}
+
+static void ExcelQueryToOooQuery( OUString& aStr, ScQueryEntry& rEntry )
+{
+ if (rEntry.eOp != SC_EQUAL && rEntry.eOp != SC_NOT_EQUAL)
+ return;
+
+ sal_Int32 nLen = aStr.getLength();
+ sal_Unicode nStart = aStr[0];
+ sal_Unicode nEnd = aStr[ nLen-1 ];
+ if( nLen > 2 && nStart == '*' && nEnd == '*' )
+ {
+ aStr = aStr.copy( 1, nLen-2 );
+ rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_CONTAINS : SC_DOES_NOT_CONTAIN;
+ }
+ else if( nLen > 1 && nStart == '*' && nEnd != '*' )
+ {
+ aStr = aStr.copy( 1 );
+ rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_ENDS_WITH : SC_DOES_NOT_END_WITH;
+ }
+ else if( nLen > 1 && nStart != '*' && nEnd == '*' )
+ {
+ aStr = aStr.copy( 0, nLen-1 );
+ rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_BEGINS_WITH : SC_DOES_NOT_BEGIN_WITH;
+ }
+ else if( nLen == 2 && nStart == '*' && nEnd == '*' )
+ {
+ aStr = aStr.copy( 1 );
+ }
+}
+
+void XclImpAutoFilterData::ReadAutoFilter(
+ XclImpStream& rStrm, svl::SharedStringPool& rPool )
+{
+ sal_uInt16 nCol, nFlags;
+ nCol = rStrm.ReaduInt16();
+ nFlags = rStrm.ReaduInt16();
+
+ ScQueryConnect eConn = ::get_flagvalue( nFlags, EXC_AFFLAG_ANDORMASK, SC_OR, SC_AND );
+ bool bSimple1 = ::get_flag(nFlags, EXC_AFFLAG_SIMPLE1);
+ bool bSimple2 = ::get_flag(nFlags, EXC_AFFLAG_SIMPLE2);
+ bool bTop10 = ::get_flag(nFlags, EXC_AFFLAG_TOP10);
+ bool bTopOfTop10 = ::get_flag(nFlags, EXC_AFFLAG_TOP10TOP);
+ bool bPercent = ::get_flag(nFlags, EXC_AFFLAG_TOP10PERC);
+ sal_uInt16 nCntOfTop10 = nFlags >> 7;
+
+ if( bTop10 )
+ {
+ ScQueryEntry& aEntry = aParam.AppendEntry();
+ ScQueryEntry::Item& rItem = aEntry.GetQueryItem();
+ aEntry.bDoQuery = true;
+ aEntry.nField = static_cast<SCCOLROW>(StartCol() + static_cast<SCCOL>(nCol));
+ aEntry.eOp = bTopOfTop10 ?
+ (bPercent ? SC_TOPPERC : SC_TOPVAL) : (bPercent ? SC_BOTPERC : SC_BOTVAL);
+ aEntry.eConnect = SC_AND;
+
+ rItem.meType = ScQueryEntry::ByString;
+ rItem.maString = rPool.intern(OUString::number(nCntOfTop10));
+
+ rStrm.Ignore(20);
+ return;
+ }
+
+ sal_uInt8 nType, nOper, nBoolErr, nVal;
+ sal_Int32 nRK;
+ double fVal;
+
+ sal_uInt8 nStrLen[2] = { 0, 0 };
+ ScQueryEntry aEntries[2];
+
+ for (size_t nE = 0; nE < 2; ++nE)
+ {
+ ScQueryEntry& rEntry = aEntries[nE];
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ bool bIgnore = false;
+
+ nType = rStrm.ReaduInt8();
+ nOper = rStrm.ReaduInt8();
+ switch( nOper )
+ {
+ case EXC_AFOPER_LESS:
+ rEntry.eOp = SC_LESS;
+ break;
+ case EXC_AFOPER_EQUAL:
+ rEntry.eOp = SC_EQUAL;
+ break;
+ case EXC_AFOPER_LESSEQUAL:
+ rEntry.eOp = SC_LESS_EQUAL;
+ break;
+ case EXC_AFOPER_GREATER:
+ rEntry.eOp = SC_GREATER;
+ break;
+ case EXC_AFOPER_NOTEQUAL:
+ rEntry.eOp = SC_NOT_EQUAL;
+ break;
+ case EXC_AFOPER_GREATEREQUAL:
+ rEntry.eOp = SC_GREATER_EQUAL;
+ break;
+ default:
+ rEntry.eOp = SC_EQUAL;
+ }
+
+ switch( nType )
+ {
+ case EXC_AFTYPE_RK:
+ nRK = rStrm.ReadInt32();
+ rStrm.Ignore( 4 );
+ rItem.maString = rPool.intern(
+ CreateFromDouble(XclTools::GetDoubleFromRK(nRK)));
+ break;
+ case EXC_AFTYPE_DOUBLE:
+ fVal = rStrm.ReadDouble();
+ rItem.maString = rPool.intern(CreateFromDouble(fVal));
+ break;
+ case EXC_AFTYPE_STRING:
+ rStrm.Ignore( 4 );
+ nStrLen[ nE ] = rStrm.ReaduInt8();
+ rStrm.Ignore( 3 );
+ rItem.maString = svl::SharedString();
+ break;
+ case EXC_AFTYPE_BOOLERR:
+ nBoolErr = rStrm.ReaduInt8();
+ nVal = rStrm.ReaduInt8();
+ rStrm.Ignore( 6 );
+ rItem.maString = rPool.intern(OUString::number(nVal));
+ bIgnore = (nBoolErr != 0);
+ break;
+ case EXC_AFTYPE_EMPTY:
+ rEntry.SetQueryByEmpty();
+ break;
+ case EXC_AFTYPE_NOTEMPTY:
+ rEntry.SetQueryByNonEmpty();
+ break;
+ default:
+ rStrm.Ignore( 8 );
+ bIgnore = true;
+ }
+
+ if (!bIgnore)
+ {
+ rEntry.bDoQuery = true;
+ rItem.meType = ScQueryEntry::ByString;
+ rEntry.nField = static_cast<SCCOLROW>(StartCol() + static_cast<SCCOL>(nCol));
+ rEntry.eConnect = nE ? eConn : SC_AND;
+ }
+ }
+
+ if (eConn == SC_AND)
+ {
+ for (size_t nE = 0; nE < 2; ++nE)
+ {
+ if (nStrLen[nE] && aEntries[nE].bDoQuery)
+ {
+ OUString aStr = rStrm.ReadUniString(nStrLen[nE]);
+ ExcelQueryToOooQuery(aStr, aEntries[nE]);
+ aEntries[nE].GetQueryItem().maString = rPool.intern(aStr);
+ aParam.AppendEntry() = aEntries[nE];
+ }
+ }
+ }
+ else
+ {
+ assert( eConn == SC_OR && "eConn should be SC_AND or SC_OR");
+ // Import only when both conditions are for simple equality, else
+ // import only the 1st condition due to conflict with the ordering of
+ // conditions. #i39464#.
+ //
+ // Example: Let A1 be a condition of column A, and B1 and B2
+ // conditions of column B, connected with OR. Excel performs 'A1 AND
+ // (B1 OR B2)' in this case, but Calc would do '(A1 AND B1) OR B2'
+ // instead.
+
+ if (bSimple1 && bSimple2 && nStrLen[0] && nStrLen[1])
+ {
+ // Two simple OR'ed equal conditions. We can import this correctly.
+ ScQueryEntry& rEntry = aParam.AppendEntry();
+ rEntry.bDoQuery = true;
+ rEntry.eOp = SC_EQUAL;
+ rEntry.eConnect = SC_AND;
+ ScQueryEntry::QueryItemsType aItems;
+ aItems.reserve(2);
+ ScQueryEntry::Item aItem1, aItem2;
+ aItem1.maString = rPool.intern(rStrm.ReadUniString(nStrLen[0]));
+ aItem1.meType = ScQueryEntry::ByString;
+ aItem2.maString = rPool.intern(rStrm.ReadUniString(nStrLen[1]));
+ aItem2.meType = ScQueryEntry::ByString;
+ aItems.push_back(aItem1);
+ aItems.push_back(aItem2);
+ rEntry.GetQueryItems().swap(aItems);
+ }
+ else if (nStrLen[0] && aEntries[0].bDoQuery)
+ {
+ // Due to conflict, we can import only the first condition.
+ OUString aStr = rStrm.ReadUniString(nStrLen[0]);
+ ExcelQueryToOooQuery(aStr, aEntries[0]);
+ aEntries[0].GetQueryItem().maString = rPool.intern(aStr);
+ aParam.AppendEntry() = aEntries[0];
+ }
+ }
+}
+
+void XclImpAutoFilterData::SetAdvancedRange( const ScRange* pRange )
+{
+ if (pRange)
+ {
+ aCriteriaRange = *pRange;
+ bCriteria = true;
+ }
+ else
+ bCriteria = false;
+}
+
+void XclImpAutoFilterData::SetExtractPos( const ScAddress& rAddr )
+{
+ aParam.nDestCol = rAddr.Col();
+ aParam.nDestRow = rAddr.Row();
+ aParam.nDestTab = rAddr.Tab();
+ aParam.bInplace = false;
+ aParam.bDestPers = true;
+}
+
+void XclImpAutoFilterData::Apply()
+{
+ // Create the ScDBData() object if the AutoFilter is activated
+ // or if we need to create the Advanced Filter.
+ if( bActive || bCriteria)
+ {
+ ScDocument& rDoc = pExcRoot->pIR->GetDoc();
+ pCurrDBData = new ScDBData(STR_DB_LOCAL_NONAME, Tab(),
+ StartCol(),StartRow(), EndCol(),EndRow() );
+ if(bCriteria)
+ {
+ EnableRemoveFilter();
+
+ pCurrDBData->SetQueryParam( aParam );
+ pCurrDBData->SetAdvancedQuerySource(&aCriteriaRange);
+ }
+ else
+ pCurrDBData->SetAdvancedQuerySource(nullptr);
+ rDoc.SetAnonymousDBData(Tab(), std::unique_ptr<ScDBData>(pCurrDBData));
+ }
+
+ if( bActive )
+ {
+ InsertQueryParam();
+ }
+}
+
+void XclImpAutoFilterData::EnableRemoveFilter()
+{
+ // only if this is a saved Advanced filter
+ if( !bActive && bAutoOrAdvanced )
+ {
+ ScQueryEntry& aEntry = aParam.AppendEntry();
+ aEntry.bDoQuery = true;
+ }
+
+ // TBD: force the automatic activation of the
+ // "Remove Filter" by setting a virtual mouse click
+ // inside the advanced range
+}
+
+void XclImpAutoFilterBuffer::Insert( RootData* pRoot, const ScRange& rRange)
+{
+ if( !GetByTab( rRange.aStart.Tab() ) )
+ maFilters.push_back( std::make_shared<XclImpAutoFilterData>( pRoot, rRange ));
+}
+
+void XclImpAutoFilterBuffer::AddAdvancedRange( const ScRange& rRange )
+{
+ XclImpAutoFilterData* pData = GetByTab( rRange.aStart.Tab() );
+ if( pData )
+ pData->SetAdvancedRange( &rRange );
+}
+
+void XclImpAutoFilterBuffer::AddExtractPos( const ScRange& rRange )
+{
+ XclImpAutoFilterData* pData = GetByTab( rRange.aStart.Tab() );
+ if( pData )
+ pData->SetExtractPos( rRange.aStart );
+}
+
+void XclImpAutoFilterBuffer::Apply()
+{
+ for( const auto& rFilterPtr : maFilters )
+ rFilterPtr->Apply();
+}
+
+XclImpAutoFilterData* XclImpAutoFilterBuffer::GetByTab( SCTAB nTab )
+{
+ for( const auto& rFilterPtr : maFilters )
+ {
+ if( rFilterPtr->Tab() == nTab )
+ return rFilterPtr.get();
+ }
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excrecds.cxx b/sc/source/filter/excel/excrecds.cxx
new file mode 100644
index 000000000..b175445bc
--- /dev/null
+++ b/sc/source/filter/excel/excrecds.cxx
@@ -0,0 +1,1177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excrecds.hxx>
+
+#include <map>
+#include <filter/msfilter/countryid.hxx>
+
+#include <svl/numformat.hxx>
+#include <sal/log.hxx>
+#include <sax/fastattribs.hxx>
+
+#include <string.h>
+
+#include <global.hxx>
+#include <document.hxx>
+#include <dbdata.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+#include <queryentry.hxx>
+#include <queryparam.hxx>
+#include <sortparam.hxx>
+#include <userlist.hxx>
+#include <root.hxx>
+
+#include <xeescher.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xlname.hxx>
+#include <xestyle.hxx>
+
+#include <xcl97rec.hxx>
+#include <tabprotection.hxx>
+
+using namespace ::oox;
+
+using ::com::sun::star::uno::Sequence;
+
+//--------------------------------------------------------- class ExcDummy_00 -
+const sal_uInt8 ExcDummy_00::pMyData[] = {
+ 0x5c, 0x00, 0x20, 0x00, 0x04, 'C', 'a', 'l', 'c', // WRITEACCESS
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
+};
+const std::size_t ExcDummy_00::nMyLen = sizeof( ExcDummy_00::pMyData );
+
+//-------------------------------------------------------- class ExcDummy_04x -
+const sal_uInt8 ExcDummy_040::pMyData[] = {
+ 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, // BACKUP
+ 0x8d, 0x00, 0x02, 0x00, 0x00, 0x00, // HIDEOBJ
+};
+const std::size_t ExcDummy_040::nMyLen = sizeof( ExcDummy_040::pMyData );
+
+const sal_uInt8 ExcDummy_041::pMyData[] = {
+ 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, // PRECISION
+ 0xda, 0x00, 0x02, 0x00, 0x00, 0x00 // BOOKBOOL
+};
+const std::size_t ExcDummy_041::nMyLen = sizeof( ExcDummy_041::pMyData );
+
+//-------------------------------------------------------- class ExcDummy_02a -
+const sal_uInt8 ExcDummy_02a::pMyData[] = {
+ 0x0d, 0x00, 0x02, 0x00, 0x01, 0x00, // CALCMODE
+ 0x0c, 0x00, 0x02, 0x00, 0x64, 0x00, // CALCCOUNT
+ 0x0f, 0x00, 0x02, 0x00, 0x01, 0x00, // REFMODE
+ 0x11, 0x00, 0x02, 0x00, 0x00, 0x00, // ITERATION
+ 0x10, 0x00, 0x08, 0x00, 0xfc, 0xa9, 0xf1, 0xd2, 0x4d, // DELTA
+ 0x62, 0x50, 0x3f,
+ 0x5f, 0x00, 0x02, 0x00, 0x01, 0x00 // SAVERECALC
+};
+const std::size_t ExcDummy_02a::nMyLen = sizeof( ExcDummy_02a::pMyData );
+
+//----------------------------------------------------------- class ExcRecord -
+
+void ExcRecord::Save( XclExpStream& rStrm )
+{
+ SetRecHeader( GetNum(), GetLen() );
+ XclExpRecord::Save( rStrm );
+}
+
+void ExcRecord::SaveCont( XclExpStream& /*rStrm*/ )
+{
+}
+
+void ExcRecord::WriteBody( XclExpStream& rStrm )
+{
+ SaveCont( rStrm );
+}
+
+void ExcRecord::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+}
+
+//--------------------------------------------------------- class ExcEmptyRec -
+
+void ExcEmptyRec::Save( XclExpStream& /*rStrm*/ )
+{
+}
+
+sal_uInt16 ExcEmptyRec::GetNum() const
+{
+ return 0;
+}
+
+std::size_t ExcEmptyRec::GetLen() const
+{
+ return 0;
+}
+
+//--------------------------------------------------------- class ExcDummyRec -
+
+void ExcDummyRec::Save( XclExpStream& rStrm )
+{
+ rStrm.Write( GetData(), GetLen() ); // raw write mode
+}
+
+sal_uInt16 ExcDummyRec::GetNum() const
+{
+ return 0x0000;
+}
+
+//------------------------------------------------------- class ExcBoolRecord -
+
+void ExcBoolRecord::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16>(bVal ? 0x0001 : 0x0000);
+}
+
+std::size_t ExcBoolRecord::GetLen() const
+{
+ return 2;
+}
+
+//--------------------------------------------------------- class ExcBof_Base -
+
+ExcBof_Base::ExcBof_Base()
+ : nDocType(0)
+ , nVers(0)
+ , nRupBuild(0x096C) // copied from Excel
+ , nRupYear(0x07C9) // copied from Excel
+{
+}
+
+//-------------------------------------------------------------- class ExcBof -
+
+ExcBof::ExcBof()
+{
+ nDocType = 0x0010;
+ nVers = 0x0500;
+}
+
+void ExcBof::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << nVers << nDocType << nRupBuild << nRupYear;
+}
+
+sal_uInt16 ExcBof::GetNum() const
+{
+ return 0x0809;
+}
+
+std::size_t ExcBof::GetLen() const
+{
+ return 8;
+}
+
+//------------------------------------------------------------- class ExcBofW -
+
+ExcBofW::ExcBofW()
+{
+ nDocType = 0x0005;
+ nVers = 0x0500;
+}
+
+void ExcBofW::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << nVers << nDocType << nRupBuild << nRupYear;
+}
+
+sal_uInt16 ExcBofW::GetNum() const
+{
+ return 0x0809;
+}
+
+std::size_t ExcBofW::GetLen() const
+{
+ return 8;
+}
+
+//-------------------------------------------------------------- class ExcEof -
+
+sal_uInt16 ExcEof::GetNum() const
+{
+ return 0x000A;
+}
+
+std::size_t ExcEof::GetLen() const
+{
+ return 0;
+}
+
+//--------------------------------------------------------- class ExcDummy_00 -
+
+std::size_t ExcDummy_00::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_00::GetData() const
+{
+ return pMyData;
+}
+
+//-------------------------------------------------------- class ExcDummy_04x -
+
+std::size_t ExcDummy_040::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_040::GetData() const
+{
+ return pMyData;
+}
+
+std::size_t ExcDummy_041::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_041::GetData() const
+{
+ return pMyData;
+}
+
+//------------------------------------------------------------- class Exc1904 -
+
+Exc1904::Exc1904( const ScDocument& rDoc )
+{
+ const Date& rDate = rDoc.GetFormatTable()->GetNullDate();
+ bVal = (rDate == Date( 1, 1, 1904 ));
+ bDateCompatibility = (rDate != Date( 30, 12, 1899 ));
+}
+
+sal_uInt16 Exc1904::GetNum() const
+{
+ return 0x0022;
+}
+
+void Exc1904::SaveXml( XclExpXmlStream& rStrm )
+{
+ bool bISOIEC = ( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 );
+
+ if( bISOIEC )
+ {
+ rStrm.WriteAttributes(XML_dateCompatibility, ToPsz(bDateCompatibility));
+ }
+
+ if( !bISOIEC || bDateCompatibility )
+ {
+ rStrm.WriteAttributes(XML_date1904, ToPsz(bVal));
+ }
+}
+
+//------------------------------------------------------ class ExcBundlesheet -
+
+ExcBundlesheetBase::ExcBundlesheetBase( const RootData& rRootData, SCTAB nTabNum ) :
+ m_nStrPos( STREAM_SEEK_TO_END ),
+ m_nOwnPos( STREAM_SEEK_TO_END ),
+ nGrbit( rRootData.pER->GetTabInfo().IsVisibleTab( nTabNum ) ? 0x0000 : 0x0001 ),
+ nTab( nTabNum )
+{
+}
+
+ExcBundlesheetBase::ExcBundlesheetBase() :
+ m_nStrPos( STREAM_SEEK_TO_END ),
+ m_nOwnPos( STREAM_SEEK_TO_END ),
+ nGrbit( 0x0000 ),
+ nTab( SCTAB_GLOBAL )
+{
+}
+
+void ExcBundlesheetBase::UpdateStreamPos( XclExpStream& rStrm )
+{
+ rStrm.SetSvStreamPos( m_nOwnPos );
+ rStrm.DisableEncryption();
+ rStrm << static_cast<sal_uInt32>(m_nStrPos);
+ rStrm.EnableEncryption();
+}
+
+sal_uInt16 ExcBundlesheetBase::GetNum() const
+{
+ return 0x0085;
+}
+
+ExcBundlesheet::ExcBundlesheet( const RootData& rRootData, SCTAB _nTab ) :
+ ExcBundlesheetBase( rRootData, _nTab )
+{
+ OUString sTabName = rRootData.pER->GetTabInfo().GetScTabName( _nTab );
+ OSL_ENSURE( sTabName.getLength() < 256, "ExcBundlesheet::ExcBundlesheet - table name too long" );
+ aName = OUStringToOString(sTabName, rRootData.pER->GetTextEncoding());
+}
+
+void ExcBundlesheet::SaveCont( XclExpStream& rStrm )
+{
+ m_nOwnPos = rStrm.GetSvStreamPos();
+ rStrm << sal_uInt32(0x00000000) // dummy (stream position of the sheet)
+ << nGrbit;
+ rStrm.WriteByteString(aName); // 8 bit length, max 255 chars
+}
+
+std::size_t ExcBundlesheet::GetLen() const
+{
+ return 7 + std::min( aName.getLength(), sal_Int32(255) );
+}
+
+//--------------------------------------------------------- class ExcDummy_02 -
+
+std::size_t ExcDummy_02a::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_02a::GetData() const
+{
+ return pMyData;
+}
+//--------------------------------------------------------- class ExcDummy_02 -
+
+XclExpCountry::XclExpCountry( const XclExpRoot& rRoot ) :
+ XclExpRecord( EXC_ID_COUNTRY, 4 )
+{
+ /* #i31530# set document country as UI country too -
+ needed for correct behaviour of number formats. */
+ mnUICountry = mnDocCountry = static_cast< sal_uInt16 >(
+ ::msfilter::ConvertLanguageToCountry( rRoot.GetDocLanguage() ) );
+}
+
+void XclExpCountry::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnUICountry << mnDocCountry;
+}
+
+// XclExpWsbool ===============================================================
+
+XclExpWsbool::XclExpWsbool( bool bFitToPages )
+ : XclExpUInt16Record( EXC_ID_WSBOOL, EXC_WSBOOL_DEFAULTFLAGS )
+{
+ if( bFitToPages )
+ SetValue( GetValue() | EXC_WSBOOL_FITTOPAGE );
+}
+
+XclExpXmlSheetPr::XclExpXmlSheetPr( bool bFitToPages, SCTAB nScTab, const Color& rTabColor, XclExpFilterManager* pManager ) :
+ mnScTab(nScTab), mpManager(pManager), mbFitToPage(bFitToPages), maTabColor(rTabColor) {}
+
+void XclExpXmlSheetPr::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_sheetPr,
+ // OOXTODO: XML_syncHorizontal,
+ // OOXTODO: XML_syncVertical,
+ // OOXTODO: XML_syncRef,
+ // OOXTODO: XML_transitionEvaluation,
+ // OOXTODO: XML_transitionEntry,
+ // OOXTODO: XML_published,
+ // OOXTODO: XML_codeName,
+ XML_filterMode, mpManager ? ToPsz(mpManager->HasFilterMode(mnScTab)) : nullptr
+ // OOXTODO: XML_enableFormatConditionsCalculation
+ );
+
+ // Note : the order of child elements is significant. Don't change the order.
+
+ // OOXTODO: XML_outlinePr
+
+ if (maTabColor != COL_AUTO)
+ rWorksheet->singleElement(XML_tabColor, XML_rgb, XclXmlUtils::ToOString(maTabColor));
+
+ rWorksheet->singleElement(XML_pageSetUpPr,
+ // OOXTODO: XML_autoPageBreaks,
+ XML_fitToPage, ToPsz(mbFitToPage));
+
+ rWorksheet->endElement( XML_sheetPr );
+}
+
+// XclExpWindowProtection ===============================================================
+
+XclExpWindowProtection::XclExpWindowProtection(bool bValue) :
+ XclExpBoolRecord(EXC_ID_WINDOWPROTECT, bValue)
+{
+}
+
+void XclExpWindowProtection::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.WriteAttributes(XML_lockWindows, ToPsz(GetBool()));
+}
+
+// XclExpDocProtection ===============================================================
+
+XclExpProtection::XclExpProtection(bool bValue) :
+ XclExpBoolRecord(EXC_ID_PROTECT, bValue)
+{
+}
+
+XclExpSheetProtection::XclExpSheetProtection(bool bValue, SCTAB nTab ) :
+ XclExpProtection( bValue),
+ mnTab(nTab)
+{
+}
+
+void XclExpSheetProtection::SaveXml( XclExpXmlStream& rStrm )
+{
+ ScDocument& rDoc = rStrm.GetRoot().GetDoc();
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnTab);
+ if ( !pTabProtect )
+ return;
+
+ const ScOoxPasswordHash& rPH = pTabProtect->getPasswordHash();
+ // Do not write any hash attributes if there is no password.
+ ScOoxPasswordHash aPH;
+ if (rPH.hasPassword())
+ aPH = rPH;
+
+ Sequence<sal_Int8> aHash = pTabProtect->getPasswordHash(PASSHASH_XL);
+ OString sHash;
+ if (aHash.getLength() >= 2)
+ {
+ sHash = OString::number(
+ ( static_cast<sal_uInt8>(aHash[0]) << 8
+ | static_cast<sal_uInt8>(aHash[1]) ),
+ 16 );
+ }
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->singleElement( XML_sheetProtection,
+ XML_algorithmName, aPH.maAlgorithmName.isEmpty() ? nullptr : aPH.maAlgorithmName.toUtf8().getStr(),
+ XML_hashValue, aPH.maHashValue.isEmpty() ? nullptr : aPH.maHashValue.toUtf8().getStr(),
+ XML_saltValue, aPH.maSaltValue.isEmpty() ? nullptr : aPH.maSaltValue.toUtf8().getStr(),
+ XML_spinCount, aPH.mnSpinCount ? OString::number( aPH.mnSpinCount).getStr() : nullptr,
+ XML_sheet, ToPsz( true ),
+ XML_password, sHash.isEmpty()? nullptr : sHash.getStr(),
+ XML_objects, pTabProtect->isOptionEnabled( ScTableProtection::OBJECTS ) ? nullptr : ToPsz( true ),
+ XML_scenarios, pTabProtect->isOptionEnabled( ScTableProtection::SCENARIOS ) ? nullptr : ToPsz( true ),
+ XML_formatCells, pTabProtect->isOptionEnabled( ScTableProtection::FORMAT_CELLS ) ? ToPsz( false ) : nullptr,
+ XML_formatColumns, pTabProtect->isOptionEnabled( ScTableProtection::FORMAT_COLUMNS ) ? ToPsz( false ) : nullptr,
+ XML_formatRows, pTabProtect->isOptionEnabled( ScTableProtection::FORMAT_ROWS ) ? ToPsz( false ) : nullptr,
+ XML_insertColumns, pTabProtect->isOptionEnabled( ScTableProtection::INSERT_COLUMNS ) ? ToPsz( false ) : nullptr,
+ XML_insertRows, pTabProtect->isOptionEnabled( ScTableProtection::INSERT_ROWS ) ? ToPsz( false ) : nullptr,
+ XML_insertHyperlinks, pTabProtect->isOptionEnabled( ScTableProtection::INSERT_HYPERLINKS ) ? ToPsz( false ) : nullptr,
+ XML_deleteColumns, pTabProtect->isOptionEnabled( ScTableProtection::DELETE_COLUMNS ) ? ToPsz( false ) : nullptr,
+ XML_deleteRows, pTabProtect->isOptionEnabled( ScTableProtection::DELETE_ROWS ) ? ToPsz( false ) : nullptr,
+ XML_selectLockedCells, pTabProtect->isOptionEnabled( ScTableProtection::SELECT_LOCKED_CELLS ) ? nullptr : ToPsz( true ),
+ XML_sort, pTabProtect->isOptionEnabled( ScTableProtection::SORT ) ? ToPsz( false ) : nullptr,
+ XML_autoFilter, pTabProtect->isOptionEnabled( ScTableProtection::AUTOFILTER ) ? ToPsz( false ) : nullptr,
+ XML_pivotTables, pTabProtect->isOptionEnabled( ScTableProtection::PIVOT_TABLES ) ? ToPsz( false ) : nullptr,
+ XML_selectUnlockedCells, pTabProtect->isOptionEnabled( ScTableProtection::SELECT_UNLOCKED_CELLS ) ? nullptr : ToPsz( true ) );
+
+ const ::std::vector<ScEnhancedProtection>& rProts( pTabProtect->getEnhancedProtection());
+ if (rProts.empty())
+ return;
+
+ rWorksheet->startElement(XML_protectedRanges);
+ for (const auto& rProt : rProts)
+ {
+ SAL_WARN_IF( rProt.maSecurityDescriptorXML.isEmpty() && !rProt.maSecurityDescriptor.empty(),
+ "sc.filter", "XclExpSheetProtection::SaveXml: losing BIFF security descriptor");
+ rWorksheet->singleElement( XML_protectedRange,
+ XML_name, rProt.maTitle.isEmpty() ? nullptr : rProt.maTitle.toUtf8().getStr(),
+ XML_securityDescriptor, rProt.maSecurityDescriptorXML.isEmpty() ? nullptr : rProt.maSecurityDescriptorXML.toUtf8().getStr(),
+ /* XXX 'password' is not part of OOXML, but Excel2013
+ * writes it if loaded from BIFF, in which case
+ * 'algorithmName', 'hashValue', 'saltValue' and
+ * 'spinCount' are absent; so do we if it was present. */
+ XML_password, rProt.mnPasswordVerifier ? OString::number( rProt.mnPasswordVerifier, 16).getStr() : nullptr,
+ XML_algorithmName, rProt.maPasswordHash.maAlgorithmName.isEmpty() ? nullptr : rProt.maPasswordHash.maAlgorithmName.toUtf8().getStr(),
+ XML_hashValue, rProt.maPasswordHash.maHashValue.isEmpty() ? nullptr : rProt.maPasswordHash.maHashValue.toUtf8().getStr(),
+ XML_saltValue, rProt.maPasswordHash.maSaltValue.isEmpty() ? nullptr : rProt.maPasswordHash.maSaltValue.toUtf8().getStr(),
+ XML_spinCount, rProt.maPasswordHash.mnSpinCount ? OString::number( rProt.maPasswordHash.mnSpinCount).getStr() : nullptr,
+ XML_sqref, rProt.maRangeList.is() ? XclXmlUtils::ToOString( rStrm.GetRoot().GetDoc(), *rProt.maRangeList).getStr() : nullptr);
+ }
+ rWorksheet->endElement( XML_protectedRanges);
+}
+
+XclExpPassHash::XclExpPassHash(const Sequence<sal_Int8>& aHash) :
+ XclExpRecord(EXC_ID_PASSWORD, 2),
+ mnHash(0x0000)
+{
+ if (aHash.getLength() >= 2)
+ {
+ mnHash = ((aHash[0] << 8) & 0xFFFF);
+ mnHash |= (aHash[1] & 0xFF);
+ }
+}
+
+XclExpPassHash::~XclExpPassHash()
+{
+}
+
+void XclExpPassHash::WriteBody(XclExpStream& rStrm)
+{
+ rStrm << mnHash;
+}
+
+XclExpFiltermode::XclExpFiltermode() :
+ XclExpEmptyRecord( EXC_ID_FILTERMODE )
+{
+}
+
+XclExpAutofilterinfo::XclExpAutofilterinfo( const ScAddress& rStartPos, SCCOL nScCol ) :
+ XclExpUInt16Record( EXC_ID_AUTOFILTERINFO, static_cast< sal_uInt16 >( nScCol ) ),
+ maStartPos( rStartPos )
+{
+}
+
+ExcFilterCondition::ExcFilterCondition() :
+ nType( EXC_AFTYPE_NOTUSED ),
+ nOper( EXC_AFOPER_EQUAL )
+{
+}
+
+ExcFilterCondition::~ExcFilterCondition()
+{
+}
+
+std::size_t ExcFilterCondition::GetTextBytes() const
+{
+ return pText ? (1 + pText->GetBufferSize()) : 0;
+}
+
+void ExcFilterCondition::SetCondition( sal_uInt8 nTp, sal_uInt8 nOp, const OUString* pT )
+{
+ nType = nTp;
+ nOper = nOp;
+ pText.reset( pT ? new XclExpString( *pT, XclStrFlags::EightBitLength ) : nullptr);
+}
+
+void ExcFilterCondition::Save( XclExpStream& rStrm )
+{
+ rStrm << nType << nOper;
+ if (nType == EXC_AFTYPE_STRING)
+ {
+ OSL_ENSURE(pText, "ExcFilterCondition::Save() -- pText is NULL!");
+ rStrm << sal_uInt32(0) << static_cast<sal_uInt8>(pText->Len()) << sal_uInt16(0) << sal_uInt8(0);
+ }
+ else
+ rStrm << sal_uInt32(0) << sal_uInt32(0);
+}
+
+static const char* lcl_GetOperator( sal_uInt8 nOper )
+{
+ switch( nOper )
+ {
+ case EXC_AFOPER_EQUAL: return "equal";
+ case EXC_AFOPER_GREATER: return "greaterThan";
+ case EXC_AFOPER_GREATEREQUAL: return "greaterThanOrEqual";
+ case EXC_AFOPER_LESS: return "lessThan";
+ case EXC_AFOPER_LESSEQUAL: return "lessThanOrEqual";
+ case EXC_AFOPER_NOTEQUAL: return "notEqual";
+ case EXC_AFOPER_NONE:
+ default: return "**none**";
+ }
+}
+
+static OString lcl_GetValue( sal_uInt8 nType, const XclExpString* pStr )
+{
+ if (nType == EXC_AFTYPE_STRING)
+ return XclXmlUtils::ToOString(*pStr);
+ else
+ return OString();
+}
+
+void ExcFilterCondition::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( IsEmpty() )
+ return;
+
+ rStrm.GetCurrentStream()->singleElement( XML_customFilter,
+ XML_operator, lcl_GetOperator( nOper ),
+ XML_val, lcl_GetValue(nType, pText.get()) );
+}
+
+void ExcFilterCondition::SaveText( XclExpStream& rStrm )
+{
+ if( nType == EXC_AFTYPE_STRING )
+ {
+ OSL_ENSURE( pText, "ExcFilterCondition::SaveText() -- pText is NULL!" );
+ pText->WriteFlagField( rStrm );
+ pText->WriteBuffer( rStrm );
+ }
+}
+
+XclExpAutofilter::XclExpAutofilter( const XclExpRoot& rRoot, sal_uInt16 nC ) :
+ XclExpRecord( EXC_ID_AUTOFILTER, 24 ),
+ XclExpRoot( rRoot ),
+ meType(FilterCondition),
+ nCol( nC ),
+ nFlags( 0 ),
+ bHasBlankValue( false )
+{
+}
+
+bool XclExpAutofilter::AddCondition( ScQueryConnect eConn, sal_uInt8 nType, sal_uInt8 nOp,
+ const OUString* pText, bool bSimple )
+{
+ if( !aCond[ 1 ].IsEmpty() )
+ return false;
+
+ sal_uInt16 nInd = aCond[ 0 ].IsEmpty() ? 0 : 1;
+
+ if( nInd == 1 )
+ nFlags |= (eConn == SC_OR) ? EXC_AFFLAG_OR : EXC_AFFLAG_AND;
+ if( bSimple )
+ nFlags |= (nInd == 0) ? EXC_AFFLAG_SIMPLE1 : EXC_AFFLAG_SIMPLE2;
+
+ aCond[ nInd ].SetCondition( nType, nOp, pText );
+
+ AddRecSize( aCond[ nInd ].GetTextBytes() );
+
+ return true;
+}
+
+bool XclExpAutofilter::HasCondition() const
+{
+ return !aCond[0].IsEmpty();
+}
+
+bool XclExpAutofilter::AddEntry( const ScQueryEntry& rEntry )
+{
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+
+ if (rItems.empty())
+ {
+ if (GetOutput() != EXC_OUTPUT_BINARY)
+ {
+ // tdf#123353 XLSX export
+ meType = BlankValue;
+ return false;
+ }
+ // XLS export
+ return true;
+ }
+
+ if (GetOutput() != EXC_OUTPUT_BINARY && rItems.size() > 1)
+ {
+ AddMultiValueEntry(rEntry);
+ return false;
+ }
+
+ bool bConflict = false;
+ OUString sText;
+ const ScQueryEntry::Item& rItem = rItems[0];
+ if (!rItem.maString.isEmpty())
+ {
+ sText = rItem.maString.getString();
+ switch( rEntry.eOp )
+ {
+ case SC_CONTAINS:
+ case SC_DOES_NOT_CONTAIN:
+ {
+ sText = "*" + sText + "*";
+ }
+ break;
+ case SC_BEGINS_WITH:
+ case SC_DOES_NOT_BEGIN_WITH:
+ sText += "*";
+ break;
+ case SC_ENDS_WITH:
+ case SC_DOES_NOT_END_WITH:
+ sText = "*" + sText;
+ break;
+ default:
+ {
+ //nothing
+ }
+ }
+ }
+
+ // empty/nonempty fields
+ if (rEntry.IsQueryByEmpty())
+ {
+ bConflict = !AddCondition(rEntry.eConnect, EXC_AFTYPE_EMPTY, EXC_AFOPER_NONE, nullptr, true);
+ bHasBlankValue = true;
+ }
+ else if(rEntry.IsQueryByNonEmpty())
+ bConflict = !AddCondition( rEntry.eConnect, EXC_AFTYPE_NOTEMPTY, EXC_AFOPER_NONE, nullptr, true );
+ else if (rEntry.IsQueryByTextColor() || rEntry.IsQueryByBackgroundColor())
+ {
+ AddColorEntry(rEntry);
+ }
+ // other conditions
+ else
+ {
+ // top10 flags
+ sal_uInt16 nNewFlags = 0x0000;
+ switch( rEntry.eOp )
+ {
+ case SC_TOPVAL:
+ nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP);
+ break;
+ case SC_BOTVAL:
+ nNewFlags = EXC_AFFLAG_TOP10;
+ break;
+ case SC_TOPPERC:
+ nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP | EXC_AFFLAG_TOP10PERC);
+ break;
+ case SC_BOTPERC:
+ nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10PERC);
+ break;
+ default:;
+ }
+ bool bNewTop10 = ::get_flag( nNewFlags, EXC_AFFLAG_TOP10 );
+
+ bConflict = HasTop10() && bNewTop10;
+ if( !bConflict )
+ {
+ if( bNewTop10 )
+ {
+ sal_uInt32 nIndex = 0;
+ double fVal = 0.0;
+ if (GetFormatter().IsNumberFormat(sText, nIndex, fVal))
+ {
+ if (fVal < 0) fVal = 0;
+ if (fVal >= 501) fVal = 500;
+ }
+ nFlags |= (nNewFlags | static_cast<sal_uInt16>(fVal) << 7);
+ }
+ // normal condition
+ else
+ {
+ if (GetOutput() != EXC_OUTPUT_BINARY && rEntry.eOp == SC_EQUAL)
+ {
+ AddMultiValueEntry(rEntry);
+ return false;
+ }
+
+ sal_uInt8 nOper = EXC_AFOPER_NONE;
+
+ switch( rEntry.eOp )
+ {
+ case SC_EQUAL: nOper = EXC_AFOPER_EQUAL; break;
+ case SC_LESS: nOper = EXC_AFOPER_LESS; break;
+ case SC_GREATER: nOper = EXC_AFOPER_GREATER; break;
+ case SC_LESS_EQUAL: nOper = EXC_AFOPER_LESSEQUAL; break;
+ case SC_GREATER_EQUAL: nOper = EXC_AFOPER_GREATEREQUAL; break;
+ case SC_NOT_EQUAL: nOper = EXC_AFOPER_NOTEQUAL; break;
+ case SC_CONTAINS:
+ case SC_BEGINS_WITH:
+ case SC_ENDS_WITH:
+ nOper = EXC_AFOPER_EQUAL; break;
+ case SC_DOES_NOT_CONTAIN:
+ case SC_DOES_NOT_BEGIN_WITH:
+ case SC_DOES_NOT_END_WITH:
+ nOper = EXC_AFOPER_NOTEQUAL; break;
+ default:;
+ }
+ bConflict = !AddCondition( rEntry.eConnect, EXC_AFTYPE_STRING, nOper, &sText);
+ }
+ }
+ }
+ return bConflict;
+}
+
+void XclExpAutofilter::AddMultiValueEntry( const ScQueryEntry& rEntry )
+{
+ meType = MultiValue;
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ for (const auto& rItem : rItems)
+ {
+ if( rItem.maString.isEmpty() )
+ bHasBlankValue = true;
+ else
+ maMultiValues.push_back(std::make_pair(rItem.maString.getString(), rItem.meType == ScQueryEntry::ByDate));
+ }
+}
+
+void XclExpAutofilter::AddColorEntry(const ScQueryEntry& rEntry)
+{
+ meType = ColorValue;
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ for (const auto& rItem : rItems)
+ {
+ maColorValues.push_back(
+ std::make_pair(rItem.maColor, rItem.meType == ScQueryEntry::ByBackgroundColor));
+ // Ensure that selected color(s) will be added to dxf: selection can be not in list
+ // of already added to dfx colors taken from filter range
+ if (GetDxfs().GetDxfByColor(rItem.maColor) == -1)
+ GetDxfs().AddColor(rItem.maColor);
+ }
+}
+
+void XclExpAutofilter::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << nCol << nFlags;
+ aCond[ 0 ].Save( rStrm );
+ aCond[ 1 ].Save( rStrm );
+ aCond[ 0 ].SaveText( rStrm );
+ aCond[ 1 ].SaveText( rStrm );
+}
+
+void XclExpAutofilter::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (meType == FilterCondition && !HasCondition() && !HasTop10())
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement( XML_filterColumn,
+ XML_colId, OString::number(nCol)
+ // OOXTODO: XML_hiddenButton, AutoFilter12 fHideArrow?
+ // OOXTODO: XML_showButton
+ );
+
+ switch (meType)
+ {
+ case FilterCondition:
+ {
+ if( HasTop10() )
+ {
+ rWorksheet->singleElement( XML_top10,
+ XML_top, ToPsz( get_flag( nFlags, EXC_AFFLAG_TOP10TOP ) ),
+ XML_percent, ToPsz( get_flag( nFlags, EXC_AFFLAG_TOP10PERC ) ),
+ XML_val, OString::number(nFlags >> 7)
+ // OOXTODO: XML_filterVal
+ );
+ }
+ else
+ {
+ rWorksheet->startElement(XML_customFilters, XML_and,
+ ToPsz((nFlags & EXC_AFFLAG_ANDORMASK) == EXC_AFFLAG_AND));
+ aCond[0].SaveXml(rStrm);
+ aCond[1].SaveXml(rStrm);
+ rWorksheet->endElement(XML_customFilters);
+ }
+ // OOXTODO: XML_dynamicFilter, XML_extLst, XML_filters, XML_iconFilter
+ }
+ break;
+ case ColorValue:
+ {
+ if (!maColorValues.empty())
+ {
+ Color color = maColorValues[0].first;
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ if (maColorValues[0].second) // is background color
+ {
+ pAttrList->add(XML_cellColor, OString::number(1));
+ }
+ else
+ {
+ pAttrList->add(XML_cellColor, OString::number(0));
+ }
+ pAttrList->add(XML_dxfId, OString::number(GetDxfs().GetDxfByColor(color)));
+ rWorksheet->singleElement(XML_colorFilter, pAttrList);
+ }
+ }
+ break;
+ case BlankValue:
+ {
+ rWorksheet->singleElement(XML_filters, XML_blank, "1");
+ }
+ break;
+ case MultiValue:
+ {
+ if( bHasBlankValue )
+ rWorksheet->startElement(XML_filters, XML_blank, "1");
+ else
+ rWorksheet->startElement(XML_filters);
+
+ for (const auto& rMultiValue : maMultiValues)
+ {
+ OString aStr = OUStringToOString(rMultiValue.first, RTL_TEXTENCODING_UTF8);
+ if( !rMultiValue.second )
+ {
+ const char* pz = aStr.getStr();
+ rWorksheet->singleElement(XML_filter, XML_val, pz);
+ }
+ else
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+ sal_Int32 aDateGroup[3] = { XML_year, XML_month, XML_day };
+ sal_Int32 idx = 0;
+ for (size_t i = 0; idx >= 0 && i < 3; i++)
+ {
+ OString kw = aStr.getToken(0, '-', idx);
+ kw = kw.trim();
+ if (!kw.isEmpty())
+ {
+ pAttrList->add(aDateGroup[i], kw);
+ }
+ }
+ // TODO: date filter can only handle YYYY-MM-DD date formats, so XML_dateTimeGrouping value
+ // will be "day" as default, until date filter cannot handle HH:MM:SS.
+ pAttrList->add(XML_dateTimeGrouping, "day");
+ rWorksheet->singleElement(XML_dateGroupItem, pAttrList);
+ }
+ }
+ rWorksheet->endElement(XML_filters);
+ }
+ break;
+ }
+ rWorksheet->endElement( XML_filterColumn );
+}
+
+ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot& rRoot, SCTAB nTab, const ScDBData* pDefinedData ) :
+ XclExpRoot( rRoot ),
+ mbAutoFilter (false)
+{
+ XclExpNameManager& rNameMgr = GetNameManager();
+
+ bool bFound = false;
+ bool bAdvanced = false;
+ const ScDBData* pData = (pDefinedData ? pDefinedData : rRoot.GetDoc().GetAnonymousDBData(nTab));
+ ScRange aAdvRange;
+ if (pData)
+ {
+ bAdvanced = pData->GetAdvancedQuerySource( aAdvRange );
+ bFound = (pData->HasQueryParam() || pData->HasAutoFilter() || bAdvanced);
+ }
+ if( !bFound )
+ return;
+
+ ScQueryParam aParam;
+ pData->GetQueryParam( aParam );
+
+ ScRange aRange( aParam.nCol1, aParam.nRow1, aParam.nTab,
+ aParam.nCol2, aParam.nRow2, aParam.nTab );
+ SCCOL nColCnt = aParam.nCol2 - aParam.nCol1 + 1;
+
+ maRef = aRange;
+
+ // #i2394# built-in defined names must be sorted by containing sheet name
+ if (!pDefinedData)
+ rNameMgr.InsertBuiltInName( EXC_BUILTIN_FILTERDATABASE, aRange );
+
+ // advanced filter
+ if( bAdvanced )
+ {
+ // filter criteria, excel allows only same table
+ if( !pDefinedData && aAdvRange.aStart.Tab() == nTab )
+ rNameMgr.InsertBuiltInName( EXC_BUILTIN_CRITERIA, aAdvRange );
+
+ // filter destination range, excel allows only same table
+ if( !aParam.bInplace )
+ {
+ ScRange aDestRange( aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
+ aDestRange.aEnd.IncCol( nColCnt - 1 );
+ if( !pDefinedData && aDestRange.aStart.Tab() == nTab )
+ rNameMgr.InsertBuiltInName( EXC_BUILTIN_EXTRACT, aDestRange );
+ }
+
+ m_pFilterMode = new XclExpFiltermode;
+ }
+ // AutoFilter
+ else
+ {
+ bool bConflict = false;
+ bool bContLoop = true;
+ bool bHasOr = false;
+ SCCOLROW nFirstField = aParam.GetEntry( 0 ).nField;
+
+ // create AUTOFILTER records for filtered columns
+ for( SCSIZE nEntry = 0; !bConflict && bContLoop && (nEntry < aParam.GetEntryCount()); nEntry++ )
+ {
+ const ScQueryEntry& rEntry = aParam.GetEntry( nEntry );
+
+ bContLoop = rEntry.bDoQuery;
+ if( bContLoop )
+ {
+ XclExpAutofilter* pFilter = GetByCol( static_cast<SCCOL>(rEntry.nField) - aRange.aStart.Col() );
+
+ if( nEntry > 0 )
+ bHasOr |= (rEntry.eConnect == SC_OR);
+
+ bConflict = (nEntry > 1) && bHasOr;
+ if( !bConflict )
+ bConflict = (nEntry == 1) && (rEntry.eConnect == SC_OR) &&
+ (nFirstField != rEntry.nField);
+ if( !bConflict )
+ bConflict = pFilter->AddEntry( rEntry );
+ }
+ }
+
+ // additional tests for conflicts
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); !bConflict && (nPos < nSize); ++nPos )
+ {
+ XclExpAutofilterRef xFilter = maFilterList.GetRecord( nPos );
+ bConflict = xFilter->HasCondition() && xFilter->HasTop10();
+ }
+
+ if( bConflict )
+ maFilterList.RemoveAllRecords();
+
+ if( !maFilterList.IsEmpty() )
+ m_pFilterMode = new XclExpFiltermode;
+ m_pFilterInfo = new XclExpAutofilterinfo( aRange.aStart, nColCnt );
+
+ if (maFilterList.IsEmpty () && !bConflict)
+ mbAutoFilter = true;
+
+ // get sort criteria
+ {
+ ScSortParam aSortParam;
+ pData->GetSortParam( aSortParam );
+
+ ScUserList* pList = ScGlobal::GetUserList();
+ if (aSortParam.bUserDef && pList && pList->size() > aSortParam.nUserIndex)
+ {
+ // get sorted area without headers
+ maSortRef = ScRange(
+ aParam.nCol1, aParam.nRow1 + (aSortParam.bHasHeader? 1 : 0), aParam.nTab,
+ aParam.nCol2, aParam.nRow2, aParam.nTab );
+
+ // get sorted columns with custom lists
+ const ScUserListData& rData = (*pList)[aSortParam.nUserIndex];
+
+ // get column index and sorting direction
+ SCCOLROW nField = 0;
+ bool bSortAscending=true;
+ for (const auto & rKey : aSortParam.maKeyState)
+ {
+ if (rKey.bDoSort)
+ {
+ nField = rKey.nField;
+ bSortAscending = rKey.bAscending;
+ break;
+ }
+ }
+
+ // remember sort criteria
+ const ScRange aSortedColumn(
+ nField, aParam.nRow1 + (aSortParam.bHasHeader? 1 : 0), aParam.nTab,
+ nField, aParam.nRow2, aParam.nTab );
+ const OUString aItemList = rData.GetString();
+
+ maSortCustomList.emplace_back(aSortedColumn, aItemList, !bSortAscending);
+ }
+ }
+ }
+}
+
+ExcAutoFilterRecs::~ExcAutoFilterRecs()
+{
+}
+
+XclExpAutofilter* ExcAutoFilterRecs::GetByCol( SCCOL nCol )
+{
+ XclExpAutofilterRef xFilter;
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); nPos < nSize; ++nPos )
+ {
+ xFilter = maFilterList.GetRecord( nPos );
+ if( xFilter->GetCol() == static_cast<sal_uInt16>(nCol) )
+ return xFilter.get();
+ }
+ xFilter = new XclExpAutofilter( GetRoot(), static_cast<sal_uInt16>(nCol) );
+ maFilterList.AppendRecord( xFilter );
+ return xFilter.get();
+}
+
+bool ExcAutoFilterRecs::IsFiltered( SCCOL nCol )
+{
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); nPos < nSize; ++nPos )
+ if( maFilterList.GetRecord( nPos )->GetCol() == static_cast<sal_uInt16>(nCol) )
+ return true;
+ return false;
+}
+
+void ExcAutoFilterRecs::AddObjRecs()
+{
+ if( m_pFilterInfo )
+ {
+ ScAddress aAddr( m_pFilterInfo->GetStartPos() );
+ for( SCCOL nObj = 0, nCount = m_pFilterInfo->GetColCount(); nObj < nCount; nObj++ )
+ {
+ std::unique_ptr<XclObj> pObjRec(new XclObjDropDown( GetObjectManager(), aAddr, IsFiltered( nObj ) ));
+ GetObjectManager().AddObj( std::move(pObjRec) );
+ aAddr.IncCol();
+ }
+ }
+}
+
+void ExcAutoFilterRecs::Save( XclExpStream& rStrm )
+{
+ if( m_pFilterMode )
+ m_pFilterMode->Save( rStrm );
+ if( m_pFilterInfo )
+ m_pFilterInfo->Save( rStrm );
+ maFilterList.Save( rStrm );
+}
+
+void ExcAutoFilterRecs::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maFilterList.IsEmpty() && !mbAutoFilter )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_autoFilter, XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maRef));
+ // OOXTODO: XML_extLst, XML_sortState
+ if( !maFilterList.IsEmpty() )
+ maFilterList.SaveXml( rStrm );
+
+ if (!maSortCustomList.empty())
+ {
+ rWorksheet->startElement(XML_sortState, XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maSortRef));
+
+ for (const auto & rSortCriteria : maSortCustomList)
+ {
+ if (std::get<2>(rSortCriteria))
+ rWorksheet->singleElement(XML_sortCondition,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(),
+ std::get<0>(rSortCriteria)),
+ XML_descending, "1",
+ XML_customList, std::get<1>(rSortCriteria).toUtf8().getStr());
+ else
+ rWorksheet->singleElement(XML_sortCondition,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(),
+ std::get<0>(rSortCriteria)),
+ XML_customList, std::get<1>(rSortCriteria).toUtf8().getStr());
+ }
+
+ rWorksheet->endElement(XML_sortState);
+ }
+
+ rWorksheet->endElement( XML_autoFilter );
+}
+
+bool ExcAutoFilterRecs::HasFilterMode() const
+{
+ return m_pFilterMode != nullptr;
+}
+
+XclExpFilterManager::XclExpFilterManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpFilterManager::InitTabFilter( SCTAB nScTab )
+{
+ maFilterMap[ nScTab ] = new ExcAutoFilterRecs( GetRoot(), nScTab, nullptr );
+}
+
+XclExpRecordRef XclExpFilterManager::CreateRecord( SCTAB nScTab )
+{
+ XclExpTabFilterRef xRec;
+ XclExpTabFilterMap::iterator aIt = maFilterMap.find( nScTab );
+ if( aIt != maFilterMap.end() )
+ {
+ xRec = aIt->second;
+ xRec->AddObjRecs();
+ }
+ return xRec;
+}
+
+bool XclExpFilterManager::HasFilterMode( SCTAB nScTab )
+{
+ XclExpTabFilterMap::iterator aIt = maFilterMap.find( nScTab );
+ if( aIt != maFilterMap.end() )
+ {
+ return aIt->second->HasFilterMode();
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/exctools.cxx b/sc/source/filter/excel/exctools.cxx
new file mode 100644
index 000000000..6e9d91777
--- /dev/null
+++ b/sc/source/filter/excel/exctools.cxx
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/diagnose.h>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <scextopt.hxx>
+#include <olinetab.hxx>
+
+#include <root.hxx>
+#include <excimp8.hxx>
+#include <namebuff.hxx>
+#include <otlnbuff.hxx>
+#include <formel.hxx>
+#include <xilink.hxx>
+
+#include <memory>
+#include <vector>
+
+RootData::RootData()
+{
+ eDateiTyp = BiffX;
+ pFmlaConverter = nullptr;
+
+ pTabId = nullptr;
+ pUserBViewList = nullptr;
+
+ pIR = nullptr;
+ pER = nullptr;
+ pColRowBuff = nullptr;
+}
+
+RootData::~RootData()
+{
+ pExtSheetBuff.reset();
+ pShrfmlaBuff.reset();
+ pExtNameBuff.reset();
+ pAutoFilterBuffer.reset();
+}
+
+XclImpOutlineBuffer::XclImpOutlineBuffer( SCSIZE nNewSize ) :
+ maLevels(0, nNewSize, 0),
+ mpOutlineArray(nullptr),
+ mnEndPos(nNewSize),
+ mnMaxLevel(0),
+ mbButtonAfter(true)
+{
+}
+
+XclImpOutlineBuffer::~XclImpOutlineBuffer()
+{
+}
+
+void XclImpOutlineBuffer::SetLevel( SCSIZE nIndex, sal_uInt8 nVal, bool bCollapsed )
+{
+ maLevels.insert_back(nIndex, nIndex+1, nVal);
+ if (nVal > mnMaxLevel)
+ mnMaxLevel = nVal;
+ if (bCollapsed)
+ maCollapsedPosSet.insert(nIndex);
+}
+
+void XclImpOutlineBuffer::SetOutlineArray( ScOutlineArray* pOArray )
+{
+ mpOutlineArray = pOArray;
+}
+
+void XclImpOutlineBuffer::MakeScOutline()
+{
+ if (!mpOutlineArray)
+ return;
+
+ ::std::vector<SCSIZE> aOutlineStack;
+ aOutlineStack.reserve(mnMaxLevel);
+ for (const auto& [nPos, nLevel] : maLevels)
+ {
+ if (nPos >= mnEndPos)
+ {
+ // Don't go beyond the max allowed position.
+ OSL_ENSURE(aOutlineStack.empty(), "XclImpOutlineBuffer::MakeScOutline: outline stack not empty but expected to be.");
+ break;
+ }
+ sal_uInt8 nCurLevel = static_cast<sal_uInt8>(aOutlineStack.size());
+ if (nLevel > nCurLevel)
+ {
+ for (sal_uInt8 i = 0; i < nLevel - nCurLevel; ++i)
+ aOutlineStack.push_back(nPos);
+ }
+ else
+ {
+ OSL_ENSURE(nLevel <= nCurLevel, "XclImpOutlineBuffer::MakeScOutline: unexpected level!");
+ for (sal_uInt8 i = 0; i < nCurLevel - nLevel; ++i)
+ {
+ if (aOutlineStack.empty())
+ {
+ // Something is wrong.
+ return;
+ }
+ SCSIZE nFirstPos = aOutlineStack.back();
+ aOutlineStack.pop_back();
+ bool bCollapsed = false;
+ if (mbButtonAfter)
+ bCollapsed = maCollapsedPosSet.count(nPos) > 0;
+ else if (nFirstPos > 0)
+ bCollapsed = maCollapsedPosSet.count(nFirstPos-1) > 0;
+
+ bool bDummy;
+ mpOutlineArray->Insert(nFirstPos, nPos-1, bDummy, bCollapsed);
+ }
+ }
+ }
+}
+
+void XclImpOutlineBuffer::SetLevelRange( SCSIZE nF, SCSIZE nL, sal_uInt8 nVal, bool bCollapsed )
+{
+ if (nF > nL)
+ // invalid range
+ return;
+
+ maLevels.insert_back(nF, nL+1, nVal);
+
+ if (bCollapsed)
+ maCollapsedPosSet.insert(nF);
+}
+
+void XclImpOutlineBuffer::SetButtonMode( bool bRightOrUnder )
+{
+ mbButtonAfter = bRightOrUnder;
+}
+
+ExcScenarioCell::ExcScenarioCell( const sal_uInt16 nC, const sal_uInt16 nR )
+ : nCol( nC ), nRow( nR )
+{
+}
+
+ExcScenario::ExcScenario( XclImpStream& rIn, const RootData& rR )
+ : nTab( rR.pIR->GetCurrScTab() )
+{
+ sal_uInt16 nCref;
+ sal_uInt8 nName, nComment;
+
+ nCref = rIn.ReaduInt16();
+ nProtected = rIn.ReaduInt8();
+ rIn.Ignore( 1 ); // Hide
+ nName = rIn.ReaduInt8();
+ nComment = rIn.ReaduInt8();
+ rIn.Ignore( 1 ); // instead of nUser!
+
+ if( nName )
+ aName = rIn.ReadUniString( nName );
+ else
+ {
+ aName = "Scenery";
+ rIn.Ignore( 1 );
+ }
+
+ rIn.ReadUniString(); // username
+
+ if( nComment )
+ aComment = rIn.ReadUniString();
+
+ sal_uInt16 n = nCref;
+ sal_uInt16 nC, nR;
+ aEntries.reserve(n);
+ while( n )
+ {
+ nR = rIn.ReaduInt16();
+ nC = rIn.ReaduInt16();
+
+ aEntries.emplace_back( nC, nR );
+
+ n--;
+ }
+
+ for (auto& rEntry : aEntries)
+ rEntry.SetValue(rIn.ReadUniString());
+}
+
+void ExcScenario::Apply( const XclImpRoot& rRoot, const bool bLast )
+{
+ ScDocument& r = rRoot.GetDoc();
+ OUString aSzenName( aName );
+ sal_uInt16 nNewTab = nTab + 1;
+
+ if( !r.InsertTab( nNewTab, aSzenName ) )
+ return;
+
+ r.SetScenario( nNewTab, true );
+ // do not show scenario frames
+ const ScScenarioFlags nFlags = ScScenarioFlags::CopyAll
+ | (nProtected ? ScScenarioFlags::Protected : ScScenarioFlags::NONE);
+ /* | ScScenarioFlags::ShowFrame*/
+ r.SetScenarioData( nNewTab, aComment, COL_LIGHTGRAY, nFlags);
+
+ for (const auto& rEntry : aEntries)
+ {
+ sal_uInt16 nCol = rEntry.nCol;
+ sal_uInt16 nRow = rEntry.nRow;
+ OUString aVal = rEntry.GetValue();
+
+ r.ApplyFlagsTab( nCol, nRow, nCol, nRow, nNewTab, ScMF::Scenario );
+
+ r.SetString( nCol, nRow, nNewTab, aVal );
+ }
+
+ if( bLast )
+ r.SetActiveScenario( nNewTab, true );
+
+ // modify what the Active tab is set to if the new
+ // scenario tab occurs before the active tab.
+ ScExtDocSettings& rDocSett = rRoot.GetExtDocOptions().GetDocSettings();
+ if( (static_cast< SCCOL >( nTab ) < rDocSett.mnDisplTab) && (rDocSett.mnDisplTab < MAXTAB) )
+ ++rDocSett.mnDisplTab;
+ rRoot.GetTabInfo().InsertScTab( nNewTab );
+}
+
+void ExcScenarioList::Apply( const XclImpRoot& rRoot )
+{
+ sal_uInt16 n = static_cast<sal_uInt16>(aEntries.size());
+
+ std::vector< std::unique_ptr<ExcScenario> >::reverse_iterator iter;
+ for (iter = aEntries.rbegin(); iter != aEntries.rend(); ++iter)
+ {
+ n--;
+ (*iter)->Apply(rRoot, n == nLastScenario);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/expop2.cxx b/sc/source/filter/excel/expop2.cxx
new file mode 100644
index 000000000..ee8ba0fff
--- /dev/null
+++ b/sc/source/filter/excel/expop2.cxx
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <unotools/fltrcfg.hxx>
+
+#include <osl/diagnose.h>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docinf.hxx>
+#include <filter/msfilter/svxmsbas.hxx>
+
+#include <oox/ole/vbaexport.hxx>
+
+#include <scerrors.hxx>
+
+#include <root.hxx>
+#include <excdoc.hxx>
+#include <exp_op.hxx>
+
+#include <xehelper.hxx>
+
+#include <officecfg/Office/Calc.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+namespace com::sun::star::document { class XDocumentProperties; }
+
+namespace {
+
+enum class VBAExportMode
+{
+ NONE,
+ REEXPORT_STREAM,
+ FULL_EXPORT
+};
+
+}
+
+ExportBiff5::ExportBiff5( XclExpRootData& rExpData, SvStream& rStrm ):
+ ExportTyp( rStrm ),
+ XclExpRoot( rExpData )
+{
+ // only need part of the Root data
+ pExcRoot = &GetOldRoot();
+ pExcRoot->pER = this; // ExcRoot -> XclExpRoot
+ pExcRoot->eDateiTyp = Biff5;
+ pExcDoc.reset( new ExcDocument( *this ) );
+}
+
+ExportBiff5::~ExportBiff5()
+{
+}
+
+ErrCode ExportBiff5::Write()
+{
+ SfxObjectShell* pDocShell = GetDocShell();
+ OSL_ENSURE( pDocShell, "ExportBiff5::Write - no document shell" );
+
+ tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
+ OSL_ENSURE( xRootStrg.is(), "ExportBiff5::Write - no root storage" );
+
+ VBAExportMode eVbaExportMode = VBAExportMode::NONE;
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ if (officecfg::Office::Calc::Filter::Import::VBA::UseExport::get())
+ eVbaExportMode = VBAExportMode::FULL_EXPORT;
+ else
+ {
+ const SvtFilterOptions& rFilterOpt = SvtFilterOptions::Get();
+ if (rFilterOpt.IsLoadExcelBasicStorage())
+ eVbaExportMode = VBAExportMode::REEXPORT_STREAM;
+ }
+ }
+
+ if ( pDocShell && xRootStrg.is() && eVbaExportMode == VBAExportMode::FULL_EXPORT)
+ {
+ VbaExport aExport(pDocShell->GetModel());
+ if (aExport.containsVBAProject())
+ {
+ tools::SvRef<SotStorage> xVBARoot = xRootStrg->OpenSotStorage("_VBA_PROJECT_CUR");
+ aExport.exportVBA( xVBARoot.get() );
+ }
+ }
+ else if( pDocShell && xRootStrg.is() && eVbaExportMode == VBAExportMode::REEXPORT_STREAM )
+ {
+ SvxImportMSVBasic aBasicImport( *pDocShell, *xRootStrg );
+ const ErrCode nErr = aBasicImport.SaveOrDelMSVBAStorage( true, EXC_STORAGE_VBA_PROJECT );
+ if( nErr != ERRCODE_NONE )
+ pDocShell->SetError(nErr);
+ }
+
+ pExcDoc->ReadDoc(); // ScDoc -> ExcDoc
+ pExcDoc->Write( aOut ); // wechstreamen
+
+ if( pDocShell && xRootStrg.is() )
+ {
+ using namespace ::com::sun::star;
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps
+ = xDPS->getDocumentProperties();
+ if ( SvtFilterOptions::Get().IsEnableCalcPreview() )
+ {
+ std::shared_ptr<GDIMetaFile> xMetaFile =
+ pDocShell->GetPreviewMetaFile();
+ uno::Sequence<sal_Int8> metaFile(
+ sfx2::convertMetaFile(xMetaFile.get()));
+ sfx2::SaveOlePropertySet( xDocProps, xRootStrg.get(), &metaFile );
+ }
+ else
+ sfx2::SaveOlePropertySet( xDocProps, xRootStrg.get() );
+ }
+
+ const XclExpAddressConverter& rAddrConv = GetAddressConverter();
+ if( rAddrConv.IsRowTruncated() )
+ return SCWARN_EXPORT_MAXROW;
+ if( rAddrConv.IsColTruncated() )
+ return SCWARN_EXPORT_MAXCOL;
+ if( rAddrConv.IsTabTruncated() )
+ return SCWARN_EXPORT_MAXTAB;
+
+ return ERRCODE_NONE;
+}
+
+ExportBiff8::ExportBiff8( XclExpRootData& rExpData, SvStream& rStrm ) :
+ ExportBiff5( rExpData, rStrm )
+{
+ pExcRoot->eDateiTyp = Biff8;
+}
+
+ExportBiff8::~ExportBiff8()
+{
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/export/SparklineExt.cxx b/sc/source/filter/excel/export/SparklineExt.cxx
new file mode 100644
index 000000000..487698e19
--- /dev/null
+++ b/sc/source/filter/excel/export/SparklineExt.cxx
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <export/SparklineExt.hxx>
+
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineList.hxx>
+
+using namespace oox;
+
+namespace xcl::exp
+{
+SparklineExt::SparklineExt(const XclExpRoot& rRoot)
+ : XclExpExt(rRoot)
+{
+ maURI = "{05C60535-1F16-4fd2-B633-F4F36F0B64E0}";
+}
+
+void SparklineExt::SaveXml(XclExpXmlStream& rStream)
+{
+ auto& rDocument = GetDoc();
+
+ auto* pSparklineList = rDocument.GetSparklineList(GetCurrScTab());
+ if (!pSparklineList)
+ return;
+
+ auto const& rSparklineGroups = pSparklineList->getSparklineGroups();
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStream.GetCurrentStream();
+ rWorksheet->startElement(XML_ext, FSNS(XML_xmlns, XML_x14),
+ rStream.getNamespaceURL(OOX_NS(xls14Lst)), XML_uri, maURI);
+
+ rWorksheet->startElementNS(XML_x14, XML_sparklineGroups, FSNS(XML_xmlns, XML_xm),
+ rStream.getNamespaceURL(OOX_NS(xm)));
+
+ for (auto const& pSparklineGroup : rSparklineGroups)
+ {
+ auto const& rSparklineVector = pSparklineList->getSparklinesFor(pSparklineGroup);
+ addSparklineGroup(rStream, *pSparklineGroup, rSparklineVector);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_sparklineGroups);
+ rWorksheet->endElement(XML_ext);
+}
+
+void SparklineExt::addSparklineGroupAttributes(
+ rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList,
+ sc::SparklineAttributes& rAttributes)
+{
+ if (rAttributes.getLineWeight() != 0.75)
+ pAttrList->add(XML_lineWeight, OString::number(rAttributes.getLineWeight()));
+
+ if (rAttributes.getType() != sc::SparklineType::Line)
+ {
+ if (rAttributes.getType() == sc::SparklineType::Column)
+ pAttrList->add(XML_type, "column");
+ else if (rAttributes.getType() == sc::SparklineType::Stacked)
+ pAttrList->add(XML_type, "stacked");
+ }
+
+ if (rAttributes.isDateAxis())
+ pAttrList->add(XML_dateAxis, "1");
+
+ if (rAttributes.getDisplayEmptyCellsAs() != sc::DisplayEmptyCellsAs::Zero)
+ {
+ if (rAttributes.getDisplayEmptyCellsAs() == sc::DisplayEmptyCellsAs::Gap)
+ pAttrList->add(XML_displayEmptyCellsAs, "gap");
+ else if (rAttributes.getDisplayEmptyCellsAs() == sc::DisplayEmptyCellsAs::Span)
+ pAttrList->add(XML_displayEmptyCellsAs, "span");
+ }
+
+ if (rAttributes.isMarkers())
+ pAttrList->add(XML_markers, "1");
+ if (rAttributes.isHigh())
+ pAttrList->add(XML_high, "1");
+ if (rAttributes.isLow())
+ pAttrList->add(XML_low, "1");
+ if (rAttributes.isFirst())
+ pAttrList->add(XML_first, "1");
+ if (rAttributes.isLast())
+ pAttrList->add(XML_last, "1");
+ if (rAttributes.isNegative())
+ pAttrList->add(XML_negative, "1");
+ if (rAttributes.shouldDisplayXAxis())
+ pAttrList->add(XML_displayXAxis, "1");
+ if (rAttributes.shouldDisplayHidden())
+ pAttrList->add(XML_displayHidden, "1");
+
+ if (rAttributes.getMinAxisType() != sc::AxisType::Individual)
+ {
+ if (rAttributes.getMinAxisType() == sc::AxisType::Group)
+ pAttrList->add(XML_minAxisType, "group");
+ else if (rAttributes.getMinAxisType() == sc::AxisType::Custom)
+ pAttrList->add(XML_minAxisType, "custom");
+ }
+
+ if (rAttributes.getMaxAxisType() != sc::AxisType::Individual)
+ {
+ if (rAttributes.getMaxAxisType() == sc::AxisType::Group)
+ pAttrList->add(XML_maxAxisType, "group");
+ else if (rAttributes.getMaxAxisType() == sc::AxisType::Custom)
+ pAttrList->add(XML_maxAxisType, "custom");
+ }
+
+ if (rAttributes.isRightToLeft())
+ pAttrList->add(XML_rightToLeft, "1");
+
+ if (rAttributes.getManualMax() && rAttributes.getMaxAxisType() == sc::AxisType::Custom)
+ pAttrList->add(XML_manualMax, OString::number(*rAttributes.getManualMax()));
+
+ if (rAttributes.getManualMin() && rAttributes.getMinAxisType() == sc::AxisType::Custom)
+ pAttrList->add(XML_manualMin, OString::number(*rAttributes.getManualMin()));
+}
+
+void SparklineExt::addSparklineGroupColors(XclExpXmlStream& rStream,
+ sc::SparklineAttributes& rAttributes)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStream.GetCurrentStream();
+
+ rWorksheet->singleElementNS(XML_x14, XML_colorSeries, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorSeries()));
+
+ if (rAttributes.getColorNegative() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorNegative, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorNegative()));
+ }
+
+ if (rAttributes.getColorAxis() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorAxis, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorAxis()));
+ }
+
+ if (rAttributes.getColorMarkers() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorMarkers, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorMarkers()));
+ }
+
+ if (rAttributes.getColorFirst() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorFirst, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorFirst()));
+ }
+
+ if (rAttributes.getColorLast() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorLast, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorLast()));
+ }
+
+ if (rAttributes.getColorHigh() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorHigh, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorHigh()));
+ }
+
+ if (rAttributes.getColorLow() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorLow, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorLow()));
+ }
+}
+
+void SparklineExt::addSparklineGroup(XclExpXmlStream& rStream, sc::SparklineGroup& rSparklineGroup,
+ std::vector<std::shared_ptr<sc::Sparkline>> const& rSparklines)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStream.GetCurrentStream();
+
+ // Sparkline Group Attributes
+ auto pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ // Write ID
+ OString sUID = rSparklineGroup.getID().getString();
+ pAttrList->addNS(XML_xr2, XML_uid, sUID);
+
+ // Write attributes
+ addSparklineGroupAttributes(pAttrList, rSparklineGroup.getAttributes());
+
+ rWorksheet->startElementNS(XML_x14, XML_sparklineGroup, pAttrList);
+
+ addSparklineGroupColors(rStream, rSparklineGroup.getAttributes());
+
+ // Sparklines
+
+ rWorksheet->startElementNS(XML_x14, XML_sparklines);
+ for (auto const& rSparkline : rSparklines)
+ {
+ rWorksheet->startElementNS(XML_x14, XML_sparkline);
+
+ {
+ rWorksheet->startElementNS(XML_xm, XML_f);
+
+ OUString sRangeFormula;
+ ScRefFlags eFlags = ScRefFlags::VALID | ScRefFlags::TAB_3D;
+ rSparkline->getInputRange().Format(sRangeFormula, eFlags, GetDoc(),
+ formula::FormulaGrammar::CONV_XL_OOX, ' ', true);
+
+ rWorksheet->writeEscaped(sRangeFormula);
+ rWorksheet->endElementNS(XML_xm, XML_f);
+ }
+
+ {
+ rWorksheet->startElementNS(XML_xm, XML_sqref);
+
+ ScAddress::Details detailsXL(formula::FormulaGrammar::CONV_XL_OOX);
+ ScAddress aAddress(rSparkline->getColumn(), rSparkline->getRow(), GetCurrScTab());
+ OUString sLocation = aAddress.Format(ScRefFlags::VALID, &GetDoc(), detailsXL);
+
+ rWorksheet->writeEscaped(sLocation);
+ rWorksheet->endElementNS(XML_xm, XML_sqref);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_sparkline);
+ }
+ rWorksheet->endElementNS(XML_x14, XML_sparklines);
+ rWorksheet->endElementNS(XML_x14, XML_sparklineGroup);
+}
+
+SparklineBuffer::SparklineBuffer(const XclExpRoot& rRoot, XclExtLstRef const& xExtLst)
+ : XclExpRoot(rRoot)
+{
+ auto& rDocument = GetDoc();
+ auto* pSparklineList = rDocument.GetSparklineList(GetCurrScTab());
+ if (pSparklineList && !pSparklineList->getSparklineGroups().empty())
+ {
+ xExtLst->AddRecord(new xcl::exp::SparklineExt(GetRoot()));
+ }
+}
+
+} // end namespace xcl::exp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/fontbuff.cxx b/sc/source/filter/excel/fontbuff.cxx
new file mode 100644
index 000000000..40e04fcb0
--- /dev/null
+++ b/sc/source/filter/excel/fontbuff.cxx
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <lotfntbf.hxx>
+
+#include <scitems.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <osl/diagnose.h>
+#include <svl/itemset.hxx>
+
+void LotusFontBuffer::Fill( const sal_uInt8 nIndex, SfxItemSet& rItemSet )
+{
+ sal_uInt8 nIntIndex = nIndex & 0x07;
+
+ ENTRY* pCurrent = pData + nIntIndex;
+
+ if( pCurrent->pFont )
+ rItemSet.Put( *pCurrent->pFont );
+
+ if( pCurrent->pHeight )
+ rItemSet.Put( *pCurrent->pHeight );
+
+ if( nIndex & 0x08 )
+ {
+ SvxWeightItem aWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT );
+ rItemSet.Put( aWeightItem );
+ }
+
+ if( nIndex & 0x10 )
+ {
+ SvxPostureItem aAttr( ITALIC_NORMAL, ATTR_FONT_POSTURE );
+ rItemSet.Put( aAttr );
+ }
+
+ FontLineStyle eUnderline;
+ switch( nIndex & 0x60 ) // Bit 5+6
+ {
+ case 0x60:
+ case 0x20: eUnderline = LINESTYLE_SINGLE; break;
+ case 0x40: eUnderline = LINESTYLE_DOUBLE; break;
+ default: eUnderline = LINESTYLE_NONE;
+ }
+ if( eUnderline != LINESTYLE_NONE )
+ {
+ SvxUnderlineItem aUndItem( eUnderline, ATTR_FONT_UNDERLINE );
+ rItemSet.Put( aUndItem );
+ }
+}
+
+void LotusFontBuffer::SetName( const sal_uInt16 nIndex, const OUString& rName )
+{
+ OSL_ENSURE( nIndex < nSize, "*LotusFontBuffer::SetName(): Array too small!" );
+ if( nIndex < nSize )
+ {
+ ENTRY* pEntry = pData + nIndex;
+ pEntry->TmpName( rName );
+
+ if( pEntry->nType >= 0 )
+ MakeFont( pEntry );
+ }
+}
+
+void LotusFontBuffer::SetHeight( const sal_uInt16 nIndex, const sal_uInt16 nHeight )
+{
+ OSL_ENSURE( nIndex < nSize, "*LotusFontBuffer::SetHeight(): Array too small!" );
+ if( nIndex < nSize )
+ pData[ nIndex ].Height( std::make_unique<SvxFontHeightItem>( static_cast<sal_uInt32>(nHeight) * 20, 100, ATTR_FONT_HEIGHT ) );
+}
+
+void LotusFontBuffer::SetType( const sal_uInt16 nIndex, const sal_uInt16 nType )
+{
+ OSL_ENSURE( nIndex < nSize, "*LotusFontBuffer::SetType(): Array too small!" );
+ if( nIndex < nSize )
+ {
+ ENTRY* pEntry = pData + nIndex;
+ pEntry->Type( nType );
+
+ if( pEntry->xTmpName )
+ MakeFont( pEntry );
+ }
+}
+
+void LotusFontBuffer::MakeFont( ENTRY* pEntry )
+{
+ FontFamily eFamily = FAMILY_DONTKNOW;
+ FontPitch ePitch = PITCH_DONTKNOW;
+ rtl_TextEncoding eCharSet = RTL_TEXTENCODING_DONTKNOW;
+
+ switch( pEntry->nType )
+ {
+ case 0x00: // Helvetica
+ eFamily = FAMILY_SWISS;
+ ePitch = PITCH_VARIABLE;
+ break;
+ case 0x01: // Times Roman
+ eFamily = FAMILY_ROMAN;
+ ePitch = PITCH_VARIABLE;
+ break;
+ case 0x02: // Courier
+ ePitch = PITCH_FIXED;
+ break;
+ case 0x03: // Symbol
+ eCharSet = RTL_TEXTENCODING_SYMBOL;
+ break;
+ }
+
+ pEntry->pFont.reset( new SvxFontItem( eFamily, *pEntry->xTmpName, OUString(), ePitch, eCharSet, ATTR_FONT ) );
+
+ pEntry->xTmpName.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/frmbase.cxx b/sc/source/filter/excel/frmbase.cxx
new file mode 100644
index 000000000..73ef59dad
--- /dev/null
+++ b/sc/source/filter/excel/frmbase.cxx
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <formel.hxx>
+
+#include <osl/diagnose.h>
+
+ScRangeListTabs::ScRangeListTabs( const XclImpRoot& rRoot )
+: XclImpRoot( rRoot )
+{
+}
+
+ScRangeListTabs::~ScRangeListTabs()
+{
+}
+
+void ScRangeListTabs::Append( const ScAddress& aSRD, SCTAB nTab )
+{
+ ScAddress a = aSRD;
+ ScDocument& rDoc = GetRoot().GetDoc();
+
+ if (a.Tab() > MAXTAB)
+ a.SetTab(MAXTAB);
+
+ if (a.Col() > rDoc.MaxCol())
+ a.SetCol(rDoc.MaxCol());
+
+ if (a.Row() > rDoc.MaxRow())
+ a.SetRow(rDoc.MaxRow());
+
+ if( nTab == SCTAB_MAX)
+ return;
+ if( nTab < 0)
+ nTab = a.Tab();
+
+ if (nTab < 0 || MAXTAB < nTab)
+ return;
+
+ TabRangeType::iterator itr = m_TabRanges.find(nTab);
+ if (itr == m_TabRanges.end())
+ {
+ // No entry for this table yet. Insert a new one.
+ std::pair<TabRangeType::iterator, bool> r =
+ m_TabRanges.insert(std::make_pair(nTab, RangeListType()));
+
+ if (!r.second)
+ // Insertion failed.
+ return;
+
+ itr = r.first;
+ }
+ itr->second.push_back(ScRange(a.Col(),a.Row(),a.Tab()));
+}
+
+void ScRangeListTabs::Append( const ScRange& aCRD, SCTAB nTab )
+{
+ ScRange a = aCRD;
+ ScDocument& rDoc = GetRoot().GetDoc();
+
+ // ignore 3D ranges
+ if (a.aStart.Tab() != a.aEnd.Tab())
+ return;
+
+ if (a.aStart.Tab() > MAXTAB)
+ a.aStart.SetTab(MAXTAB);
+ else if (a.aStart.Tab() < 0)
+ a.aStart.SetTab(0);
+
+ if (a.aStart.Col() > rDoc.MaxCol())
+ a.aStart.SetCol(rDoc.MaxCol());
+ else if (a.aStart.Col() < 0)
+ a.aStart.SetCol(0);
+
+ if (a.aStart.Row() > rDoc.MaxRow())
+ a.aStart.SetRow(rDoc.MaxRow());
+ else if (a.aStart.Row() < 0)
+ a.aStart.SetRow(0);
+
+ if (a.aEnd.Col() > rDoc.MaxCol())
+ a.aEnd.SetCol(rDoc.MaxCol());
+ else if (a.aEnd.Col() < 0)
+ a.aEnd.SetCol(0);
+
+ if (a.aEnd.Row() > rDoc.MaxRow())
+ a.aEnd.SetRow(rDoc.MaxRow());
+ else if (a.aEnd.Row() < 0)
+ a.aEnd.SetRow(0);
+
+ if( nTab == SCTAB_MAX)
+ return;
+
+ if( nTab < -1)
+ nTab = a.aStart.Tab();
+
+ if (nTab < 0 || MAXTAB < nTab)
+ return;
+
+ TabRangeType::iterator itr = m_TabRanges.find(nTab);
+ if (itr == m_TabRanges.end())
+ {
+ // No entry for this table yet. Insert a new one.
+ std::pair<TabRangeType::iterator, bool> r =
+ m_TabRanges.insert(std::make_pair(nTab, RangeListType()));
+
+ if (!r.second)
+ // Insertion failed.
+ return;
+
+ itr = r.first;
+ }
+ itr->second.push_back(a);
+}
+
+const ScRange* ScRangeListTabs::First( SCTAB n )
+{
+ OSL_ENSURE( ValidTab(n), "-ScRangeListTabs::First(): Good bye!" );
+
+ TabRangeType::iterator itr = m_TabRanges.find(n);
+ if (itr == m_TabRanges.end())
+ // No range list exists for this table.
+ return nullptr;
+
+ const RangeListType& rList = itr->second;
+ maItrCur = rList.begin();
+ maItrCurEnd = rList.end();
+ return rList.empty() ? nullptr : &(*maItrCur);
+}
+
+const ScRange* ScRangeListTabs::Next ()
+{
+ ++maItrCur;
+ if (maItrCur == maItrCurEnd)
+ return nullptr;
+
+ return &(*maItrCur);
+}
+
+ConverterBase::ConverterBase( svl::SharedStringPool& rSPool ) :
+ aPool(rSPool),
+ aEingPos( 0, 0, 0 )
+{
+}
+
+ConverterBase::~ConverterBase()
+{
+}
+
+void ConverterBase::Reset()
+{
+ aPool.Reset();
+ aStack.Reset();
+}
+
+ExcelConverterBase::ExcelConverterBase( svl::SharedStringPool& rSPool ) :
+ ConverterBase(rSPool)
+{
+}
+
+ExcelConverterBase::~ExcelConverterBase()
+{
+}
+
+void ExcelConverterBase::Reset( const ScAddress& rEingPos )
+{
+ ConverterBase::Reset();
+ aEingPos = rEingPos;
+}
+
+void ExcelConverterBase::Reset()
+{
+ ConverterBase::Reset();
+ aEingPos.Set( 0, 0, 0 );
+}
+
+LotusConverterBase::LotusConverterBase( SvStream &rStr, svl::SharedStringPool& rSPool ) :
+ ConverterBase(rSPool),
+ aIn( rStr ),
+ nBytesLeft( 0 )
+{
+}
+
+LotusConverterBase::~LotusConverterBase()
+{
+}
+
+void LotusConverterBase::Reset( const ScAddress& rEingPos )
+{
+ ConverterBase::Reset();
+ nBytesLeft = 0;
+ aEingPos = rEingPos;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/impop.cxx b/sc/source/filter/excel/impop.cxx
new file mode 100644
index 000000000..9ddc6e6e7
--- /dev/null
+++ b/sc/source/filter/excel/impop.cxx
@@ -0,0 +1,1414 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <imp_op.hxx>
+
+#include <filter/msfilter/countryid.hxx>
+
+#include <scitems.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <sfx2/docfile.hxx>
+#include <svx/svxids.hrc>
+#include <svl/numformat.hxx>
+#include <unotools/configmgr.hxx>
+#include <sal/log.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <tools/urlobj.hxx>
+#include <docuno.hxx>
+
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <olinetab.hxx>
+#include <stlpool.hxx>
+#include <viewopti.hxx>
+#include <docoptio.hxx>
+#include <scextopt.hxx>
+#include <unonames.hxx>
+#include <paramisc.hxx>
+#include <colrowst.hxx>
+#include <otlnbuff.hxx>
+#include <xistyle.hxx>
+
+#include <namebuff.hxx>
+#include <xltools.hxx>
+#include <xltable.hxx>
+#include <xltracer.hxx>
+#include <xihelper.hxx>
+#include <xipage.hxx>
+#include <xiview.hxx>
+#include <xiescher.hxx>
+#include <xicontent.hxx>
+
+#include <excform.hxx>
+#include <documentimport.hxx>
+
+#if defined(_WIN32)
+#include <math.h>
+#endif
+
+using namespace ::com::sun::star;
+
+ImportTyp::ImportTyp(ScDocument& rDoc, rtl_TextEncoding eQ)
+ : eQuellChar(eQ)
+ , rD(rDoc)
+
+{
+}
+
+ImportTyp::~ImportTyp()
+{
+}
+
+ImportExcel::ImportExcel( XclImpRootData& rImpData, SvStream& rStrm ):
+ ImportTyp( rImpData.mrDoc, rImpData.meTextEnc ),
+ XclImpRoot( rImpData ),
+ maStrm( rStrm, GetRoot() ),
+ aIn( maStrm ),
+ maScOleSize( ScAddress::INITIALIZE_INVALID ),
+ pColOutlineBuff(nullptr),
+ pRowOutlineBuff(nullptr),
+ pColRowBuff(nullptr),
+ mpLastFormula(nullptr),
+ mnLastRefIdx( 0 ),
+ mnIxfeIndex( 0 ),
+ mnLastRecId(0),
+ mbBiff2HasXfs(false),
+ mbBiff2HasXfsValid(false)
+{
+ nBdshtTab = 0;
+
+ // fill in root data - after new's without root as parameter
+ pExcRoot = &GetOldRoot();
+ pExcRoot->pIR = this; // ExcRoot -> XclImpRoot
+ pExcRoot->eDateiTyp = BiffX;
+ pExcRoot->pExtSheetBuff.reset( new ExtSheetBuffer( pExcRoot ) ); //&aExtSheetBuff;
+ pExcRoot->pShrfmlaBuff.reset( new SharedFormulaBuffer( pExcRoot ) ); //&aShrfrmlaBuff;
+ pExcRoot->pExtNameBuff.reset( new ExtNameBuff ( *this ) );
+
+ pOutlineListBuffer.reset(new XclImpOutlineListBuffer);
+
+ // from Biff8 on
+ pFormConv.reset(new ExcelToSc( GetRoot() ));
+ pExcRoot->pFmlaConverter = pFormConv.get();
+
+ bTabTruncated = false;
+
+ // Excel document per Default on 31.12.1899, accords to Excel settings with 1.1.1900
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetDate( 30, 12, 1899 );
+ rD.SetDocOptions( aOpt );
+ rD.GetFormatTable()->ChangeNullDate( 30, 12, 1899 );
+
+ ScDocOptions aDocOpt( rD.GetDocOptions() );
+ aDocOpt.SetIgnoreCase( true ); // always in Excel
+ aDocOpt.SetFormulaRegexEnabled( false ); // regular expressions? what's that?
+ aDocOpt.SetFormulaWildcardsEnabled( true ); // Excel uses wildcard expressions
+ aDocOpt.SetLookUpColRowNames( false ); // default: no natural language refs
+ rD.SetDocOptions( aDocOpt );
+}
+
+ImportExcel::~ImportExcel()
+{
+ GetDoc().SetSrcCharSet( GetTextEncoding() );
+
+ pOutlineListBuffer.reset();
+
+ pFormConv.reset();
+}
+
+void ImportExcel::SetLastFormula( SCCOL nCol, SCROW nRow, double fVal, sal_uInt16 nXF, ScFormulaCell* pCell )
+{
+ LastFormulaMapType::iterator it = maLastFormulaCells.find(nCol);
+ if (it == maLastFormulaCells.end())
+ {
+ std::pair<LastFormulaMapType::iterator, bool> r =
+ maLastFormulaCells.emplace(nCol, LastFormula());
+ it = r.first;
+ }
+
+ it->second.mnCol = nCol;
+ it->second.mnRow = nRow;
+ it->second.mpCell = pCell;
+ it->second.mfValue = fVal;
+ it->second.mnXF = nXF;
+
+ mpLastFormula = &it->second;
+}
+
+void ImportExcel::ReadFileSharing()
+{
+ sal_uInt16 nRecommendReadOnly, nPasswordHash;
+ nRecommendReadOnly = maStrm.ReaduInt16();
+ nPasswordHash = maStrm.ReaduInt16();
+
+ if((nRecommendReadOnly == 0) && (nPasswordHash == 0))
+ return;
+
+ if( SfxItemSet* pItemSet = GetMedium().GetItemSet() )
+ pItemSet->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+
+ if( SfxObjectShell* pShell = GetDocShell() )
+ {
+ if( nRecommendReadOnly != 0 )
+ pShell->SetLoadReadonly( true );
+ if( nPasswordHash != 0 )
+ pShell->SetModifyPasswordHash( nPasswordHash );
+ }
+}
+
+sal_uInt16 ImportExcel::ReadXFIndex( const ScAddress& rScPos, bool bBiff2 )
+{
+ sal_uInt16 nXFIdx = 0;
+ if( bBiff2 )
+ {
+ /* #i71453# On first call, check if the file contains XF records (by
+ trying to access the first XF with index 0). If there are no XFs,
+ the explicit formatting information contained in each cell record
+ will be used instead. */
+ if( !mbBiff2HasXfsValid )
+ {
+ mbBiff2HasXfsValid = true;
+ mbBiff2HasXfs = GetXFBuffer().GetXF( 0 ) != nullptr;
+ }
+ // read formatting information (includes the XF identifier)
+ sal_uInt8 nFlags1, nFlags2, nFlags3;
+ nFlags1 = maStrm.ReaduInt8();
+ nFlags2 = maStrm.ReaduInt8();
+ nFlags3 = maStrm.ReaduInt8();
+ /* If the file contains XFs, extract and set the XF identifier,
+ otherwise get the explicit formatting. */
+ if( mbBiff2HasXfs )
+ {
+ nXFIdx = ::extract_value< sal_uInt16 >( nFlags1, 0, 6 );
+ /* If the identifier is equal to 63, then the real identifier is
+ contained in the preceding IXFE record (stored in mnBiff2XfId). */
+ if( nXFIdx == 63 )
+ nXFIdx = mnIxfeIndex;
+ }
+ else
+ {
+ /* Let the XclImpXF class do the conversion of the imported
+ formatting. The XF buffer is empty, therefore will not do any
+ conversion based on the XF index later on. */
+ XclImpXF::ApplyPatternForBiff2CellFormat( GetRoot(), rScPos, nFlags1, nFlags2, nFlags3 );
+ }
+ }
+ else
+ nXFIdx = aIn.ReaduInt16();
+ return nXFIdx;
+}
+
+void ImportExcel::ReadDimensions()
+{
+ XclRange aXclUsedArea;
+ if( (maStrm.GetRecId() == EXC_ID2_DIMENSIONS) || (GetBiff() <= EXC_BIFF5) )
+ {
+ maStrm >> aXclUsedArea;
+ if( (aXclUsedArea.GetColCount() > 1) && (aXclUsedArea.GetRowCount() > 1) )
+ {
+ // Excel stores first unused row/column index
+ --aXclUsedArea.maLast.mnCol;
+ --aXclUsedArea.maLast.mnRow;
+ // create the Calc range
+ SCTAB nScTab = GetCurrScTab();
+ ScRange& rScUsedArea = GetExtDocOptions().GetOrCreateTabSettings( nScTab ).maUsedArea;
+ GetAddressConverter().ConvertRange( rScUsedArea, aXclUsedArea, nScTab, nScTab, false );
+ // if any error occurs in ConvertRange(), rScUsedArea keeps untouched
+ }
+ }
+ else
+ {
+ sal_uInt32 nXclRow1 = 0, nXclRow2 = 0;
+ nXclRow1 = maStrm.ReaduInt32();
+ nXclRow2 = maStrm.ReaduInt32();
+ aXclUsedArea.maFirst.mnCol = maStrm.ReaduInt16();
+ aXclUsedArea.maLast.mnCol = maStrm.ReaduInt16();
+ if( (nXclRow1 < nXclRow2) && (aXclUsedArea.GetColCount() > 1) &&
+ (nXclRow1 <= o3tl::make_unsigned( GetScMaxPos().Row() )) )
+ {
+ // Excel stores first unused row/column index
+ --nXclRow2;
+ --aXclUsedArea.maLast.mnCol;
+ // convert row indexes to 16-bit values
+ aXclUsedArea.maFirst.mnRow = static_cast< sal_uInt16 >( nXclRow1 );
+ aXclUsedArea.maLast.mnRow = limit_cast< sal_uInt16 >( nXclRow2, aXclUsedArea.maFirst.mnRow, SAL_MAX_UINT16 );
+ // create the Calc range
+ SCTAB nScTab = GetCurrScTab();
+ ScRange& rScUsedArea = GetExtDocOptions().GetOrCreateTabSettings( nScTab ).maUsedArea;
+ GetAddressConverter().ConvertRange( rScUsedArea, aXclUsedArea, nScTab, nScTab, false );
+ // if any error occurs in ConvertRange(), rScUsedArea keeps untouched
+ }
+ }
+}
+
+void ImportExcel::ReadBlank()
+{
+ XclAddress aXclPos;
+ aIn >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, maStrm.GetRecId() == EXC_ID2_BLANK );
+
+ GetXFRangeBuffer().SetBlankXF( aScPos, nXFIdx );
+ }
+}
+
+void ImportExcel::ReadInteger()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, true );
+ sal_uInt16 nValue;
+ nValue = maStrm.ReaduInt16();
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ GetDocImport().setNumericCell(aScPos, nValue);
+ }
+}
+
+void ImportExcel::ReadNumber()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, maStrm.GetRecId() == EXC_ID2_NUMBER );
+ double fValue;
+ fValue = maStrm.ReadDouble();
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ GetDocImport().setNumericCell(aScPos, fValue);
+ }
+}
+
+void ImportExcel::ReadLabel()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( !GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ return;
+
+ /* Record ID BIFF XF type String type
+ 0x0004 2-7 3 byte 8-bit length, byte string
+ 0x0004 8 3 byte 16-bit length, unicode string
+ 0x0204 2-7 2 byte 16-bit length, byte string
+ 0x0204 8 2 byte 16-bit length, unicode string */
+ bool bBiff2 = maStrm.GetRecId() == EXC_ID2_LABEL;
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, bBiff2 );
+ XclStrFlags nFlags = (bBiff2 && (GetBiff() <= EXC_BIFF5)) ? XclStrFlags::EightBitLength : XclStrFlags::NONE;
+ XclImpString aString;
+
+ // #i63105# use text encoding from FONT record
+ rtl_TextEncoding eOldTextEnc = GetTextEncoding();
+ if( const XclImpFont* pFont = GetXFBuffer().GetFont( nXFIdx ) )
+ SetTextEncoding( pFont->GetFontEncoding() );
+ aString.Read( maStrm, nFlags );
+ SetTextEncoding( eOldTextEnc );
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ XclImpStringHelper::SetToDocument(GetDocImport(), aScPos, GetRoot(), aString, nXFIdx);
+}
+
+void ImportExcel::ReadBoolErr()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( !GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ return;
+
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, maStrm.GetRecId() == EXC_ID2_BOOLERR );
+ sal_uInt8 nValue, nType;
+ nValue = maStrm.ReaduInt8();
+ nType = maStrm.ReaduInt8();
+
+ if( nType == EXC_BOOLERR_BOOL )
+ GetXFRangeBuffer().SetBoolXF( aScPos, nXFIdx );
+ else
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+
+ double fValue;
+ std::unique_ptr<ScTokenArray> pScTokArr = ErrorToFormula( nType != EXC_BOOLERR_BOOL, nValue, fValue );
+ ScFormulaCell* pCell = pScTokArr
+ ? new ScFormulaCell(rD, aScPos, std::move(pScTokArr))
+ : new ScFormulaCell(rD, aScPos);
+ pCell->SetHybridDouble( fValue );
+ GetDocImport().setFormulaCell(aScPos, pCell);
+}
+
+void ImportExcel::ReadRk()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, false );
+ sal_Int32 nRk;
+ nRk = maStrm.ReadInt32();
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ GetDocImport().setNumericCell(aScPos, XclTools::GetDoubleFromRK(nRk));
+ }
+}
+
+void ImportExcel::Window1()
+{
+ GetDocViewSettings().ReadWindow1( maStrm );
+}
+
+void ImportExcel::Row25()
+{
+ sal_uInt16 nRow, nRowHeight;
+
+ nRow = aIn.ReaduInt16();
+ aIn.Ignore( 4 );
+
+ if( !GetRoot().GetDoc().ValidRow( nRow ) )
+ return;
+
+ nRowHeight = aIn.ReaduInt16(); // specify direct in Twips
+ aIn.Ignore( 2 );
+
+ if( GetBiff() == EXC_BIFF2 )
+ {// -------------------- BIFF2
+ pColRowBuff->SetHeight( nRow, nRowHeight );
+ }
+ else
+ {// -------------------- BIFF5
+ sal_uInt16 nGrbit;
+
+ aIn.Ignore( 2 ); // reserved
+ nGrbit = aIn.ReaduInt16();
+
+ sal_uInt8 nLevel = ::extract_value< sal_uInt8 >( nGrbit, 0, 3 );
+ pRowOutlineBuff->SetLevel( nRow, nLevel, ::get_flag( nGrbit, EXC_ROW_COLLAPSED ) );
+ pColRowBuff->SetRowSettings( nRow, nRowHeight, nGrbit );
+ }
+}
+
+void ImportExcel::Bof2()
+{
+ sal_uInt16 nSubType;
+ maStrm.DisableDecryption();
+ maStrm.Ignore( 2 );
+ nSubType = maStrm.ReaduInt16();
+
+ if( nSubType == 0x0020 ) // Chart
+ pExcRoot->eDateiTyp = Biff2C;
+ else if( nSubType == 0x0040 ) // Macro
+ pExcRoot->eDateiTyp = Biff2M;
+ else // #i51490# Excel interprets invalid indexes as worksheet
+ pExcRoot->eDateiTyp = Biff2;
+}
+
+void ImportExcel::Eof()
+{
+ // POST: cannot be called after an invalid table!
+ EndSheet();
+ IncCurrScTab();
+}
+
+void ImportExcel::SheetPassword()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetSheetProtectBuffer().ReadPasswordHash( aIn, GetCurrScTab() );
+}
+
+void ImportExcel::Externsheet()
+{
+ OUString aUrl, aTabName;
+ bool bSameWorkBook;
+ OUString aEncodedUrl( aIn.ReadByteString( false ) );
+ XclImpUrlHelper::DecodeUrl( aUrl, aTabName, bSameWorkBook, *pExcRoot->pIR, aEncodedUrl );
+ mnLastRefIdx = pExcRoot->pExtSheetBuff->Add( aUrl, aTabName, bSameWorkBook );
+}
+
+void ImportExcel:: WinProtection()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetDocProtectBuffer().ReadWinProtect( aIn );
+}
+
+void ImportExcel::Columndefault()
+{// Default Cell Attributes
+ sal_uInt16 nColMic, nColMac;
+ sal_uInt8 nOpt0;
+
+ nColMic = aIn.ReaduInt16();
+ nColMac = aIn.ReaduInt16();
+
+ OSL_ENSURE( aIn.GetRecLeft() == static_cast<std::size_t>(nColMac - nColMic) * 3 + 2,
+ "ImportExcel::Columndefault - wrong record size" );
+
+ nColMac--;
+
+ if( nColMac > rD.MaxCol() )
+ nColMac = static_cast<sal_uInt16>(rD.MaxCol());
+
+ for( sal_uInt16 nCol = nColMic ; nCol <= nColMac ; nCol++ )
+ {
+ nOpt0 = aIn.ReaduInt8();
+ aIn.Ignore( 2 ); // only 0. Attribute-Byte used
+
+ if( nOpt0 & 0x80 ) // Col hidden?
+ pColRowBuff->HideCol( nCol );
+ }
+}
+
+void ImportExcel::Array25()
+{
+ sal_uInt16 nFormLen;
+ sal_uInt16 nFirstRow = aIn.ReaduInt16();
+ sal_uInt16 nLastRow = aIn.ReaduInt16();
+ sal_uInt8 nFirstCol = aIn.ReaduInt8();
+ sal_uInt8 nLastCol = aIn.ReaduInt8();
+
+ if( GetBiff() == EXC_BIFF2 )
+ {// BIFF2
+ aIn.Ignore( 1 );
+ nFormLen = aIn.ReaduInt8();
+ }
+ else
+ {// BIFF5
+ aIn.Ignore( 6 );
+ nFormLen = aIn.ReaduInt16();
+ }
+
+ std::unique_ptr<ScTokenArray> pResult;
+
+ if (GetRoot().GetDoc().ValidColRow(nLastCol, nLastRow))
+ {
+ // the read mark is now on the formula, length in nFormLen
+
+ pFormConv->Reset( ScAddress( static_cast<SCCOL>(nFirstCol),
+ static_cast<SCROW>(nFirstRow), GetCurrScTab() ) );
+ pFormConv->Convert(pResult, maStrm, nFormLen, true);
+
+ SAL_WARN_IF(!pResult, "sc", "*ImportExcel::Array25(): ScTokenArray is NULL!");
+ }
+
+ if (pResult)
+ {
+ ScDocumentImport& rDoc = GetDocImport();
+ ScRange aArrayRange(nFirstCol, nFirstRow, GetCurrScTab(), nLastCol, nLastRow, GetCurrScTab());
+ rDoc.setMatrixCells(aArrayRange, *pResult, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1);
+ }
+}
+
+void ImportExcel::Rec1904()
+{
+ sal_uInt16 n1904;
+
+ n1904 = aIn.ReaduInt16();
+
+ if( n1904 )
+ {// 1904 date system
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetDate( 1, 1, 1904 );
+ rD.SetDocOptions( aOpt );
+ rD.GetFormatTable()->ChangeNullDate( 1, 1, 1904 );
+ }
+}
+
+void ImportExcel::Externname25()
+{
+ sal_uInt32 nRes;
+ sal_uInt16 nOpt;
+
+ nOpt = aIn.ReaduInt16();
+ nRes = aIn.ReaduInt32();
+
+ aIn.ReadByteString( false ); // name
+
+ if( ( nOpt & 0x0001 ) || ( ( nOpt & 0xFFFE ) == 0x0000 ) )
+ {// external name
+ pExcRoot->pExtNameBuff->AddName( mnLastRefIdx );
+ }
+ else if( nOpt & 0x0010 )
+ {// ole link
+ pExcRoot->pExtNameBuff->AddOLE( mnLastRefIdx, nRes ); // nRes is storage ID
+ }
+ else
+ {// dde link
+ pExcRoot->pExtNameBuff->AddDDE( mnLastRefIdx );
+ }
+}
+
+void ImportExcel::Colwidth()
+{// Column Width
+ sal_uInt8 nColFirst, nColLast;
+ sal_uInt16 nColWidth;
+
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+ nColWidth = aIn.ReaduInt16();
+
+//TODO: add a check for the unlikely case of changed MAXCOL (-> XclImpAddressConverter)
+// if( nColLast > rD.MaxCol() )
+// nColLast = static_cast<sal_uInt16>(rD.MaxCol());
+
+ sal_uInt16 nScWidth = XclTools::GetScColumnWidth( nColWidth, GetCharWidth() );
+ pColRowBuff->SetWidthRange( nColFirst, nColLast, nScWidth );
+}
+
+void ImportExcel::Defrowheight2()
+{
+ sal_uInt16 nDefHeight;
+ nDefHeight = maStrm.ReaduInt16();
+ nDefHeight &= 0x7FFF;
+ pColRowBuff->SetDefHeight( nDefHeight, EXC_DEFROW_UNSYNCED );
+}
+
+void ImportExcel::SheetProtect()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetSheetProtectBuffer().ReadProtect( aIn, GetCurrScTab() );
+}
+
+void ImportExcel::DocProtect()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetDocProtectBuffer().ReadDocProtect( aIn );
+}
+
+void ImportExcel::DocPassword()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetDocProtectBuffer().ReadPasswordHash( aIn );
+}
+
+void ImportExcel::Codepage()
+{
+ SetCodePage( maStrm.ReaduInt16() );
+}
+
+void ImportExcel::Ixfe()
+{
+ mnIxfeIndex = maStrm.ReaduInt16();
+}
+
+void ImportExcel::DefColWidth()
+{
+ // stored as entire characters -> convert to 1/256 of characters (as in COLINFO)
+ double fDefWidth = 256.0 * maStrm.ReaduInt16();
+
+ if (!pColRowBuff)
+ {
+ SAL_WARN("sc", "*ImportExcel::DefColWidth(): pColRowBuff is NULL!");
+ return;
+ }
+
+ // #i3006# additional space for default width - Excel adds space depending on font size
+ tools::Long nFontHt = GetFontBuffer().GetAppFontData().mnHeight;
+ fDefWidth += XclTools::GetXclDefColWidthCorrection( nFontHt );
+
+ sal_uInt16 nScWidth = XclTools::GetScColumnWidth( limit_cast< sal_uInt16 >( fDefWidth ), GetCharWidth() );
+ pColRowBuff->SetDefWidth( nScWidth );
+}
+
+void ImportExcel::Colinfo()
+{// Column Formatting Information
+ sal_uInt16 nColFirst, nColLast, nColWidth, nXF;
+ sal_uInt16 nOpt;
+
+ nColFirst = aIn.ReaduInt16();
+ nColLast = aIn.ReaduInt16();
+ nColWidth = aIn.ReaduInt16();
+ nXF = aIn.ReaduInt16();
+ nOpt = aIn.ReaduInt16();
+
+ if( nColFirst > rD.MaxCol() )
+ return;
+
+ if( nColLast > rD.MaxCol() )
+ nColLast = static_cast<sal_uInt16>(rD.MaxCol());
+
+ bool bHidden = ::get_flag( nOpt, EXC_COLINFO_HIDDEN );
+ bool bCollapsed = ::get_flag( nOpt, EXC_COLINFO_COLLAPSED );
+ sal_uInt8 nLevel = ::extract_value< sal_uInt8 >( nOpt, 8, 3 );
+ pColOutlineBuff->SetLevelRange( nColFirst, nColLast, nLevel, bCollapsed );
+
+ if( bHidden )
+ pColRowBuff->HideColRange( nColFirst, nColLast );
+
+ sal_uInt16 nScWidth = XclTools::GetScColumnWidth( nColWidth, GetCharWidth() );
+ pColRowBuff->SetWidthRange( nColFirst, nColLast, nScWidth );
+ pColRowBuff->SetDefaultXF( nColFirst, nColLast, nXF );
+}
+
+void ImportExcel::Wsbool()
+{
+ sal_uInt16 nFlags;
+ nFlags = aIn.ReaduInt16();
+
+ pRowOutlineBuff->SetButtonMode( ::get_flag( nFlags, EXC_WSBOOL_ROWBELOW ) );
+ pColOutlineBuff->SetButtonMode( ::get_flag( nFlags, EXC_WSBOOL_COLBELOW ) );
+
+ GetPageSettings().SetFitToPages( ::get_flag( nFlags, EXC_WSBOOL_FITTOPAGE ) );
+}
+
+void ImportExcel::Boundsheet()
+{
+ sal_uInt16 nGrbit = 0;
+
+ if( GetBiff() == EXC_BIFF5 )
+ {
+ aIn.DisableDecryption();
+ maSheetOffsets.push_back( aIn.ReaduInt32() );
+ aIn.EnableDecryption();
+ nGrbit = aIn.ReaduInt16();
+ }
+
+ OUString aName( aIn.ReadByteString( false ) );
+
+ SCTAB nScTab = nBdshtTab;
+ if( nScTab > 0 )
+ {
+ OSL_ENSURE( !rD.HasTable( nScTab ), "ImportExcel::Boundsheet - sheet exists already" );
+ rD.MakeTable( nScTab );
+ }
+
+ if( ( nGrbit & 0x0001 ) || ( nGrbit & 0x0002 ) )
+ rD.SetVisible( nScTab, false );
+
+ if( !rD.RenameTab( nScTab, aName ) )
+ {
+ rD.CreateValidTabName( aName );
+ rD.RenameTab( nScTab, aName );
+ }
+
+ nBdshtTab++;
+}
+
+void ImportExcel::Country()
+{
+ sal_uInt16 nUICountry, nDocCountry;
+ nUICountry = maStrm.ReaduInt16();
+ nDocCountry = maStrm.ReaduInt16();
+
+ // Store system language in XclRoot
+ LanguageType eLanguage = ::msfilter::ConvertCountryToLanguage( static_cast< ::msfilter::CountryId >( nDocCountry ) );
+ if( eLanguage != LANGUAGE_DONTKNOW )
+ SetDocLanguage( eLanguage );
+
+ // Set Excel UI language in add-in name translator
+ eLanguage = ::msfilter::ConvertCountryToLanguage( static_cast< ::msfilter::CountryId >( nUICountry ) );
+ if( eLanguage != LANGUAGE_DONTKNOW )
+ SetUILanguage( eLanguage );
+}
+
+void ImportExcel::ReadUsesElfs()
+{
+ if( maStrm.ReaduInt16() != 0 )
+ {
+ ScDocOptions aDocOpt = GetDoc().GetDocOptions();
+ aDocOpt.SetLookUpColRowNames( true );
+ GetDoc().SetDocOptions( aDocOpt );
+ }
+}
+
+void ImportExcel::Hideobj()
+{
+ sal_uInt16 nHide;
+ ScVObjMode eOle, eChart, eDraw;
+
+ nHide = aIn.ReaduInt16();
+
+ ScViewOptions aOpts( rD.GetViewOptions() );
+
+ switch( nHide )
+ {
+ case 1: // Placeholders
+ eOle = VOBJ_MODE_SHOW; // in Excel 97 only charts as place holder are displayed
+ eChart = VOBJ_MODE_SHOW; //#i80528# VOBJ_MODE_DUMMY replaced by VOBJ_MODE_SHOW now
+ eDraw = VOBJ_MODE_SHOW;
+ break;
+ case 2: // Hide all
+ eOle = VOBJ_MODE_HIDE;
+ eChart = VOBJ_MODE_HIDE;
+ eDraw = VOBJ_MODE_HIDE;
+ break;
+ default: // Show all
+ eOle = VOBJ_MODE_SHOW;
+ eChart = VOBJ_MODE_SHOW;
+ eDraw = VOBJ_MODE_SHOW;
+ break;
+ }
+
+ aOpts.SetObjMode( VOBJ_TYPE_OLE, eOle );
+ aOpts.SetObjMode( VOBJ_TYPE_CHART, eChart );
+ aOpts.SetObjMode( VOBJ_TYPE_DRAW, eDraw );
+
+ rD.SetViewOptions( aOpts );
+}
+
+void ImportExcel::Standardwidth()
+{
+ sal_uInt16 nScWidth = XclTools::GetScColumnWidth( maStrm.ReaduInt16(), GetCharWidth() );
+ if (!pColRowBuff)
+ {
+ SAL_WARN("sc", "*ImportExcel::Standardwidth(): pColRowBuff is NULL!");
+ return;
+ }
+ pColRowBuff->SetDefWidth( nScWidth, true );
+}
+
+void ImportExcel::Shrfmla()
+{
+ switch (mnLastRecId)
+ {
+ case EXC_ID2_FORMULA:
+ case EXC_ID3_FORMULA:
+ case EXC_ID4_FORMULA:
+ // This record MUST immediately follow a FORMULA record.
+ break;
+ default:
+ return;
+ }
+
+ if (!mpLastFormula)
+ // The last FORMULA record should have left this data.
+ return;
+
+ aIn.Ignore( 8 );
+ sal_uInt16 nLenExpr = aIn.ReaduInt16();
+
+ // read mark is now on the formula
+
+ std::unique_ptr<ScTokenArray> pResult;
+
+ // The shared range in this record is erroneous more than half the time.
+ // Don't ever rely on it. Use the one from the formula cell above.
+ SCCOL nCol1 = mpLastFormula->mnCol;
+ SCROW nRow1 = mpLastFormula->mnRow;
+
+ ScAddress aPos(nCol1, nRow1, GetCurrScTab());
+ pFormConv->Reset(aPos);
+ pFormConv->Convert( pResult, maStrm, nLenExpr, true, FT_SharedFormula );
+
+ if (!pResult)
+ {
+ SAL_WARN("sc", "+ImportExcel::Shrfmla(): ScTokenArray is NULL!");
+ return;
+ }
+
+ pExcRoot->pShrfmlaBuff->Store(aPos, *pResult);
+
+ // Create formula cell for the last formula record.
+
+ ScDocumentImport& rDoc = GetDocImport();
+
+ ScFormulaCell* pCell = new ScFormulaCell(rD, aPos, std::move(pResult));
+ pCell->GetCode()->WrapReference(aPos, EXC_MAXCOL8, EXC_MAXROW8);
+ rDoc.getDoc().CheckLinkFormulaNeedingCheck( *pCell->GetCode());
+ rDoc.getDoc().EnsureTable(aPos.Tab());
+ rDoc.setFormulaCell(aPos, pCell);
+ pCell->SetNeedNumberFormat(false);
+ if (std::isfinite(mpLastFormula->mfValue))
+ pCell->SetResultDouble(mpLastFormula->mfValue);
+
+ GetXFRangeBuffer().SetXF(aPos, mpLastFormula->mnXF);
+ mpLastFormula->mpCell = pCell;
+}
+
+void ImportExcel::Mulrk()
+{
+ /* rw (2 bytes): An Rw structure that specifies the row containing the
+ cells with numeric data.
+
+ colFirst (2 bytes): A Col structure that specifies the first column in
+ the series of numeric cells within the sheet. The value of colFirst.col
+ MUST be less than or equal to 254.
+
+ rgrkrec (variable): An array of RkRec structures. Each element in the
+ array specifies an RkRec in the row. The number of entries in the array
+ MUST be equal to the value given by the following formula:
+
+ Number of entries in rgrkrec = (colLast.col – colFirst.col +1)
+
+ colLast (2 bytes): A Col structure that specifies the last column in
+ the set of numeric cells within the sheet. This colLast.col value MUST
+ be greater than the colFirst.col value. */
+
+ XclAddress aXclPos;
+ aIn >> aXclPos;
+
+ XclAddress aCurrXclPos(aXclPos);
+ while (true)
+ {
+ if (aXclPos.mnCol > aCurrXclPos.mnCol)
+ break;
+ if (aIn.GetRecLeft() <= 2)
+ break;
+
+ sal_uInt16 nXF = aIn.ReaduInt16();
+ sal_Int32 nRkNum = aIn.ReadInt32();
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aCurrXclPos, GetCurrScTab(), true ) )
+ {
+ GetXFRangeBuffer().SetXF( aScPos, nXF );
+ GetDocImport().setNumericCell(aScPos, XclTools::GetDoubleFromRK(nRkNum));
+ }
+ ++aCurrXclPos.mnCol;
+ }
+}
+
+void ImportExcel::Mulblank()
+{
+ /* rw (2 bytes): An Rw structure that specifies a row containing the blank
+ cells.
+
+ colFirst (2 bytes): A Col structure that specifies the first column in
+ the series of blank cells within the sheet. The value of colFirst.col
+ MUST be less than or equal to 254.
+
+ rgixfe (variable): An array of IXFCell structures. Each element of this
+ array contains an IXFCell structure corresponding to a blank cell in the
+ series. The number of entries in the array MUST be equal to the value
+ given by the following formula:
+
+ Number of entries in rgixfe = (colLast.col – colFirst.col +1)
+
+ colLast (2 bytes): A Col structure that specifies the last column in
+ the series of blank cells within the sheet. This colLast.col value MUST
+ be greater than colFirst.col value. */
+
+ XclAddress aXclPos;
+ aIn >> aXclPos;
+
+ XclAddress aCurrXclPos(aXclPos);
+ while (true)
+ {
+ if (aXclPos.mnCol > aCurrXclPos.mnCol)
+ break;
+ if (aIn.GetRecLeft() <= 2)
+ break;
+
+ sal_uInt16 nXF = aIn.ReaduInt16();
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aCurrXclPos, GetCurrScTab(), true ) )
+ GetXFRangeBuffer().SetBlankXF( aScPos, nXF );
+ ++aCurrXclPos.mnCol;
+ }
+}
+
+void ImportExcel::Rstring()
+{
+ XclAddress aXclPos;
+ sal_uInt16 nXFIdx;
+ aIn >> aXclPos;
+ nXFIdx = aIn.ReaduInt16();
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( !GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ return;
+
+ // unformatted Unicode string with separate formatting information
+ XclImpString aString;
+ aString.Read( maStrm );
+
+ // character formatting runs
+ if( !aString.IsRich() )
+ aString.ReadFormats( maStrm );
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ XclImpStringHelper::SetToDocument(GetDocImport(), aScPos, *this, aString, nXFIdx);
+}
+
+void ImportExcel::Cellmerging()
+{
+ XclImpAddressConverter& rAddrConv = GetAddressConverter();
+ SCTAB nScTab = GetCurrScTab();
+
+ sal_uInt16 nCount = maStrm.ReaduInt16();
+ sal_uInt16 nIdx = 0;
+ while (true)
+ {
+ if (maStrm.GetRecLeft() < 8)
+ break;
+ if (nIdx >= nCount)
+ break;
+ XclRange aXclRange;
+ maStrm >> aXclRange; // 16-bit rows and columns
+ ScRange aScRange( ScAddress::UNINITIALIZED );
+ if( rAddrConv.ConvertRange( aScRange, aXclRange, nScTab, nScTab, true ) )
+ GetXFRangeBuffer().SetMerge( aScRange.aStart.Col(), aScRange.aStart.Row(), aScRange.aEnd.Col(), aScRange.aEnd.Row() );
+ ++nIdx;
+ }
+}
+
+void ImportExcel::Olesize()
+{
+ XclRange aXclOleSize( ScAddress::UNINITIALIZED );
+ maStrm.Ignore( 2 );
+ aXclOleSize.Read( maStrm, false );
+
+ SCTAB nScTab = GetCurrScTab();
+ GetAddressConverter().ConvertRange( maScOleSize, aXclOleSize, nScTab, nScTab, false );
+}
+
+void ImportExcel::Row34()
+{
+ sal_uInt16 nRow, nRowHeight, nGrbit, nXF;
+
+ nRow = aIn.ReaduInt16();
+ aIn.Ignore( 4 );
+
+ SCROW nScRow = static_cast< SCROW >( nRow );
+
+ if( !GetRoot().GetDoc().ValidRow( nScRow ) )
+ return;
+
+ nRowHeight = aIn.ReaduInt16(); // specify direct in Twips
+ aIn.Ignore( 4 );
+
+ nRowHeight = nRowHeight & 0x7FFF; // Bit 15: Row Height not changed manually
+ if( !nRowHeight )
+ nRowHeight = (GetBiff() == EXC_BIFF2) ? 0x25 : 0x225;
+
+ nGrbit = aIn.ReaduInt16();
+ nXF = aIn.ReaduInt16();
+
+ sal_uInt8 nLevel = ::extract_value< sal_uInt8 >( nGrbit, 0, 3 );
+ pRowOutlineBuff->SetLevel( nScRow, nLevel, ::get_flag( nGrbit, EXC_ROW_COLLAPSED ) );
+ pColRowBuff->SetRowSettings( nScRow, nRowHeight, nGrbit );
+
+ if( nGrbit & EXC_ROW_USEDEFXF )
+ GetXFRangeBuffer().SetRowDefXF( nScRow, nXF & EXC_ROW_XFMASK );
+}
+
+void ImportExcel::Bof3()
+{
+ sal_uInt16 nSubType;
+ maStrm.DisableDecryption();
+ maStrm.Ignore( 2 );
+ nSubType = maStrm.ReaduInt16();
+
+ OSL_ENSURE( nSubType != 0x0100, "*ImportExcel::Bof3(): Biff3 as Workbook?!" );
+ if( nSubType == 0x0100 ) // Book
+ pExcRoot->eDateiTyp = Biff3W;
+ else if( nSubType == 0x0020 ) // Chart
+ pExcRoot->eDateiTyp = Biff3C;
+ else if( nSubType == 0x0040 ) // Macro
+ pExcRoot->eDateiTyp = Biff3M;
+ else // #i51490# Excel interprets invalid indexes as worksheet
+ pExcRoot->eDateiTyp = Biff3;
+}
+
+void ImportExcel::Array34()
+{
+ sal_uInt16 nFirstRow, nLastRow, nFormLen;
+ sal_uInt8 nFirstCol, nLastCol;
+
+ nFirstRow = aIn.ReaduInt16();
+ nLastRow = aIn.ReaduInt16();
+ nFirstCol = aIn.ReaduInt8();
+ nLastCol = aIn.ReaduInt8();
+ aIn.Ignore( (GetBiff() >= EXC_BIFF5) ? 6 : 2 );
+ nFormLen = aIn.ReaduInt16();
+
+ std::unique_ptr<ScTokenArray> pResult;
+
+ if( GetRoot().GetDoc().ValidColRow( nLastCol, nLastRow ) )
+ {
+ // the read mark is now on the formula, length in nFormLen
+
+ pFormConv->Reset( ScAddress( static_cast<SCCOL>(nFirstCol),
+ static_cast<SCROW>(nFirstRow), GetCurrScTab() ) );
+ pFormConv->Convert( pResult, maStrm, nFormLen, true );
+
+ SAL_WARN_IF(!pResult, "sc", "+ImportExcel::Array34(): ScTokenArray is NULL!");
+ }
+
+ if (pResult)
+ {
+ ScDocumentImport& rDoc = GetDocImport();
+ ScRange aArrayRange(nFirstCol, nFirstRow, GetCurrScTab(), nLastCol, nLastRow, GetCurrScTab());
+ rDoc.setMatrixCells(aArrayRange, *pResult, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1);
+ }
+}
+
+void ImportExcel::Defrowheight345()
+{
+ sal_uInt16 nFlags, nDefHeight;
+ nFlags = maStrm.ReaduInt16();
+ nDefHeight = maStrm.ReaduInt16();
+
+ if (!pColRowBuff)
+ {
+ SAL_WARN("sc", "*ImportExcel::Defrowheight345(): pColRowBuff is NULL!");
+ return;
+ }
+
+ pColRowBuff->SetDefHeight( nDefHeight, nFlags );
+}
+
+void ImportExcel::TableOp()
+{
+ sal_uInt16 nFirstRow = aIn.ReaduInt16();
+ sal_uInt16 nLastRow = aIn.ReaduInt16();
+ sal_uInt8 nFirstCol = aIn.ReaduInt8();
+ sal_uInt8 nLastCol = aIn.ReaduInt8();
+ sal_uInt16 nGrbit = aIn.ReaduInt16();
+ sal_uInt16 nInpRow = aIn.ReaduInt16();
+ sal_uInt16 nInpCol = aIn.ReaduInt16();
+ sal_uInt16 nInpRow2 = aIn.ReaduInt16();
+ sal_uInt16 nInpCol2 = aIn.ReaduInt16();
+
+ if (utl::ConfigManager::IsFuzzing())
+ {
+ //shrink to smallish arbitrary value to not timeout
+ nLastRow = std::min<sal_uInt16>(nLastRow, MAXROW_30 / 2);
+ }
+
+ if( GetRoot().GetDoc().ValidColRow( nLastCol, nLastRow ) )
+ {
+ if( nFirstCol && nFirstRow )
+ {
+ ScTabOpParam aTabOpParam;
+ aTabOpParam.meMode = (nGrbit & EXC_TABLEOP_BOTH) ? ScTabOpParam::Both : ((nGrbit & EXC_TABLEOP_ROW) ? ScTabOpParam::Row : ScTabOpParam::Column);
+ sal_uInt16 nCol = nFirstCol - 1;
+ sal_uInt16 nRow = nFirstRow - 1;
+ SCTAB nTab = GetCurrScTab();
+ switch (aTabOpParam.meMode)
+ {
+ case ScTabOpParam::Column:
+ aTabOpParam.aRefFormulaCell.Set(
+ static_cast<SCCOL>(nFirstCol),
+ static_cast<SCROW>(nFirstRow - 1), nTab, false,
+ false, false );
+ aTabOpParam.aRefFormulaEnd.Set(
+ static_cast<SCCOL>(nLastCol),
+ static_cast<SCROW>(nFirstRow - 1), nTab, false,
+ false, false );
+ aTabOpParam.aRefColCell.Set( static_cast<SCCOL>(nInpCol),
+ static_cast<SCROW>(nInpRow), nTab, false, false,
+ false );
+ nRow++;
+ break;
+ case ScTabOpParam::Row:
+ aTabOpParam.aRefFormulaCell.Set(
+ static_cast<SCCOL>(nFirstCol - 1),
+ static_cast<SCROW>(nFirstRow), nTab, false, false,
+ false );
+ aTabOpParam.aRefFormulaEnd.Set(
+ static_cast<SCCOL>(nFirstCol - 1),
+ static_cast<SCROW>(nLastRow), nTab, false, false,
+ false );
+ aTabOpParam.aRefRowCell.Set( static_cast<SCCOL>(nInpCol),
+ static_cast<SCROW>(nInpRow), nTab, false, false,
+ false );
+ nCol++;
+ break;
+ case ScTabOpParam::Both: // TWO-INPUT
+ aTabOpParam.aRefFormulaCell.Set(
+ static_cast<SCCOL>(nFirstCol - 1),
+ static_cast<SCROW>(nFirstRow - 1), nTab, false,
+ false, false );
+ aTabOpParam.aRefRowCell.Set( static_cast<SCCOL>(nInpCol),
+ static_cast<SCROW>(nInpRow), nTab, false, false,
+ false );
+ aTabOpParam.aRefColCell.Set( static_cast<SCCOL>(nInpCol2),
+ static_cast<SCROW>(nInpRow2), nTab, false, false,
+ false );
+ break;
+ }
+
+ ScDocumentImport& rDoc = GetDocImport();
+ ScRange aTabOpRange(nCol, nRow, nTab, nLastCol, nLastRow, nTab);
+ rDoc.setTableOpCells(aTabOpRange, aTabOpParam);
+ }
+ }
+ else
+ {
+ bTabTruncated = true;
+ GetTracer().TraceInvalidRow(nLastRow, rD.MaxRow());
+ }
+}
+
+void ImportExcel::Bof4()
+{
+ sal_uInt16 nSubType;
+ maStrm.DisableDecryption();
+ maStrm.Ignore( 2 );
+ nSubType = maStrm.ReaduInt16();
+
+ if( nSubType == 0x0100 ) // Book
+ pExcRoot->eDateiTyp = Biff4W;
+ else if( nSubType == 0x0020 ) // Chart
+ pExcRoot->eDateiTyp = Biff4C;
+ else if( nSubType == 0x0040 ) // Macro
+ pExcRoot->eDateiTyp = Biff4M;
+ else // #i51490# Excel interprets invalid indexes as worksheet
+ pExcRoot->eDateiTyp = Biff4;
+}
+
+void ImportExcel::Bof5()
+{
+ //POST: eDateiTyp = Type of the file to be read
+ sal_uInt16 nSubType, nVers;
+ BiffTyp eDatei;
+
+ maStrm.DisableDecryption();
+ nVers = maStrm.ReaduInt16();
+ nSubType = maStrm.ReaduInt16( );
+
+ switch( nSubType )
+ {
+ case 0x0005: eDatei = Biff5W; break; // workbook globals
+ case 0x0006: eDatei = Biff5V; break; // VB module
+ case 0x0020: eDatei = Biff5C; break; // chart
+ case 0x0040: eDatei = Biff5M4; break; // macro sheet
+ case 0x0010: // worksheet
+ default: eDatei = Biff5; break; // tdf#144732 Excel interprets invalid indexes as worksheet
+ }
+
+ if( nVers == 0x0600 && (GetBiff() == EXC_BIFF8) )
+ eDatei = static_cast<BiffTyp>( eDatei - Biff5 + Biff8 );
+
+ pExcRoot->eDateiTyp = eDatei;
+}
+
+void ImportExcel::EndSheet()
+{
+ pExcRoot->pExtSheetBuff->Reset();
+
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ pExcRoot->pExtNameBuff->Reset();
+ mnLastRefIdx = 0;
+ }
+
+ FinalizeTable();
+}
+
+void ImportExcel::NewTable()
+{
+ SCTAB nTab = GetCurrScTab();
+ if( nTab > 0 && !rD.HasTable( nTab ) )
+ rD.MakeTable( nTab );
+
+ if (nTab == 0 && GetBiff() == EXC_BIFF2)
+ {
+ // For Excel 2.1 Worksheet file, we need to set the file name as the
+ // sheet name.
+ INetURLObject aURL(GetDocUrl());
+ rD.RenameTab(0, aURL.getBase());
+ }
+
+ pExcRoot->pShrfmlaBuff->Clear();
+ maLastFormulaCells.clear();
+ mpLastFormula = nullptr;
+
+ InitializeTable( nTab );
+
+ XclImpOutlineDataBuffer* pNewItem = new XclImpOutlineDataBuffer( GetRoot(), nTab );
+ pOutlineListBuffer->push_back( std::unique_ptr<XclImpOutlineDataBuffer>(pNewItem) );
+ pExcRoot->pColRowBuff = pColRowBuff = pNewItem->GetColRowBuff();
+ pColOutlineBuff = pNewItem->GetColOutline();
+ pRowOutlineBuff = pNewItem->GetRowOutline();
+}
+
+std::unique_ptr<ScTokenArray> ImportExcel::ErrorToFormula( bool bErrOrVal, sal_uInt8 nError, double& rVal )
+{
+ return pFormConv->GetBoolErr( XclTools::ErrorToEnum( rVal, bErrOrVal, nError ) );
+}
+
+void ImportExcel::AdjustRowHeight()
+{
+ /* Speed up chart import: import all sheets without charts, then
+ update row heights (here), last load all charts -> do not any longer
+ update inside of ScDocShell::ConvertFrom() (causes update of existing
+ charts during each and every change of row height). */
+ if( ScModelObj* pDocObj = GetDocModelObj() )
+ pDocObj->UpdateAllRowHeights();
+}
+
+void ImportExcel::PostDocLoad()
+{
+ /* Set automatic page numbering in Default page style (default is "page number = 1").
+ Otherwise hidden tables (i.e. for scenarios) which have Default page style will
+ break automatic page numbering. */
+ if( SfxStyleSheetBase* pStyleSheet = GetStyleSheetPool().Find( ScResId( STR_STYLENAME_STANDARD ), SfxStyleFamily::Page ) )
+ pStyleSheet->GetItemSet().Put( SfxUInt16Item( ATTR_PAGE_FIRSTPAGENO, 0 ) );
+
+ // outlines for all sheets, sets hidden rows and columns (#i11776# after filtered ranges)
+ for (auto& rxBuffer : *pOutlineListBuffer)
+ rxBuffer->Convert();
+
+ // document view settings (before visible OLE area)
+ GetDocViewSettings().Finalize();
+
+ // process all drawing objects (including OLE, charts, controls; after hiding rows/columns; before visible OLE area)
+ GetObjectManager().ConvertObjects();
+
+ // visible area (used if this document is an embedded OLE object)
+ if( SfxObjectShell* pDocShell = GetDocShell() )
+ {
+ // visible area if embedded
+ const ScExtDocSettings& rDocSett = GetExtDocOptions().GetDocSettings();
+ SCTAB nDisplScTab = rDocSett.mnDisplTab;
+
+ /* #i44077# If a new OLE object is inserted from file, there is no
+ OLESIZE record in the Excel file. Calculate used area from file
+ contents (used cells and drawing objects). */
+ if( !maScOleSize.IsValid() )
+ {
+ // used area of displayed sheet (cell contents)
+ if( const ScExtTabSettings* pTabSett = GetExtDocOptions().GetTabSettings( nDisplScTab ) )
+ maScOleSize = pTabSett->maUsedArea;
+ // add all valid drawing objects
+ ScRange aScObjArea = GetObjectManager().GetUsedArea( nDisplScTab );
+ if( aScObjArea.IsValid() )
+ maScOleSize.ExtendTo( aScObjArea );
+ }
+
+ // valid size found - set it at the document
+ if( maScOleSize.IsValid() )
+ {
+ pDocShell->SetVisArea( GetDoc().GetMMRect(
+ maScOleSize.aStart.Col(), maScOleSize.aStart.Row(),
+ maScOleSize.aEnd.Col(), maScOleSize.aEnd.Row(), nDisplScTab ) );
+ GetDoc().SetVisibleTab( nDisplScTab );
+ }
+ }
+
+ // open forms in alive mode (has no effect, if no controls in document)
+ if( ScModelObj* pDocObj = GetDocModelObj() )
+ pDocObj->setPropertyValue( SC_UNO_APPLYFMDES, uno::Any( false ) );
+
+ // enables extended options to be set to the view after import
+ GetExtDocOptions().SetChanged( true );
+
+ // root data owns the extended document options -> create a new object
+ GetDoc().SetExtDocOptions( std::make_unique<ScExtDocOptions>( GetExtDocOptions() ) );
+
+ const SCTAB nLast = rD.GetTableCount();
+ const ScRange* p;
+
+ if( GetRoot().GetPrintAreaBuffer().HasRanges() )
+ {
+ for( SCTAB n = 0 ; n < nLast ; n++ )
+ {
+ p = GetRoot().GetPrintAreaBuffer().First(n);
+ if( p )
+ {
+ rD.ClearPrintRanges( n );
+ while( p )
+ {
+ rD.AddPrintRange( n, *p );
+ p = GetRoot().GetPrintAreaBuffer().Next();
+ }
+ }
+ else
+ {
+ // #i4063# no print ranges -> print entire sheet
+ rD.SetPrintEntireSheet( n );
+ }
+ }
+ GetTracer().TracePrintRange();
+ }
+
+ if( !GetRoot().GetTitleAreaBuffer().HasRanges() )
+ return;
+
+ for( SCTAB n = 0 ; n < nLast ; n++ )
+ {
+ p = GetRoot().GetTitleAreaBuffer().First(n);
+ if( p )
+ {
+ bool bRowVirgin = true;
+ bool bColVirgin = true;
+
+ while( p )
+ {
+ if( p->aStart.Col() == 0 && p->aEnd.Col() == rD.MaxCol() && bRowVirgin )
+ {
+ rD.SetRepeatRowRange( n, *p );
+ bRowVirgin = false;
+ }
+
+ if( p->aStart.Row() == 0 && p->aEnd.Row() == rD.MaxRow() && bColVirgin )
+ {
+ rD.SetRepeatColRange( n, *p );
+ bColVirgin = false;
+ }
+
+ p = GetRoot().GetTitleAreaBuffer().Next();
+ }
+ }
+ }
+}
+
+XclImpOutlineDataBuffer::XclImpOutlineDataBuffer( const XclImpRoot& rRoot, SCTAB nScTab ) :
+ XclImpRoot( rRoot ),
+ mxColOutlineBuff( std::make_shared<XclImpOutlineBuffer>( rRoot.GetXclMaxPos().Col() + 1 ) ),
+ mxRowOutlineBuff( std::make_shared<XclImpOutlineBuffer>( rRoot.GetXclMaxPos().Row() + 1 ) ),
+ mxColRowBuff( std::make_shared<XclImpColRowSettings>( rRoot ) ),
+ mnScTab( nScTab )
+{
+}
+
+XclImpOutlineDataBuffer::~XclImpOutlineDataBuffer()
+{
+}
+
+void XclImpOutlineDataBuffer::Convert()
+{
+ mxColOutlineBuff->SetOutlineArray( &GetDoc().GetOutlineTable( mnScTab, true )->GetColArray() );
+ mxColOutlineBuff->MakeScOutline();
+
+ mxRowOutlineBuff->SetOutlineArray( &GetDoc().GetOutlineTable( mnScTab, true )->GetRowArray() );
+ mxRowOutlineBuff->MakeScOutline();
+
+ mxColRowBuff->ConvertHiddenFlags( mnScTab );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/namebuff.cxx b/sc/source/filter/excel/namebuff.cxx
new file mode 100644
index 000000000..523145209
--- /dev/null
+++ b/sc/source/filter/excel/namebuff.cxx
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <namebuff.hxx>
+
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <tokenarray.hxx>
+
+#include <root.hxx>
+#include <xiroot.hxx>
+
+#include <osl/diagnose.h>
+
+sal_uInt32 StringHashEntry::MakeHashCode( const OUString& r )
+{
+ sal_uInt32 n = 0;
+ const sal_Unicode* pCurrent = r.getStr();
+ sal_Unicode cCurrent = *pCurrent;
+
+ while( cCurrent )
+ {
+ n *= 70;
+ n += static_cast<sal_uInt32>(cCurrent);
+ pCurrent++;
+ cCurrent = *pCurrent;
+ }
+
+ return n;
+}
+
+SharedFormulaBuffer::SharedFormulaBuffer(RootData* pRD)
+ : ExcRoot(pRD)
+{
+}
+
+SharedFormulaBuffer::~SharedFormulaBuffer()
+{
+ Clear();
+}
+
+void SharedFormulaBuffer::Clear()
+{
+ maTokenArrays.clear();
+}
+
+void SharedFormulaBuffer::Store( const ScAddress& rPos, const ScTokenArray& rArray )
+{
+ ScTokenArray aCode(rArray.CloneValue());
+ aCode.GenHash();
+ maTokenArrays.emplace(rPos, std::move(aCode));
+}
+
+const ScTokenArray* SharedFormulaBuffer::Find( const ScAddress& rRefPos ) const
+{
+ TokenArraysType::const_iterator it = maTokenArrays.find(rRefPos);
+ if (it == maTokenArrays.end())
+ return nullptr;
+
+ return &it->second;
+}
+
+sal_Int16 ExtSheetBuffer::Add( const OUString& rFPAN, const OUString& rTN, const bool bSWB )
+{
+ maEntries.emplace_back( rFPAN, rTN, bSWB );
+ // return 1-based index of EXTERNSHEET
+ return static_cast< sal_Int16 >( maEntries.size() );
+}
+
+bool ExtSheetBuffer::GetScTabIndex( sal_uInt16 nExcIndex, sal_uInt16& rScIndex )
+{
+ OSL_ENSURE( nExcIndex,
+ "*ExtSheetBuffer::GetScTabIndex(): Sheet-Index == 0!" );
+
+ if ( !nExcIndex || nExcIndex > maEntries.size() )
+ return false;
+
+ Cont* pCur = &maEntries[ nExcIndex - 1 ];
+ sal_uInt16& rTabNum = pCur->nTabNum;
+
+ if( rTabNum < 0xFFFD )
+ {
+ rScIndex = rTabNum;
+ return true;
+ }
+
+ if( rTabNum == 0xFFFF )
+ {// create new table
+ SCTAB nNewTabNum;
+ if( pCur->bSWB )
+ {// table is in the same workbook!
+ if( pExcRoot->pIR->GetDoc().GetTable( pCur->aTab, nNewTabNum ) )
+ {
+ rScIndex = rTabNum = static_cast<sal_uInt16>(nNewTabNum);
+ return true;
+ }
+ else
+ rTabNum = 0xFFFD;
+ }
+ else if( pExcRoot->pIR->GetDocShell() )
+ {// table is 'really' external
+ if( pExcRoot->pIR->GetExtDocOptions().GetDocSettings().mnLinkCnt == 0 )
+ {
+ OUString aURL( ScGlobal::GetAbsDocName( pCur->aFile,
+ pExcRoot->pIR->GetDocShell() ) );
+ OUString aTabName( ScGlobal::GetDocTabName( aURL, pCur->aTab ) );
+ if( pExcRoot->pIR->GetDoc().LinkExternalTab( nNewTabNum, aTabName, aURL, pCur->aTab ) )
+ {
+ rScIndex = rTabNum = static_cast<sal_uInt16>(nNewTabNum);
+ return true;
+ }
+ else
+ rTabNum = 0xFFFE; // no table is created for now -> and likely
+ // will not be created later...
+ }
+ else
+ rTabNum = 0xFFFE;
+
+ }
+ }
+
+ return false;
+}
+
+void ExtSheetBuffer::Reset()
+{
+ maEntries.clear();
+}
+
+bool ExtName::IsOLE() const
+{
+ return ( nFlags & 0x0002 ) != 0;
+}
+
+ExtNameBuff::ExtNameBuff( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void ExtNameBuff::AddDDE( sal_Int16 nRefIdx )
+{
+ ExtName aNew( 0x0001 );
+ maExtNames[ nRefIdx ].push_back( aNew );
+}
+
+void ExtNameBuff::AddOLE( sal_Int16 nRefIdx, sal_uInt32 nStorageId )
+{
+ ExtName aNew( 0x0002 );
+ aNew.nStorageId = nStorageId;
+ maExtNames[ nRefIdx ].push_back( aNew );
+}
+
+void ExtNameBuff::AddName( sal_Int16 nRefIdx )
+{
+ ExtName aNew( 0x0004 );
+ maExtNames[ nRefIdx ].push_back( aNew );
+}
+
+const ExtName* ExtNameBuff::GetNameByIndex( sal_Int16 nRefIdx, sal_uInt16 nNameIdx ) const
+{
+ OSL_ENSURE( nNameIdx > 0, "ExtNameBuff::GetNameByIndex() - invalid name index" );
+ ExtNameMap::const_iterator aIt = maExtNames.find( nRefIdx );
+ return ((aIt != maExtNames.end()) && (0 < nNameIdx) && (nNameIdx <= aIt->second.size())) ? &aIt->second[ nNameIdx - 1 ] : nullptr;
+}
+
+void ExtNameBuff::Reset()
+{
+ maExtNames.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/ooxml-export-TODO.txt b/sc/source/filter/excel/ooxml-export-TODO.txt
new file mode 100644
index 000000000..d995598d2
--- /dev/null
+++ b/sc/source/filter/excel/ooxml-export-TODO.txt
@@ -0,0 +1,164 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+TODO/Unimplemented Calc OOXML Export Features:
+=============================================
+
+Partially implemented features are not mentioned here; grep for OOXTODO within
+sc/source/filter/*.
+
+In updated OfficeFileFormatsProtocols.zip [MS-XLS].pdf,
+Section §2.3.1 (p.154) provides the record name :: record number mapping, and
+Section §2.3.2 (p.165) provides the record number :: record name mapping.
+
+Elements:
+ - Workbook (§3.2):
+ - customWorkbookViews (§3.2.3)
+ - ext (§3.2.7)
+ - extLst (§3.2.10)
+ - fileRecoveryPr (§3.2.11) [ CRASHRECERR? 865h ]
+ - fileSharing (§3.2.12) [ FILESHARING 5Bh ]
+ - functionGroup (§3.2.14) [ FNGRP12 898h; FNGROUPNAME 9Ah ]
+ - functionGroups (§3.2.15) [ FNGROUPCOUNT: 9Ch ]
+ - oleSize (§3.2.16) [ OLESIZE DEh ]
+ - smartTagPr (§3.2.21) [ BOOKEXT 863h ]
+ - smartTagType (§3.2.22) [ unknown record ]
+ - smartTagTypes (§3.2.23) [ unknown record ]
+ - webPublishing (§3.2.24) [ WOPT 80Bh ]
+ - webPublishObject (§3.2.25) [ WEBPUB 801h ]
+ - webPublishObjects (§3.2.26) [ unsupported ]
+ - Worksheets (§3.3.1):
+ - autoFilter (§3.3.1.1) [ AutoFilter 9Eh ]
+ - cellSmartTag (§3.3.1.4) [ FEAT 868h ]
+ - cellSmartTagPr (§3.3.1.5) [ FEAT? 868h ]
+ - cellSmartTags (§3.3.1.6) [ FEAT 868h ]
+ - cellWatch (§3.3.1.7) [ CELLWATCH 86Ch ]
+ - cellWatches (§3.3.1.8) [ CELLWATCH 86Ch ]
+ - cfRule (§3.3.1.9) [ CF 1B1h ]
+ - cfvo (§3.3.1.10) [ CF12 87Ah ]
+ - chartsheet (§3.3.1.11) [ CHARTFRTINFO 850h, FRTWRAPPER 851h...]
+ - color (§3.3.1.14) [ DXF 88Dh xfpropBorder?
+ XFEXT 87Dh xclrType? ]
+ - colorScale (§3.3.1.15) [ DXF 88Dh? ]
+ - control (§3.3.1.18) [ ??? ]
+ - controls (§3.3.1.19) [ ??? ]
+ - customPr (§3.3.1.20) [ ??? ]
+ - customProperties (§3.3.1.21) [ ??? ]
+ - customSheetView (§3.3.1.22) [ ???; for charts; see chartsheet? ]
+ - customSheetView (§3.3.1.23) [ ??? ]
+ - customSheetViews (§3.3.1.24) [ ???; for charts; see chartsheet? ]
+ - customSheetViews (§3.3.1.25) [ ??? ]
+ - dataBar (§3.3.1.26) [ CF12 87Ah ct=Databar ]
+ - dataConsolidate (§3.3.1.27) [ DCON 50h ]
+ - dataRef (§3.3.1.28) [ DCONBIN 1B5h ]
+ - dataRefs (§3.3.1.29) [ ??? ]
+ - dialogsheet (§3.3.1.32) [ ??? ]
+ - drawing (§3.3.1.34) [ ??? ]
+ - evenFooter (§3.3.1.35) [ HeaderFooter 89Ch ]
+ - evenHeader (§3.3.1.36) [ HeaderFooter 89Ch ]
+ - firstFooter (§3.3.1.38) [ HeaderFooter 89Ch ]
+ - firstHeader (§3.3.1.39) [ HeaderFooter 89Ch ]
+ - formula (§3.3.1.40) [ CF 1B1h ]
+ - iconSet (§3.3.1.46) [ CF12 87Ah ct=CFMultistate ]
+ - ignoredError (§3.3.1.47) [ Feat/FeatFormulaErr2/FFErrorCheck 868h ]
+ - ignoredErrors (§3.3.1.48) [ Feat 868h ]
+ - legacyDrawing (§3.3.1.51) [ MsoDrawing ECh ]
+ - legacyDrawingHF (§3.3.1.52) [ ??? ]
+ - oleObject (§3.3.1.57) [ ??? ]
+ - oleObjects (§3.3.1.58) [ ??? ]
+ - outlinePr (§3.3.1.59) [ ??? ]
+ - pageSetup (§3.3.1.62) [ ???; for charts; see chartsheet? ]
+ - picture (§3.3.1.65) [ BkHim E9h; see XclExpBitmap ]
+ - pivotArea (§3.3.1.66) [ ??? ]
+ - pivotSelection (§3.3.1.67) [ ??? ]
+ - protectedRange (§3.3.1.69) [ ??? ]
+ - protectedRanges (§3.3.1.70) [ ??? ]
+ - sheetCalcPr (§3.3.1.76) [ REFRESHALL?? ]
+ - sheetFormatPr (§3.3.1.78) [ lots of records? ]
+ @defaultColWidth: DefColWidth
+ @defaultRowHeight: DEFROWHEIGHT
+ @baseColWidth: ColInfo/coldx?
+ @customHeight: ColInfo/fUserSet?
+ @zeroHeight: ColInfo/fHidden?
+ @thickTop: ?
+ @thickBottom: ?
+ @outlineLevelRow: ?
+ @outlineLevelCol: ColInfo/iOutLevel?
+ - sheetPr (§3.3.1.80) [ ??? ; for charts ]
+ - sheetView (§3.3.1.84) [ ??? ; for charts ]
+ - sheetViews (§3.3.1.86) [ ??? ; for charts ]
+ - smartTags (§3.3.1.87) [ FEAT 868h; isf=ISFFACTOID ]
+ - sortCondition (§3.3.1.88) [ SortData 895h? ]
+ - sortState (§3.3.1.89) [ Sort 90h ]
+ - tabColor (§3.3.1.90) [ SheetExt 862h ]
+ - tablePart (§3.3.1.91) [ ??? ]
+ - tableParts (§3.3.1.92) [ ??? ]
+ - webPublishItem (§3.3.1.94) [ WebPub 801h ]
+ - webPublishItems (§3.3.1.95)
+ - AutoFilter Settings (§3.3.2):
+ - colorFilter (§3.3.2.1) [ AutoFilter12 87Eh,
+ DXFN12NoCB struct ]
+ - dateGroupItem (§3.3.2.4) [ AutoFilter12 87Eh,
+ AF12DateInfo struct ]
+ - dynamicFilter (§3.3.2.5) [ AutoFilter12 87Eh, cft field ]
+ - filter (§3.3.2.6) [ AutoFilter12 87Eh, rgCriteria? ]
+ - filters (§3.3.2.9) [ AutoFilter12 87Eh, rgCriteria? ]
+ - iconFilter (§3.3.2.9) [ AutoFilter12 87Eh,
+ AF12CellIcon struct ]
+ - Shared String Table (§3.4):
+ - phoneticPr (§3.4.3)
+ - rPh (§3.4.6)
+ - Tables (§3.5.1):
+ - calculatedColumnFormula (§3.5.1.1)
+ [ ??? ]
+ - table (§3.5.1.2) [ ??? ]
+ - tableColumn (§3.5.1.3) [ ??? ]
+ - tableColumns (§3.5.1.4) [ ??? ]
+ - tableStyleInfo (§3.5.1.5) [ ??? ]
+ - totalRowFormula (§3.5.1.6) [ ??? ]
+ - xmlColumnPr (§3.5.1.7) [ ??? ]
+ - Single Cell Tables (§3.5.2):
+ - singleXmlCell (§3.5.2.1) [ ??? ]
+ - singleXmlCells (§3.5.2.2) [ ??? ]
+ - xmlCellPr (§3.5.2.3) [ ??? ]
+ - xmlPr (§3.5.2.4) [ ??? ]
+ - Calculation Chain (§3.6):
+ - c (§3.6.1) [ ??? ]
+ - calcChain (§3.6.2) [ ??? ]
+ - Comments (§3.7):
+ - Note: Excel *requires* that there be a drawing object associated
+ with the comment before it will show it. If you _just_ generate the
+ <comments/> XML part and create a <Relationship/> for it, Excel
+ will NOT display the comment.
+ - As drawing is not currently implemented, comments support is
+ incomplete.
+ - TODO: text formatting. Currently we only write unformatted text
+ into comments?.xml, as I'm not sure how formatted text is handled.
+ - Styles (§3.8):
+ - dxf (§3.8.14): [ DXF 88Dh; unsupported ]
+ - dxfs (§3.8.15): [ DXF 88Dh ]
+ - gradientFill (§3.8.23): [ ??? ]
+ - horizontal (§3.8.24): [ DXF 88Dh fNewBorder, xfprops ]
+ - mruColors (§3.8.28): [ ??? ]
+ - scheme (§3.8.36): [ ??? ]
+ - stop (§3.8.38): [ ??? ]
+ - tableStyle (§3.8.40): [ TableStyle 88Fh; unsupported ]
+ - tableStyleElement (§3.8.41): [ TableStyleElement 890h; unsupported ]
+ - tableStyles (§3.8.42): [ TableStyles 88Eh; unsupported ]
+ - vertical (§3.8.44): [ DXF 88Dh fNewBorder, xfprops ]
+
diff --git a/sc/source/filter/excel/read.cxx b/sc/source/filter/excel/read.cxx
new file mode 100644
index 000000000..cf9465a37
--- /dev/null
+++ b/sc/source/filter/excel/read.cxx
@@ -0,0 +1,1314 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <document.hxx>
+#include <scerrors.hxx>
+#include <fprogressbar.hxx>
+#include <globstr.hrc>
+#include <xlcontent.hxx>
+#include <xltracer.hxx>
+#include <xltable.hxx>
+#include <xihelper.hxx>
+#include <xipage.hxx>
+#include <xiview.hxx>
+#include <xilink.hxx>
+#include <xiname.hxx>
+#include <xlname.hxx>
+#include <xicontent.hxx>
+#include <xiescher.hxx>
+#include <xipivot.hxx>
+#include <xistyle.hxx>
+#include <XclImpChangeTrack.hxx>
+#include <documentimport.hxx>
+
+#include <root.hxx>
+#include <imp_op.hxx>
+#include <excimp8.hxx>
+
+#include <unotools/configmgr.hxx>
+
+#include <memory>
+
+namespace
+{
+ bool TryStartNextRecord(XclImpStream& rIn, std::size_t nProgressBasePos)
+ {
+ bool bValid = true;
+ // i#115255 fdo#40304 BOUNDSHEET doesn't point to a valid
+ // BOF record position. Scan the records manually (from
+ // the BOUNDSHEET position) until we find a BOF. Some 3rd
+ // party Russian programs generate invalid xls docs with
+ // this kind of silliness.
+ if (rIn.PeekRecId(nProgressBasePos) == EXC_ID5_BOF)
+ // BOUNDSHEET points to a valid BOF record. Good.
+ rIn.StartNextRecord(nProgressBasePos);
+ else
+ {
+ while (bValid && rIn.GetRecId() != EXC_ID5_BOF)
+ bValid = rIn.StartNextRecord();
+ }
+ return bValid;
+ }
+}
+
+ErrCode ImportExcel::Read()
+{
+ XclImpPageSettings& rPageSett = GetPageSettings();
+ XclImpTabViewSettings& rTabViewSett = GetTabViewSettings();
+ XclImpPalette& rPal = GetPalette();
+ XclImpFontBuffer& rFontBfr = GetFontBuffer();
+ XclImpNumFmtBuffer& rNumFmtBfr = GetNumFmtBuffer();
+ XclImpXFBuffer& rXFBfr = GetXFBuffer();
+ XclImpNameManager& rNameMgr = GetNameManager();
+ // call to GetCurrSheetDrawing() cannot be cached (changes in new sheets)
+
+ enum STATE {
+ Z_BiffNull, // not a valid Biff-Format
+ Z_Biff2, // Biff2: only one table
+
+ Z_Biff3, // Biff3: only one table
+
+ Z_Biff4, // Biff4: only one table
+ Z_Biff4W, // Biff4 Workbook: Globals
+ Z_Biff4T, // Biff4 Workbook: a table itself
+ Z_Biff4E, // Biff4 Workbook: between tables
+
+ Z_Biff5WPre,// Biff5: Prefetch Workbook
+ Z_Biff5W, // Biff5: Globals
+ Z_Biff5TPre,// Biff5: Prefetch for Shrfmla/Array Formula
+ Z_Biff5T, // Biff5: a table itself
+ Z_Biff5E, // Biff5: between tables
+ Z_Biffn0, // all Biffs: skip table till next EOF
+ Z_End };
+
+ STATE eCurrent = Z_BiffNull, ePrev = Z_BiffNull;
+
+ ErrCode eLastErr = ERRCODE_NONE;
+ sal_uInt16 nOpcode;
+ sal_uInt16 nBofLevel = 0;
+
+ std::unique_ptr< ScfSimpleProgressBar > pProgress( new ScfSimpleProgressBar(
+ aIn.GetSvStreamSize(), GetDocShell(), STR_LOAD_DOC ) );
+
+ /* #i104057# Need to track a base position for progress bar calculation,
+ because sheet substreams may not be in order of sheets. */
+ std::size_t nProgressBasePos = 0;
+ std::size_t nProgressBaseSize = 0;
+
+ for (; eCurrent != Z_End; mnLastRecId = nOpcode)
+ {
+ if( eCurrent == Z_Biff5E )
+ {
+ sal_uInt16 nScTab = GetCurrScTab();
+ if( nScTab < maSheetOffsets.size() )
+ {
+ nProgressBaseSize += (aIn.GetSvStreamPos() - nProgressBasePos);
+ nProgressBasePos = maSheetOffsets[ nScTab ];
+
+ bool bValid = TryStartNextRecord(aIn, nProgressBasePos);
+ if (!bValid)
+ {
+ // Safeguard ourselves from potential infinite loop.
+ eCurrent = Z_End;
+ }
+ }
+ else
+ eCurrent = Z_End;
+ }
+ else
+ aIn.StartNextRecord();
+
+ nOpcode = aIn.GetRecId();
+
+ if( !aIn.IsValid() )
+ {
+ // finalize table if EOF is missing
+ switch( eCurrent )
+ {
+ case Z_Biff2:
+ case Z_Biff3:
+ case Z_Biff4:
+ case Z_Biff4T:
+ case Z_Biff5TPre:
+ case Z_Biff5T:
+ rNumFmtBfr.CreateScFormats();
+ Eof();
+ break;
+ default:;
+ }
+ break;
+ }
+
+ if( eCurrent == Z_End )
+ break;
+
+ if( eCurrent != Z_Biff5TPre && eCurrent != Z_Biff5WPre )
+ pProgress->ProgressAbs( nProgressBaseSize + aIn.GetSvStreamPos() - nProgressBasePos );
+
+ switch( eCurrent )
+ {
+
+ case Z_BiffNull: // ------------------------------- Z_BiffNull -
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF:
+ {
+ // #i23425# don't rely on the record ID, but on the detected BIFF version
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ Bof2();
+ if( pExcRoot->eDateiTyp == Biff2 )
+ {
+ eCurrent = Z_Biff2;
+ NewTable();
+ }
+ break;
+ case EXC_BIFF3:
+ Bof3();
+ if( pExcRoot->eDateiTyp == Biff3 )
+ {
+ eCurrent = Z_Biff3;
+ NewTable();
+ }
+ break;
+ case EXC_BIFF4:
+ Bof4();
+ if( pExcRoot->eDateiTyp == Biff4 )
+ {
+ eCurrent = Z_Biff4;
+ NewTable();
+ }
+ else if( pExcRoot->eDateiTyp == Biff4W )
+ eCurrent = Z_Biff4W;
+ break;
+ case EXC_BIFF5:
+ Bof5();
+ if( pExcRoot->eDateiTyp == Biff5W )
+ {
+ eCurrent = Z_Biff5WPre;
+
+ nBdshtTab = 0;
+
+ aIn.StoreGlobalPosition(); // store position
+ }
+ else if( pExcRoot->eDateiTyp == Biff5 )
+ {
+ // #i62752# possible to have BIFF5 sheet without globals
+ NewTable();
+ eCurrent = Z_Biff5TPre; // Shrfmla Prefetch, Row-Prefetch
+ nBofLevel = 0;
+ aIn.StoreGlobalPosition(); // store position
+ }
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case Z_Biff2: // ---------------------------------- Z_Biff2 -
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case 0x06: Formula25(); break; // FORMULA [ 2 5]
+ case 0x08: Row25(); break; // ROW [ 2 5]
+ case 0x0A: // EOF [ 2345]
+ rNumFmtBfr.CreateScFormats();
+ rNameMgr.ConvertAllTokens();
+ Eof();
+ eCurrent = Z_End;
+ break;
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x18: rNameMgr.ReadName( maStrm ); break;
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x1E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x20: Columndefault(); break; // COLUMNDEFAULT[ 2 ]
+ case 0x21: Array25(); break; // ARRAY [ 2 5]
+ case 0x23: Externname25(); break; // EXTERNNAME [ 2 5]
+ case 0x24: Colwidth(); break; // COLWIDTH [ 2 ]
+ case 0x25: Defrowheight2(); break; // DEFAULTROWHEI[ 2 ]
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29: rPageSett.ReadMargin( maStrm ); break;
+ case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID2_FONT: rFontBfr.ReadFont( maStrm ); break;
+ case EXC_ID_EFONT: rFontBfr.ReadEfont( maStrm ); break;
+ case 0x3E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x43: rXFBfr.ReadXF( maStrm ); break;
+ case 0x44: Ixfe(); break; // IXFE [ 2 ]
+ }
+ }
+ break;
+
+ case Z_Biff3: // ---------------------------------- Z_Biff3 -
+ {
+ switch( nOpcode )
+ {
+ // skip chart substream
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case 0x0A: // EOF [ 2345]
+ rNumFmtBfr.CreateScFormats();
+ rNameMgr.ConvertAllTokens();
+ Eof();
+ eCurrent = Z_End;
+ break;
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x1A:
+ case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break;
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x1E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x22: Rec1904(); break; // 1904 [ 2345]
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29: rPageSett.ReadMargin( maStrm ); break;
+ case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ case 0x0206: Formula3(); break; // FORMULA [ 3 ]
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case 0x0218: rNameMgr.ReadName( maStrm ); break;
+ case 0x0221: Array34(); break; // ARRAY [ 34 ]
+ case 0x0223: break; // EXTERNNAME [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x0231: rFontBfr.ReadFont( maStrm ); break;
+ case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case 0x0243: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ }
+ }
+ break;
+
+ case Z_Biff4: // ---------------------------------- Z_Biff4 -
+ {
+ switch( nOpcode )
+ {
+ // skip chart substream
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case 0x0A: // EOF [ 2345]
+ rNumFmtBfr.CreateScFormats();
+ rNameMgr.ConvertAllTokens();
+ Eof();
+ eCurrent = Z_End;
+ break;
+ case 0x12: SheetProtect(); break; // SHEET PROTECTION
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x1A:
+ case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break;
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x22: Rec1904(); break; // 1904 [ 2345]
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29: rPageSett.ReadMargin( maStrm ); break;
+ case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x55: DefColWidth(); break;
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45]
+ case 0xA1: rPageSett.ReadSetup( maStrm ); break;
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case 0x0218: rNameMgr.ReadName( maStrm ); break;
+ case 0x0221: Array34(); break; // ARRAY [ 34 ]
+ case 0x0223: break; // EXTERNNAME [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x0231: rFontBfr.ReadFont( maStrm ); break;
+ case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case 0x0406: Formula4(); break; // FORMULA [ 4 ]
+ case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x0443: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ }
+ }
+ break;
+
+ case Z_Biff4W: // --------------------------------- Z_Biff4W -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ rNameMgr.ConvertAllTokens();
+ eCurrent = Z_End;
+ break;
+ case 0x12: DocProtect(); break; // PROTECT [ 5]
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x55: DefColWidth(); break;
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x8F: break; // BUNDLEHEADER [ 4 ]
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45]
+ case 0x0218: rNameMgr.ReadName( maStrm ); break;
+ case 0x0223: break; // EXTERNNAME [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x0231: rFontBfr.ReadFont( maStrm ); break;
+ case EXC_ID4_BOF: // BOF [ 4 ]
+ Bof4();
+ if( pExcRoot->eDateiTyp == Biff4 )
+ {
+ eCurrent = Z_Biff4T;
+ NewTable();
+ }
+ else
+ eCurrent = Z_End;
+ break;
+ case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x0443: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ }
+
+ }
+ break;
+
+ case Z_Biff4T: // --------------------------------- Z_Biff4T -
+ {
+ switch( nOpcode )
+ {
+ // skip chart substream
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case 0x0A: // EOF [ 2345]
+ rNameMgr.ConvertAllTokens();
+ Eof();
+ eCurrent = Z_Biff4E;
+ break;
+ case 0x12: SheetProtect(); break; // SHEET PROTECTION
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x1A:
+ case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break;
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x55: DefColWidth(); break;
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x8F: break; // BUNDLEHEADER [ 4 ]
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45]
+ case 0xA1: rPageSett.ReadSetup( maStrm ); break;
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case 0x0218: rNameMgr.ReadName( maStrm ); break;
+ case 0x0221: Array34(); break;
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x0231: rFontBfr.ReadFont( maStrm ); break;
+ case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case 0x0406: Formula4(); break;
+ case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x0443: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ }
+
+ }
+ break;
+
+ case Z_Biff4E: // --------------------------------- Z_Biff4E -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ eCurrent = Z_End;
+ break;
+ case 0x8F: break; // BUNDLEHEADER [ 4 ]
+ case EXC_ID4_BOF: // BOF [ 4 ]
+ Bof4();
+ NewTable();
+ if( pExcRoot->eDateiTyp == Biff4 )
+ {
+ eCurrent = Z_Biff4T;
+ }
+ else
+ {
+ ePrev = eCurrent;
+ eCurrent = Z_Biffn0;
+ }
+ break;
+ }
+
+ }
+ break;
+ case Z_Biff5WPre: // ------------------------------ Z_Biff5WPre -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ eCurrent = Z_Biff5W;
+ aIn.SeekGlobalPosition(); // and back to old position
+ break;
+ case 0x12: DocProtect(); break; // PROTECT [ 5]
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x3D: Window1(); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x85: Boundsheet(); break; // BOUNDSHEET [ 5]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ // PALETTE follows XFs, but already needed while reading the XFs
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ }
+ }
+ break;
+ case Z_Biff5W: // --------------------------------- Z_Biff5W -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ rNumFmtBfr.CreateScFormats();
+ rXFBfr.CreateUserStyles();
+ rNameMgr.ConvertAllTokens();
+ eCurrent = Z_Biff5E;
+ break;
+ case 0x18: rNameMgr.ReadName( maStrm ); break;
+ case 0x1E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x22: Rec1904(); break; // 1904 [ 2345]
+ case 0x31: rFontBfr.ReadFont( maStrm ); break;
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x8D: Hideobj(); break; // HIDEOBJ [ 345]
+ case 0xDE: Olesize(); break;
+ case 0xE0: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ }
+
+ }
+ break;
+
+ case Z_Biff5TPre: // ------------------------------- Z_Biff5Pre -
+ {
+ if (nOpcode == EXC_ID5_BOF)
+ nBofLevel++;
+ else if( (nOpcode == 0x000A) && nBofLevel )
+ nBofLevel--;
+ else if( !nBofLevel ) // don't read chart records
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case 0x08: Row25(); break; // ROW [ 2 5]
+ case 0x0A: // EOF [ 2345]
+ eCurrent = Z_Biff5T;
+ aIn.SeekGlobalPosition(); // and back to old position
+ break;
+ case 0x12: SheetProtect(); break; // SHEET PROTECTION
+ case 0x1A:
+ case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x21: Array25(); break; // ARRAY [ 2 5]
+ case 0x23: Externname25(); break; // EXTERNNAME [ 2 5]
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x55: DefColWidth(); break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345]
+ case 0x81: Wsbool(); break; // WSBOOL [ 2345]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45]
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case 0x0221: Array34(); break; // ARRAY [ 34 ]
+ case 0x0223: break; // EXTERNNAME [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ }
+ }
+ }
+ break;
+
+ case Z_Biff5T: // --------------------------------- Z_Biff5T -
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case EXC_ID2_FORMULA:
+ case EXC_ID3_FORMULA:
+ case EXC_ID4_FORMULA: Formula25(); break;
+ case EXC_ID_SHRFMLA: Shrfmla(); break;
+ case 0x0A: Eof(); eCurrent = Z_Biff5E; break;
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x23: Externname25(); break; // EXTERNNAME [ 2 5]
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29: rPageSett.ReadMargin( maStrm ); break;
+ case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break;
+ case 0x83:
+ case 0x84: rPageSett.ReadCenter( maStrm ); break;
+ case 0xA0: rTabViewSett.ReadScl( maStrm ); break;
+ case 0xA1: rPageSett.ReadSetup( maStrm ); break;
+ case 0xBD: Mulrk(); break; // MULRK [ 5]
+ case 0xBE: Mulblank(); break; // MULBLANK [ 5]
+ case 0xD6: Rstring(); break; // RSTRING [ 5]
+ case 0x00E5: Cellmerging(); break; // #i62300#
+ case 0x0236: TableOp(); break; // TABLE [ 5]
+ case EXC_ID5_BOF: // BOF [ 5]
+ XclTools::SkipSubStream( maStrm );
+ break;
+ }
+
+ }
+ break;
+
+ case Z_Biff5E: // --------------------------------- Z_Biff5E -
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID5_BOF: // BOF [ 5]
+ Bof5();
+ NewTable();
+ switch( pExcRoot->eDateiTyp )
+ {
+ case Biff5:
+ case Biff5M4:
+ eCurrent = Z_Biff5TPre; // Shrfmla Prefetch, Row-Prefetch
+ nBofLevel = 0;
+ aIn.StoreGlobalPosition(); // store position
+ break;
+ case Biff5C: // chart sheet
+ GetCurrSheetDrawing().ReadTabChart( maStrm );
+ Eof();
+ GetTracer().TraceChartOnlySheet();
+ break;
+ case Biff5V:
+ default:
+ rD.SetVisible( GetCurrScTab(), false );
+ ePrev = eCurrent;
+ eCurrent = Z_Biffn0;
+ }
+ OSL_ENSURE( pExcRoot->eDateiTyp != Biff5W,
+ "+ImportExcel::Read(): Doppel-Whopper-Workbook!" );
+
+ break;
+ }
+
+ }
+ break;
+ case Z_Biffn0: // --------------------------------- Z_Biffn0 -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ eCurrent = ePrev;
+ IncCurrScTab();
+ break;
+ }
+
+ }
+ break;
+
+ case Z_End: // ----------------------------------- Z_End -
+ OSL_FAIL( "*ImportExcel::Read(): Not possible state!" );
+ break;
+ default: OSL_FAIL( "-ImportExcel::Read(): state forgotten!" );
+ }
+ }
+
+ if( eLastErr == ERRCODE_NONE )
+ {
+ pProgress.reset();
+
+ GetDocImport().finalize();
+ if (!utl::ConfigManager::IsFuzzing())
+ AdjustRowHeight();
+ PostDocLoad();
+
+ rD.CalcAfterLoad(false);
+
+ const XclImpAddressConverter& rAddrConv = GetAddressConverter();
+ if( rAddrConv.IsTabTruncated() )
+ eLastErr = SCWARN_IMPORT_SHEET_OVERFLOW;
+ else if( bTabTruncated || rAddrConv.IsRowTruncated() )
+ eLastErr = SCWARN_IMPORT_ROW_OVERFLOW;
+ else if( rAddrConv.IsColTruncated() )
+ eLastErr = SCWARN_IMPORT_COLUMN_OVERFLOW;
+ }
+
+ return eLastErr;
+}
+
+ErrCode ImportExcel8::Read()
+{
+#ifdef EXC_INCL_DUMPER
+ {
+ Biff8RecDumper aDumper( GetRoot(), sal_True );
+ if( aDumper.Dump( aIn ) )
+ return ERRCODE_ABORT;
+ }
+#endif
+ // read the entire BIFF8 stream
+ // don't look too close - this stuff seriously needs to be reworked
+
+ XclImpPageSettings& rPageSett = GetPageSettings();
+ XclImpTabViewSettings& rTabViewSett = GetTabViewSettings();
+ XclImpPalette& rPal = GetPalette();
+ XclImpFontBuffer& rFontBfr = GetFontBuffer();
+ XclImpNumFmtBuffer& rNumFmtBfr = GetNumFmtBuffer();
+ XclImpXFBuffer& rXFBfr = GetXFBuffer();
+ XclImpSst& rSst = GetSst();
+ XclImpTabInfo& rTabInfo = GetTabInfo();
+ XclImpNameManager& rNameMgr = GetNameManager();
+ XclImpLinkManager& rLinkMgr = GetLinkManager();
+ XclImpObjectManager& rObjMgr = GetObjectManager();
+ // call to GetCurrSheetDrawing() cannot be cached (changes in new sheets)
+ XclImpCondFormatManager& rCondFmtMgr = GetCondFormatManager();
+ XclImpValidationManager& rValidMgr = GetValidationManager();
+ XclImpPivotTableManager& rPTableMgr = GetPivotTableManager();
+ XclImpWebQueryBuffer& rWQBfr = GetWebQueryBuffer();
+
+ bool bInUserView = false; // true = In USERSVIEW(BEGIN|END) record block.
+
+ enum XclImpReadState
+ {
+ EXC_STATE_BEFORE_GLOBALS, /// Before workbook globals (wait for initial BOF).
+ EXC_STATE_GLOBALS_PRE, /// Prefetch for workbook globals.
+ EXC_STATE_GLOBALS, /// Workbook globals.
+ EXC_STATE_BEFORE_SHEET, /// Before worksheet (wait for new worksheet BOF).
+ EXC_STATE_SHEET_PRE, /// Prefetch for worksheet.
+ EXC_STATE_SHEET, /// Worksheet.
+ EXC_STATE_END /// Stop reading.
+ };
+
+ XclImpReadState eCurrent = EXC_STATE_BEFORE_GLOBALS;
+
+ ErrCode eLastErr = ERRCODE_NONE;
+
+ std::unique_ptr< ScfSimpleProgressBar > pProgress( new ScfSimpleProgressBar(
+ aIn.GetSvStreamSize(), GetDocShell(), STR_LOAD_DOC ) );
+
+ /* #i104057# Need to track a base position for progress bar calculation,
+ because sheet substreams may not be in order of sheets. */
+ std::size_t nProgressBasePos = 0;
+ std::size_t nProgressBaseSize = 0;
+
+ bool bSheetHasCodeName = false;
+
+ std::vector<OUString> aCodeNames;
+ std::vector < SCTAB > nTabsWithNoCodeName;
+
+ sal_uInt16 nRecId = 0;
+
+ for (; eCurrent != EXC_STATE_END; mnLastRecId = nRecId)
+ {
+ if( eCurrent == EXC_STATE_BEFORE_SHEET )
+ {
+ sal_uInt16 nScTab = GetCurrScTab();
+ if( nScTab < maSheetOffsets.size() )
+ {
+ nProgressBaseSize += (maStrm.GetSvStreamPos() - nProgressBasePos);
+ nProgressBasePos = maSheetOffsets[ nScTab ];
+
+ bool bValid = TryStartNextRecord(aIn, nProgressBasePos);
+ if (!bValid)
+ {
+ // Safeguard ourselves from potential infinite loop.
+ eCurrent = EXC_STATE_END;
+ }
+
+ // import only 256 sheets
+ if( nScTab > GetScMaxPos().Tab() )
+ {
+ if( maStrm.GetRecId() != EXC_ID_EOF )
+ XclTools::SkipSubStream( maStrm );
+ // #i29930# show warning box
+ GetAddressConverter().CheckScTab( nScTab );
+ eCurrent = EXC_STATE_END;
+ }
+ else
+ {
+ // #i109800# SHEET record may point to any record inside the
+ // sheet substream
+ bool bIsBof = maStrm.GetRecId() == EXC_ID5_BOF;
+ if( bIsBof )
+ Bof5(); // read the BOF record
+ else
+ pExcRoot->eDateiTyp = Biff8; // on missing BOF, assume a standard worksheet
+ NewTable();
+ switch( pExcRoot->eDateiTyp )
+ {
+ case Biff8: // worksheet
+ case Biff8M4: // macro sheet
+ eCurrent = EXC_STATE_SHEET_PRE; // Shrfmla Prefetch, Row-Prefetch
+ // go to next record
+ if( bIsBof ) maStrm.StartNextRecord();
+ maStrm.StoreGlobalPosition();
+ break;
+ case Biff8C: // chart sheet
+ GetCurrSheetDrawing().ReadTabChart( maStrm );
+ Eof();
+ GetTracer().TraceChartOnlySheet();
+ break;
+ case Biff8W: // workbook
+ OSL_FAIL( "ImportExcel8::Read - double workbook globals" );
+ [[fallthrough]];
+ case Biff8V: // VB module
+ default:
+ // TODO: do not create a sheet in the Calc document
+ rD.SetVisible( nScTab, false );
+ XclTools::SkipSubStream( maStrm );
+ IncCurrScTab();
+ }
+ }
+ }
+ else
+ eCurrent = EXC_STATE_END;
+ }
+ else
+ aIn.StartNextRecord();
+
+ if( !aIn.IsValid() )
+ {
+ // #i63591# finalize table if EOF is missing
+ switch( eCurrent )
+ {
+ case EXC_STATE_SHEET_PRE:
+ eCurrent = EXC_STATE_SHEET;
+ aIn.SeekGlobalPosition();
+ continue; // next iteration in while loop
+ case EXC_STATE_SHEET:
+ Eof();
+ eCurrent = EXC_STATE_END;
+ break;
+ default:
+ eCurrent = EXC_STATE_END;
+ }
+ }
+
+ if( eCurrent == EXC_STATE_END )
+ break;
+
+ if( eCurrent != EXC_STATE_SHEET_PRE && eCurrent != EXC_STATE_GLOBALS_PRE )
+ pProgress->ProgressAbs( nProgressBaseSize + aIn.GetSvStreamPos() - nProgressBasePos );
+
+ nRecId = aIn.GetRecId();
+
+ /* #i39464# Ignore records between USERSVIEWBEGIN and USERSVIEWEND
+ completely (user specific view settings). Otherwise view settings
+ and filters are loaded multiple times, which at least causes
+ problems in auto-filters. */
+ switch( nRecId )
+ {
+ case EXC_ID_USERSVIEWBEGIN:
+ OSL_ENSURE( !bInUserView, "ImportExcel8::Read - nested user view settings" );
+ bInUserView = true;
+ break;
+ case EXC_ID_USERSVIEWEND:
+ OSL_ENSURE( bInUserView, "ImportExcel8::Read - not in user view settings" );
+ bInUserView = false;
+ break;
+ }
+
+ if( !bInUserView ) switch( eCurrent )
+ {
+
+ // before workbook globals: wait for initial workbook globals BOF
+ case EXC_STATE_BEFORE_GLOBALS:
+ {
+ if( nRecId == EXC_ID5_BOF )
+ {
+ OSL_ENSURE( GetBiff() == EXC_BIFF8, "ImportExcel8::Read - wrong BIFF version" );
+ Bof5();
+ if( pExcRoot->eDateiTyp == Biff8W )
+ {
+ eCurrent = EXC_STATE_GLOBALS_PRE;
+ maStrm.StoreGlobalPosition();
+ nBdshtTab = 0;
+ }
+ else if( pExcRoot->eDateiTyp == Biff8 )
+ {
+ // #i62752# possible to have BIFF8 sheet without globals
+ NewTable();
+ eCurrent = EXC_STATE_SHEET_PRE; // Shrfmla Prefetch, Row-Prefetch
+ bSheetHasCodeName = false; // reset
+ aIn.StoreGlobalPosition();
+ }
+ }
+ }
+ break;
+
+ // prefetch for workbook globals
+ case EXC_STATE_GLOBALS_PRE:
+ {
+ switch( nRecId )
+ {
+ case EXC_ID_EOF:
+ case EXC_ID_EXTSST:
+ /* #i56376# evil hack: if EOF for globals is missing,
+ simulate it. This hack works only for the bugdoc
+ given in the issue, where the sheet substreams
+ start directly after the EXTSST record. A future
+ implementation should be more robust against
+ missing EOFs. */
+ if( (nRecId == EXC_ID_EOF) ||
+ ((nRecId == EXC_ID_EXTSST) && (maStrm.GetNextRecId() == EXC_ID5_BOF)) )
+ {
+ eCurrent = EXC_STATE_GLOBALS;
+ aIn.SeekGlobalPosition();
+ }
+ break;
+ case 0x12: DocProtect(); break; // PROTECT [ 5678]
+ case 0x13: DocPassword(); break;
+ case 0x19: WinProtection(); break;
+ case 0x2F: // FILEPASS [ 2345 ]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = EXC_STATE_END;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x3D: Window1(); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345 ]
+ case 0x85: Boundsheet(); break; // BOUNDSHEET [ 5 ]
+ case 0x8C: Country(); break; // COUNTRY [ 345 ]
+
+ // PALETTE follows XFs, but already needed while reading the XFs
+ case EXC_ID_PALETTE: rPal.ReadPalette( maStrm ); break;
+ }
+ }
+ break;
+
+ // workbook globals
+ case EXC_STATE_GLOBALS:
+ {
+ switch( nRecId )
+ {
+ case EXC_ID_EOF:
+ case EXC_ID_EXTSST:
+ /* #i56376# evil hack: if EOF for globals is missing,
+ simulate it. This hack works only for the bugdoc
+ given in the issue, where the sheet substreams
+ start directly after the EXTSST record. A future
+ implementation should be more robust against
+ missing EOFs. */
+ if( (nRecId == EXC_ID_EOF) ||
+ ((nRecId == EXC_ID_EXTSST) && (maStrm.GetNextRecId() == EXC_ID5_BOF)) )
+ {
+ rNumFmtBfr.CreateScFormats();
+ rXFBfr.CreateUserStyles();
+ rPTableMgr.ReadPivotCaches( maStrm );
+ rNameMgr.ConvertAllTokens();
+ eCurrent = EXC_STATE_BEFORE_SHEET;
+ }
+ break;
+ case 0x0E: Precision(); break; // PRECISION
+ case 0x22: Rec1904(); break; // 1904 [ 2345 ]
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x8D: Hideobj(); break; // HIDEOBJ [ 345 ]
+ case 0xD3: SetHasBasic(); break;
+ case 0xDE: Olesize(); break;
+
+ case EXC_ID_CODENAME: ReadCodeName( aIn, true ); break;
+ case EXC_ID_USESELFS: ReadUsesElfs(); break;
+
+ case EXC_ID2_FONT: rFontBfr.ReadFont( maStrm ); break;
+ case EXC_ID4_FORMAT: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case EXC_ID5_XF: rXFBfr.ReadXF( maStrm ); break;
+ case EXC_ID_STYLE: rXFBfr.ReadStyle( maStrm ); break;
+
+ case EXC_ID_SST: rSst.ReadSst( maStrm ); break;
+ case EXC_ID_TABID: rTabInfo.ReadTabid( maStrm ); break;
+ case EXC_ID_NAME: rNameMgr.ReadName( maStrm ); break;
+
+ case EXC_ID_EXTERNSHEET: rLinkMgr.ReadExternsheet( maStrm ); break;
+ case EXC_ID_SUPBOOK: rLinkMgr.ReadSupbook( maStrm ); break;
+ case EXC_ID_XCT: rLinkMgr.ReadXct( maStrm ); break;
+ case EXC_ID_CRN: rLinkMgr.ReadCrn( maStrm ); break;
+ case EXC_ID_EXTERNNAME: rLinkMgr.ReadExternname( maStrm, pFormConv.get() ); break;
+
+ case EXC_ID_MSODRAWINGGROUP:rObjMgr.ReadMsoDrawingGroup( maStrm ); break;
+
+ case EXC_ID_SXIDSTM: rPTableMgr.ReadSxidstm( maStrm ); break;
+ case EXC_ID_SXVS: rPTableMgr.ReadSxvs( maStrm ); break;
+ case EXC_ID_DCONREF: rPTableMgr.ReadDconref( maStrm ); break;
+ case EXC_ID_DCONNAME: rPTableMgr.ReadDConName( maStrm ); break;
+ }
+
+ }
+ break;
+
+ // prefetch for worksheet
+ case EXC_STATE_SHEET_PRE:
+ {
+ switch( nRecId )
+ {
+ // skip chart substream
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID_WINDOW2: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case EXC_ID_SCL: rTabViewSett.ReadScl( maStrm ); break;
+ case EXC_ID_PANE: rTabViewSett.ReadPane( maStrm ); break;
+ case EXC_ID_SELECTION: rTabViewSett.ReadSelection( maStrm ); break;
+
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+
+ case EXC_ID_CODENAME: ReadCodeName( aIn, false ); bSheetHasCodeName = true; break;
+
+ case 0x0A: // EOF [ 2345 ]
+ {
+ eCurrent = EXC_STATE_SHEET;
+ OUString sName;
+ GetDoc().GetName( GetCurrScTab(), sName );
+ if ( !bSheetHasCodeName )
+ {
+ nTabsWithNoCodeName.push_back( GetCurrScTab() );
+ }
+ else
+ {
+ OUString sCodeName;
+ GetDoc().GetCodeName( GetCurrScTab(), sCodeName );
+ aCodeNames.push_back( sCodeName );
+ }
+
+ bSheetHasCodeName = false; // reset
+
+ aIn.SeekGlobalPosition(); // and back to old position
+ break;
+ }
+ case 0x12: SheetProtect(); break;
+ case 0x13: SheetPassword(); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345 ]
+ case 0x55: DefColWidth(); break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345 ]
+ case 0x81: Wsbool(); break; // WSBOOL [ 2345 ]
+ case 0x8C: Country(); break; // COUNTRY [ 345 ]
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45 ]
+ case 0x9B: FilterMode(); break; // FILTERMODE
+ case EXC_ID_AUTOFILTERINFO: AutoFilterInfo(); break;// AUTOFILTERINFO
+ case EXC_ID_AUTOFILTER: AutoFilter(); break; // AUTOFILTER
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case EXC_ID2_ARRAY:
+ case EXC_ID3_ARRAY: Array34(); break; // ARRAY [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345 ]
+ case 0x0867: FeatHdr(); break; // FEATHDR
+ case 0x0868: Feat(); break; // FEAT
+ }
+ }
+ break;
+
+ // worksheet
+ case EXC_STATE_SHEET:
+ {
+ switch( nRecId )
+ {
+ // skip unknown substreams
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID_EOF: Eof(); eCurrent = EXC_STATE_BEFORE_SHEET; break;
+
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case EXC_ID2_FORMULA:
+ case EXC_ID3_FORMULA:
+ case EXC_ID4_FORMULA: Formula25(); break;
+ case EXC_ID_SHRFMLA: Shrfmla(); break;
+ case 0x000C: Calccount(); break; // CALCCOUNT
+ case 0x0010: Delta(); break; // DELTA
+ case 0x0011: Iteration(); break; // ITERATION
+ case 0x007E:
+ case 0x00AE: Scenman(); break; // SCENMAN
+ case 0x00AF: Scenario(); break; // SCENARIO
+ case 0x00BD: Mulrk(); break; // MULRK [ 5 ]
+ case 0x00BE: Mulblank(); break; // MULBLANK [ 5 ]
+ case 0x00D6: Rstring(); break; // RSTRING [ 5 ]
+ case 0x00E5: Cellmerging(); break; // CELLMERGING
+ case 0x00FD: Labelsst(); break; // LABELSST [ 8 ]
+ case 0x0236: TableOp(); break; // TABLE
+
+ case EXC_ID_HORPAGEBREAKS:
+ case EXC_ID_VERPAGEBREAKS: rPageSett.ReadPageBreaks( maStrm ); break;
+ case EXC_ID_HEADER:
+ case EXC_ID_FOOTER: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case EXC_ID_LEFTMARGIN:
+ case EXC_ID_RIGHTMARGIN:
+ case EXC_ID_TOPMARGIN:
+ case EXC_ID_BOTTOMMARGIN: rPageSett.ReadMargin( maStrm ); break;
+ case EXC_ID_PRINTHEADERS: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case EXC_ID_PRINTGRIDLINES: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case EXC_ID_HCENTER:
+ case EXC_ID_VCENTER: rPageSett.ReadCenter( maStrm ); break;
+ case EXC_ID_SETUP: rPageSett.ReadSetup( maStrm ); break;
+ case EXC_ID8_IMGDATA: rPageSett.ReadImgData( maStrm ); break;
+
+ case EXC_ID_MSODRAWING: GetCurrSheetDrawing().ReadMsoDrawing( maStrm ); break;
+ // #i61786# weird documents: OBJ without MSODRAWING -> read in BIFF5 format
+ case EXC_ID_OBJ: GetCurrSheetDrawing().ReadObj( maStrm ); break;
+ case EXC_ID_NOTE: GetCurrSheetDrawing().ReadNote( maStrm ); break;
+
+ case EXC_ID_HLINK: XclImpHyperlink::ReadHlink( maStrm ); break;
+ case EXC_ID_LABELRANGES: XclImpLabelranges::ReadLabelranges( maStrm ); break;
+
+ case EXC_ID_CONDFMT: rCondFmtMgr.ReadCondfmt( maStrm ); break;
+ case EXC_ID_CF: rCondFmtMgr.ReadCF( maStrm ); break;
+
+ case EXC_ID_DVAL: XclImpValidationManager::ReadDval( maStrm ); break;
+ case EXC_ID_DV: rValidMgr.ReadDV( maStrm ); break;
+
+ case EXC_ID_QSI: rWQBfr.ReadQsi( maStrm ); break;
+ case EXC_ID_WQSTRING: rWQBfr.ReadWqstring( maStrm ); break;
+ case EXC_ID_PQRY: rWQBfr.ReadParamqry( maStrm ); break;
+ case EXC_ID_WQSETT: rWQBfr.ReadWqsettings( maStrm ); break;
+ case EXC_ID_WQTABLES: rWQBfr.ReadWqtables( maStrm ); break;
+
+ case EXC_ID_SXVIEW: rPTableMgr.ReadSxview( maStrm ); break;
+ case EXC_ID_SXVD: rPTableMgr.ReadSxvd( maStrm ); break;
+ case EXC_ID_SXVI: rPTableMgr.ReadSxvi( maStrm ); break;
+ case EXC_ID_SXIVD: rPTableMgr.ReadSxivd( maStrm ); break;
+ case EXC_ID_SXPI: rPTableMgr.ReadSxpi( maStrm ); break;
+ case EXC_ID_SXDI: rPTableMgr.ReadSxdi( maStrm ); break;
+ case EXC_ID_SXVDEX: rPTableMgr.ReadSxvdex( maStrm ); break;
+ case EXC_ID_SXEX: rPTableMgr.ReadSxex( maStrm ); break;
+ case EXC_ID_SHEETEXT: rTabViewSett.ReadTabBgColor( maStrm, rPal ); break;
+ case EXC_ID_SXVIEWEX9: rPTableMgr.ReadSxViewEx9( maStrm ); break;
+ case EXC_ID_SXADDL: rPTableMgr.ReadSxAddl( maStrm ); break;
+ }
+ }
+ break;
+
+ default:;
+ }
+ }
+
+ if( eLastErr == ERRCODE_NONE )
+ {
+ // In some strange circumstances the codename might be missing
+ // # Create any missing Sheet CodeNames
+ for ( const auto& rTab : nTabsWithNoCodeName )
+ {
+ SCTAB nTab = 1;
+ while ( true )
+ {
+ OUStringBuffer aBuf;
+ aBuf.append("Sheet");
+ aBuf.append(static_cast<sal_Int32>(nTab++));
+ OUString sTmpName = aBuf.makeStringAndClear();
+
+ if ( std::find(aCodeNames.begin(), aCodeNames.end(), sTmpName) == aCodeNames.end() ) // generated codename not found
+ {
+ // Set new codename
+ GetDoc().SetCodeName( rTab, sTmpName );
+ // Record newly used codename
+ aCodeNames.push_back(sTmpName);
+ break;
+ }
+ }
+ }
+ // #i45843# Convert pivot tables before calculation, so they are available
+ // for the GETPIVOTDATA function.
+ if( GetBiff() == EXC_BIFF8 )
+ GetPivotTableManager().ConvertPivotTables();
+
+ ScDocumentImport& rDoc = GetDocImport();
+ rDoc.finalize();
+ pProgress.reset();
+#if 0
+ // Excel documents look much better without this call; better in the
+ // sense that the row heights are identical to the original heights in
+ // Excel.
+ if ( !rD.IsAdjustHeightLocked())
+ AdjustRowHeight();
+#endif
+ PostDocLoad();
+
+ rD.CalcAfterLoad(false);
+
+ // import change tracking data
+ XclImpChangeTrack aImpChTr( GetRoot(), maStrm );
+ aImpChTr.Apply();
+
+ const XclImpAddressConverter& rAddrConv = GetAddressConverter();
+ if( rAddrConv.IsTabTruncated() )
+ eLastErr = SCWARN_IMPORT_SHEET_OVERFLOW;
+ else if( bTabTruncated || rAddrConv.IsRowTruncated() )
+ eLastErr = SCWARN_IMPORT_ROW_OVERFLOW;
+ else if( rAddrConv.IsColTruncated() )
+ eLastErr = SCWARN_IMPORT_COLUMN_OVERFLOW;
+
+ if( GetBiff() == EXC_BIFF8 )
+ GetPivotTableManager().MaybeRefreshPivotTables();
+ }
+
+ return eLastErr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/tokstack.cxx b/sc/source/filter/excel/tokstack.cxx
new file mode 100644
index 000000000..a0e3974ca
--- /dev/null
+++ b/sc/source/filter/excel/tokstack.cxx
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tokstack.hxx>
+#include <scmatrix.hxx>
+
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+#include <string.h>
+
+const sal_uInt16 TokenPool::nScTokenOff = 8192;
+
+TokenStack::TokenStack( )
+ : pStack( new TokenId[ nSize ] )
+{
+ Reset();
+}
+
+TokenStack::~TokenStack()
+{
+}
+
+// !ATTENTION!": to the outside the numbering starts with 1!
+// !ATTENTION!": SC-Token are stored with an offset nScTokenOff
+// -> to distinguish from other tokens
+
+TokenPool::TokenPool( svl::SharedStringPool& rSPool ) :
+ mrStringPool(rSPool)
+{
+ // pool for Id-sequences
+ nP_Id = 256;
+ pP_Id.reset( new sal_uInt16[ nP_Id ] );
+
+ // pool for Ids
+ nElement = 32;
+ pElement.reset( new sal_uInt16[ nElement ] );
+ pType.reset( new E_TYPE[ nElement ] );
+ pSize.reset( new sal_uInt16[ nElement ] );
+ nP_IdLast = 0;
+
+ nP_Matrix = 16;
+ ppP_Matrix.reset( new ScMatrix*[ nP_Matrix ] );
+ memset( ppP_Matrix.get(), 0, sizeof( ScMatrix* ) * nP_Matrix );
+
+ Reset();
+}
+
+TokenPool::~TokenPool()
+{
+ ClearMatrix();
+}
+
+/** Returns the new number of elements, or 0 if overflow. */
+static sal_uInt16 lcl_canGrow( sal_uInt16 nOld )
+{
+ if (!nOld)
+ return 1;
+ if (nOld == SAL_MAX_UINT16)
+ return 0;
+ sal_uInt32 nNew = ::std::max( static_cast<sal_uInt32>(nOld) * 2,
+ static_cast<sal_uInt32>(nOld) + 1);
+ if (nNew > SAL_MAX_UINT16)
+ nNew = SAL_MAX_UINT16;
+ if (nNew - 1 < nOld)
+ nNew = 0;
+ return static_cast<sal_uInt16>(nNew);
+}
+
+bool TokenPool::GrowId()
+{
+ sal_uInt16 nP_IdNew = lcl_canGrow( nP_Id);
+ if (!nP_IdNew)
+ return false;
+
+ sal_uInt16* pP_IdNew = new (::std::nothrow) sal_uInt16[ nP_IdNew ];
+ if (!pP_IdNew)
+ return false;
+
+ for( sal_uInt16 nL = 0 ; nL < nP_Id ; nL++ )
+ pP_IdNew[ nL ] = pP_Id[ nL ];
+
+ nP_Id = nP_IdNew;
+
+ pP_Id.reset( pP_IdNew );
+ return true;
+}
+
+bool TokenPool::CheckElementOrGrow()
+{
+ // Last possible ID to be assigned somewhere is nElementCurrent+1
+ if (nElementCurrent + 1 == nScTokenOff - 1)
+ {
+ SAL_WARN("sc.filter","TokenPool::CheckElementOrGrow - last possible ID " << nElementCurrent+1);
+ return false;
+ }
+
+ if (nElementCurrent >= nElement)
+ return GrowElement();
+
+ return true;
+}
+
+bool TokenPool::GrowElement()
+{
+ sal_uInt16 nElementNew = lcl_canGrow( nElement);
+ if (!nElementNew)
+ return false;
+
+ std::unique_ptr<sal_uInt16[]> pElementNew(new (::std::nothrow) sal_uInt16[ nElementNew ]);
+ std::unique_ptr<E_TYPE[]> pTypeNew(new (::std::nothrow) E_TYPE[ nElementNew ]);
+ std::unique_ptr<sal_uInt16[]> pSizeNew(new (::std::nothrow) sal_uInt16[ nElementNew ]);
+ if (!pElementNew || !pTypeNew || !pSizeNew)
+ {
+ return false;
+ }
+
+ for( sal_uInt16 nL = 0 ; nL < nElement ; nL++ )
+ {
+ pElementNew[ nL ] = pElement[ nL ];
+ pTypeNew[ nL ] = pType[ nL ];
+ pSizeNew[ nL ] = pSize[ nL ];
+ }
+
+ nElement = nElementNew;
+
+ pElement = std::move( pElementNew );
+ pType = std::move( pTypeNew );
+ pSize = std::move( pSizeNew );
+ return true;
+}
+
+bool TokenPool::GrowMatrix()
+{
+ sal_uInt16 nNewSize = lcl_canGrow( nP_Matrix);
+ if (!nNewSize)
+ return false;
+
+ ScMatrix** ppNew = new (::std::nothrow) ScMatrix*[ nNewSize ];
+ if (!ppNew)
+ return false;
+
+ memset( ppNew, 0, sizeof( ScMatrix* ) * nNewSize );
+ for( sal_uInt16 nL = 0 ; nL < nP_Matrix ; nL++ )
+ ppNew[ nL ] = ppP_Matrix[ nL ];
+
+ ppP_Matrix.reset( ppNew );
+ nP_Matrix = nNewSize;
+ return true;
+}
+
+bool TokenPool::GetElement( const sal_uInt16 nId, ScTokenArray* pScToken )
+{
+ if (nId >= nElementCurrent)
+ {
+ SAL_WARN("sc.filter","TokenPool::GetElement - Id too large, " << nId << " >= " << nElementCurrent);
+ return false;
+ }
+
+ bool bRet = true;
+ if( pType[ nId ] == T_Id )
+ bRet = GetElementRek( nId, pScToken );
+ else
+ {
+ switch( pType[ nId ] )
+ {
+ case T_Str:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ auto* p = ppP_Str.getIfInRange( n );
+ if (p)
+ pScToken->AddString(mrStringPool.intern(**p));
+ else
+ bRet = false;
+ }
+ break;
+ case T_D:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ if (n < pP_Dbl.m_writemark)
+ pScToken->AddDouble( pP_Dbl[ n ] );
+ else
+ bRet = false;
+ }
+ break;
+ case T_Err:
+ break;
+/* TODO: in case we had FormulaTokenArray::AddError() */
+#if 0
+ {
+ sal_uInt16 n = pElement[ nId ];
+ if (n < nP_Err)
+ pScToken->AddError( pP_Err[ n ] );
+ else
+ bRet = false;
+ }
+#endif
+ case T_RefC:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ auto p = ppP_RefTr.getIfInRange(n);
+ if (p)
+ pScToken->AddSingleReference( **p );
+ else
+ bRet = false;
+ }
+ break;
+ case T_RefA:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ if (n < ppP_RefTr.m_writemark && ppP_RefTr[ n ] && n+1 < ppP_RefTr.m_writemark && ppP_RefTr[ n + 1 ])
+ {
+ ScComplexRefData aScComplexRefData;
+ aScComplexRefData.Ref1 = *ppP_RefTr[ n ];
+ aScComplexRefData.Ref2 = *ppP_RefTr[ n + 1 ];
+ pScToken->AddDoubleReference( aScComplexRefData );
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case T_RN:
+ {
+ sal_uInt16 n = pElement[nId];
+ if (n < maRangeNames.size())
+ {
+ const RangeName& r = maRangeNames[n];
+ pScToken->AddRangeName(r.mnIndex, r.mnSheet);
+ }
+ }
+ break;
+ case T_Ext:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ auto p = ppP_Ext.getIfInRange(n);
+
+ if( p )
+ {
+ if( (*p)->eId == ocEuroConvert )
+ pScToken->AddOpCode( (*p)->eId );
+ else
+ pScToken->AddExternal( (*p)->aText, (*p)->eId );
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case T_Nlf:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ auto p = ppP_Nlf.getIfInRange(n);
+
+ if( p )
+ pScToken->AddColRowName( **p );
+ else
+ bRet = false;
+ }
+ break;
+ case T_Matrix:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ ScMatrix* p = ( n < nP_Matrix )? ppP_Matrix[ n ] : nullptr;
+
+ if( p )
+ pScToken->AddMatrix( p );
+ else
+ bRet = false;
+ }
+ break;
+ case T_ExtName:
+ {
+ sal_uInt16 n = pElement[nId];
+ if (n < maExtNames.size())
+ {
+ const ExtName& r = maExtNames[n];
+ pScToken->AddExternalName(r.mnFileId, mrStringPool.intern( r.maName));
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case T_ExtRefC:
+ {
+ sal_uInt16 n = pElement[nId];
+ if (n < maExtCellRefs.size())
+ {
+ const ExtCellRef& r = maExtCellRefs[n];
+ pScToken->AddExternalSingleReference(r.mnFileId, mrStringPool.intern( r.maTabName), r.maRef);
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case T_ExtRefA:
+ {
+ sal_uInt16 n = pElement[nId];
+ if (n < maExtAreaRefs.size())
+ {
+ const ExtAreaRef& r = maExtAreaRefs[n];
+ pScToken->AddExternalDoubleReference(r.mnFileId, mrStringPool.intern( r.maTabName), r.maRef);
+ }
+ else
+ bRet = false;
+ }
+ break;
+ default:
+ OSL_FAIL("-TokenPool::GetElement(): undefined state!?");
+ bRet = false;
+ }
+ }
+ return bRet;
+}
+
+bool TokenPool::GetElementRek( const sal_uInt16 nId, ScTokenArray* pScToken )
+{
+#ifdef DBG_UTIL
+ m_nRek++;
+ OSL_ENSURE(m_nRek <= nP_Id, "*TokenPool::GetElement(): recursion loops!?");
+#endif
+
+ OSL_ENSURE( nId < nElementCurrent, "*TokenPool::GetElementRek(): nId >= nElementCurrent" );
+
+ if (nId >= nElementCurrent)
+ {
+ SAL_WARN("sc.filter", "*TokenPool::GetElementRek(): nId >= nElementCurrent");
+#ifdef DBG_UTIL
+ m_nRek--;
+#endif
+ return false;
+ }
+
+ if (pType[ nId ] != T_Id)
+ {
+ SAL_WARN("sc.filter", "-TokenPool::GetElementRek(): pType[ nId ] != T_Id");
+#ifdef DBG_UTIL
+ m_nRek--;
+#endif
+ return false;
+ }
+
+ bool bRet = true;
+ sal_uInt16 nCnt = pSize[ nId ];
+ sal_uInt16 nFirstId = pElement[ nId ];
+ if (nFirstId >= nP_Id)
+ {
+ SAL_WARN("sc.filter", "TokenPool::GetElementRek: nFirstId >= nP_Id");
+ nCnt = 0;
+ bRet = false;
+ }
+ sal_uInt16* pCurrent = nCnt ? &pP_Id[ nFirstId ] : nullptr;
+ if (nCnt > nP_Id - nFirstId)
+ {
+ SAL_WARN("sc.filter", "TokenPool::GetElementRek: nCnt > nP_Id - nFirstId");
+ nCnt = nP_Id - nFirstId;
+ bRet = false;
+ }
+ for( ; nCnt > 0 ; nCnt--, pCurrent++ )
+ {
+ assert(pCurrent);
+ if( *pCurrent < nScTokenOff )
+ {// recursion or not?
+ if (*pCurrent >= nElementCurrent)
+ {
+ SAL_WARN("sc.filter", "TokenPool::GetElementRek: *pCurrent >= nElementCurrent");
+ bRet = false;
+ }
+ else
+ {
+ if (pType[ *pCurrent ] == T_Id)
+ bRet = GetElementRek( *pCurrent, pScToken );
+ else
+ bRet = GetElement( *pCurrent, pScToken );
+ }
+ }
+ else // elementary SC_Token
+ pScToken->AddOpCode( static_cast<DefTokenId>( *pCurrent - nScTokenOff ) );
+ }
+
+#ifdef DBG_UTIL
+ m_nRek--;
+#endif
+ return bRet;
+}
+
+void TokenPool::operator >>( TokenId& rId )
+{
+ rId = static_cast<TokenId>( nElementCurrent + 1 );
+
+ if (!CheckElementOrGrow())
+ return;
+
+ pElement[ nElementCurrent ] = nP_IdLast; // Start of Token-sequence
+ pType[ nElementCurrent ] = T_Id; // set Typeinfo
+ pSize[ nElementCurrent ] = nP_IdCurrent - nP_IdLast;
+ // write from nP_IdLast to nP_IdCurrent-1 -> length of the sequence
+
+ nElementCurrent++; // start value for next sequence
+ nP_IdLast = nP_IdCurrent;
+}
+
+TokenId TokenPool::Store( const double& rDouble )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( pP_Dbl.m_writemark >= pP_Dbl.m_capacity )
+ if (!pP_Dbl.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = pP_Dbl.m_writemark; // Index in Double-Array
+ pType[ nElementCurrent ] = T_D; // set Typeinfo Double
+
+ pP_Dbl[ pP_Dbl.m_writemark ] = rDouble;
+
+ pSize[ nElementCurrent ] = 1; // does not matter
+
+ nElementCurrent++;
+ pP_Dbl.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::Store( const sal_uInt16 nIndex )
+{
+ return StoreName(nIndex, -1);
+}
+
+TokenId TokenPool::Store( const OUString& rString )
+{
+ // mostly copied to Store( const char* ), to avoid a temporary string
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_Str.m_writemark >= ppP_Str.m_capacity )
+ if (!ppP_Str.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_Str.m_writemark; // Index in String-Array
+ pType[ nElementCurrent ] = T_Str; // set Typeinfo String
+
+ // create String
+ if( !ppP_Str[ ppP_Str.m_writemark ] )
+ //...but only, if it does not exist already
+ ppP_Str[ ppP_Str.m_writemark ].reset( new OUString( rString ) );
+ else
+ //...copy otherwise
+ *ppP_Str[ ppP_Str.m_writemark ] = rString;
+
+ /* attention truncate to 16 bits */
+ pSize[ nElementCurrent ] = static_cast<sal_uInt16>(ppP_Str[ ppP_Str.m_writemark ]->getLength());
+
+ nElementCurrent++;
+ ppP_Str.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::Store( const ScSingleRefData& rTr )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_RefTr.m_writemark >= ppP_RefTr.m_capacity )
+ if (!ppP_RefTr.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_RefTr.m_writemark;
+ pType[ nElementCurrent ] = T_RefC; // set Typeinfo Cell-Ref
+
+ if( !ppP_RefTr[ ppP_RefTr.m_writemark ] )
+ ppP_RefTr[ ppP_RefTr.m_writemark ].reset( new ScSingleRefData( rTr ) );
+ else
+ *ppP_RefTr[ ppP_RefTr.m_writemark ] = rTr;
+
+ nElementCurrent++;
+ ppP_RefTr.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::Store( const ScComplexRefData& rTr )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_RefTr.m_writemark + 1 >= ppP_RefTr.m_capacity )
+ if (!ppP_RefTr.Grow(2))
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_RefTr.m_writemark;
+ pType[ nElementCurrent ] = T_RefA; // setTypeinfo Area-Ref
+
+ if( !ppP_RefTr[ ppP_RefTr.m_writemark ] )
+ ppP_RefTr[ ppP_RefTr.m_writemark ].reset( new ScSingleRefData( rTr.Ref1 ) );
+ else
+ *ppP_RefTr[ ppP_RefTr.m_writemark ] = rTr.Ref1;
+ ppP_RefTr.m_writemark++;
+
+ if( !ppP_RefTr[ ppP_RefTr.m_writemark ] )
+ ppP_RefTr[ ppP_RefTr.m_writemark ].reset( new ScSingleRefData( rTr.Ref2 ) );
+ else
+ *ppP_RefTr[ ppP_RefTr.m_writemark ] = rTr.Ref2;
+ ppP_RefTr.m_writemark++;
+
+ nElementCurrent++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::Store( const DefTokenId e, const OUString& r )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_Ext.m_writemark >= ppP_Ext.m_capacity )
+ if (!ppP_Ext.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_Ext.m_writemark;
+ pType[ nElementCurrent ] = T_Ext; // set Typeinfo String
+
+ if( ppP_Ext[ ppP_Ext.m_writemark ] )
+ {
+ ppP_Ext[ ppP_Ext.m_writemark ]->eId = e;
+ ppP_Ext[ ppP_Ext.m_writemark ]->aText = r;
+ }
+ else
+ ppP_Ext[ ppP_Ext.m_writemark ].reset( new EXTCONT( e, r ) );
+
+ nElementCurrent++;
+ ppP_Ext.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::StoreNlf( const ScSingleRefData& rTr )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_Nlf.m_writemark >= ppP_Nlf.m_capacity )
+ if (!ppP_Nlf.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_Nlf.m_writemark;
+ pType[ nElementCurrent ] = T_Nlf;
+
+ if( ppP_Nlf[ ppP_Nlf.m_writemark ] )
+ {
+ *ppP_Nlf[ ppP_Nlf.m_writemark ] = rTr;
+ }
+ else
+ ppP_Nlf[ ppP_Nlf.m_writemark ].reset( new ScSingleRefData( rTr ) );
+
+ nElementCurrent++;
+ ppP_Nlf.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreMatrix()
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( nP_MatrixCurrent >= nP_Matrix )
+ if (!GrowMatrix())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = nP_MatrixCurrent;
+ pType[ nElementCurrent ] = T_Matrix;
+
+ ScMatrix* pM = new ScMatrix( 0, 0 );
+ pM->IncRef( );
+ ppP_Matrix[ nP_MatrixCurrent ] = pM;
+
+ nElementCurrent++;
+ nP_MatrixCurrent++;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreName( sal_uInt16 nIndex, sal_Int16 nSheet )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[nElementCurrent] = static_cast<sal_uInt16>(maRangeNames.size());
+ pType[nElementCurrent] = T_RN;
+
+ maRangeNames.emplace_back();
+ RangeName& r = maRangeNames.back();
+ r.mnIndex = nIndex;
+ r.mnSheet = nSheet;
+
+ ++nElementCurrent;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreExtName( sal_uInt16 nFileId, const OUString& rName )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[nElementCurrent] = static_cast<sal_uInt16>(maExtNames.size());
+ pType[nElementCurrent] = T_ExtName;
+
+ maExtNames.emplace_back();
+ ExtName& r = maExtNames.back();
+ r.mnFileId = nFileId;
+ r.maName = rName;
+
+ ++nElementCurrent;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreExtRef( sal_uInt16 nFileId, const OUString& rTabName, const ScSingleRefData& rRef )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[nElementCurrent] = static_cast<sal_uInt16>(maExtCellRefs.size());
+ pType[nElementCurrent] = T_ExtRefC;
+
+ maExtCellRefs.emplace_back();
+ ExtCellRef& r = maExtCellRefs.back();
+ r.mnFileId = nFileId;
+ r.maTabName = rTabName;
+ r.maRef = rRef;
+
+ ++nElementCurrent;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreExtRef( sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rRef )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[nElementCurrent] = static_cast<sal_uInt16>(maExtAreaRefs.size());
+ pType[nElementCurrent] = T_ExtRefA;
+
+ maExtAreaRefs.emplace_back();
+ ExtAreaRef& r = maExtAreaRefs.back();
+ r.mnFileId = nFileId;
+ r.maTabName = rTabName;
+ r.maRef = rRef;
+
+ ++nElementCurrent;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+void TokenPool::Reset()
+{
+ nP_IdCurrent = nP_IdLast = nElementCurrent
+ = ppP_Str.m_writemark = pP_Dbl.m_writemark = pP_Err.m_writemark
+ = ppP_RefTr.m_writemark = ppP_Ext.m_writemark = ppP_Nlf.m_writemark = nP_MatrixCurrent = 0;
+ maRangeNames.clear();
+ maExtNames.clear();
+ maExtCellRefs.clear();
+ maExtAreaRefs.clear();
+ ClearMatrix();
+}
+
+bool TokenPool::IsSingleOp( const TokenId& rId, const DefTokenId eId ) const
+{
+ sal_uInt16 nId = static_cast<sal_uInt16>(rId);
+ if( nId && nId <= nElementCurrent )
+ {// existent?
+ nId--;
+ if( T_Id == pType[ nId ] )
+ {// Token-Sequence?
+ if( pSize[ nId ] == 1 )
+ {// EXACTLY 1 Token
+ sal_uInt16 nPid = pElement[ nId ];
+ if (nPid < nP_Id)
+ {
+ sal_uInt16 nSecId = pP_Id[ nPid ];
+ if( nSecId >= nScTokenOff )
+ {// Default-Token?
+ return static_cast<DefTokenId>( nSecId - nScTokenOff ) == eId; // wanted?
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+const OUString* TokenPool::GetExternal( const TokenId& rId ) const
+{
+ const OUString* p = nullptr;
+ sal_uInt16 n = static_cast<sal_uInt16>(rId);
+ if( n && n <= nElementCurrent )
+ {
+ n--;
+ if( pType[ n ] == T_Ext )
+ {
+ sal_uInt16 nExt = pElement[ n ];
+ if ( nExt < ppP_Ext.m_writemark && ppP_Ext[ nExt ] )
+ p = &ppP_Ext[ nExt ]->aText;
+ }
+ }
+
+ return p;
+}
+
+ScMatrix* TokenPool::GetMatrix( unsigned int n ) const
+{
+ if( n < nP_MatrixCurrent )
+ return ppP_Matrix[ n ];
+ else
+ SAL_WARN("sc.filter", "GetMatrix: " << n << " >= " << nP_MatrixCurrent);
+ return nullptr;
+}
+
+void TokenPool::ClearMatrix()
+{
+ for(sal_uInt16 n = 0 ; n < nP_Matrix ; n++ )
+ {
+ if( ppP_Matrix[ n ] )
+ {
+ ppP_Matrix[ n ]->DecRef( );
+ ppP_Matrix[n] = nullptr;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xechart.cxx b/sc/source/filter/excel/xechart.cxx
new file mode 100644
index 000000000..64525457f
--- /dev/null
+++ b/sc/source/filter/excel/xechart.cxx
@@ -0,0 +1,3475 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xechart.hxx>
+
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
+#include <com/sun/star/chart/ChartAxisPosition.hpp>
+#include <com/sun/star/chart/ChartLegendExpansion.hpp>
+#include <com/sun/star/chart/DataLabelPlacement.hpp>
+#include <com/sun/star/chart/ErrorBarStyle.hpp>
+#include <com/sun/star/chart/MissingValueTreatment.hpp>
+#include <com/sun/star/chart/TimeInterval.hpp>
+#include <com/sun/star/chart/TimeUnit.hpp>
+#include <com/sun/star/chart/XAxisSupplier.hpp>
+#include <com/sun/star/chart/XDiagramPositioning.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/XDiagram.hpp>
+#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
+#include <com/sun/star/chart2/XChartTypeContainer.hpp>
+#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
+#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
+#include <com/sun/star/chart2/XTitled.hpp>
+#include <com/sun/star/chart2/XColorScheme.hpp>
+#include <com/sun/star/chart2/data/XDataSource.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/chart2/CurveStyle.hpp>
+#include <com/sun/star/chart2/DataPointGeometry3D.hpp>
+#include <com/sun/star/chart2/DataPointLabel.hpp>
+#include <com/sun/star/chart2/LegendPosition.hpp>
+#include <com/sun/star/chart2/RelativePosition.hpp>
+#include <com/sun/star/chart2/RelativeSize.hpp>
+#include <com/sun/star/chart2/StackingDirection.hpp>
+#include <com/sun/star/chart2/TickmarkStyle.hpp>
+
+#include <tools/gen.hxx>
+#include <filter/msfilter/escherex.hxx>
+
+#include <document.hxx>
+#include <compiler.hxx>
+#include <tokenarray.hxx>
+#include <xeescher.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xepage.hxx>
+#include <xestyle.hxx>
+#include <xltools.hxx>
+
+#include <memory>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::i18n::XBreakIterator;
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::drawing::XShapes;
+
+using ::com::sun::star::chart2::IncrementData;
+using ::com::sun::star::chart2::RelativePosition;
+using ::com::sun::star::chart2::RelativeSize;
+using ::com::sun::star::chart2::ScaleData;
+using ::com::sun::star::chart2::SubIncrement;
+using ::com::sun::star::chart2::XAxis;
+using ::com::sun::star::chart2::XChartDocument;
+using ::com::sun::star::chart2::XChartTypeContainer;
+using ::com::sun::star::chart2::XColorScheme;
+using ::com::sun::star::chart2::XCoordinateSystem;
+using ::com::sun::star::chart2::XCoordinateSystemContainer;
+using ::com::sun::star::chart2::XChartType;
+using ::com::sun::star::chart2::XDataSeries;
+using ::com::sun::star::chart2::XDataSeriesContainer;
+using ::com::sun::star::chart2::XDiagram;
+using ::com::sun::star::chart2::XFormattedString;
+using ::com::sun::star::chart2::XLegend;
+using ::com::sun::star::chart2::XRegressionCurve;
+using ::com::sun::star::chart2::XRegressionCurveContainer;
+using ::com::sun::star::chart2::XTitle;
+using ::com::sun::star::chart2::XTitled;
+
+using ::com::sun::star::chart2::data::XDataSequence;
+using ::com::sun::star::chart2::data::XDataSource;
+using ::com::sun::star::chart2::data::XLabeledDataSequence;
+
+using ::formula::FormulaToken;
+using ::formula::FormulaTokenArrayPlainIterator;
+
+namespace cssc = ::com::sun::star::chart;
+namespace cssc2 = ::com::sun::star::chart2;
+
+// Helpers ====================================================================
+
+namespace {
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclChRectangle& rRect )
+{
+ return rStrm << rRect.mnX << rRect.mnY << rRect.mnWidth << rRect.mnHeight;
+}
+
+void lclSaveRecord( XclExpStream& rStrm, XclExpRecordRef const & xRec )
+{
+ if( xRec )
+ xRec->Save( rStrm );
+}
+
+/** Saves the passed record (group) together with a leading value record. */
+template< typename Type >
+void lclSaveRecord( XclExpStream& rStrm, XclExpRecordRef const & xRec, sal_uInt16 nRecId, Type nValue )
+{
+ if( xRec )
+ {
+ XclExpValueRecord< Type >( nRecId, nValue ).Save( rStrm );
+ xRec->Save( rStrm );
+ }
+}
+
+template<typename ValueType, typename KeyType>
+void lclSaveRecord(XclExpStream& rStrm, ValueType* pRec, sal_uInt16 nRecId, KeyType nValue)
+{
+ if (pRec)
+ {
+ XclExpValueRecord<KeyType>(nRecId, nValue).Save(rStrm);
+ pRec->Save(rStrm);
+ }
+}
+
+void lclWriteChFrBlockRecord( XclExpStream& rStrm, const XclChFrBlock& rFrBlock, bool bBegin )
+{
+ sal_uInt16 nRecId = bBegin ? EXC_ID_CHFRBLOCKBEGIN : EXC_ID_CHFRBLOCKEND;
+ rStrm.StartRecord( nRecId, 12 );
+ rStrm << nRecId << EXC_FUTUREREC_EMPTYFLAGS << rFrBlock.mnType << rFrBlock.mnContext << rFrBlock.mnValue1 << rFrBlock.mnValue2;
+ rStrm.EndRecord();
+}
+
+template< typename Type >
+bool lclIsAutoAnyOrGetValue( Type& rValue, const Any& rAny )
+{
+ return !rAny.hasValue() || !(rAny >>= rValue);
+}
+
+bool lclIsAutoAnyOrGetScaledValue( double& rfValue, const Any& rAny, bool bLogScale )
+{
+ bool bIsAuto = lclIsAutoAnyOrGetValue( rfValue, rAny );
+ if( !bIsAuto && bLogScale )
+ rfValue = log( rfValue ) / log( 10.0 );
+ return bIsAuto;
+}
+
+sal_uInt16 lclGetTimeValue( const XclExpRoot& rRoot, double fSerialDate, sal_uInt16 nTimeUnit )
+{
+ DateTime aDateTime = rRoot.GetDateTimeFromDouble( fSerialDate );
+ switch( nTimeUnit )
+ {
+ case EXC_CHDATERANGE_DAYS:
+ return ::limit_cast< sal_uInt16, double >( fSerialDate, 0, SAL_MAX_UINT16 );
+ case EXC_CHDATERANGE_MONTHS:
+ return ::limit_cast< sal_uInt16, sal_uInt16 >( 12 * (aDateTime.GetYear() - rRoot.GetBaseYear()) + aDateTime.GetMonth() - 1, 0, SAL_MAX_INT16 );
+ case EXC_CHDATERANGE_YEARS:
+ return ::limit_cast< sal_uInt16, sal_uInt16 >( aDateTime.GetYear() - rRoot.GetBaseYear(), 0, SAL_MAX_INT16 );
+ default:
+ OSL_ENSURE( false, "lclGetTimeValue - unexpected time unit" );
+ }
+ return ::limit_cast< sal_uInt16, double >( fSerialDate, 0, SAL_MAX_UINT16 );
+}
+
+bool lclConvertTimeValue( const XclExpRoot& rRoot, sal_uInt16& rnValue, const Any& rAny, sal_uInt16 nTimeUnit )
+{
+ double fSerialDate = 0;
+ bool bAuto = lclIsAutoAnyOrGetValue( fSerialDate, rAny );
+ if( !bAuto )
+ rnValue = lclGetTimeValue( rRoot, fSerialDate, nTimeUnit );
+ return bAuto;
+}
+
+sal_uInt16 lclGetTimeUnit( sal_Int32 nApiTimeUnit )
+{
+ switch( nApiTimeUnit )
+ {
+ case cssc::TimeUnit::DAY: return EXC_CHDATERANGE_DAYS;
+ case cssc::TimeUnit::MONTH: return EXC_CHDATERANGE_MONTHS;
+ case cssc::TimeUnit::YEAR: return EXC_CHDATERANGE_YEARS;
+ default: OSL_ENSURE( false, "lclGetTimeUnit - unexpected time unit" );
+ }
+ return EXC_CHDATERANGE_DAYS;
+}
+
+bool lclConvertTimeInterval( sal_uInt16& rnValue, sal_uInt16& rnTimeUnit, const Any& rAny )
+{
+ cssc::TimeInterval aInterval;
+ bool bAuto = lclIsAutoAnyOrGetValue( aInterval, rAny );
+ if( !bAuto )
+ {
+ rnValue = ::limit_cast< sal_uInt16, sal_Int32 >( aInterval.Number, 1, SAL_MAX_UINT16 );
+ rnTimeUnit = lclGetTimeUnit( aInterval.TimeUnit );
+ }
+ return bAuto;
+}
+
+} // namespace
+
+// Common =====================================================================
+
+/** Stores global data needed in various classes of the Chart export filter. */
+struct XclExpChRootData : public XclChRootData
+{
+ typedef ::std::vector< XclChFrBlock > XclChFrBlockVector;
+
+ XclExpChChart& mrChartData; /// The chart data object.
+ XclChFrBlockVector maWrittenFrBlocks; /// Stack of future record levels already written out.
+ XclChFrBlockVector maUnwrittenFrBlocks; /// Stack of future record levels not yet written out.
+
+ explicit XclExpChRootData( XclExpChChart& rChartData ) : mrChartData( rChartData ) {}
+
+ /** Registers a new future record level. */
+ void RegisterFutureRecBlock( const XclChFrBlock& rFrBlock );
+ /** Initializes the current future record level (writes all unwritten CHFRBLOCKBEGIN records). */
+ void InitializeFutureRecBlock( XclExpStream& rStrm );
+ /** Finalizes the current future record level (writes CHFRBLOCKEND record if needed). */
+ void FinalizeFutureRecBlock( XclExpStream& rStrm );
+};
+
+void XclExpChRootData::RegisterFutureRecBlock( const XclChFrBlock& rFrBlock )
+{
+ maUnwrittenFrBlocks.push_back( rFrBlock );
+}
+
+void XclExpChRootData::InitializeFutureRecBlock( XclExpStream& rStrm )
+{
+ // first call from a future record writes all missing CHFRBLOCKBEGIN records
+ if( maUnwrittenFrBlocks.empty() )
+ return;
+
+ // write the leading CHFRINFO record
+ if( maWrittenFrBlocks.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_CHFRINFO, 20 );
+ rStrm << EXC_ID_CHFRINFO << EXC_FUTUREREC_EMPTYFLAGS << EXC_CHFRINFO_EXCELXP2003 << EXC_CHFRINFO_EXCELXP2003 << sal_uInt16( 3 );
+ rStrm << sal_uInt16( 0x0850 ) << sal_uInt16( 0x085A ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x086A ) << sal_uInt16( 0x086B );
+ rStrm.EndRecord();
+ }
+ // write all unwritten CHFRBLOCKBEGIN records
+ for( const auto& rUnwrittenFrBlock : maUnwrittenFrBlocks )
+ {
+ OSL_ENSURE( rUnwrittenFrBlock.mnType != EXC_CHFRBLOCK_TYPE_UNKNOWN, "XclExpChRootData::InitializeFutureRecBlock - unknown future record block type" );
+ lclWriteChFrBlockRecord( rStrm, rUnwrittenFrBlock, true );
+ }
+ // move all record infos to vector of written blocks
+ maWrittenFrBlocks.insert( maWrittenFrBlocks.end(), maUnwrittenFrBlocks.begin(), maUnwrittenFrBlocks.end() );
+ maUnwrittenFrBlocks.clear();
+}
+
+void XclExpChRootData::FinalizeFutureRecBlock( XclExpStream& rStrm )
+{
+ OSL_ENSURE( !maUnwrittenFrBlocks.empty() || !maWrittenFrBlocks.empty(), "XclExpChRootData::FinalizeFutureRecBlock - no future record level found" );
+ if( !maUnwrittenFrBlocks.empty() )
+ {
+ // no future record has been written, just forget the topmost level
+ maUnwrittenFrBlocks.pop_back();
+ }
+ else if( !maWrittenFrBlocks.empty() )
+ {
+ // write the CHFRBLOCKEND record for the topmost block and delete it
+ lclWriteChFrBlockRecord( rStrm, maWrittenFrBlocks.back(), false );
+ maWrittenFrBlocks.pop_back();
+ }
+}
+
+XclExpChRoot::XclExpChRoot( const XclExpRoot& rRoot, XclExpChChart& rChartData ) :
+ XclExpRoot( rRoot ),
+ mxChData( std::make_shared<XclExpChRootData>( rChartData ) )
+{
+}
+
+XclExpChRoot::~XclExpChRoot()
+{
+}
+
+Reference< XChartDocument > const & XclExpChRoot::GetChartDocument() const
+{
+ return mxChData->mxChartDoc;
+}
+
+XclExpChChart& XclExpChRoot::GetChartData() const
+{
+ return mxChData->mrChartData;
+}
+
+const XclChTypeInfo& XclExpChRoot::GetChartTypeInfo( XclChTypeId eType ) const
+{
+ return mxChData->mxTypeInfoProv->GetTypeInfo( eType );
+}
+
+const XclChTypeInfo& XclExpChRoot::GetChartTypeInfo( std::u16string_view rServiceName ) const
+{
+ return mxChData->mxTypeInfoProv->GetTypeInfoFromService( rServiceName );
+}
+
+const XclChFormatInfo& XclExpChRoot::GetFormatInfo( XclChObjectType eObjType ) const
+{
+ return mxChData->mxFmtInfoProv->GetFormatInfo( eObjType );
+}
+
+void XclExpChRoot::InitConversion( css::uno::Reference< css::chart2::XChartDocument > const & xChartDoc, const tools::Rectangle& rChartRect ) const
+{
+ mxChData->InitConversion( GetRoot(), xChartDoc, rChartRect );
+}
+
+void XclExpChRoot::FinishConversion() const
+{
+ mxChData->FinishConversion();
+}
+
+bool XclExpChRoot::IsSystemColor( const Color& rColor, sal_uInt16 nSysColorIdx ) const
+{
+ XclExpPalette& rPal = GetPalette();
+ return rPal.IsSystemColor( nSysColorIdx ) && (rColor == rPal.GetDefColor( nSysColorIdx ));
+}
+
+void XclExpChRoot::SetSystemColor( Color& rColor, sal_uInt32& rnColorId, sal_uInt16 nSysColorIdx ) const
+{
+ OSL_ENSURE( GetPalette().IsSystemColor( nSysColorIdx ), "XclExpChRoot::SetSystemColor - invalid color index" );
+ rColor = GetPalette().GetDefColor( nSysColorIdx );
+ rnColorId = XclExpPalette::GetColorIdFromIndex( nSysColorIdx );
+}
+
+sal_Int32 XclExpChRoot::CalcChartXFromHmm( sal_Int32 nPosX ) const
+{
+ return ::limit_cast< sal_Int32, double >( (nPosX - mxChData->mnBorderGapX) / mxChData->mfUnitSizeX, 0, EXC_CHART_TOTALUNITS );
+}
+
+sal_Int32 XclExpChRoot::CalcChartYFromHmm( sal_Int32 nPosY ) const
+{
+ return ::limit_cast< sal_Int32, double >( (nPosY - mxChData->mnBorderGapY) / mxChData->mfUnitSizeY, 0, EXC_CHART_TOTALUNITS );
+}
+
+XclChRectangle XclExpChRoot::CalcChartRectFromHmm( const css::awt::Rectangle& rRect ) const
+{
+ XclChRectangle aRect;
+ aRect.mnX = CalcChartXFromHmm( rRect.X );
+ aRect.mnY = CalcChartYFromHmm( rRect.Y );
+ aRect.mnWidth = CalcChartXFromHmm( rRect.Width );
+ aRect.mnHeight = CalcChartYFromHmm( rRect.Height );
+ return aRect;
+}
+
+void XclExpChRoot::ConvertLineFormat( XclChLineFormat& rLineFmt,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().ReadLineProperties(
+ rLineFmt, *mxChData->mxLineDashTable, rPropSet, ePropMode );
+}
+
+bool XclExpChRoot::ConvertAreaFormat( XclChAreaFormat& rAreaFmt,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const
+{
+ return GetChartPropSetHelper().ReadAreaProperties( rAreaFmt, rPropSet, ePropMode );
+}
+
+void XclExpChRoot::ConvertEscherFormat(
+ XclChEscherFormat& rEscherFmt, XclChPicFormat& rPicFmt,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().ReadEscherProperties( rEscherFmt, rPicFmt,
+ *mxChData->mxGradientTable, *mxChData->mxHatchTable, *mxChData->mxBitmapTable, rPropSet, ePropMode );
+}
+
+sal_uInt16 XclExpChRoot::ConvertFont( const ScfPropertySet& rPropSet, sal_Int16 nScript ) const
+{
+ XclFontData aFontData;
+ GetFontPropSetHelper().ReadFontProperties( aFontData, rPropSet, EXC_FONTPROPSET_CHART, nScript );
+ return GetFontBuffer().Insert( aFontData, EXC_COLOR_CHARTTEXT );
+}
+
+sal_uInt16 XclExpChRoot::ConvertPieRotation( const ScfPropertySet& rPropSet )
+{
+ sal_Int32 nApiRot = 0;
+ rPropSet.GetProperty( nApiRot, EXC_CHPROP_STARTINGANGLE );
+ return static_cast< sal_uInt16 >( (450 - (nApiRot % 360)) % 360 );
+}
+
+void XclExpChRoot::RegisterFutureRecBlock( const XclChFrBlock& rFrBlock )
+{
+ mxChData->RegisterFutureRecBlock( rFrBlock );
+}
+
+void XclExpChRoot::InitializeFutureRecBlock( XclExpStream& rStrm )
+{
+ mxChData->InitializeFutureRecBlock( rStrm );
+}
+
+void XclExpChRoot::FinalizeFutureRecBlock( XclExpStream& rStrm )
+{
+ mxChData->FinalizeFutureRecBlock( rStrm );
+}
+
+XclExpChGroupBase::XclExpChGroupBase( const XclExpChRoot& rRoot,
+ sal_uInt16 nFrType, sal_uInt16 nRecId, std::size_t nRecSize ) :
+ XclExpRecord( nRecId, nRecSize ),
+ XclExpChRoot( rRoot ),
+ maFrBlock( nFrType )
+{
+}
+
+XclExpChGroupBase::~XclExpChGroupBase()
+{
+}
+
+void XclExpChGroupBase::Save( XclExpStream& rStrm )
+{
+ // header record
+ XclExpRecord::Save( rStrm );
+ // group records
+ if( !HasSubRecords() )
+ return;
+
+ // register the future record context corresponding to this record group
+ RegisterFutureRecBlock( maFrBlock );
+ // CHBEGIN record
+ XclExpEmptyRecord( EXC_ID_CHBEGIN ).Save( rStrm );
+ // embedded records
+ WriteSubRecords( rStrm );
+ // finalize the future records, must be done before the closing CHEND
+ FinalizeFutureRecBlock( rStrm );
+ // CHEND record
+ XclExpEmptyRecord( EXC_ID_CHEND ).Save( rStrm );
+}
+
+bool XclExpChGroupBase::HasSubRecords() const
+{
+ return true;
+}
+
+void XclExpChGroupBase::SetFutureRecordContext( sal_uInt16 nFrContext, sal_uInt16 nFrValue1, sal_uInt16 nFrValue2 )
+{
+ maFrBlock.mnContext = nFrContext;
+ maFrBlock.mnValue1 = nFrValue1;
+ maFrBlock.mnValue2 = nFrValue2;
+}
+
+XclExpChFutureRecordBase::XclExpChFutureRecordBase( const XclExpChRoot& rRoot,
+ XclFutureRecType eRecType, sal_uInt16 nRecId, std::size_t nRecSize ) :
+ XclExpFutureRecord( eRecType, nRecId, nRecSize ),
+ XclExpChRoot( rRoot )
+{
+}
+
+void XclExpChFutureRecordBase::Save( XclExpStream& rStrm )
+{
+ InitializeFutureRecBlock( rStrm );
+ XclExpFutureRecord::Save( rStrm );
+}
+
+// Frame formatting ===========================================================
+
+XclExpChFramePos::XclExpChFramePos( sal_uInt16 nTLMode ) :
+ XclExpRecord( EXC_ID_CHFRAMEPOS, 20 )
+{
+ maData.mnTLMode = nTLMode;
+ maData.mnBRMode = EXC_CHFRAMEPOS_PARENT;
+}
+
+void XclExpChFramePos::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnTLMode << maData.mnBRMode << maData.maRect;
+}
+
+XclExpChLineFormat::XclExpChLineFormat( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHLINEFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 12 : 10 ),
+ mnColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
+{
+}
+
+void XclExpChLineFormat::SetDefault( XclChFrameType eDefFrameType )
+{
+ switch( eDefFrameType )
+ {
+ case EXC_CHFRAMETYPE_AUTO:
+ SetAuto( true );
+ break;
+ case EXC_CHFRAMETYPE_INVISIBLE:
+ SetAuto( false );
+ maData.mnPattern = EXC_CHLINEFORMAT_NONE;
+ break;
+ default:
+ OSL_FAIL( "XclExpChLineFormat::SetDefault - unknown frame type" );
+ }
+}
+
+void XclExpChLineFormat::Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ rRoot.ConvertLineFormat( maData, rPropSet, rFmtInfo.mePropMode );
+ if( HasLine() )
+ {
+ // detect system color, set color identifier (TODO: detect automatic series line)
+ if( (eObjType != EXC_CHOBJTYPE_LINEARSERIES) && rRoot.IsSystemColor( maData.maColor, rFmtInfo.mnAutoLineColorIdx ) )
+ {
+ // store color index from automatic format data
+ mnColorId = XclExpPalette::GetColorIdFromIndex( rFmtInfo.mnAutoLineColorIdx );
+ // try to set automatic mode
+ bool bAuto = (maData.mnPattern == EXC_CHLINEFORMAT_SOLID) && (maData.mnWeight == rFmtInfo.mnAutoLineWeight);
+ ::set_flag( maData.mnFlags, EXC_CHLINEFORMAT_AUTO, bAuto );
+ }
+ else
+ {
+ // user defined color - register in palette
+ mnColorId = rRoot.GetPalette().InsertColor( maData.maColor, EXC_COLOR_CHARTLINE );
+ }
+ }
+ else
+ {
+ // no line - set default system color
+ rRoot.SetSystemColor( maData.maColor, mnColorId, EXC_COLOR_CHWINDOWTEXT );
+ }
+}
+
+bool XclExpChLineFormat::IsDefault( XclChFrameType eDefFrameType ) const
+{
+ return
+ ((eDefFrameType == EXC_CHFRAMETYPE_INVISIBLE) && !HasLine()) ||
+ ((eDefFrameType == EXC_CHFRAMETYPE_AUTO) && IsAuto());
+}
+
+void XclExpChLineFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maColor << maData.mnPattern << maData.mnWeight << maData.mnFlags;
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ rStrm << rStrm.GetRoot().GetPalette().GetColorIndex( mnColorId );
+}
+
+namespace {
+
+/** Creates a CHLINEFORMAT record from the passed property set. */
+XclExpChLineFormatRef lclCreateLineFormat( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ XclExpChLineFormatRef xLineFmt = new XclExpChLineFormat( rRoot );
+ xLineFmt->Convert( rRoot, rPropSet, eObjType );
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ if( rFmtInfo.mbDeleteDefFrame && xLineFmt->IsDefault( rFmtInfo.meDefFrameType ) )
+ xLineFmt.clear();
+ return xLineFmt;
+}
+
+} // namespace
+
+XclExpChAreaFormat::XclExpChAreaFormat( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHAREAFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 16 : 12 ),
+ mnPattColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ),
+ mnBackColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
+{
+}
+
+bool XclExpChAreaFormat::Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ bool bComplexFill = rRoot.ConvertAreaFormat( maData, rPropSet, rFmtInfo.mePropMode );
+ if( HasArea() )
+ {
+ bool bSolid = maData.mnPattern == EXC_PATT_SOLID;
+ // detect system color, set color identifier (TODO: detect automatic series area)
+ if( (eObjType != EXC_CHOBJTYPE_FILLEDSERIES) && rRoot.IsSystemColor( maData.maPattColor, rFmtInfo.mnAutoPattColorIdx ) )
+ {
+ // store color index from automatic format data
+ mnPattColorId = XclExpPalette::GetColorIdFromIndex( rFmtInfo.mnAutoPattColorIdx );
+ // set automatic mode
+ ::set_flag( maData.mnFlags, EXC_CHAREAFORMAT_AUTO, bSolid );
+ }
+ else
+ {
+ // user defined color - register color in palette
+ mnPattColorId = rRoot.GetPalette().InsertColor( maData.maPattColor, EXC_COLOR_CHARTAREA );
+ }
+ // background color (default system color for solid fills)
+ if( bSolid )
+ rRoot.SetSystemColor( maData.maBackColor, mnBackColorId, EXC_COLOR_CHWINDOWTEXT );
+ else
+ mnBackColorId = rRoot.GetPalette().InsertColor( maData.maBackColor, EXC_COLOR_CHARTAREA );
+ }
+ else
+ {
+ // no area - set default system colors
+ rRoot.SetSystemColor( maData.maPattColor, mnPattColorId, EXC_COLOR_CHWINDOWBACK );
+ rRoot.SetSystemColor( maData.maBackColor, mnBackColorId, EXC_COLOR_CHWINDOWTEXT );
+ }
+ return bComplexFill;
+}
+
+void XclExpChAreaFormat::SetDefault( XclChFrameType eDefFrameType )
+{
+ switch( eDefFrameType )
+ {
+ case EXC_CHFRAMETYPE_AUTO:
+ SetAuto( true );
+ break;
+ case EXC_CHFRAMETYPE_INVISIBLE:
+ SetAuto( false );
+ maData.mnPattern = EXC_PATT_NONE;
+ break;
+ default:
+ OSL_FAIL( "XclExpChAreaFormat::SetDefault - unknown frame type" );
+ }
+}
+
+bool XclExpChAreaFormat::IsDefault( XclChFrameType eDefFrameType ) const
+{
+ return
+ ((eDefFrameType == EXC_CHFRAMETYPE_INVISIBLE) && !HasArea()) ||
+ ((eDefFrameType == EXC_CHFRAMETYPE_AUTO) && IsAuto());
+}
+
+void XclExpChAreaFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maPattColor << maData.maBackColor << maData.mnPattern << maData.mnFlags;
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ {
+ const XclExpPalette& rPal = rStrm.GetRoot().GetPalette();
+ rStrm << rPal.GetColorIndex( mnPattColorId ) << rPal.GetColorIndex( mnBackColorId );
+ }
+}
+
+XclExpChEscherFormat::XclExpChEscherFormat( const XclExpChRoot& rRoot ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_UNKNOWN, EXC_ID_CHESCHERFORMAT ),
+ mnColor1Id( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ),
+ mnColor2Id( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
+}
+
+void XclExpChEscherFormat::Convert( const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ const XclChFormatInfo& rFmtInfo = GetFormatInfo( eObjType );
+ ConvertEscherFormat( maData, maPicFmt, rPropSet, rFmtInfo.mePropMode );
+ // register colors in palette
+ mnColor1Id = RegisterColor( ESCHER_Prop_fillColor );
+ mnColor2Id = RegisterColor( ESCHER_Prop_fillBackColor );
+}
+
+bool XclExpChEscherFormat::IsValid() const
+{
+ return static_cast< bool >(maData.mxEscherSet);
+}
+
+void XclExpChEscherFormat::Save( XclExpStream& rStrm )
+{
+ if( maData.mxEscherSet )
+ {
+ // replace RGB colors with palette indexes in the Escher container
+ const XclExpPalette& rPal = GetPalette();
+ maData.mxEscherSet->AddOpt( ESCHER_Prop_fillColor, 0x08000000 | rPal.GetColorIndex( mnColor1Id ) );
+ maData.mxEscherSet->AddOpt( ESCHER_Prop_fillBackColor, 0x08000000 | rPal.GetColorIndex( mnColor2Id ) );
+
+ // save the record group
+ XclExpChGroupBase::Save( rStrm );
+ }
+}
+
+bool XclExpChEscherFormat::HasSubRecords() const
+{
+ // no subrecords for gradients
+ return maPicFmt.mnBmpMode != EXC_CHPICFORMAT_NONE;
+}
+
+void XclExpChEscherFormat::WriteSubRecords( XclExpStream& rStrm )
+{
+ rStrm.StartRecord( EXC_ID_CHPICFORMAT, 14 );
+ rStrm << maPicFmt.mnBmpMode << sal_uInt16( 0 ) << maPicFmt.mnFlags << maPicFmt.mfScale;
+ rStrm.EndRecord();
+}
+
+sal_uInt32 XclExpChEscherFormat::RegisterColor( sal_uInt16 nPropId )
+{
+ sal_uInt32 nBGRValue;
+ if( maData.mxEscherSet && maData.mxEscherSet->GetOpt( nPropId, nBGRValue ) )
+ {
+ // swap red and blue
+ Color aColor( nBGRValue & 0xff, (nBGRValue >> 8) & 0xff, (nBGRValue >> 16) & 0xff );
+ return GetPalette().InsertColor( aColor, EXC_COLOR_CHARTAREA );
+ }
+ return XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK );
+}
+
+void XclExpChEscherFormat::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE( maData.mxEscherSet, "XclExpChEscherFormat::WriteBody - missing property container" );
+ // write Escher property container via temporary memory stream
+ SvMemoryStream aMemStrm;
+ maData.mxEscherSet->Commit( aMemStrm );
+ aMemStrm.FlushBuffer();
+ aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ rStrm.CopyFromStream( aMemStrm );
+}
+
+XclExpChFrameBase::XclExpChFrameBase()
+{
+}
+
+XclExpChFrameBase::~XclExpChFrameBase()
+{
+}
+
+void XclExpChFrameBase::ConvertFrameBase( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ // line format
+ mxLineFmt = new XclExpChLineFormat( rRoot );
+ mxLineFmt->Convert( rRoot, rPropSet, eObjType );
+ // area format (only for frame objects)
+ if( !rRoot.GetFormatInfo( eObjType ).mbIsFrame )
+ return;
+
+ mxAreaFmt = new XclExpChAreaFormat( rRoot );
+ bool bComplexFill = mxAreaFmt->Convert( rRoot, rPropSet, eObjType );
+ if( (rRoot.GetBiff() == EXC_BIFF8) && bComplexFill )
+ {
+ mxEscherFmt = new XclExpChEscherFormat( rRoot );
+ mxEscherFmt->Convert( rPropSet, eObjType );
+ if( mxEscherFmt->IsValid() )
+ mxAreaFmt->SetAuto( false );
+ else
+ mxEscherFmt.clear();
+ }
+}
+
+void XclExpChFrameBase::SetDefaultFrameBase( const XclExpChRoot& rRoot,
+ XclChFrameType eDefFrameType, bool bIsFrame )
+{
+ // line format
+ mxLineFmt = new XclExpChLineFormat( rRoot );
+ mxLineFmt->SetDefault( eDefFrameType );
+ // area format (only for frame objects)
+ if( bIsFrame )
+ {
+ mxAreaFmt = new XclExpChAreaFormat( rRoot );
+ mxAreaFmt->SetDefault( eDefFrameType );
+ mxEscherFmt.clear();
+ }
+}
+
+bool XclExpChFrameBase::IsDefaultFrameBase( XclChFrameType eDefFrameType ) const
+{
+ return
+ (!mxLineFmt || mxLineFmt->IsDefault( eDefFrameType )) &&
+ (!mxAreaFmt || mxAreaFmt->IsDefault( eDefFrameType ));
+}
+
+void XclExpChFrameBase::WriteFrameRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxLineFmt );
+ lclSaveRecord( rStrm, mxAreaFmt );
+ lclSaveRecord( rStrm, mxEscherFmt );
+}
+
+XclExpChFrame::XclExpChFrame( const XclExpChRoot& rRoot, XclChObjectType eObjType ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_FRAME, EXC_ID_CHFRAME, 4 ),
+ meObjType( eObjType )
+{
+}
+
+void XclExpChFrame::Convert( const ScfPropertySet& rPropSet )
+{
+ ConvertFrameBase( GetChRoot(), rPropSet, meObjType );
+}
+
+void XclExpChFrame::SetAutoFlags( bool bAutoPos, bool bAutoSize )
+{
+ ::set_flag( maData.mnFlags, EXC_CHFRAME_AUTOPOS, bAutoPos );
+ ::set_flag( maData.mnFlags, EXC_CHFRAME_AUTOSIZE, bAutoSize );
+}
+
+bool XclExpChFrame::IsDefault() const
+{
+ return IsDefaultFrameBase( GetFormatInfo( meObjType ).meDefFrameType );
+}
+
+bool XclExpChFrame::IsDeleteable() const
+{
+ return IsDefault() && GetFormatInfo( meObjType ).mbDeleteDefFrame;
+}
+
+void XclExpChFrame::Save( XclExpStream& rStrm )
+{
+ switch( meObjType )
+ {
+ // wall/floor frame without CHFRAME header record
+ case EXC_CHOBJTYPE_WALL3D:
+ case EXC_CHOBJTYPE_FLOOR3D:
+ WriteFrameRecords( rStrm );
+ break;
+ default:
+ XclExpChGroupBase::Save( rStrm );
+ }
+}
+
+void XclExpChFrame::WriteSubRecords( XclExpStream& rStrm )
+{
+ WriteFrameRecords( rStrm );
+}
+
+void XclExpChFrame::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnFormat << maData.mnFlags;
+}
+
+namespace {
+
+/** Creates a CHFRAME record from the passed property set. */
+XclExpChFrameRef lclCreateFrame( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ XclExpChFrameRef xFrame = new XclExpChFrame( rRoot, eObjType );
+ xFrame->Convert( rPropSet );
+ if( xFrame->IsDeleteable() )
+ xFrame.clear();
+ return xFrame;
+}
+
+} // namespace
+
+// Source links ===============================================================
+
+namespace {
+
+void lclAddDoubleRefData(
+ ScTokenArray& orArray, const FormulaToken& rToken,
+ SCTAB nScTab1, SCCOL nScCol1, SCROW nScRow1,
+ SCTAB nScTab2, SCCOL nScCol2, SCROW nScRow2 )
+{
+ ScComplexRefData aComplexRef;
+ aComplexRef.InitRange(ScRange(nScCol1,nScRow1,nScTab1,nScCol2,nScRow2,nScTab2));
+ aComplexRef.Ref1.SetFlag3D( true );
+
+ if( orArray.GetLen() > 0 )
+ orArray.AddOpCode( ocUnion );
+
+ OSL_ENSURE( (rToken.GetType() == ::formula::svDoubleRef) || (rToken.GetType() == ::formula::svExternalDoubleRef),
+ "lclAddDoubleRefData - double reference token expected");
+ if( rToken.GetType() == ::formula::svExternalDoubleRef )
+ orArray.AddExternalDoubleReference(
+ rToken.GetIndex(), rToken.GetString(), aComplexRef);
+ else
+ orArray.AddDoubleReference( aComplexRef );
+}
+
+} // namespace
+
+XclExpChSourceLink::XclExpChSourceLink( const XclExpChRoot& rRoot, sal_uInt8 nDestType ) :
+ XclExpRecord( EXC_ID_CHSOURCELINK ),
+ XclExpChRoot( rRoot )
+{
+ maData.mnDestType = nDestType;
+ maData.mnLinkType = EXC_CHSRCLINK_DIRECTLY;
+}
+
+sal_uInt16 XclExpChSourceLink::ConvertDataSequence( Reference< XDataSequence > const & xDataSeq, bool bSplitToColumns, sal_uInt16 nDefCount )
+{
+ mxLinkFmla.reset();
+ maData.mnLinkType = EXC_CHSRCLINK_DEFAULT;
+
+ if( !xDataSeq.is() )
+ return nDefCount;
+
+ // Compile the range representation string into token array. Note that the
+ // source range text depends on the current grammar.
+ OUString aRangeRepr = xDataSeq->getSourceRangeRepresentation();
+ ScCompiler aComp( GetDoc(), ScAddress(), GetDoc().GetGrammar() );
+ std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(aRangeRepr));
+ if( !pArray )
+ return nDefCount;
+
+ ScTokenArray aArray(GetRoot().GetDoc());
+ sal_uInt32 nValueCount = 0;
+ FormulaTokenArrayPlainIterator aIter(*pArray);
+ for( const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next() )
+ {
+ switch( pToken->GetType() )
+ {
+ case ::formula::svSingleRef:
+ case ::formula::svExternalSingleRef:
+ // for a single ref token, just add it to the new token array as is
+ if( aArray.GetLen() > 0 )
+ aArray.AddOpCode( ocUnion );
+ aArray.AddToken( *pToken );
+ ++nValueCount;
+ break;
+
+ case ::formula::svDoubleRef:
+ case ::formula::svExternalDoubleRef:
+ {
+ // split 3-dimensional ranges into single sheets
+ const ScComplexRefData& rComplexRef = *pToken->GetDoubleRef();
+ ScAddress aAbs1 = rComplexRef.Ref1.toAbs(GetRoot().GetDoc(), ScAddress());
+ ScAddress aAbs2 = rComplexRef.Ref2.toAbs(GetRoot().GetDoc(), ScAddress());
+ for (SCTAB nScTab = aAbs1.Tab(); nScTab <= aAbs2.Tab(); ++nScTab)
+ {
+ // split 2-dimensional ranges into single columns
+ if (bSplitToColumns && (aAbs1.Col() < aAbs2.Col()) && (aAbs1.Row() < aAbs2.Row()))
+ for (SCCOL nScCol = aAbs1.Col(); nScCol <= aAbs2.Col(); ++nScCol)
+ lclAddDoubleRefData(aArray, *pToken, nScTab, nScCol, aAbs1.Row(), nScTab, nScCol, aAbs2.Row());
+ else
+ lclAddDoubleRefData(aArray, *pToken, nScTab, aAbs1.Col(), aAbs1.Row(), nScTab, aAbs2.Col(), aAbs2.Row());
+ }
+ sal_uInt32 nTabs = static_cast<sal_uInt32>(aAbs2.Tab() - aAbs1.Tab() + 1);
+ sal_uInt32 nCols = static_cast<sal_uInt32>(aAbs2.Col() - aAbs1.Col() + 1);
+ sal_uInt32 nRows = static_cast<sal_uInt32>(aAbs2.Row() - aAbs1.Row() + 1);
+ nValueCount += nCols * nRows * nTabs;
+ }
+ break;
+
+ default:;
+ }
+ }
+
+ const ScAddress aBaseCell;
+ mxLinkFmla = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CHART, aArray, &aBaseCell );
+ maData.mnLinkType = EXC_CHSRCLINK_WORKSHEET;
+ return ulimit_cast< sal_uInt16 >( nValueCount, EXC_CHDATAFORMAT_MAXPOINTCOUNT );
+}
+
+void XclExpChSourceLink::ConvertString( const OUString& aString )
+{
+ mxString = XclExpStringHelper::CreateString( GetRoot(), aString, XclStrFlags::ForceUnicode | XclStrFlags::EightBitLength | XclStrFlags::SeparateFormats );
+}
+
+sal_uInt16 XclExpChSourceLink::ConvertStringSequence( const Sequence< Reference< XFormattedString > >& rStringSeq )
+{
+ mxString.reset();
+ sal_uInt16 nFontIdx = EXC_FONT_APP;
+ if( rStringSeq.hasElements() )
+ {
+ mxString = XclExpStringHelper::CreateString( GetRoot(), OUString(), XclStrFlags::ForceUnicode | XclStrFlags::EightBitLength | XclStrFlags::SeparateFormats );
+ Reference< XBreakIterator > xBreakIt = GetDoc().GetBreakIterator();
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+
+ // convert all formatted string entries from the sequence
+ for( const Reference< XFormattedString >& rString : rStringSeq )
+ {
+ if( rString.is() )
+ {
+ sal_uInt16 nWstrnFontIdx = EXC_FONT_NOTFOUND;
+ sal_uInt16 nAsianFontIdx = EXC_FONT_NOTFOUND;
+ sal_uInt16 nCmplxFontIdx = EXC_FONT_NOTFOUND;
+ OUString aText = rString->getString();
+ ScfPropertySet aStrProp( rString );
+
+ // #i63255# get script type for leading weak characters
+ sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( GetRoot(), aText );
+
+ // process all script portions
+ sal_Int32 nPortionPos = 0;
+ sal_Int32 nTextLen = aText.getLength();
+ while( nPortionPos < nTextLen )
+ {
+ // get script type and end position of next script portion
+ sal_Int16 nScript = xBreakIt->getScriptType( aText, nPortionPos );
+ sal_Int32 nPortionEnd = xBreakIt->endOfScript( aText, nPortionPos, nScript );
+
+ // reuse previous script for following weak portions
+ if( nScript == ApiScriptType::WEAK )
+ nScript = nLastScript;
+
+ // Excel start position of this portion
+ sal_uInt16 nXclPortionStart = mxString->Len();
+ // add portion text to Excel string
+ XclExpStringHelper::AppendString( *mxString, GetRoot(), aText.subView( nPortionPos, nPortionEnd - nPortionPos ) );
+ if( nXclPortionStart < mxString->Len() )
+ {
+ // find font index variable dependent on script type
+ sal_uInt16& rnFontIdx = (nScript == ApiScriptType::COMPLEX) ? nCmplxFontIdx :
+ ((nScript == ApiScriptType::ASIAN) ? nAsianFontIdx : nWstrnFontIdx);
+
+ // insert font into buffer (if not yet done)
+ if( rnFontIdx == EXC_FONT_NOTFOUND )
+ rnFontIdx = ConvertFont( aStrProp, nScript );
+
+ // insert font index into format run vector
+ mxString->AppendFormat( nXclPortionStart, rnFontIdx );
+ }
+
+ // go to next script portion
+ nLastScript = nScript;
+ nPortionPos = nPortionEnd;
+ }
+ }
+ }
+ if( !mxString->IsEmpty() )
+ {
+ // get leading font index
+ const XclFormatRunVec& rFormats = mxString->GetFormats();
+ OSL_ENSURE( !rFormats.empty() && (rFormats.front().mnChar == 0),
+ "XclExpChSourceLink::ConvertStringSequenc - missing leading format" );
+ // remove leading format run, if entire string is equally formatted
+ if( rFormats.size() == 1 )
+ nFontIdx = mxString->RemoveLeadingFont();
+ else if( !rFormats.empty() )
+ nFontIdx = rFormats.front().mnFontIdx;
+ // add trailing format run, if string is rich-formatted
+ if( mxString->IsRich() )
+ mxString->AppendTrailingFormat( EXC_FONT_APP );
+ }
+ }
+ return nFontIdx;
+}
+
+void XclExpChSourceLink::ConvertNumFmt( const ScfPropertySet& rPropSet, bool bPercent )
+{
+ sal_Int32 nApiNumFmt = 0;
+ if( bPercent ? rPropSet.GetProperty( nApiNumFmt, EXC_CHPROP_PERCENTAGENUMFMT ) : rPropSet.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) )
+ {
+ ::set_flag( maData.mnFlags, EXC_CHSRCLINK_NUMFMT );
+ maData.mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) );
+ }
+}
+
+void XclExpChSourceLink::AppendString( std::u16string_view rStr )
+{
+ if (!mxString)
+ return;
+ XclExpStringHelper::AppendString( *mxString, GetRoot(), rStr );
+}
+
+void XclExpChSourceLink::Save( XclExpStream& rStrm )
+{
+ // CHFORMATRUNS record
+ if( mxString && mxString->IsRich() )
+ {
+ std::size_t nRecSize = (1 + mxString->GetFormatsCount()) * ((GetBiff() == EXC_BIFF8) ? 2 : 1);
+ rStrm.StartRecord( EXC_ID_CHFORMATRUNS, nRecSize );
+ mxString->WriteFormats( rStrm, true );
+ rStrm.EndRecord();
+ }
+ // CHSOURCELINK record
+ XclExpRecord::Save( rStrm );
+ // CHSTRING record
+ if( mxString && !mxString->IsEmpty() )
+ {
+ rStrm.StartRecord( EXC_ID_CHSTRING, 2 + mxString->GetSize() );
+ rStrm << sal_uInt16( 0 ) << *mxString;
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpChSourceLink::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnDestType
+ << maData.mnLinkType
+ << maData.mnFlags
+ << maData.mnNumFmtIdx
+ << mxLinkFmla;
+}
+
+// Text =======================================================================
+
+XclExpChFont::XclExpChFont( sal_uInt16 nFontIdx ) :
+ XclExpUInt16Record( EXC_ID_CHFONT, nFontIdx )
+{
+}
+
+XclExpChObjectLink::XclExpChObjectLink( sal_uInt16 nLinkTarget, const XclChDataPointPos& rPointPos ) :
+ XclExpRecord( EXC_ID_CHOBJECTLINK, 6 )
+{
+ maData.mnTarget = nLinkTarget;
+ maData.maPointPos = rPointPos;
+}
+
+void XclExpChObjectLink::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnTarget << maData.maPointPos.mnSeriesIdx << maData.maPointPos.mnPointIdx;
+}
+
+XclExpChFrLabelProps::XclExpChFrLabelProps( const XclExpChRoot& rRoot ) :
+ XclExpChFutureRecordBase( rRoot, EXC_FUTUREREC_UNUSEDREF, EXC_ID_CHFRLABELPROPS, 4 )
+{
+}
+
+void XclExpChFrLabelProps::Convert( const ScfPropertySet& rPropSet,
+ bool bShowCateg, bool bShowValue, bool bShowPercent, bool bShowBubble )
+{
+ // label value flags
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWSERIES, false );
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWCATEG, bShowCateg );
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWVALUE, bShowValue );
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWPERCENT, bShowPercent );
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWBUBBLE, bShowBubble );
+
+ // label value separator
+ maData.maSeparator = rPropSet.GetStringProperty( EXC_CHPROP_LABELSEPARATOR );
+ if( maData.maSeparator.isEmpty() )
+ maData.maSeparator = " ";
+}
+
+void XclExpChFrLabelProps::WriteBody( XclExpStream& rStrm )
+{
+ XclExpString aXclSep( maData.maSeparator, XclStrFlags::ForceUnicode | XclStrFlags::SmartFlags );
+ rStrm << maData.mnFlags << aXclSep;
+}
+
+XclExpChFontBase::~XclExpChFontBase()
+{
+}
+
+void XclExpChFontBase::ConvertFontBase( const XclExpChRoot& rRoot, sal_uInt16 nFontIdx )
+{
+ if( const XclExpFont* pFont = rRoot.GetFontBuffer().GetFont( nFontIdx ) )
+ {
+ XclExpChFontRef xFont = new XclExpChFont( nFontIdx );
+ SetFont( xFont, pFont->GetFontData().maColor, pFont->GetFontColorId() );
+ }
+}
+
+void XclExpChFontBase::ConvertFontBase( const XclExpChRoot& rRoot, const ScfPropertySet& rPropSet )
+{
+ ConvertFontBase( rRoot, rRoot.ConvertFont( rPropSet, rRoot.GetDefApiScript() ) );
+}
+
+void XclExpChFontBase::ConvertRotationBase(const ScfPropertySet& rPropSet, bool bSupportsStacked )
+{
+ sal_uInt16 nRotation = XclChPropSetHelper::ReadRotationProperties( rPropSet, bSupportsStacked );
+ SetRotation( nRotation );
+}
+
+XclExpChText::XclExpChText( const XclExpChRoot& rRoot ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_TEXT, EXC_ID_CHTEXT, (rRoot.GetBiff() == EXC_BIFF8) ? 32 : 26 ),
+ mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
+{
+}
+
+void XclExpChText::SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId )
+{
+ mxFont = xFont;
+ maData.maTextColor = rColor;
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR, rColor == COL_AUTO );
+ mnTextColorId = nColorId;
+}
+
+void XclExpChText::SetRotation( sal_uInt16 nRotation )
+{
+ maData.mnRotation = nRotation;
+ ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 8, 3 );
+}
+
+void XclExpChText::ConvertTitle( Reference< XTitle > const & xTitle, sal_uInt16 nTarget, const OUString* pSubTitle )
+{
+ switch( nTarget )
+ {
+ case EXC_CHOBJLINK_TITLE: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_TITLE ); break;
+ case EXC_CHOBJLINK_YAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 1 ); break;
+ case EXC_CHOBJLINK_XAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE ); break;
+ case EXC_CHOBJLINK_ZAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 2 ); break;
+ }
+
+ mxSrcLink.clear();
+ mxObjLink = new XclExpChObjectLink( nTarget, XclChDataPointPos( 0, 0 ) );
+
+ if( xTitle.is() )
+ {
+ // title frame formatting
+ ScfPropertySet aTitleProp( xTitle );
+ mxFrame = lclCreateFrame( GetChRoot(), aTitleProp, EXC_CHOBJTYPE_TEXT );
+
+ // string sequence
+ mxSrcLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
+ sal_uInt16 nFontIdx = mxSrcLink->ConvertStringSequence( xTitle->getText() );
+ if (pSubTitle)
+ {
+ // append subtitle as the 2nd line of the title.
+ OUString aSubTitle = "\n" + *pSubTitle;
+ mxSrcLink->AppendString(aSubTitle);
+ }
+
+ ConvertFontBase( GetChRoot(), nFontIdx );
+
+ // rotation
+ ConvertRotationBase( aTitleProp, true );
+
+ // manual text position - only for main title
+ mxFramePos = new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT );
+ if( nTarget == EXC_CHOBJLINK_TITLE )
+ {
+ Any aRelPos;
+ if( aTitleProp.GetAnyProperty( aRelPos, EXC_CHPROP_RELATIVEPOSITION ) && aRelPos.has< RelativePosition >() ) try
+ {
+ // calculate absolute position for CHTEXT record
+ Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
+ Reference< XShape > xTitleShape( xChart1Doc->getTitle(), UNO_SET_THROW );
+ css::awt::Point aPos = xTitleShape->getPosition();
+ css::awt::Size aSize = xTitleShape->getSize();
+ css::awt::Rectangle aRect( aPos.X, aPos.Y, aSize.Width, aSize.Height );
+ maData.maRect = CalcChartRectFromHmm( aRect );
+ ::insert_value( maData.mnFlags2, EXC_CHTEXT_POS_MOVED, 0, 4 );
+ // manual title position implies manual plot area
+ GetChartData().SetManualPlotArea();
+ // calculate the default title position in chart units
+ sal_Int32 nDefPosX = ::std::max< sal_Int32 >( (EXC_CHART_TOTALUNITS - maData.maRect.mnWidth) / 2, 0 );
+ sal_Int32 nDefPosY = 85;
+ // set the position relative to the standard position
+ XclChRectangle& rFrameRect = mxFramePos->GetFramePosData().maRect;
+ rFrameRect.mnX = maData.maRect.mnX - nDefPosX;
+ rFrameRect.mnY = maData.maRect.mnY - nDefPosY;
+ }
+ catch( Exception& )
+ {
+ }
+ }
+ }
+ else
+ {
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED );
+ }
+}
+
+void XclExpChText::ConvertLegend( const ScfPropertySet& rPropSet )
+{
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOGEN );
+ ConvertFontBase( GetChRoot(), rPropSet );
+}
+
+bool XclExpChText::ConvertDataLabel( const ScfPropertySet& rPropSet,
+ const XclChTypeInfo& rTypeInfo, const XclChDataPointPos& rPointPos )
+{
+ SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_DATALABEL, rPointPos.mnPointIdx, rPointPos.mnSeriesIdx );
+
+ cssc2::DataPointLabel aPointLabel;
+ if( !rPropSet.GetProperty( aPointLabel, EXC_CHPROP_LABEL ) )
+ return false;
+
+ // percentage only allowed in pie and donut charts
+ bool bIsPie = rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE;
+ // bubble sizes only allowed in bubble charts
+ bool bIsBubble = rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES;
+ OSL_ENSURE( (GetBiff() == EXC_BIFF8) || !bIsBubble, "XclExpChText::ConvertDataLabel - bubble charts only in BIFF8" );
+
+ // raw show flags
+ bool bShowValue = !bIsBubble && aPointLabel.ShowNumber; // Chart2 uses 'ShowNumber' for bubble size
+ bool bShowPercent = bIsPie && aPointLabel.ShowNumberInPercent; // percentage only in pie/donut charts
+ bool bShowCateg = aPointLabel.ShowCategoryName;
+ bool bShowBubble = bIsBubble && aPointLabel.ShowNumber; // Chart2 uses 'ShowNumber' for bubble size
+ bool bShowAny = bShowValue || bShowPercent || bShowCateg || bShowBubble;
+
+ // create the CHFRLABELPROPS record for extended settings in BIFF8
+ if( bShowAny && (GetBiff() == EXC_BIFF8) )
+ {
+ mxLabelProps = new XclExpChFrLabelProps( GetChRoot() );
+ mxLabelProps->Convert( rPropSet, bShowCateg, bShowValue, bShowPercent, bShowBubble );
+ }
+
+ // restrict to combinations allowed in CHTEXT
+ if( bShowPercent ) bShowValue = false; // percent wins over value
+ if( bShowValue ) bShowCateg = false; // value wins over category
+ if( bShowValue || bShowCateg ) bShowBubble = false; // value or category wins over bubble size
+
+ // set all flags
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE, bShowValue );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT, bShowPercent );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG, bShowCateg );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bShowPercent && bShowCateg );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWBUBBLE, bShowBubble );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL, bShowAny && aPointLabel.ShowLegendSymbol );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bShowAny );
+
+ if( bShowAny )
+ {
+ // font settings
+ ConvertFontBase( GetChRoot(), rPropSet );
+ ConvertRotationBase( rPropSet, false );
+ // label placement
+ sal_Int32 nPlacement = 0;
+ sal_uInt16 nLabelPos = EXC_CHTEXT_POS_AUTO;
+ if( rPropSet.GetProperty( nPlacement, EXC_CHPROP_LABELPLACEMENT ) )
+ {
+ using namespace cssc::DataLabelPlacement;
+ if( nPlacement == rTypeInfo.mnDefaultLabelPos )
+ {
+ nLabelPos = EXC_CHTEXT_POS_DEFAULT;
+ }
+ else switch( nPlacement )
+ {
+ case AVOID_OVERLAP: nLabelPos = EXC_CHTEXT_POS_AUTO; break;
+ case CENTER: nLabelPos = EXC_CHTEXT_POS_CENTER; break;
+ case TOP: nLabelPos = EXC_CHTEXT_POS_ABOVE; break;
+ case TOP_LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break;
+ case LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break;
+ case BOTTOM_LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break;
+ case BOTTOM: nLabelPos = EXC_CHTEXT_POS_BELOW; break;
+ case BOTTOM_RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break;
+ case RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break;
+ case TOP_RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break;
+ case INSIDE: nLabelPos = EXC_CHTEXT_POS_INSIDE; break;
+ case OUTSIDE: nLabelPos = EXC_CHTEXT_POS_OUTSIDE; break;
+ case NEAR_ORIGIN: nLabelPos = EXC_CHTEXT_POS_AXIS; break;
+ default: OSL_FAIL( "XclExpChText::ConvertDataLabel - unknown label placement type" );
+ }
+ }
+ ::insert_value( maData.mnFlags2, nLabelPos, 0, 4 );
+ // source link (contains number format)
+ mxSrcLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
+ if( bShowValue || bShowPercent )
+ // percentage format wins over value format
+ mxSrcLink->ConvertNumFmt( rPropSet, bShowPercent );
+ // object link
+ mxObjLink = new XclExpChObjectLink( EXC_CHOBJLINK_DATA, rPointPos );
+ }
+
+ /* Return true to indicate valid label settings:
+ - for existing labels at entire series
+ - for any settings at single data point (to be able to delete a point label) */
+ return bShowAny || (rPointPos.mnPointIdx != EXC_CHDATAFORMAT_ALLPOINTS);
+}
+
+void XclExpChText::ConvertTrendLineEquation( const ScfPropertySet& rPropSet, const XclChDataPointPos& rPointPos )
+{
+ // required flags
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT );
+ if( GetBiff() == EXC_BIFF8 )
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG ); // must set this to make equation visible in Excel
+ // frame formatting
+ mxFrame = lclCreateFrame( GetChRoot(), rPropSet, EXC_CHOBJTYPE_TEXT );
+ // font settings
+ maData.mnHAlign = EXC_CHTEXT_ALIGN_TOPLEFT;
+ maData.mnVAlign = EXC_CHTEXT_ALIGN_TOPLEFT;
+ ConvertFontBase( GetChRoot(), rPropSet );
+ // source link (contains number format)
+ mxSrcLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
+ mxSrcLink->ConvertNumFmt( rPropSet, false );
+ // object link
+ mxObjLink = new XclExpChObjectLink( EXC_CHOBJLINK_DATA, rPointPos );
+}
+
+sal_uInt16 XclExpChText::GetAttLabelFlags() const
+{
+ sal_uInt16 nFlags = 0;
+ ::set_flag( nFlags, EXC_CHATTLABEL_SHOWVALUE, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE ) );
+ ::set_flag( nFlags, EXC_CHATTLABEL_SHOWPERCENT, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT ) );
+ ::set_flag( nFlags, EXC_CHATTLABEL_SHOWCATEGPERC, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC ) );
+ ::set_flag( nFlags, EXC_CHATTLABEL_SHOWCATEG, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG ) );
+ return nFlags;
+}
+
+void XclExpChText::WriteSubRecords( XclExpStream& rStrm )
+{
+ // CHFRAMEPOS record
+ lclSaveRecord( rStrm, mxFramePos );
+ // CHFONT record
+ lclSaveRecord( rStrm, mxFont );
+ // CHSOURCELINK group
+ lclSaveRecord( rStrm, mxSrcLink );
+ // CHFRAME group
+ lclSaveRecord( rStrm, mxFrame );
+ // CHOBJECTLINK record
+ lclSaveRecord( rStrm, mxObjLink );
+ // CHFRLABELPROPS record
+ lclSaveRecord( rStrm, mxLabelProps );
+}
+
+void XclExpChText::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnHAlign
+ << maData.mnVAlign
+ << maData.mnBackMode
+ << maData.maTextColor
+ << maData.maRect
+ << maData.mnFlags;
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ rStrm << GetPalette().GetColorIndex( mnTextColorId )
+ << maData.mnFlags2
+ << maData.mnRotation;
+ }
+}
+
+namespace {
+
+/** Creates and returns an Excel text object from the passed title. */
+XclExpChTextRef lclCreateTitle( const XclExpChRoot& rRoot, Reference< XTitled > const & xTitled, sal_uInt16 nTarget,
+ const OUString* pSubTitle = nullptr )
+{
+ Reference< XTitle > xTitle;
+ if( xTitled.is() )
+ xTitle = xTitled->getTitleObject();
+
+ XclExpChTextRef xText = new XclExpChText( rRoot );
+ xText->ConvertTitle( xTitle, nTarget, pSubTitle );
+ /* Do not delete the CHTEXT group for the main title. A missing CHTEXT
+ will be interpreted as auto-generated title showing the series title in
+ charts that contain exactly one data series. */
+ if( (nTarget != EXC_CHOBJLINK_TITLE) && !xText->HasString() )
+ xText.clear();
+
+ return xText;
+}
+
+}
+
+// Data series ================================================================
+
+XclExpChMarkerFormat::XclExpChMarkerFormat( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHMARKERFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 20 : 12 ),
+ mnLineColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ),
+ mnFillColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) )
+{
+}
+
+void XclExpChMarkerFormat::Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx )
+{
+ XclChPropSetHelper::ReadMarkerProperties( maData, rPropSet, nFormatIdx );
+ /* Set marker line/fill color to series line color.
+ TODO: remove this if OOChart supports own colors in markers. */
+ Color aLineColor;
+ if( rPropSet.GetColorProperty( aLineColor, EXC_CHPROP_COLOR ) )
+ maData.maLineColor = maData.maFillColor = aLineColor;
+ // register colors in palette
+ RegisterColors( rRoot );
+}
+
+void XclExpChMarkerFormat::ConvertStockSymbol( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, bool bCloseSymbol )
+{
+ // clear the automatic flag
+ ::set_flag( maData.mnFlags, EXC_CHMARKERFORMAT_AUTO, false );
+ // symbol type and color
+ if( bCloseSymbol )
+ {
+ // set symbol type for the 'close' data series
+ maData.mnMarkerType = EXC_CHMARKERFORMAT_DOWJ;
+ maData.mnMarkerSize = EXC_CHMARKERFORMAT_DOUBLESIZE;
+ // set symbol line/fill color to series line color
+ Color aLineColor;
+ if( rPropSet.GetColorProperty( aLineColor, EXC_CHPROP_COLOR ) )
+ {
+ maData.maLineColor = maData.maFillColor = aLineColor;
+ RegisterColors( rRoot );
+ }
+ }
+ else
+ {
+ // set invisible symbol
+ maData.mnMarkerType = EXC_CHMARKERFORMAT_NOSYMBOL;
+ }
+}
+
+void XclExpChMarkerFormat::RegisterColors( const XclExpChRoot& rRoot )
+{
+ if( HasMarker() )
+ {
+ if( HasLineColor() )
+ mnLineColorId = rRoot.GetPalette().InsertColor( maData.maLineColor, EXC_COLOR_CHARTLINE );
+ if( HasFillColor() )
+ mnFillColorId = rRoot.GetPalette().InsertColor( maData.maFillColor, EXC_COLOR_CHARTAREA );
+ }
+}
+
+void XclExpChMarkerFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maLineColor << maData.maFillColor << maData.mnMarkerType << maData.mnFlags;
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ {
+ const XclExpPalette& rPal = rStrm.GetRoot().GetPalette();
+ rStrm << rPal.GetColorIndex( mnLineColorId ) << rPal.GetColorIndex( mnFillColorId ) << maData.mnMarkerSize;
+ }
+}
+
+XclExpChPieFormat::XclExpChPieFormat() :
+ XclExpUInt16Record( EXC_ID_CHPIEFORMAT, 0 )
+{
+}
+
+void XclExpChPieFormat::Convert( const ScfPropertySet& rPropSet )
+{
+ double fApiDist(0.0);
+ if( rPropSet.GetProperty( fApiDist, EXC_CHPROP_OFFSET ) )
+ SetValue( limit_cast< sal_uInt16 >( fApiDist * 100.0, 0, 100 ) );
+}
+
+XclExpCh3dDataFormat::XclExpCh3dDataFormat() :
+ XclExpRecord( EXC_ID_CH3DDATAFORMAT, 2 )
+{
+}
+
+void XclExpCh3dDataFormat::Convert( const ScfPropertySet& rPropSet )
+{
+ sal_Int32 nApiType(0);
+ if( !rPropSet.GetProperty( nApiType, EXC_CHPROP_GEOMETRY3D ) )
+ return;
+
+ using namespace cssc2::DataPointGeometry3D;
+ switch( nApiType )
+ {
+ case CUBOID:
+ maData.mnBase = EXC_CH3DDATAFORMAT_RECT;
+ maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT;
+ break;
+ case PYRAMID:
+ maData.mnBase = EXC_CH3DDATAFORMAT_RECT;
+ maData.mnTop = EXC_CH3DDATAFORMAT_SHARP;
+ break;
+ case CYLINDER:
+ maData.mnBase = EXC_CH3DDATAFORMAT_CIRC;
+ maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT;
+ break;
+ case CONE:
+ maData.mnBase = EXC_CH3DDATAFORMAT_CIRC;
+ maData.mnTop = EXC_CH3DDATAFORMAT_SHARP;
+ break;
+ default:
+ OSL_FAIL( "XclExpCh3dDataFormat::Convert - unknown 3D bar format" );
+ }
+}
+
+void XclExpCh3dDataFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnBase << maData.mnTop;
+}
+
+XclExpChAttachedLabel::XclExpChAttachedLabel( sal_uInt16 nFlags ) :
+ XclExpUInt16Record( EXC_ID_CHATTACHEDLABEL, nFlags )
+{
+}
+
+XclExpChDataFormat::XclExpChDataFormat( const XclExpChRoot& rRoot,
+ const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_DATAFORMAT, EXC_ID_CHDATAFORMAT, 8 )
+{
+ maData.maPointPos = rPointPos;
+ maData.mnFormatIdx = nFormatIdx;
+}
+
+void XclExpChDataFormat::ConvertDataSeries( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo )
+{
+ // line and area formatting
+ ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType() );
+
+ // data point symbols
+ bool bIsFrame = rTypeInfo.IsSeriesFrameFormat();
+ if( !bIsFrame )
+ {
+ mxMarkerFmt = new XclExpChMarkerFormat( GetChRoot() );
+ mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx );
+ }
+
+ // pie segments
+ if( rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE )
+ {
+ mxPieFmt = new XclExpChPieFormat();
+ mxPieFmt->Convert( rPropSet );
+ }
+
+ // 3D bars (only allowed for entire series in BIFF8)
+ if( IsSeriesFormat() && (GetBiff() == EXC_BIFF8) && rTypeInfo.mb3dChart && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR) )
+ {
+ mx3dDataFmt = new XclExpCh3dDataFormat();
+ mx3dDataFmt->Convert( rPropSet );
+ }
+
+ // spline
+ if( IsSeriesFormat() && rTypeInfo.mbSpline && !bIsFrame )
+ mxSeriesFmt = new XclExpUInt16Record( EXC_ID_CHSERIESFORMAT, EXC_CHSERIESFORMAT_SMOOTHED );
+
+ // data point labels
+ XclExpChTextRef xLabel = new XclExpChText( GetChRoot() );
+ if( xLabel->ConvertDataLabel( rPropSet, rTypeInfo, maData.maPointPos ) )
+ {
+ // CHTEXT groups for data labels are stored in global CHCHART group
+ GetChartData().SetDataLabel( xLabel );
+ mxAttLabel = new XclExpChAttachedLabel( xLabel->GetAttLabelFlags() );
+ }
+}
+
+void XclExpChDataFormat::ConvertStockSeries( const ScfPropertySet& rPropSet, bool bCloseSymbol )
+{
+ // set line format to invisible
+ SetDefaultFrameBase( GetChRoot(), EXC_CHFRAMETYPE_INVISIBLE, false );
+ // set symbols to invisible or to 'close' series symbol
+ mxMarkerFmt = new XclExpChMarkerFormat( GetChRoot() );
+ mxMarkerFmt->ConvertStockSymbol( GetChRoot(), rPropSet, bCloseSymbol );
+}
+
+void XclExpChDataFormat::ConvertLine( const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ ConvertFrameBase( GetChRoot(), rPropSet, eObjType );
+}
+
+void XclExpChDataFormat::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mx3dDataFmt );
+ WriteFrameRecords( rStrm );
+ lclSaveRecord( rStrm, mxPieFmt );
+ lclSaveRecord( rStrm, mxMarkerFmt );
+ lclSaveRecord( rStrm, mxSeriesFmt );
+ lclSaveRecord( rStrm, mxAttLabel );
+}
+
+void XclExpChDataFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maPointPos.mnPointIdx
+ << maData.maPointPos.mnSeriesIdx
+ << maData.mnFormatIdx
+ << maData.mnFlags;
+}
+
+XclExpChSerTrendLine::XclExpChSerTrendLine( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHSERTRENDLINE, 28 ),
+ XclExpChRoot( rRoot )
+{
+}
+
+bool XclExpChSerTrendLine::Convert( Reference< XRegressionCurve > const & xRegCurve, sal_uInt16 nSeriesIdx )
+{
+ if( !xRegCurve.is() )
+ return false;
+
+ // trend line type
+ ScfPropertySet aCurveProp( xRegCurve );
+
+ OUString aService = aCurveProp.GetServiceName();
+ if( aService == "com.sun.star.chart2.LinearRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_POLYNOMIAL;
+ maData.mnOrder = 1;
+ }
+ else if( aService == "com.sun.star.chart2.ExponentialRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_EXPONENTIAL;
+ }
+ else if( aService == "com.sun.star.chart2.LogarithmicRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_LOGARITHMIC;
+ }
+ else if( aService == "com.sun.star.chart2.PotentialRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_POWER;
+ }
+ else if( aService == "com.sun.star.chart2.PolynomialRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_POLYNOMIAL;
+ sal_Int32 aDegree;
+ aCurveProp.GetProperty(aDegree, EXC_CHPROP_POLYNOMIAL_DEGREE);
+ maData.mnOrder = static_cast<sal_uInt8> (aDegree);
+ }
+ else if( aService == "com.sun.star.chart2.MovingAverageRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_MOVING_AVG;
+ sal_Int32 aPeriod;
+ aCurveProp.GetProperty(aPeriod, EXC_CHPROP_MOVING_AVERAGE_PERIOD);
+ maData.mnOrder = static_cast<sal_uInt8> (aPeriod);
+ }
+ else
+ {
+ return false;
+ }
+
+ aCurveProp.GetProperty(maData.mfForecastFor, EXC_CHPROP_EXTRAPOLATE_FORWARD);
+ aCurveProp.GetProperty(maData.mfForecastBack, EXC_CHPROP_EXTRAPOLATE_BACKWARD);
+ bool bIsForceIntercept = false;
+ aCurveProp.GetProperty(bIsForceIntercept, EXC_CHPROP_FORCE_INTERCEPT);
+ if (bIsForceIntercept)
+ aCurveProp.GetProperty(maData.mfIntercept, EXC_CHPROP_INTERCEPT_VALUE);
+
+ // line formatting
+ XclChDataPointPos aPointPos( nSeriesIdx );
+ mxDataFmt = new XclExpChDataFormat( GetChRoot(), aPointPos, 0 );
+ mxDataFmt->ConvertLine( aCurveProp, EXC_CHOBJTYPE_TRENDLINE );
+
+ // #i83100# show equation and correlation coefficient
+ ScfPropertySet aEquationProp( xRegCurve->getEquationProperties() );
+ maData.mnShowEquation = aEquationProp.GetBoolProperty( EXC_CHPROP_SHOWEQUATION ) ? 1 : 0;
+ maData.mnShowRSquared = aEquationProp.GetBoolProperty( EXC_CHPROP_SHOWCORRELATION ) ? 1 : 0;
+
+ // #i83100# formatting of the equation text box
+ if( (maData.mnShowEquation != 0) || (maData.mnShowRSquared != 0) )
+ {
+ mxLabel = new XclExpChText( GetChRoot() );
+ mxLabel->ConvertTrendLineEquation( aEquationProp, aPointPos );
+ }
+
+ // missing features
+ // #i5085# manual trend line size
+ // #i34093# manual crossing point
+ return true;
+}
+
+void XclExpChSerTrendLine::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnLineType
+ << maData.mnOrder
+ << maData.mfIntercept
+ << maData.mnShowEquation
+ << maData.mnShowRSquared
+ << maData.mfForecastFor
+ << maData.mfForecastBack;
+}
+
+XclExpChSerErrorBar::XclExpChSerErrorBar( const XclExpChRoot& rRoot, sal_uInt8 nBarType ) :
+ XclExpRecord( EXC_ID_CHSERERRORBAR, 14 ),
+ XclExpChRoot( rRoot )
+{
+ maData.mnBarType = nBarType;
+}
+
+bool XclExpChSerErrorBar::Convert( XclExpChSourceLink& rValueLink, sal_uInt16& rnValueCount, const ScfPropertySet& rPropSet )
+{
+ sal_Int32 nBarStyle = 0;
+ bool bOk = rPropSet.GetProperty( nBarStyle, EXC_CHPROP_ERRORBARSTYLE );
+ if( bOk )
+ {
+ switch( nBarStyle )
+ {
+ case cssc::ErrorBarStyle::ABSOLUTE:
+ maData.mnSourceType = EXC_CHSERERR_FIXED;
+ rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_POSITIVEERROR );
+ break;
+ case cssc::ErrorBarStyle::RELATIVE:
+ maData.mnSourceType = EXC_CHSERERR_PERCENT;
+ rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_POSITIVEERROR );
+ break;
+ case cssc::ErrorBarStyle::STANDARD_DEVIATION:
+ maData.mnSourceType = EXC_CHSERERR_STDDEV;
+ rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_WEIGHT );
+ break;
+ case cssc::ErrorBarStyle::STANDARD_ERROR:
+ maData.mnSourceType = EXC_CHSERERR_STDERR;
+ break;
+ case cssc::ErrorBarStyle::FROM_DATA:
+ {
+ bOk = false;
+ maData.mnSourceType = EXC_CHSERERR_CUSTOM;
+ Reference< XDataSource > xDataSource( rPropSet.GetApiPropertySet(), UNO_QUERY );
+ if( xDataSource.is() )
+ {
+ // find first sequence with current role
+ OUString aRole = XclChartHelper::GetErrorBarValuesRole( maData.mnBarType );
+ Reference< XDataSequence > xValueSeq;
+
+ const Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences();
+ for( const Reference< XLabeledDataSequence >& rLabeledSeq : aLabeledSeqVec )
+ {
+ Reference< XDataSequence > xTmpValueSeq = rLabeledSeq->getValues();
+ ScfPropertySet aValueProp( xTmpValueSeq );
+ OUString aCurrRole;
+ if( aValueProp.GetProperty( aCurrRole, EXC_CHPROP_ROLE ) && (aCurrRole == aRole) )
+ {
+ xValueSeq = xTmpValueSeq;
+ break;
+ }
+ }
+ if( xValueSeq.is() )
+ {
+ // #i86465# pass value count back to series
+ rnValueCount = maData.mnValueCount = rValueLink.ConvertDataSequence( xValueSeq, true );
+ bOk = maData.mnValueCount > 0;
+ }
+ }
+ }
+ break;
+ default:
+ bOk = false;
+ }
+ }
+ return bOk;
+}
+
+void XclExpChSerErrorBar::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnBarType
+ << maData.mnSourceType
+ << maData.mnLineEnd
+ << sal_uInt8( 1 ) // must be 1 to make line visible
+ << maData.mfValue
+ << maData.mnValueCount;
+}
+
+namespace {
+
+/** Returns the property set of the specified data point. */
+ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > const & xDataSeries, sal_Int32 nPointIdx )
+{
+ ScfPropertySet aPropSet;
+ try
+ {
+ aPropSet.Set( xDataSeries->getDataPointByIndex( nPointIdx ) );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "lclGetPointPropSet - no data point property set" );
+ }
+ return aPropSet;
+}
+
+} // namespace
+
+XclExpChSeries::XclExpChSeries( const XclExpChRoot& rRoot, sal_uInt16 nSeriesIdx ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_SERIES, EXC_ID_CHSERIES, (rRoot.GetBiff() == EXC_BIFF8) ? 12 : 8 ),
+ mnGroupIdx( EXC_CHSERGROUP_NONE ),
+ mnSeriesIdx( nSeriesIdx ),
+ mnParentIdx( EXC_CHSERIES_INVALID )
+{
+ // CHSOURCELINK records are always required, even if unused
+ mxTitleLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
+ mxValueLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_VALUES );
+ mxCategLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_CATEGORY );
+ if( GetBiff() == EXC_BIFF8 )
+ mxBubbleLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_BUBBLES );
+}
+
+bool XclExpChSeries::ConvertDataSeries(
+ Reference< XDiagram > const & xDiagram, Reference< XDataSeries > const & xDataSeries,
+ const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx )
+{
+ bool bOk = false;
+ Reference< XDataSource > xDataSource( xDataSeries, UNO_QUERY );
+ if( xDataSource.is() )
+ {
+ Reference< XDataSequence > xYValueSeq, xTitleSeq, xXValueSeq, xBubbleSeq;
+
+ // find first sequence with role 'values-y'
+ const Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences();
+ for( const Reference< XLabeledDataSequence >& rLabeledSeq : aLabeledSeqVec )
+ {
+ Reference< XDataSequence > xTmpValueSeq = rLabeledSeq->getValues();
+ ScfPropertySet aValueProp( xTmpValueSeq );
+ OUString aRole;
+ if( aValueProp.GetProperty( aRole, EXC_CHPROP_ROLE ) )
+ {
+ if( !xYValueSeq.is() && (aRole == EXC_CHPROP_ROLE_YVALUES) )
+ {
+ xYValueSeq = xTmpValueSeq;
+ if( !xTitleSeq.is() )
+ xTitleSeq = rLabeledSeq->getLabel(); // ignore role of label sequence
+ }
+ else if( !xXValueSeq.is() && !rTypeInfo.mbCategoryAxis && (aRole == EXC_CHPROP_ROLE_XVALUES) )
+ {
+ xXValueSeq = xTmpValueSeq;
+ }
+ else if( !xBubbleSeq.is() && (rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES) && (aRole == EXC_CHPROP_ROLE_SIZEVALUES) )
+ {
+ xBubbleSeq = xTmpValueSeq;
+ xTitleSeq = rLabeledSeq->getLabel(); // ignore role of label sequence
+ }
+ }
+ }
+
+ bOk = xYValueSeq.is();
+ if( bOk )
+ {
+ // chart type group index
+ mnGroupIdx = nGroupIdx;
+
+ // convert source links
+ maData.mnValueCount = mxValueLink->ConvertDataSequence( xYValueSeq, true );
+ mxTitleLink->ConvertDataSequence( xTitleSeq, true );
+
+ // X values of XY charts
+ maData.mnCategCount = mxCategLink->ConvertDataSequence( xXValueSeq, false, maData.mnValueCount );
+
+ // size values of bubble charts
+ if( mxBubbleLink )
+ mxBubbleLink->ConvertDataSequence( xBubbleSeq, false, maData.mnValueCount );
+
+ // series formatting
+ XclChDataPointPos aPointPos( mnSeriesIdx );
+ ScfPropertySet aSeriesProp( xDataSeries );
+ mxSeriesFmt = new XclExpChDataFormat( GetChRoot(), aPointPos, nFormatIdx );
+ mxSeriesFmt->ConvertDataSeries( aSeriesProp, rTypeInfo );
+
+ // trend lines
+ CreateTrendLines( xDataSeries );
+
+ // error bars
+ CreateErrorBars( aSeriesProp, EXC_CHPROP_ERRORBARX, EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS );
+ CreateErrorBars( aSeriesProp, EXC_CHPROP_ERRORBARY, EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS );
+
+ if( maData.mnValueCount > 0 )
+ {
+ const sal_Int32 nMaxPointCount = maData.mnValueCount;
+
+ /* #i91063# Create missing fill properties in pie/doughnut charts.
+ If freshly created (never saved to ODF), these charts show
+ varying point colors but do not return these points via API. */
+ if( xDiagram.is() && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE) )
+ {
+ Reference< XColorScheme > xColorScheme = xDiagram->getDefaultColorScheme();
+ if( xColorScheme.is() )
+ {
+ static const OUStringLiteral aFillStyleName = u"FillStyle";
+ static const OUStringLiteral aColorName = u"Color";
+ namespace cssd = ::com::sun::star::drawing;
+ for( sal_Int32 nPointIdx = 0; nPointIdx < nMaxPointCount; ++nPointIdx )
+ {
+ aPointPos.mnPointIdx = static_cast< sal_uInt16 >( nPointIdx );
+ ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx );
+ // test that the point fill style is solid, but no color is set
+ cssd::FillStyle eFillStyle = cssd::FillStyle_NONE;
+ if( aPointProp.GetProperty( eFillStyle, aFillStyleName ) &&
+ (eFillStyle == cssd::FillStyle_SOLID) &&
+ !aPointProp.HasProperty( aColorName ) )
+ {
+ aPointProp.SetProperty( aColorName, xColorScheme->getColorByIndex( nPointIdx ) );
+ }
+ }
+ }
+ }
+
+ // data point formatting
+ Sequence< sal_Int32 > aPointIndexes;
+ if( aSeriesProp.GetProperty( aPointIndexes, EXC_CHPROP_ATTRIBDATAPOINTS ) && aPointIndexes.hasElements() )
+ {
+ for( const sal_Int32 nPointIndex : std::as_const(aPointIndexes) )
+ {
+ if (nPointIndex >= nMaxPointCount)
+ break;
+ aPointPos.mnPointIdx = static_cast< sal_uInt16 >( nPointIndex );
+ ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIndex );
+ XclExpChDataFormatRef xPointFmt = new XclExpChDataFormat( GetChRoot(), aPointPos, nFormatIdx );
+ xPointFmt->ConvertDataSeries( aPointProp, rTypeInfo );
+ maPointFmts.AppendRecord( xPointFmt );
+ }
+ }
+ }
+ }
+ }
+ return bOk;
+}
+
+bool XclExpChSeries::ConvertStockSeries( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries,
+ std::u16string_view rValueRole, sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx, bool bCloseSymbol )
+{
+ bool bOk = false;
+ Reference< XDataSource > xDataSource( xDataSeries, UNO_QUERY );
+ if( xDataSource.is() )
+ {
+ Reference< XDataSequence > xYValueSeq, xTitleSeq;
+
+ // find first sequence with passed role
+ const Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences();
+ for( const Reference< XLabeledDataSequence >& rLabeledSeq : aLabeledSeqVec )
+ {
+ Reference< XDataSequence > xTmpValueSeq = rLabeledSeq->getValues();
+ ScfPropertySet aValueProp( xTmpValueSeq );
+ OUString aRole;
+ if( aValueProp.GetProperty( aRole, EXC_CHPROP_ROLE ) && (aRole == rValueRole) )
+ {
+ xYValueSeq = xTmpValueSeq;
+ xTitleSeq = rLabeledSeq->getLabel(); // ignore role of label sequence
+ break;
+ }
+ }
+
+ bOk = xYValueSeq.is();
+ if( bOk )
+ {
+ // chart type group index
+ mnGroupIdx = nGroupIdx;
+ // convert source links
+ maData.mnValueCount = mxValueLink->ConvertDataSequence( xYValueSeq, true );
+ mxTitleLink->ConvertDataSequence( xTitleSeq, true );
+ // series formatting
+ ScfPropertySet aSeriesProp( xDataSeries );
+ mxSeriesFmt = new XclExpChDataFormat( GetChRoot(), XclChDataPointPos( mnSeriesIdx ), nFormatIdx );
+ mxSeriesFmt->ConvertStockSeries( aSeriesProp, bCloseSymbol );
+ }
+ }
+ return bOk;
+}
+
+bool XclExpChSeries::ConvertTrendLine( const XclExpChSeries& rParent, Reference< XRegressionCurve > const & xRegCurve )
+{
+ InitFromParent( rParent );
+
+ mxTrendLine = new XclExpChSerTrendLine( GetChRoot() );
+ bool bOk = mxTrendLine->Convert( xRegCurve, mnSeriesIdx );
+ if( bOk )
+ {
+ OUString aName;
+ ScfPropertySet aProperties( xRegCurve );
+ aProperties.GetProperty(aName, EXC_CHPROP_CURVENAME);
+ mxTitleLink->ConvertString(aName);
+
+ mxSeriesFmt = mxTrendLine->GetDataFormat();
+ GetChartData().SetDataLabel( mxTrendLine->GetDataLabel() );
+ }
+ return bOk;
+}
+
+bool XclExpChSeries::ConvertErrorBar( const XclExpChSeries& rParent, const ScfPropertySet& rPropSet, sal_uInt8 nBarId )
+{
+ InitFromParent( rParent );
+ // error bar settings
+ mxErrorBar = new XclExpChSerErrorBar( GetChRoot(), nBarId );
+ bool bOk = mxErrorBar->Convert( *mxValueLink, maData.mnValueCount, rPropSet );
+ if( bOk )
+ {
+ // error bar formatting
+ mxSeriesFmt = new XclExpChDataFormat( GetChRoot(), XclChDataPointPos( mnSeriesIdx ), 0 );
+ mxSeriesFmt->ConvertLine( rPropSet, EXC_CHOBJTYPE_ERRORBAR );
+ }
+ return bOk;
+}
+
+void XclExpChSeries::ConvertCategSequence( Reference< XLabeledDataSequence > const & xCategSeq )
+{
+ if( xCategSeq.is() )
+ maData.mnCategCount = mxCategLink->ConvertDataSequence( xCategSeq->getValues(), false );
+}
+
+void XclExpChSeries::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxTitleLink );
+ lclSaveRecord( rStrm, mxValueLink );
+ lclSaveRecord( rStrm, mxCategLink );
+ lclSaveRecord( rStrm, mxBubbleLink );
+ lclSaveRecord( rStrm, mxSeriesFmt );
+ maPointFmts.Save( rStrm );
+ if( mnGroupIdx != EXC_CHSERGROUP_NONE )
+ XclExpUInt16Record( EXC_ID_CHSERGROUP, mnGroupIdx ).Save( rStrm );
+ if( mnParentIdx != EXC_CHSERIES_INVALID )
+ XclExpUInt16Record( EXC_ID_CHSERPARENT, mnParentIdx ).Save( rStrm );
+ lclSaveRecord( rStrm, mxTrendLine );
+ lclSaveRecord( rStrm, mxErrorBar );
+}
+
+void XclExpChSeries::InitFromParent( const XclExpChSeries& rParent )
+{
+ // index to parent series is stored 1-based
+ mnParentIdx = rParent.mnSeriesIdx + 1;
+ /* #i86465# MSO2007 SP1 expects correct point counts in child series
+ (there was no problem in Excel2003 or Excel2007 without SP1...) */
+ maData.mnCategCount = rParent.maData.mnCategCount;
+ maData.mnValueCount = rParent.maData.mnValueCount;
+}
+
+void XclExpChSeries::CreateTrendLines( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries )
+{
+ Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY );
+ if( xRegCurveCont.is() )
+ {
+ const Sequence< Reference< XRegressionCurve > > aRegCurveSeq = xRegCurveCont->getRegressionCurves();
+ for( const Reference< XRegressionCurve >& rRegCurve : aRegCurveSeq )
+ {
+ XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
+ if( xSeries && !xSeries->ConvertTrendLine( *this, rRegCurve ) )
+ GetChartData().RemoveLastSeries();
+ }
+ }
+}
+
+void XclExpChSeries::CreateErrorBars( const ScfPropertySet& rPropSet,
+ const OUString& rBarPropName, sal_uInt8 nPosBarId, sal_uInt8 nNegBarId )
+{
+ Reference< XPropertySet > xErrorBar;
+ if( rPropSet.GetProperty( xErrorBar, rBarPropName ) && xErrorBar.is() )
+ {
+ ScfPropertySet aErrorProp( xErrorBar );
+ CreateErrorBar( aErrorProp, EXC_CHPROP_SHOWPOSITIVEERROR, nPosBarId );
+ CreateErrorBar( aErrorProp, EXC_CHPROP_SHOWNEGATIVEERROR, nNegBarId );
+ }
+}
+
+void XclExpChSeries::CreateErrorBar( const ScfPropertySet& rPropSet,
+ const OUString& rShowPropName, sal_uInt8 nBarId )
+{
+ if( rPropSet.GetBoolProperty( rShowPropName ) )
+ {
+ XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
+ if( xSeries && !xSeries->ConvertErrorBar( *this, rPropSet, nBarId ) )
+ GetChartData().RemoveLastSeries();
+ }
+}
+
+void XclExpChSeries::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnCategType << maData.mnValueType << maData.mnCategCount << maData.mnValueCount;
+ if( GetBiff() == EXC_BIFF8 )
+ rStrm << maData.mnBubbleType << maData.mnBubbleCount;
+}
+
+// Chart type groups ==========================================================
+
+XclExpChType::XclExpChType( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHUNKNOWN ),
+ XclExpChRoot( rRoot ),
+ maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) )
+{
+}
+
+void XclExpChType::Convert( Reference< XDiagram > const & xDiagram, Reference< XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx, bool bSwappedAxesSet, bool bHasXLabels )
+{
+ if( !xChartType.is() )
+ return;
+
+ maTypeInfo = GetChartTypeInfo( xChartType->getChartType() );
+ // special handling for some chart types
+ switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_BAR:
+ {
+ maTypeInfo = GetChartTypeInfo( bSwappedAxesSet ? EXC_CHTYPEID_HORBAR : EXC_CHTYPEID_BAR );
+ ::set_flag( maData.mnFlags, EXC_CHBAR_HORIZONTAL, bSwappedAxesSet );
+ ScfPropertySet aTypeProp( xChartType );
+ Sequence< sal_Int32 > aInt32Seq;
+ maData.mnOverlap = 0;
+ if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_OVERLAPSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) )
+ maData.mnOverlap = limit_cast< sal_Int16 >( -aInt32Seq[ nApiAxesSetIdx ], -100, 100 );
+ maData.mnGap = 150;
+ if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_GAPWIDTHSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) )
+ maData.mnGap = limit_cast< sal_uInt16 >( aInt32Seq[ nApiAxesSetIdx ], 0, 500 );
+ }
+ break;
+ case EXC_CHTYPECATEG_RADAR:
+ ::set_flag( maData.mnFlags, EXC_CHRADAR_AXISLABELS, bHasXLabels );
+ break;
+ case EXC_CHTYPECATEG_PIE:
+ {
+ ScfPropertySet aTypeProp( xChartType );
+ bool bDonut = aTypeProp.GetBoolProperty( EXC_CHPROP_USERINGS );
+ maTypeInfo = GetChartTypeInfo( bDonut ? EXC_CHTYPEID_DONUT : EXC_CHTYPEID_PIE );
+ maData.mnPieHole = bDonut ? 50 : 0;
+ // #i85166# starting angle of first pie slice
+ ScfPropertySet aDiaProp( xDiagram );
+ maData.mnRotation = XclExpChRoot::ConvertPieRotation( aDiaProp );
+ }
+ break;
+ case EXC_CHTYPECATEG_SCATTER:
+ if( GetBiff() == EXC_BIFF8 )
+ ::set_flag( maData.mnFlags, EXC_CHSCATTER_BUBBLES, maTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES );
+ break;
+ default:;
+ }
+ SetRecId( maTypeInfo.mnRecId );
+}
+
+void XclExpChType::SetStacked( bool bPercent )
+{
+ switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_LINE:
+ ::set_flag( maData.mnFlags, EXC_CHLINE_STACKED );
+ ::set_flag( maData.mnFlags, EXC_CHLINE_PERCENT, bPercent );
+ break;
+ case EXC_CHTYPECATEG_BAR:
+ ::set_flag( maData.mnFlags, EXC_CHBAR_STACKED );
+ ::set_flag( maData.mnFlags, EXC_CHBAR_PERCENT, bPercent );
+ maData.mnOverlap = -100;
+ break;
+ default:;
+ }
+}
+
+void XclExpChType::WriteBody( XclExpStream& rStrm )
+{
+ switch( GetRecId() )
+ {
+ case EXC_ID_CHBAR:
+ rStrm << maData.mnOverlap << maData.mnGap << maData.mnFlags;
+ break;
+
+ case EXC_ID_CHLINE:
+ case EXC_ID_CHAREA:
+ case EXC_ID_CHRADARLINE:
+ case EXC_ID_CHRADARAREA:
+ rStrm << maData.mnFlags;
+ break;
+
+ case EXC_ID_CHPIE:
+ rStrm << maData.mnRotation << maData.mnPieHole;
+ if( GetBiff() == EXC_BIFF8 )
+ rStrm << maData.mnFlags;
+ break;
+
+ case EXC_ID_CHSCATTER:
+ if( GetBiff() == EXC_BIFF8 )
+ rStrm << maData.mnBubbleSize << maData.mnBubbleType << maData.mnFlags;
+ break;
+
+ default:
+ OSL_FAIL( "XclExpChType::WriteBody - unknown chart type" );
+ }
+}
+
+XclExpChChart3d::XclExpChChart3d() :
+ XclExpRecord( EXC_ID_CHCHART3D, 14 )
+{
+}
+
+void XclExpChChart3d::Convert( const ScfPropertySet& rPropSet, bool b3dWallChart )
+{
+ sal_Int32 nRotationY = 0;
+ rPropSet.GetProperty( nRotationY, EXC_CHPROP_ROTATIONVERTICAL );
+ sal_Int32 nRotationX = 0;
+ rPropSet.GetProperty( nRotationX, EXC_CHPROP_ROTATIONHORIZONTAL );
+ sal_Int32 nPerspective = 15;
+ rPropSet.GetProperty( nPerspective, EXC_CHPROP_PERSPECTIVE );
+
+ if( b3dWallChart )
+ {
+ // Y rotation (Excel [0..359], Chart2 [-179,180])
+ if( nRotationY < 0 ) nRotationY += 360;
+ maData.mnRotation = static_cast< sal_uInt16 >( nRotationY );
+ // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180])
+ maData.mnElevation = limit_cast< sal_Int16 >( nRotationX, -90, 90 );
+ // perspective (Excel and Chart2 [0,100])
+ maData.mnEyeDist = limit_cast< sal_uInt16 >( nPerspective, 0, 100 );
+ // flags
+ maData.mnFlags = 0;
+ ::set_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D, !rPropSet.GetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES ) );
+ ::set_flag( maData.mnFlags, EXC_CHCHART3D_AUTOHEIGHT );
+ ::set_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS );
+ }
+ else
+ {
+ // Y rotation not used in pie charts, but 'first pie slice angle'
+ maData.mnRotation = XclExpChRoot::ConvertPieRotation( rPropSet );
+ // X rotation a.k.a. elevation (map Chart2 [-80,-10] to Excel [10..80])
+ maData.mnElevation = limit_cast< sal_Int16 >( (nRotationX + 270) % 180, 10, 80 );
+ // perspective (Excel and Chart2 [0,100])
+ maData.mnEyeDist = limit_cast< sal_uInt16 >( nPerspective, 0, 100 );
+ // flags
+ maData.mnFlags = 0;
+ }
+}
+
+void XclExpChChart3d::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnRotation
+ << maData.mnElevation
+ << maData.mnEyeDist
+ << maData.mnRelHeight
+ << maData.mnRelDepth
+ << maData.mnDepthGap
+ << maData.mnFlags;
+}
+
+XclExpChLegend::XclExpChLegend( const XclExpChRoot& rRoot ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_LEGEND, EXC_ID_CHLEGEND, 20 )
+{
+}
+
+void XclExpChLegend::Convert( const ScfPropertySet& rPropSet )
+{
+ // frame properties
+ mxFrame = lclCreateFrame( GetChRoot(), rPropSet, EXC_CHOBJTYPE_LEGEND );
+ // text properties
+ mxText = new XclExpChText( GetChRoot() );
+ mxText->ConvertLegend( rPropSet );
+
+ // legend position and size
+ Any aRelPosAny, aRelSizeAny;
+ rPropSet.GetAnyProperty( aRelPosAny, EXC_CHPROP_RELATIVEPOSITION );
+ rPropSet.GetAnyProperty( aRelSizeAny, EXC_CHPROP_RELATIVESIZE );
+ cssc::ChartLegendExpansion eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
+ rPropSet.GetProperty( eApiExpand, EXC_CHPROP_EXPANSION );
+ if( aRelPosAny.has< RelativePosition >() || ((eApiExpand == cssc::ChartLegendExpansion_CUSTOM) && aRelSizeAny.has< RelativeSize >()) )
+ {
+ try
+ {
+ /* The 'RelativePosition' or 'RelativeSize' properties are used as
+ indicator of manually changed legend position/size, but due to
+ the different anchor modes used by this property (in the
+ RelativePosition.Anchor member) it cannot be used to calculate
+ the position easily. For this, the Chart1 API will be used
+ instead. */
+ Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
+ Reference< XShape > xChart1Legend( xChart1Doc->getLegend(), UNO_SET_THROW );
+ // coordinates in CHLEGEND record written but not used by Excel
+ mxFramePos = new XclExpChFramePos( EXC_CHFRAMEPOS_CHARTSIZE );
+ XclChFramePos& rFramePos = mxFramePos->GetFramePosData();
+ rFramePos.mnTLMode = EXC_CHFRAMEPOS_CHARTSIZE;
+ css::awt::Point aLegendPos = xChart1Legend->getPosition();
+ rFramePos.maRect.mnX = maData.maRect.mnX = CalcChartXFromHmm( aLegendPos.X );
+ rFramePos.maRect.mnY = maData.maRect.mnY = CalcChartYFromHmm( aLegendPos.Y );
+ // legend size, Excel expects points in CHFRAMEPOS record
+ rFramePos.mnBRMode = EXC_CHFRAMEPOS_ABSSIZE_POINTS;
+ css::awt::Size aLegendSize = xChart1Legend->getSize();
+ rFramePos.maRect.mnWidth = o3tl::convert(aLegendSize.Width, o3tl::Length::mm100, o3tl::Length::pt);
+ rFramePos.maRect.mnHeight = o3tl::convert(aLegendSize.Height, o3tl::Length::mm100, o3tl::Length::pt);
+ maData.maRect.mnWidth = CalcChartXFromHmm( aLegendSize.Width );
+ maData.maRect.mnHeight = CalcChartYFromHmm( aLegendSize.Height );
+ eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
+ // manual legend position implies manual plot area
+ GetChartData().SetManualPlotArea();
+ maData.mnDockMode = EXC_CHLEGEND_NOTDOCKED;
+ // a CHFRAME record with cleared auto flags is needed
+ if( !mxFrame )
+ mxFrame = new XclExpChFrame( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
+ mxFrame->SetAutoFlags( false, false );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclExpChLegend::Convert - cannot get legend shape" );
+ maData.mnDockMode = EXC_CHLEGEND_RIGHT;
+ eApiExpand = cssc::ChartLegendExpansion_HIGH;
+ }
+ }
+ else
+ {
+ cssc2::LegendPosition eApiPos = cssc2::LegendPosition_LINE_END;
+ rPropSet.GetProperty( eApiPos, EXC_CHPROP_ANCHORPOSITION );
+ switch( eApiPos )
+ {
+ case cssc2::LegendPosition_LINE_START: maData.mnDockMode = EXC_CHLEGEND_LEFT; break;
+ case cssc2::LegendPosition_LINE_END: maData.mnDockMode = EXC_CHLEGEND_RIGHT; break;
+ case cssc2::LegendPosition_PAGE_START: maData.mnDockMode = EXC_CHLEGEND_TOP; break;
+ case cssc2::LegendPosition_PAGE_END: maData.mnDockMode = EXC_CHLEGEND_BOTTOM; break;
+ default:
+ OSL_FAIL( "XclExpChLegend::Convert - unrecognized legend position" );
+ maData.mnDockMode = EXC_CHLEGEND_RIGHT;
+ eApiExpand = cssc::ChartLegendExpansion_HIGH;
+ }
+ }
+ ::set_flag( maData.mnFlags, EXC_CHLEGEND_STACKED, eApiExpand == cssc::ChartLegendExpansion_HIGH );
+
+ // other flags
+ ::set_flag( maData.mnFlags, EXC_CHLEGEND_AUTOSERIES );
+ const sal_uInt16 nAutoFlags = EXC_CHLEGEND_DOCKED | EXC_CHLEGEND_AUTOPOSX | EXC_CHLEGEND_AUTOPOSY;
+ ::set_flag( maData.mnFlags, nAutoFlags, maData.mnDockMode != EXC_CHLEGEND_NOTDOCKED );
+}
+
+void XclExpChLegend::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxFramePos );
+ lclSaveRecord( rStrm, mxText );
+ lclSaveRecord( rStrm, mxFrame );
+}
+
+void XclExpChLegend::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maRect << maData.mnDockMode << maData.mnSpacing << maData.mnFlags;
+}
+
+XclExpChDropBar::XclExpChDropBar( const XclExpChRoot& rRoot, XclChObjectType eObjType ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_DROPBAR, EXC_ID_CHDROPBAR, 2 ),
+ meObjType( eObjType )
+{
+}
+
+void XclExpChDropBar::Convert( const ScfPropertySet& rPropSet )
+{
+ if( rPropSet.Is() )
+ ConvertFrameBase( GetChRoot(), rPropSet, meObjType );
+ else
+ SetDefaultFrameBase( GetChRoot(), EXC_CHFRAMETYPE_INVISIBLE, true );
+}
+
+void XclExpChDropBar::WriteSubRecords( XclExpStream& rStrm )
+{
+ WriteFrameRecords( rStrm );
+}
+
+void XclExpChDropBar::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(100); // Distance between bars (CHDROPBAR record).
+}
+
+XclExpChTypeGroup::XclExpChTypeGroup( const XclExpChRoot& rRoot, sal_uInt16 nGroupIdx ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_TYPEGROUP, EXC_ID_CHTYPEGROUP, 20 ),
+ maType( rRoot ),
+ maTypeInfo( maType.GetTypeInfo() )
+{
+ maData.mnGroupIdx = nGroupIdx;
+}
+
+void XclExpChTypeGroup::ConvertType(
+ Reference< XDiagram > const & xDiagram, Reference< XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx, bool b3dChart, bool bSwappedAxesSet, bool bHasXLabels )
+{
+ // chart type settings
+ maType.Convert( xDiagram, xChartType, nApiAxesSetIdx, bSwappedAxesSet, bHasXLabels );
+
+ // spline - TODO: get from single series (#i66858#)
+ ScfPropertySet aTypeProp( xChartType );
+ cssc2::CurveStyle eCurveStyle;
+ bool bSpline = aTypeProp.GetProperty( eCurveStyle, EXC_CHPROP_CURVESTYLE ) &&
+ (eCurveStyle != cssc2::CurveStyle_LINES);
+
+ // extended type info
+ maTypeInfo.Set( maType.GetTypeInfo(), b3dChart, bSpline );
+
+ // 3d chart settings
+ if( maTypeInfo.mb3dChart ) // only true, if Excel chart supports 3d mode
+ {
+ mxChart3d = new XclExpChChart3d();
+ ScfPropertySet aDiaProp( xDiagram );
+ mxChart3d->Convert( aDiaProp, Is3dWallChart() );
+ }
+}
+
+void XclExpChTypeGroup::ConvertSeries(
+ Reference< XDiagram > const & xDiagram, Reference< XChartType > const & xChartType,
+ sal_Int32 nGroupAxesSetIdx, bool bPercent, bool bConnectBars )
+{
+ Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY );
+ if( !xSeriesCont.is() )
+ return;
+
+ std::vector< Reference< XDataSeries > > aSeriesVec;
+
+ // copy data series attached to the current axes set to the vector
+ const Sequence< Reference< XDataSeries > > aSeriesSeq = xSeriesCont->getDataSeries();
+ for( const Reference< XDataSeries >& rSeries : aSeriesSeq )
+ {
+ ScfPropertySet aSeriesProp( rSeries );
+ sal_Int32 nSeriesAxesSetIdx(0);
+ if( aSeriesProp.GetProperty( nSeriesAxesSetIdx, EXC_CHPROP_ATTAXISINDEX ) && (nSeriesAxesSetIdx == nGroupAxesSetIdx) )
+ aSeriesVec.push_back( rSeries );
+ }
+
+ // Are there any series in the current axes set?
+ if( aSeriesVec.empty() )
+ return;
+
+ // stacking direction (stacked/percent/deep 3d) from first series
+ ScfPropertySet aSeriesProp( aSeriesVec.front() );
+ cssc2::StackingDirection eStacking;
+ if( !aSeriesProp.GetProperty( eStacking, EXC_CHPROP_STACKINGDIR ) )
+ eStacking = cssc2::StackingDirection_NO_STACKING;
+
+ // stacked or percent chart
+ if( maTypeInfo.mbSupportsStacking && (eStacking == cssc2::StackingDirection_Y_STACKING) )
+ {
+ // percent overrides simple stacking
+ maType.SetStacked( bPercent );
+
+ // connected data points (only in stacked bar charts)
+ if (bConnectBars && (maTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR))
+ {
+ sal_uInt16 nKey = EXC_CHCHARTLINE_CONNECT;
+ m_ChartLines.insert(std::make_pair(nKey, std::make_unique<XclExpChLineFormat>(GetChRoot())));
+ }
+ }
+ else
+ {
+ // reverse series order for some unstacked 2D chart types
+ if( maTypeInfo.mbReverseSeries && !Is3dChart() )
+ ::std::reverse( aSeriesVec.begin(), aSeriesVec.end() );
+ }
+
+ // deep 3d chart or clustered 3d chart (stacked is not clustered)
+ if( (eStacking == cssc2::StackingDirection_NO_STACKING) && Is3dWallChart() )
+ mxChart3d->SetClustered();
+
+ // varied point colors
+ ::set_flag( maData.mnFlags, EXC_CHTYPEGROUP_VARIEDCOLORS, aSeriesProp.GetBoolProperty( EXC_CHPROP_VARYCOLORSBY ) );
+
+ // process all series
+ for( const auto& rxSeries : aSeriesVec )
+ {
+ // create Excel series object, stock charts need special processing
+ if( maTypeInfo.meTypeId == EXC_CHTYPEID_STOCK )
+ CreateAllStockSeries( xChartType, rxSeries );
+ else
+ CreateDataSeries( xDiagram, rxSeries );
+ }
+}
+
+void XclExpChTypeGroup::ConvertCategSequence( Reference< XLabeledDataSequence > const & xCategSeq )
+{
+ for( size_t nIdx = 0, nSize = maSeries.GetSize(); nIdx < nSize; ++nIdx )
+ maSeries.GetRecord( nIdx )->ConvertCategSequence( xCategSeq );
+}
+
+void XclExpChTypeGroup::ConvertLegend( const ScfPropertySet& rPropSet )
+{
+ if( rPropSet.GetBoolProperty( EXC_CHPROP_SHOW ) )
+ {
+ mxLegend = new XclExpChLegend( GetChRoot() );
+ mxLegend->Convert( rPropSet );
+ }
+}
+
+void XclExpChTypeGroup::WriteSubRecords( XclExpStream& rStrm )
+{
+ maType.Save( rStrm );
+ lclSaveRecord( rStrm, mxChart3d );
+ lclSaveRecord( rStrm, mxLegend );
+ lclSaveRecord( rStrm, mxUpBar );
+ lclSaveRecord( rStrm, mxDownBar );
+ for (auto const& it : m_ChartLines)
+ {
+ lclSaveRecord( rStrm, it.second.get(), EXC_ID_CHCHARTLINE, it.first );
+ }
+}
+
+sal_uInt16 XclExpChTypeGroup::GetFreeFormatIdx() const
+{
+ return static_cast< sal_uInt16 >( maSeries.GetSize() );
+}
+
+void XclExpChTypeGroup::CreateDataSeries(
+ Reference< XDiagram > const & xDiagram, Reference< XDataSeries > const & xDataSeries )
+{
+ // let chart create series object with correct series index
+ XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
+ if( xSeries )
+ {
+ if( xSeries->ConvertDataSeries( xDiagram, xDataSeries, maTypeInfo, GetGroupIdx(), GetFreeFormatIdx() ) )
+ maSeries.AppendRecord( xSeries );
+ else
+ GetChartData().RemoveLastSeries();
+ }
+}
+
+void XclExpChTypeGroup::CreateAllStockSeries(
+ Reference< XChartType > const & xChartType, Reference< XDataSeries > const & xDataSeries )
+{
+ // create existing series objects
+ bool bHasOpen = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_OPENVALUES, false );
+ bool bHasHigh = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_HIGHVALUES, false );
+ bool bHasLow = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_LOWVALUES, false );
+ bool bHasClose = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_CLOSEVALUES, !bHasOpen );
+
+ // formatting of special stock chart elements
+ ScfPropertySet aTypeProp( xChartType );
+ // hi-lo lines
+ if( bHasHigh && bHasLow && aTypeProp.GetBoolProperty( EXC_CHPROP_SHOWHIGHLOW ) )
+ {
+ ScfPropertySet aSeriesProp( xDataSeries );
+ XclExpChLineFormatRef xLineFmt = new XclExpChLineFormat( GetChRoot() );
+ xLineFmt->Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE );
+ sal_uInt16 nKey = EXC_CHCHARTLINE_HILO;
+ m_ChartLines.insert(std::make_pair(nKey, std::make_unique<XclExpChLineFormat>(GetChRoot())));
+ }
+ // dropbars
+ if( !(bHasOpen && bHasClose) )
+ return;
+
+ // dropbar type is dependent on position in the file - always create both
+ Reference< XPropertySet > xWhitePropSet, xBlackPropSet;
+ // white dropbar format
+ aTypeProp.GetProperty( xWhitePropSet, EXC_CHPROP_WHITEDAY );
+ ScfPropertySet aWhiteProp( xWhitePropSet );
+ mxUpBar = new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_WHITEDROPBAR );
+ mxUpBar->Convert( aWhiteProp );
+ // black dropbar format
+ aTypeProp.GetProperty( xBlackPropSet, EXC_CHPROP_BLACKDAY );
+ ScfPropertySet aBlackProp( xBlackPropSet );
+ mxDownBar = new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_BLACKDROPBAR );
+ mxDownBar->Convert( aBlackProp );
+}
+
+bool XclExpChTypeGroup::CreateStockSeries( Reference< XDataSeries > const & xDataSeries,
+ std::u16string_view rValueRole, bool bCloseSymbol )
+{
+ bool bOk = false;
+ // let chart create series object with correct series index
+ XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
+ if( xSeries )
+ {
+ bOk = xSeries->ConvertStockSeries( xDataSeries,
+ rValueRole, GetGroupIdx(), GetFreeFormatIdx(), bCloseSymbol );
+ if( bOk )
+ maSeries.AppendRecord( xSeries );
+ else
+ GetChartData().RemoveLastSeries();
+ }
+ return bOk;
+}
+
+void XclExpChTypeGroup::WriteBody( XclExpStream& rStrm )
+{
+ rStrm.WriteZeroBytes( 16 );
+ rStrm << maData.mnFlags << maData.mnGroupIdx;
+}
+
+// Axes =======================================================================
+
+XclExpChLabelRange::XclExpChLabelRange( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHLABELRANGE, 8 ),
+ XclExpChRoot( rRoot )
+{
+}
+
+void XclExpChLabelRange::Convert( const ScaleData& rScaleData, const ScfPropertySet& rChart1Axis, bool bMirrorOrient )
+{
+ /* Base time unit (using the property 'ExplicitTimeIncrement' from the old
+ chart API allows to detect axis type (date axis, if property exists),
+ and to receive the base time unit currently used in case the base time
+ unit is set to 'automatic'. */
+ cssc::TimeIncrement aTimeIncrement;
+ if( rChart1Axis.GetProperty( aTimeIncrement, EXC_CHPROP_EXPTIMEINCREMENT ) )
+ {
+ // property exists -> this is a date axis currently
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS );
+
+ // automatic base time unit, if the UNO Any 'rScaleData.TimeIncrement.TimeResolution' does not contain a valid value...
+ bool bAutoBase = !rScaleData.TimeIncrement.TimeResolution.has< cssc::TimeIncrement >();
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOBASE, bAutoBase );
+
+ // ...but get the current base time unit from the property of the old chart API
+ sal_Int32 nApiTimeUnit = 0;
+ bool bValidBaseUnit = aTimeIncrement.TimeResolution >>= nApiTimeUnit;
+ OSL_ENSURE( bValidBaseUnit, "XclExpChLabelRange::Convert - cannot get base time unit" );
+ maDateData.mnBaseUnit = bValidBaseUnit ? lclGetTimeUnit( nApiTimeUnit ) : EXC_CHDATERANGE_DAYS;
+
+ /* Min/max values depend on base time unit, they specify the number of
+ days, months, or years starting from null date. */
+ bool bAutoMin = lclConvertTimeValue( GetRoot(), maDateData.mnMinDate, rScaleData.Minimum, maDateData.mnBaseUnit );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMIN, bAutoMin );
+ bool bAutoMax = lclConvertTimeValue( GetRoot(), maDateData.mnMaxDate, rScaleData.Maximum, maDateData.mnBaseUnit );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAX, bAutoMax );
+ }
+
+ // automatic axis type detection
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTODATE, rScaleData.AutoDateAxis );
+
+ // increment
+ bool bAutoMajor = lclConvertTimeInterval( maDateData.mnMajorStep, maDateData.mnMajorUnit, rScaleData.TimeIncrement.MajorTimeInterval );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAJOR, bAutoMajor );
+ bool bAutoMinor = lclConvertTimeInterval( maDateData.mnMinorStep, maDateData.mnMinorUnit, rScaleData.TimeIncrement.MinorTimeInterval );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMINOR, bAutoMinor );
+
+ // origin
+ double fOrigin = 0.0;
+ if( !lclIsAutoAnyOrGetValue( fOrigin, rScaleData.Origin ) )
+ maLabelData.mnCross = limit_cast< sal_uInt16 >( fOrigin, 1, 31999 );
+
+ // reverse order
+ if( (rScaleData.Orientation == cssc2::AxisOrientation_REVERSE) != bMirrorOrient )
+ ::set_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_REVERSE );
+}
+
+void XclExpChLabelRange::ConvertAxisPosition( const ScfPropertySet& rPropSet )
+{
+ cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE;
+ rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION );
+ double fCrossingPos = 1.0;
+ rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE );
+
+ bool bDateAxis = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS );
+ switch( eAxisPos )
+ {
+ case cssc::ChartAxisPosition_ZERO:
+ case cssc::ChartAxisPosition_START:
+ maLabelData.mnCross = 1;
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
+ break;
+ case cssc::ChartAxisPosition_END:
+ ::set_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_MAXCROSS );
+ break;
+ case cssc::ChartAxisPosition_VALUE:
+ maLabelData.mnCross = limit_cast< sal_uInt16 >( fCrossingPos, 1, 31999 );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS, false );
+ if( bDateAxis )
+ maDateData.mnCross = lclGetTimeValue( GetRoot(), fCrossingPos, maDateData.mnBaseUnit );
+ break;
+ default:
+ maLabelData.mnCross = 1;
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
+ }
+}
+
+void XclExpChLabelRange::Save( XclExpStream& rStrm )
+{
+ // the CHLABELRANGE record
+ XclExpRecord::Save( rStrm );
+
+ // the CHDATERANGE record with date axis settings (BIFF8 only)
+ if( GetBiff() != EXC_BIFF8 )
+ return;
+
+ rStrm.StartRecord( EXC_ID_CHDATERANGE, 18 );
+ rStrm << maDateData.mnMinDate
+ << maDateData.mnMaxDate
+ << maDateData.mnMajorStep
+ << maDateData.mnMajorUnit
+ << maDateData.mnMinorStep
+ << maDateData.mnMinorUnit
+ << maDateData.mnBaseUnit
+ << maDateData.mnCross
+ << maDateData.mnFlags;
+ rStrm.EndRecord();
+}
+
+void XclExpChLabelRange::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maLabelData.mnCross << maLabelData.mnLabelFreq << maLabelData.mnTickFreq << maLabelData.mnFlags;
+}
+
+XclExpChValueRange::XclExpChValueRange( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHVALUERANGE, 42 ),
+ XclExpChRoot( rRoot )
+{
+}
+
+void XclExpChValueRange::Convert( const ScaleData& rScaleData )
+{
+ // scaling algorithm
+ bool bLogScale = ScfApiHelper::GetServiceName( rScaleData.Scaling ) == "com.sun.star.chart2.LogarithmicScaling";
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, bLogScale );
+
+ // min/max
+ bool bAutoMin = lclIsAutoAnyOrGetScaledValue( maData.mfMin, rScaleData.Minimum, bLogScale );
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN, bAutoMin );
+ bool bAutoMax = lclIsAutoAnyOrGetScaledValue( maData.mfMax, rScaleData.Maximum, bLogScale );
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX, bAutoMax );
+
+ // origin
+ bool bAutoCross = lclIsAutoAnyOrGetScaledValue( maData.mfCross, rScaleData.Origin, bLogScale );
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, bAutoCross );
+
+ // major increment
+ const IncrementData& rIncrementData = rScaleData.IncrementData;
+ const bool bAutoMajor = lclIsAutoAnyOrGetValue( maData.mfMajorStep, rIncrementData.Distance ) || (maData.mfMajorStep <= 0.0);
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAJOR, bAutoMajor );
+ // minor increment
+ const Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements;
+ sal_Int32 nCount = 0;
+
+ // tdf#114168 If IntervalCount is 5, then enable automatic minor calculation.
+ // During import, if minorUnit is set and majorUnit not, then it is impossible
+ // to calculate IntervalCount.
+ const bool bAutoMinor = bLogScale || bAutoMajor || !rSubIncrementSeq.hasElements() ||
+ lclIsAutoAnyOrGetValue( nCount, rSubIncrementSeq[ 0 ].IntervalCount ) || (nCount < 1) || (nCount == 5);
+
+ if( maData.mfMajorStep && !bAutoMinor )
+ maData.mfMinorStep = maData.mfMajorStep / nCount;
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMINOR, bAutoMinor );
+
+ // reverse order
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE, rScaleData.Orientation == cssc2::AxisOrientation_REVERSE );
+}
+
+void XclExpChValueRange::ConvertAxisPosition( const ScfPropertySet& rPropSet )
+{
+ cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE;
+ double fCrossingPos = 0.0;
+ if( !(rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION ) && rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE )) )
+ return;
+
+ switch( eAxisPos )
+ {
+ case cssc::ChartAxisPosition_ZERO:
+ case cssc::ChartAxisPosition_START:
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS );
+ break;
+ case cssc::ChartAxisPosition_END:
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_MAXCROSS );
+ break;
+ case cssc::ChartAxisPosition_VALUE:
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, false );
+ maData.mfCross = ::get_flagvalue< double >( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, log( fCrossingPos ) / log( 10.0 ), fCrossingPos );
+ break;
+ default:
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS );
+ }
+}
+
+void XclExpChValueRange::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mfMin
+ << maData.mfMax
+ << maData.mfMajorStep
+ << maData.mfMinorStep
+ << maData.mfCross
+ << maData.mnFlags;
+}
+
+namespace {
+
+sal_uInt8 lclGetXclTickPos( sal_Int32 nApiTickmarks )
+{
+ using namespace cssc2::TickmarkStyle;
+ sal_uInt8 nXclTickPos = 0;
+ ::set_flag( nXclTickPos, EXC_CHTICK_INSIDE, ::get_flag( nApiTickmarks, INNER ) );
+ ::set_flag( nXclTickPos, EXC_CHTICK_OUTSIDE, ::get_flag( nApiTickmarks, OUTER ) );
+ return nXclTickPos;
+}
+
+} // namespace
+
+XclExpChTick::XclExpChTick( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHTICK, (rRoot.GetBiff() == EXC_BIFF8) ? 30 : 26 ),
+ XclExpChRoot( rRoot ),
+ mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
+{
+}
+
+void XclExpChTick::Convert( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nAxisType )
+{
+ // tick mark style
+ sal_Int32 nApiTickmarks = 0;
+ if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MAJORTICKS ) )
+ maData.mnMajor = lclGetXclTickPos( nApiTickmarks );
+ if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MINORTICKS ) )
+ maData.mnMinor = lclGetXclTickPos( nApiTickmarks );
+
+ // axis labels
+ if( (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) && (nAxisType == EXC_CHAXIS_X) )
+ {
+ /* Radar charts disable their category labels via chart type, not via
+ axis, and axis labels are always 'near axis'. */
+ maData.mnLabelPos = EXC_CHTICK_NEXT;
+ }
+ else if( !rPropSet.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS ) )
+ {
+ // no labels
+ maData.mnLabelPos = EXC_CHTICK_NOLABEL;
+ }
+ else if( rTypeInfo.mb3dChart && (nAxisType == EXC_CHAXIS_Y) )
+ {
+ // Excel expects 'near axis' at Y axes in 3D charts
+ maData.mnLabelPos = EXC_CHTICK_NEXT;
+ }
+ else
+ {
+ cssc::ChartAxisLabelPosition eApiLabelPos = cssc::ChartAxisLabelPosition_NEAR_AXIS;
+ rPropSet.GetProperty( eApiLabelPos, EXC_CHPROP_LABELPOSITION );
+ switch( eApiLabelPos )
+ {
+ case cssc::ChartAxisLabelPosition_NEAR_AXIS:
+ case cssc::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE: maData.mnLabelPos = EXC_CHTICK_NEXT; break;
+ case cssc::ChartAxisLabelPosition_OUTSIDE_START: maData.mnLabelPos = EXC_CHTICK_LOW; break;
+ case cssc::ChartAxisLabelPosition_OUTSIDE_END: maData.mnLabelPos = EXC_CHTICK_HIGH; break;
+ default: maData.mnLabelPos = EXC_CHTICK_NEXT;
+ }
+ }
+}
+
+void XclExpChTick::SetFontColor( const Color& rColor, sal_uInt32 nColorId )
+{
+ maData.maTextColor = rColor;
+ ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOCOLOR, rColor == COL_AUTO );
+ mnTextColorId = nColorId;
+}
+
+void XclExpChTick::SetRotation( sal_uInt16 nRotation )
+{
+ maData.mnRotation = nRotation;
+ ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOROT, false );
+ ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 2, 3 );
+}
+
+void XclExpChTick::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnMajor
+ << maData.mnMinor
+ << maData.mnLabelPos
+ << maData.mnBackMode;
+ rStrm.WriteZeroBytes( 16 );
+ rStrm << maData.maTextColor
+ << maData.mnFlags;
+ if( GetBiff() == EXC_BIFF8 )
+ rStrm << GetPalette().GetColorIndex( mnTextColorId ) << maData.mnRotation;
+}
+
+namespace {
+
+/** Returns an API axis object from the passed coordinate system. */
+Reference< XAxis > lclGetApiAxis( Reference< XCoordinateSystem > const & xCoordSystem,
+ sal_Int32 nApiAxisDim, sal_Int32 nApiAxesSetIdx )
+{
+ Reference< XAxis > xAxis;
+ if( (nApiAxisDim >= 0) && xCoordSystem.is() ) try
+ {
+ xAxis = xCoordSystem->getAxisByDimension( nApiAxisDim, nApiAxesSetIdx );
+ }
+ catch( Exception& )
+ {
+ }
+ return xAxis;
+}
+
+Reference< cssc::XAxis > lclGetApiChart1Axis( Reference< XChartDocument > const & xChartDoc,
+ sal_Int32 nApiAxisDim, sal_Int32 nApiAxesSetIdx )
+{
+ Reference< cssc::XAxis > xChart1Axis;
+ try
+ {
+ Reference< cssc::XChartDocument > xChart1Doc( xChartDoc, UNO_QUERY_THROW );
+ Reference< cssc::XAxisSupplier > xChart1AxisSupp( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
+ switch( nApiAxesSetIdx )
+ {
+ case EXC_CHART_AXESSET_PRIMARY:
+ xChart1Axis = xChart1AxisSupp->getAxis( nApiAxisDim );
+ break;
+ case EXC_CHART_AXESSET_SECONDARY:
+ xChart1Axis = xChart1AxisSupp->getSecondaryAxis( nApiAxisDim );
+ break;
+ }
+ }
+ catch( Exception& )
+ {
+ }
+ return xChart1Axis;
+}
+
+} // namespace
+
+XclExpChAxis::XclExpChAxis( const XclExpChRoot& rRoot, sal_uInt16 nAxisType ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXIS, EXC_ID_CHAXIS, 18 ),
+ mnNumFmtIdx( EXC_FORMAT_NOTFOUND )
+{
+ maData.mnType = nAxisType;
+}
+
+void XclExpChAxis::SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId )
+{
+ mxFont = xFont;
+ if( mxTick )
+ mxTick->SetFontColor( rColor, nColorId );
+}
+
+void XclExpChAxis::SetRotation( sal_uInt16 nRotation )
+{
+ if( mxTick )
+ mxTick->SetRotation( nRotation );
+}
+
+void XclExpChAxis::Convert( Reference< XAxis > const & xAxis, Reference< XAxis > const & xCrossingAxis,
+ Reference< cssc::XAxis > const & xChart1Axis, const XclChExtTypeInfo& rTypeInfo )
+{
+ ScfPropertySet aAxisProp( xAxis );
+ bool bCategoryAxis = ((GetAxisType() == EXC_CHAXIS_X) && rTypeInfo.mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z);
+
+ // axis line format -------------------------------------------------------
+
+ mxAxisLine = new XclExpChLineFormat( GetChRoot() );
+ mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE );
+ // #i58688# axis enabled
+ mxAxisLine->SetShowAxis( aAxisProp.GetBoolProperty( EXC_CHPROP_SHOW ) );
+
+ // axis scaling and increment ---------------------------------------------
+
+ ScfPropertySet aCrossingProp( xCrossingAxis );
+ if( bCategoryAxis )
+ {
+ mxLabelRange = new XclExpChLabelRange( GetChRoot() );
+ mxLabelRange->SetTicksBetweenCateg( rTypeInfo.mbTicksBetweenCateg );
+ if( xAxis.is() )
+ {
+ ScfPropertySet aChart1AxisProp( xChart1Axis );
+ // #i71684# radar charts have reversed rotation direction
+ mxLabelRange->Convert( xAxis->getScaleData(), aChart1AxisProp, (GetAxisType() == EXC_CHAXIS_X) && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) );
+ }
+ // get position of crossing axis on this axis from passed axis object
+ if( aCrossingProp.Is() )
+ mxLabelRange->ConvertAxisPosition( aCrossingProp );
+ }
+ else
+ {
+ mxValueRange = new XclExpChValueRange( GetChRoot() );
+ if( xAxis.is() )
+ mxValueRange->Convert( xAxis->getScaleData() );
+ // get position of crossing axis on this axis from passed axis object
+ if( aCrossingProp.Is() )
+ mxValueRange->ConvertAxisPosition( aCrossingProp );
+ }
+
+ // axis caption text ------------------------------------------------------
+
+ // axis ticks properties
+ mxTick = new XclExpChTick( GetChRoot() );
+ mxTick->Convert( aAxisProp, rTypeInfo, GetAxisType() );
+
+ // axis label formatting and rotation
+ ConvertFontBase( GetChRoot(), aAxisProp );
+ ConvertRotationBase( aAxisProp, true );
+
+ // axis number format
+ sal_Int32 nApiNumFmt = 0;
+ if( !bCategoryAxis && aAxisProp.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) )
+ {
+ bool bLinkNumberFmtToSource = false;
+ if ( !aAxisProp.GetProperty( bLinkNumberFmtToSource, EXC_CHPROP_NUMBERFORMAT_LINKSRC ) || !bLinkNumberFmtToSource )
+ mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) );
+ }
+
+ // grid -------------------------------------------------------------------
+
+ if( !xAxis.is() )
+ return;
+
+ // main grid
+ ScfPropertySet aGridProp( xAxis->getGridProperties() );
+ if( aGridProp.GetBoolProperty( EXC_CHPROP_SHOW ) )
+ mxMajorGrid = lclCreateLineFormat( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE );
+ // sub grid
+ Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties();
+ if( aSubGridPropSeq.hasElements() )
+ {
+ ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] );
+ if( aSubGridProp.GetBoolProperty( EXC_CHPROP_SHOW ) )
+ mxMinorGrid = lclCreateLineFormat( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE );
+ }
+}
+
+void XclExpChAxis::ConvertWall( css::uno::Reference< css::chart2::XDiagram > const & xDiagram )
+{
+ if( !xDiagram.is() )
+ return;
+
+ switch( GetAxisType() )
+ {
+ case EXC_CHAXIS_X:
+ {
+ ScfPropertySet aWallProp( xDiagram->getWall() );
+ mxWallFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_WALL3D );
+ }
+ break;
+ case EXC_CHAXIS_Y:
+ {
+ ScfPropertySet aFloorProp( xDiagram->getFloor() );
+ mxWallFrame = lclCreateFrame( GetChRoot(), aFloorProp, EXC_CHOBJTYPE_FLOOR3D );
+ }
+ break;
+ default:
+ mxWallFrame.clear();
+ }
+}
+
+void XclExpChAxis::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxLabelRange );
+ lclSaveRecord( rStrm, mxValueRange );
+ if( mnNumFmtIdx != EXC_FORMAT_NOTFOUND )
+ XclExpUInt16Record( EXC_ID_CHFORMAT, mnNumFmtIdx ).Save( rStrm );
+ lclSaveRecord( rStrm, mxTick );
+ lclSaveRecord( rStrm, mxFont );
+ lclSaveRecord( rStrm, mxAxisLine, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_AXISLINE );
+ lclSaveRecord( rStrm, mxMajorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MAJORGRID );
+ lclSaveRecord( rStrm, mxMinorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MINORGRID );
+ lclSaveRecord( rStrm, mxWallFrame, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_WALLS );
+}
+
+void XclExpChAxis::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnType;
+ rStrm.WriteZeroBytes( 16 );
+}
+
+XclExpChAxesSet::XclExpChAxesSet( const XclExpChRoot& rRoot, sal_uInt16 nAxesSetId ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXESSET, EXC_ID_CHAXESSET, 18 )
+{
+ maData.mnAxesSetId = nAxesSetId;
+ SetFutureRecordContext( 0, nAxesSetId );
+
+ /* Need to set a reasonable size for the plot area, otherwise Excel will
+ move away embedded shapes while auto-sizing the plot area. This is just
+ a wild guess, but will be fixed with implementing manual positioning of
+ chart elements. */
+ maData.maRect.mnX = 262;
+ maData.maRect.mnY = 626;
+ maData.maRect.mnWidth = 3187;
+ maData.maRect.mnHeight = 2633;
+}
+
+sal_uInt16 XclExpChAxesSet::Convert( Reference< XDiagram > const & xDiagram, sal_uInt16 nFirstGroupIdx )
+{
+ /* First unused chart type group index is passed to be able to continue
+ counting of chart type groups for secondary axes set. */
+ sal_uInt16 nGroupIdx = nFirstGroupIdx;
+ Reference< XCoordinateSystemContainer > xCoordSysCont( xDiagram, UNO_QUERY );
+ if( xCoordSysCont.is() )
+ {
+ Sequence< Reference< XCoordinateSystem > > aCoordSysSeq = xCoordSysCont->getCoordinateSystems();
+ if( aCoordSysSeq.hasElements() )
+ {
+ /* Process first coordinate system only. Import filter puts all
+ chart types into one coordinate system. */
+ Reference< XCoordinateSystem > xCoordSystem = aCoordSysSeq[ 0 ];
+ sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
+
+ // 3d mode
+ bool b3dChart = xCoordSystem.is() && (xCoordSystem->getDimension() == 3);
+
+ // percent charts
+ namespace ApiAxisType = cssc2::AxisType;
+ Reference< XAxis > xApiYAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_Y, nApiAxesSetIdx );
+ bool bPercent = xApiYAxis.is() && (xApiYAxis->getScaleData().AxisType == ApiAxisType::PERCENT);
+
+ // connector lines in bar charts
+ ScfPropertySet aDiaProp( xDiagram );
+ bool bConnectBars = aDiaProp.GetBoolProperty( EXC_CHPROP_CONNECTBARS );
+
+ // swapped axes sets
+ ScfPropertySet aCoordSysProp( xCoordSystem );
+ bool bSwappedAxesSet = aCoordSysProp.GetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS );
+
+ // X axis for later use
+ Reference< XAxis > xApiXAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_X, nApiAxesSetIdx );
+ // X axis labels
+ ScfPropertySet aXAxisProp( xApiXAxis );
+ bool bHasXLabels = aXAxisProp.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS );
+
+ // process chart types
+ Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY );
+ if( xChartTypeCont.is() )
+ {
+ const Sequence< Reference< XChartType > > aChartTypeSeq = xChartTypeCont->getChartTypes();
+ for( const Reference< XChartType >& rChartType : aChartTypeSeq )
+ {
+ XclExpChTypeGroupRef xTypeGroup = new XclExpChTypeGroup( GetChRoot(), nGroupIdx );
+ xTypeGroup->ConvertType( xDiagram, rChartType, nApiAxesSetIdx, b3dChart, bSwappedAxesSet, bHasXLabels );
+ /* If new chart type group cannot be inserted into a combination
+ chart with existing type groups, insert all series into last
+ contained chart type group instead of creating a new group. */
+ XclExpChTypeGroupRef xLastGroup = GetLastTypeGroup();
+ if( xLastGroup && !(xTypeGroup->IsCombinable2d() && xLastGroup->IsCombinable2d()) )
+ {
+ xLastGroup->ConvertSeries( xDiagram, rChartType, nApiAxesSetIdx, bPercent, bConnectBars );
+ }
+ else
+ {
+ xTypeGroup->ConvertSeries( xDiagram, rChartType, nApiAxesSetIdx, bPercent, bConnectBars );
+ if( xTypeGroup->IsValidGroup() )
+ {
+ maTypeGroups.AppendRecord( xTypeGroup );
+ ++nGroupIdx;
+ }
+ }
+ }
+ }
+
+ if( XclExpChTypeGroup* pGroup = GetFirstTypeGroup().get() )
+ {
+ const XclChExtTypeInfo& rTypeInfo = pGroup->GetTypeInfo();
+
+ // create axes according to chart type (no axes for pie and donut charts)
+ if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE )
+ {
+ ConvertAxis( mxXAxis, EXC_CHAXIS_X, mxXAxisTitle, EXC_CHOBJLINK_XAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_Y );
+ ConvertAxis( mxYAxis, EXC_CHAXIS_Y, mxYAxisTitle, EXC_CHOBJLINK_YAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_X );
+ if( pGroup->Is3dDeepChart() )
+ ConvertAxis( mxZAxis, EXC_CHAXIS_Z, mxZAxisTitle, EXC_CHOBJLINK_ZAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_NONE );
+ }
+
+ // X axis category ranges
+ if( rTypeInfo.mbCategoryAxis && xApiXAxis.is() )
+ {
+ const ScaleData aScaleData = xApiXAxis->getScaleData();
+ for( size_t nIdx = 0, nSize = maTypeGroups.GetSize(); nIdx < nSize; ++nIdx )
+ maTypeGroups.GetRecord( nIdx )->ConvertCategSequence( aScaleData.Categories );
+ }
+
+ // legend
+ if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) )
+ {
+ Reference< XLegend > xLegend = xDiagram->getLegend();
+ if( xLegend.is() )
+ {
+ ScfPropertySet aLegendProp( xLegend );
+ pGroup->ConvertLegend( aLegendProp );
+ }
+ }
+ }
+ }
+ }
+
+ // wall/floor/diagram frame formatting
+ if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) )
+ {
+ XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
+ if( xTypeGroup && xTypeGroup->Is3dWallChart() )
+ {
+ // wall/floor formatting (3D charts)
+ if( mxXAxis )
+ mxXAxis->ConvertWall( xDiagram );
+ if( mxYAxis )
+ mxYAxis->ConvertWall( xDiagram );
+ }
+ else
+ {
+ // diagram background formatting
+ ScfPropertySet aWallProp( xDiagram->getWall() );
+ mxPlotFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_PLOTFRAME );
+ }
+ }
+
+ // inner and outer plot area position and size
+ try
+ {
+ Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
+ Reference< cssc::XDiagramPositioning > xPositioning( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
+ // set manual flag in chart data
+ if( !xPositioning->isAutomaticDiagramPositioning() )
+ GetChartData().SetManualPlotArea();
+ // the CHAXESSET record contains the inner plot area
+ maData.maRect = CalcChartRectFromHmm( xPositioning->calculateDiagramPositionExcludingAxes() );
+ // the embedded CHFRAMEPOS record contains the outer plot area
+ mxFramePos = new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT );
+ // for pie charts, always use inner plot area size to exclude the data labels as Excel does
+ const XclExpChTypeGroup* pFirstTypeGroup = GetFirstTypeGroup().get();
+ bool bPieChart = pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE);
+ mxFramePos->GetFramePosData().maRect = bPieChart ? maData.maRect :
+ CalcChartRectFromHmm( xPositioning->calculateDiagramPositionIncludingAxes() );
+ }
+ catch( Exception& )
+ {
+ }
+
+ // return first unused chart type group index for next axes set
+ return nGroupIdx;
+}
+
+bool XclExpChAxesSet::Is3dChart() const
+{
+ XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
+ return xTypeGroup && xTypeGroup->Is3dChart();
+}
+
+void XclExpChAxesSet::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxFramePos );
+ lclSaveRecord( rStrm, mxXAxis );
+ lclSaveRecord( rStrm, mxYAxis );
+ lclSaveRecord( rStrm, mxZAxis );
+ lclSaveRecord( rStrm, mxXAxisTitle );
+ lclSaveRecord( rStrm, mxYAxisTitle );
+ lclSaveRecord( rStrm, mxZAxisTitle );
+ if( mxPlotFrame )
+ {
+ XclExpEmptyRecord( EXC_ID_CHPLOTFRAME ).Save( rStrm );
+ mxPlotFrame->Save( rStrm );
+ }
+ maTypeGroups.Save( rStrm );
+}
+
+XclExpChTypeGroupRef XclExpChAxesSet::GetFirstTypeGroup() const
+{
+ return maTypeGroups.GetFirstRecord();
+}
+
+XclExpChTypeGroupRef XclExpChAxesSet::GetLastTypeGroup() const
+{
+ return maTypeGroups.GetLastRecord();
+}
+
+void XclExpChAxesSet::ConvertAxis(
+ XclExpChAxisRef& rxChAxis, sal_uInt16 nAxisType,
+ XclExpChTextRef& rxChAxisTitle, sal_uInt16 nTitleTarget,
+ Reference< XCoordinateSystem > const & xCoordSystem, const XclChExtTypeInfo& rTypeInfo,
+ sal_Int32 nCrossingAxisDim )
+{
+ // create and convert axis object
+ rxChAxis = new XclExpChAxis( GetChRoot(), nAxisType );
+ sal_Int32 nApiAxisDim = rxChAxis->GetApiAxisDimension();
+ sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
+ Reference< XAxis > xAxis = lclGetApiAxis( xCoordSystem, nApiAxisDim, nApiAxesSetIdx );
+ Reference< XAxis > xCrossingAxis = lclGetApiAxis( xCoordSystem, nCrossingAxisDim, nApiAxesSetIdx );
+ Reference< cssc::XAxis > xChart1Axis = lclGetApiChart1Axis( GetChartDocument(), nApiAxisDim, nApiAxesSetIdx );
+ rxChAxis->Convert( xAxis, xCrossingAxis, xChart1Axis, rTypeInfo );
+
+ // create and convert axis title
+ Reference< XTitled > xTitled( xAxis, UNO_QUERY );
+ rxChAxisTitle = lclCreateTitle( GetChRoot(), xTitled, nTitleTarget );
+}
+
+void XclExpChAxesSet::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnAxesSetId << maData.maRect;
+}
+
+// The chart object ===========================================================
+
+static void lcl_getChartSubTitle(const Reference<XChartDocument>& xChartDoc,
+ OUString& rSubTitle)
+{
+ Reference< css::chart::XChartDocument > xChartDoc1(xChartDoc, UNO_QUERY);
+ if (!xChartDoc1.is())
+ return;
+
+ Reference< XPropertySet > xProp(xChartDoc1->getSubTitle(), UNO_QUERY);
+ if (!xProp.is())
+ return;
+
+ OUString aTitle;
+ Any any = xProp->getPropertyValue("String");
+ if (any >>= aTitle)
+ rSubTitle = aTitle;
+}
+
+XclExpChChart::XclExpChChart( const XclExpRoot& rRoot,
+ Reference< XChartDocument > const & xChartDoc, const tools::Rectangle& rChartRect ) :
+ XclExpChGroupBase( XclExpChRoot( rRoot, *this ), EXC_CHFRBLOCK_TYPE_CHART, EXC_ID_CHCHART, 16 )
+{
+ Size aPtSize = o3tl::convert( rChartRect.GetSize(), o3tl::Length::mm100, o3tl::Length::pt );
+ // rectangle is stored in 16.16 fixed-point format
+ maRect.mnX = maRect.mnY = 0;
+ maRect.mnWidth = static_cast< sal_Int32 >( aPtSize.Width() << 16 );
+ maRect.mnHeight = static_cast< sal_Int32 >( aPtSize.Height() << 16 );
+
+ // global chart properties (default values)
+ ::set_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY, false );
+ ::set_flag( maProps.mnFlags, EXC_CHPROPS_MANPLOTAREA );
+ maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP;
+
+ // always create both axes set objects
+ mxPrimAxesSet = std::make_shared<XclExpChAxesSet>( GetChRoot(), EXC_CHAXESSET_PRIMARY );
+ mxSecnAxesSet = std::make_shared<XclExpChAxesSet>( GetChRoot(), EXC_CHAXESSET_SECONDARY );
+
+ if( !xChartDoc.is() )
+ return;
+
+ Reference< XDiagram > xDiagram = xChartDoc->getFirstDiagram();
+
+ // global chart properties (only 'include hidden cells' attribute for now)
+ ScfPropertySet aDiagramProp( xDiagram );
+ bool bIncludeHidden = aDiagramProp.GetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS );
+ ::set_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY, !bIncludeHidden );
+
+ // initialize API conversion (remembers xChartDoc and rChartRect internally)
+ InitConversion( xChartDoc, rChartRect );
+
+ // chart frame
+ ScfPropertySet aFrameProp( xChartDoc->getPageBackground() );
+ mxFrame = lclCreateFrame( GetChRoot(), aFrameProp, EXC_CHOBJTYPE_BACKGROUND );
+
+ // chart title
+ Reference< XTitled > xTitled( xChartDoc, UNO_QUERY );
+ OUString aSubTitle;
+ lcl_getChartSubTitle(xChartDoc, aSubTitle);
+ mxTitle = lclCreateTitle( GetChRoot(), xTitled, EXC_CHOBJLINK_TITLE,
+ !aSubTitle.isEmpty() ? &aSubTitle : nullptr );
+
+ // diagrams (axes sets)
+ sal_uInt16 nFreeGroupIdx = mxPrimAxesSet->Convert( xDiagram, 0 );
+ if( !mxPrimAxesSet->Is3dChart() )
+ mxSecnAxesSet->Convert( xDiagram, nFreeGroupIdx );
+
+ // treatment of missing values
+ ScfPropertySet aDiaProp( xDiagram );
+ sal_Int32 nMissingValues = 0;
+ if( aDiaProp.GetProperty( nMissingValues, EXC_CHPROP_MISSINGVALUETREATMENT ) )
+ {
+ using namespace cssc::MissingValueTreatment;
+ switch( nMissingValues )
+ {
+ case LEAVE_GAP: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP; break;
+ case USE_ZERO: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_ZERO; break;
+ case CONTINUE: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_INTERPOLATE; break;
+ }
+ }
+
+ // finish API conversion
+ FinishConversion();
+}
+
+XclExpChSeriesRef XclExpChChart::CreateSeries()
+{
+ XclExpChSeriesRef xSeries;
+ sal_uInt16 nSeriesIdx = static_cast< sal_uInt16 >( maSeries.GetSize() );
+ if( nSeriesIdx <= EXC_CHSERIES_MAXSERIES )
+ {
+ xSeries = new XclExpChSeries( GetChRoot(), nSeriesIdx );
+ maSeries.AppendRecord( xSeries );
+ }
+ return xSeries;
+}
+
+void XclExpChChart::RemoveLastSeries()
+{
+ if( !maSeries.IsEmpty() )
+ maSeries.RemoveRecord( maSeries.GetSize() - 1 );
+}
+
+void XclExpChChart::SetDataLabel( XclExpChTextRef const & xText )
+{
+ if( xText )
+ maLabels.AppendRecord( xText );
+}
+
+void XclExpChChart::SetManualPlotArea()
+{
+ // this flag does not exist in BIFF5
+ if( GetBiff() == EXC_BIFF8 )
+ ::set_flag( maProps.mnFlags, EXC_CHPROPS_USEMANPLOTAREA );
+}
+
+void XclExpChChart::WriteSubRecords( XclExpStream& rStrm )
+{
+ // background format
+ lclSaveRecord( rStrm, mxFrame );
+
+ // data series
+ maSeries.Save( rStrm );
+
+ // CHPROPERTIES record
+ rStrm.StartRecord( EXC_ID_CHPROPERTIES, 4 );
+ rStrm << maProps.mnFlags << maProps.mnEmptyMode << sal_uInt8( 0 );
+ rStrm.EndRecord();
+
+ // axes sets (always save primary axes set)
+ sal_uInt16 nUsedAxesSets = mxSecnAxesSet->IsValidAxesSet() ? 2 : 1;
+ XclExpUInt16Record( EXC_ID_CHUSEDAXESSETS, nUsedAxesSets ).Save( rStrm );
+ mxPrimAxesSet->Save( rStrm );
+ if( mxSecnAxesSet->IsValidAxesSet() )
+ mxSecnAxesSet->Save( rStrm );
+
+ // chart title and data labels
+ lclSaveRecord( rStrm, mxTitle );
+ maLabels.Save( rStrm );
+}
+
+void XclExpChChart::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maRect;
+}
+
+XclExpChartDrawing::XclExpChartDrawing( const XclExpRoot& rRoot,
+ const Reference< XModel >& rxModel, const Size& rChartSize ) :
+ XclExpRoot( rRoot )
+{
+ if( rChartSize.IsEmpty() )
+ return;
+
+ ScfPropertySet aPropSet( rxModel );
+ Reference< XShapes > xShapes;
+ if( !(aPropSet.GetProperty( xShapes, EXC_CHPROP_ADDITIONALSHAPES ) && xShapes.is() && (xShapes->getCount() > 0)) )
+ return;
+
+ /* Create a new independent object manager with own DFF stream for the
+ DGCONTAINER, pass global manager as parent for shared usage of
+ global DFF data (picture container etc.). */
+ mxObjMgr = std::make_shared<XclExpEmbeddedObjectManager>( GetObjectManager(), rChartSize, EXC_CHART_TOTALUNITS, EXC_CHART_TOTALUNITS );
+ // initialize the drawing object list
+ mxObjMgr->StartSheet();
+ // process the draw page (convert all shapes)
+ mxObjRecs = mxObjMgr->ProcessDrawing( xShapes );
+ // finalize the DFF stream
+ mxObjMgr->EndDocument();
+}
+
+XclExpChartDrawing::~XclExpChartDrawing()
+{
+}
+
+void XclExpChartDrawing::Save( XclExpStream& rStrm )
+{
+ if( mxObjRecs )
+ mxObjRecs->Save( rStrm );
+}
+
+XclExpChart::XclExpChart( const XclExpRoot& rRoot, Reference< XModel > const & xModel, const tools::Rectangle& rChartRect ) :
+ XclExpSubStream( EXC_BOF_CHART ),
+ XclExpRoot( rRoot )
+{
+ AppendNewRecord( new XclExpChartPageSettings( rRoot ) );
+ AppendNewRecord( new XclExpBoolRecord( EXC_ID_PROTECT, false ) );
+ AppendNewRecord( new XclExpChartDrawing( rRoot, xModel, rChartRect.GetSize() ) );
+ AppendNewRecord( new XclExpUInt16Record( EXC_ID_CHUNITS, EXC_CHUNITS_TWIPS ) );
+
+ Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY );
+ AppendNewRecord( new XclExpChChart( rRoot, xChartDoc, rChartRect ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xecontent.cxx b/sc/source/filter/excel/xecontent.cxx
new file mode 100644
index 000000000..65f8bb2f4
--- /dev/null
+++ b/sc/source/filter/excel/xecontent.cxx
@@ -0,0 +1,2211 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <xecontent.hxx>
+
+#include <vector>
+#include <algorithm>
+#include <string_view>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/sheet/XAreaLinks.hpp>
+#include <com/sun/star/sheet/XAreaLink.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <sfx2/objsh.hxx>
+#include <tools/urlobj.hxx>
+#include <formula/grammar.hxx>
+#include <scitems.hxx>
+#include <editeng/flditem.hxx>
+#include <document.hxx>
+#include <validat.hxx>
+#include <unonames.hxx>
+#include <convuno.hxx>
+#include <rangenam.hxx>
+#include <tokenarray.hxx>
+#include <stlpool.hxx>
+#include <patattr.hxx>
+#include <fapihelper.hxx>
+#include <xehelper.hxx>
+#include <xestyle.hxx>
+#include <xename.hxx>
+#include <xlcontent.hxx>
+#include <xltools.hxx>
+#include <xeformula.hxx>
+#include <rtl/uuid.h>
+#include <sal/log.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <comphelper/string.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::oox;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::table::CellRangeAddress;
+using ::com::sun::star::sheet::XAreaLinks;
+using ::com::sun::star::sheet::XAreaLink;
+
+// Shared string table ========================================================
+
+namespace {
+
+/** A single string entry in the hash table. */
+struct XclExpHashEntry
+{
+ const XclExpString* mpString; /// Pointer to the string (no ownership).
+ sal_uInt32 mnSstIndex; /// The SST index of this string.
+ explicit XclExpHashEntry( const XclExpString* pString, sal_uInt32 nSstIndex ) :
+ mpString( pString ), mnSstIndex( nSstIndex ) {}
+};
+
+/** Function object for strict weak ordering. */
+struct XclExpHashEntrySWO
+{
+ bool operator()( const XclExpHashEntry& rLeft, const XclExpHashEntry& rRight ) const
+ { return *rLeft.mpString < *rRight.mpString; }
+};
+
+}
+
+/** Implementation of the SST export.
+ @descr Stores all passed strings in a hash table and prevents repeated
+ insertion of equal strings. */
+class XclExpSstImpl
+{
+public:
+ explicit XclExpSstImpl();
+
+ /** Inserts the passed string, if not already inserted, and returns the unique SST index. */
+ sal_uInt32 Insert( XclExpStringRef xString );
+
+ /** Writes the complete SST and EXTSST records. */
+ void Save( XclExpStream& rStrm );
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ typedef ::std::vector< XclExpHashEntry > XclExpHashVec;
+
+ std::vector< XclExpStringRef > maStringVector; /// List of unique strings (in SST ID order).
+ std::vector< XclExpHashVec >
+ maHashTab; /// Hashed table that manages string pointers.
+ sal_uInt32 mnTotal; /// Total count of strings (including doubles).
+ sal_uInt32 mnSize; /// Size of the SST (count of unique strings).
+};
+
+const sal_uInt32 EXC_SST_HASHTABLE_SIZE = 2048;
+
+XclExpSstImpl::XclExpSstImpl() :
+ maHashTab( EXC_SST_HASHTABLE_SIZE ),
+ mnTotal( 0 ),
+ mnSize( 0 )
+{
+}
+
+sal_uInt32 XclExpSstImpl::Insert( XclExpStringRef xString )
+{
+ OSL_ENSURE( xString, "XclExpSstImpl::Insert - empty pointer not allowed" );
+ if( !xString )
+ xString.reset( new XclExpString );
+
+ ++mnTotal;
+ sal_uInt32 nSstIndex = 0;
+
+ // calculate hash value in range [0,EXC_SST_HASHTABLE_SIZE)
+ sal_uInt16 nHash = xString->GetHash();
+ nHash = (nHash ^ (nHash / EXC_SST_HASHTABLE_SIZE)) % EXC_SST_HASHTABLE_SIZE;
+
+ XclExpHashVec& rVec = maHashTab[ nHash ];
+ XclExpHashEntry aEntry( xString.get(), mnSize );
+ XclExpHashVec::iterator aIt = ::std::lower_bound( rVec.begin(), rVec.end(), aEntry, XclExpHashEntrySWO() );
+ if( (aIt == rVec.end()) || (*aIt->mpString != *xString) )
+ {
+ nSstIndex = mnSize;
+ maStringVector.push_back( xString );
+ rVec.insert( aIt, aEntry );
+ ++mnSize;
+ }
+ else
+ {
+ nSstIndex = aIt->mnSstIndex;
+ }
+
+ return nSstIndex;
+}
+
+void XclExpSstImpl::Save( XclExpStream& rStrm )
+{
+ if( maStringVector.empty() )
+ return;
+
+ SvMemoryStream aExtSst( 8192 );
+
+ sal_uInt32 nBucket = mnSize;
+ while( nBucket > 0x0100 )
+ nBucket /= 2;
+
+ sal_uInt16 nPerBucket = llimit_cast< sal_uInt16 >( nBucket, 8 );
+ sal_uInt16 nBucketIndex = 0;
+
+ // *** write the SST record ***
+
+ rStrm.StartRecord( EXC_ID_SST, 8 );
+
+ rStrm << mnTotal << mnSize;
+ for (auto const& elem : maStringVector)
+ {
+ if( !nBucketIndex )
+ {
+ // write bucket info before string to get correct record position
+ sal_uInt32 nStrmPos = static_cast< sal_uInt32 >( rStrm.GetSvStreamPos() );
+ sal_uInt16 nRecPos = rStrm.GetRawRecPos() + 4;
+ aExtSst.WriteUInt32( nStrmPos ) // stream position
+ .WriteUInt16( nRecPos ) // position from start of SST or CONTINUE
+ .WriteUInt16( 0 ); // reserved
+ }
+
+ rStrm << *elem;
+
+ if( ++nBucketIndex == nPerBucket )
+ nBucketIndex = 0;
+ }
+
+ rStrm.EndRecord();
+
+ // *** write the EXTSST record ***
+
+ rStrm.StartRecord( EXC_ID_EXTSST, 0 );
+
+ rStrm << nPerBucket;
+ rStrm.SetSliceSize( 8 ); // size of one bucket info
+ aExtSst.Seek( STREAM_SEEK_TO_BEGIN );
+ rStrm.CopyFromStream( aExtSst );
+
+ rStrm.EndRecord();
+}
+
+void XclExpSstImpl::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maStringVector.empty() )
+ return;
+
+ sax_fastparser::FSHelperPtr pSst = rStrm.CreateOutputStream(
+ "xl/sharedStrings.xml",
+ u"sharedStrings.xml",
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
+ oox::getRelationship(Relationship::SHAREDSTRINGS));
+ rStrm.PushStream( pSst );
+
+ pSst->startElement( XML_sst,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ XML_count, OString::number(mnTotal),
+ XML_uniqueCount, OString::number(mnSize) );
+
+ for (auto const& elem : maStringVector)
+ {
+ pSst->startElement(XML_si);
+ elem->WriteXml( rStrm );
+ pSst->endElement( XML_si );
+ }
+
+ pSst->endElement( XML_sst );
+
+ rStrm.PopStream();
+}
+
+XclExpSst::XclExpSst() :
+ mxImpl( new XclExpSstImpl )
+{
+}
+
+XclExpSst::~XclExpSst()
+{
+}
+
+sal_uInt32 XclExpSst::Insert( const XclExpStringRef& xString )
+{
+ return mxImpl->Insert( xString );
+}
+
+void XclExpSst::Save( XclExpStream& rStrm )
+{
+ mxImpl->Save( rStrm );
+}
+
+void XclExpSst::SaveXml( XclExpXmlStream& rStrm )
+{
+ mxImpl->SaveXml( rStrm );
+}
+
+// Merged cells ===============================================================
+
+XclExpMergedcells::XclExpMergedcells( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpMergedcells::AppendRange( const ScRange& rRange, sal_uInt32 nBaseXFId )
+{
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ maMergedRanges.push_back( rRange );
+ maBaseXFIds.push_back( nBaseXFId );
+ }
+}
+
+sal_uInt32 XclExpMergedcells::GetBaseXFId( const ScAddress& rPos ) const
+{
+ OSL_ENSURE( maBaseXFIds.size() == maMergedRanges.size(), "XclExpMergedcells::GetBaseXFId - invalid lists" );
+ ScfUInt32Vec::const_iterator aIt = maBaseXFIds.begin();
+ ScRangeList& rNCRanges = const_cast< ScRangeList& >( maMergedRanges );
+ for ( size_t i = 0, nRanges = rNCRanges.size(); i < nRanges; ++i, ++aIt )
+ {
+ const ScRange & rScRange = rNCRanges[ i ];
+ if( rScRange.Contains( rPos ) )
+ return *aIt;
+ }
+ return EXC_XFID_NOTFOUND;
+}
+
+void XclExpMergedcells::Save( XclExpStream& rStrm )
+{
+ if( GetBiff() != EXC_BIFF8 )
+ return;
+
+ XclRangeList aXclRanges;
+ GetAddressConverter().ConvertRangeList( aXclRanges, maMergedRanges, true );
+ size_t nFirstRange = 0;
+ size_t nRemainingRanges = aXclRanges.size();
+ while( nRemainingRanges > 0 )
+ {
+ size_t nRangeCount = ::std::min< size_t >( nRemainingRanges, EXC_MERGEDCELLS_MAXCOUNT );
+ rStrm.StartRecord( EXC_ID_MERGEDCELLS, 2 + 8 * nRangeCount );
+ aXclRanges.WriteSubList( rStrm, nFirstRange, nRangeCount );
+ rStrm.EndRecord();
+ nFirstRange += nRangeCount;
+ nRemainingRanges -= nRangeCount;
+ }
+}
+
+void XclExpMergedcells::SaveXml( XclExpXmlStream& rStrm )
+{
+ size_t nCount = maMergedRanges.size();
+ if( !nCount )
+ return;
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_mergeCells, XML_count, OString::number(nCount));
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ const ScRange & rRange = maMergedRanges[ i ];
+ rWorksheet->singleElement(XML_mergeCell, XML_ref,
+ XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rRange));
+ }
+ rWorksheet->endElement( XML_mergeCells );
+}
+
+// Hyperlinks =================================================================
+
+XclExpHyperlink::XclExpHyperlink( const XclExpRoot& rRoot, const SvxURLField& rUrlField, const ScAddress& rScPos ) :
+ XclExpRecord( EXC_ID_HLINK ),
+ maScPos( rScPos ),
+ mxVarData( new SvMemoryStream ),
+ mnFlags( 0 )
+{
+ const OUString& rUrl = rUrlField.GetURL();
+ const OUString& rRepr = rUrlField.GetRepresentation();
+ INetURLObject aUrlObj( rUrl );
+ const INetProtocol eProtocol = aUrlObj.GetProtocol();
+ bool bWithRepr = !rRepr.isEmpty();
+ XclExpStream aXclStrm( *mxVarData, rRoot ); // using in raw write mode.
+
+ // description
+ if( bWithRepr )
+ {
+ XclExpString aDescr( rRepr, XclStrFlags::ForceUnicode, 255 );
+ aXclStrm << sal_uInt32( aDescr.Len() + 1 ); // string length + 1 trailing zero word
+ aDescr.WriteBuffer( aXclStrm ); // NO flags
+ aXclStrm << sal_uInt16( 0 );
+
+ mnFlags |= EXC_HLINK_DESCR;
+ m_Repr = rRepr;
+ }
+
+ // file link or URL
+ if( eProtocol == INetProtocol::File || eProtocol == INetProtocol::Smb )
+ {
+ sal_uInt16 nLevel;
+ bool bRel;
+ OUString aFileName(
+ BuildFileName(nLevel, bRel, rUrl, rRoot, rRoot.GetOutput() == EXC_OUTPUT_XML_2007));
+
+ if( eProtocol == INetProtocol::Smb )
+ {
+ // #n382718# (and #n261623#) Convert smb notation to '\\'
+ aFileName = aUrlObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ aFileName = aFileName.copy(4); // skip the 'smb:' part
+ aFileName = aFileName.replace('/', '\\');
+ }
+
+ if( !bRel )
+ mnFlags |= EXC_HLINK_ABS;
+ mnFlags |= EXC_HLINK_BODY;
+
+ OString aAsciiLink(OUStringToOString(aFileName,
+ rRoot.GetTextEncoding()));
+ XclExpString aLink( aFileName, XclStrFlags::ForceUnicode, 255 );
+ aXclStrm << XclTools::maGuidFileMoniker
+ << nLevel
+ << sal_uInt32( aAsciiLink.getLength() + 1 ); // string length + 1 trailing zero byte
+ aXclStrm.Write( aAsciiLink.getStr(), aAsciiLink.getLength() );
+ aXclStrm << sal_uInt8( 0 )
+ << sal_uInt32( 0xDEADFFFF );
+ aXclStrm.WriteZeroBytes( 20 );
+ aXclStrm << sal_uInt32( aLink.GetBufferSize() + 6 )
+ << sal_uInt32( aLink.GetBufferSize() ) // byte count, not string length
+ << sal_uInt16( 0x0003 );
+ aLink.WriteBuffer( aXclStrm ); // NO flags
+
+ if (m_Repr.isEmpty())
+ m_Repr = aFileName;
+
+ msTarget = XclXmlUtils::ToOUString( aLink );
+
+ if( bRel )
+ {
+ for( int i = 0; i < nLevel; ++i )
+ msTarget = "../" + msTarget;
+ }
+ else if (rRoot.GetOutput() != EXC_OUTPUT_XML_2007)
+ {
+ // xls expects the file:/// part appended ( or at least
+ // ms2007 does, ms2010 is more tolerant )
+ msTarget = "file:///" + msTarget;
+ }
+ }
+ else if( eProtocol != INetProtocol::NotValid )
+ {
+ XclExpString aUrl( aUrlObj.GetURLNoMark(), XclStrFlags::ForceUnicode, 255 );
+ aXclStrm << XclTools::maGuidUrlMoniker
+ << sal_uInt32( aUrl.GetBufferSize() + 2 ); // byte count + 1 trailing zero word
+ aUrl.WriteBuffer( aXclStrm ); // NO flags
+ aXclStrm << sal_uInt16( 0 );
+
+ mnFlags |= EXC_HLINK_BODY | EXC_HLINK_ABS;
+ if (m_Repr.isEmpty())
+ m_Repr = rUrl;
+
+ msTarget = XclXmlUtils::ToOUString( aUrl );
+ }
+ else if( !rUrl.isEmpty() && rUrl[0] == '#' ) // hack for #89066#
+ {
+ OUString aTextMark( rUrl.copy( 1 ) );
+
+ sal_Int32 nSepPos = aTextMark.lastIndexOf( '!' );
+ sal_Int32 nPointPos = aTextMark.lastIndexOf( '.' );
+ // last dot is the separator, if there is no ! after it
+ if(nSepPos < nPointPos)
+ {
+ nSepPos = nPointPos;
+ aTextMark = aTextMark.replaceAt( nSepPos, 1, u"!" );
+ }
+
+ if (nSepPos != -1)
+ {
+ std::u16string_view aSheetName(aTextMark.subView(0, nSepPos));
+
+ if (aSheetName.find(' ') != std::u16string_view::npos && aSheetName[0] != '\'')
+ {
+ aTextMark = "'" + aTextMark.replaceAt(nSepPos, 0, u"'");
+ }
+ }
+ else
+ {
+ SCTAB nTab;
+ if (rRoot.GetDoc().GetTable(aTextMark, nTab))
+ aTextMark += "!A1"; // tdf#143220 link to sheet not valid without cell reference
+ }
+
+ mxTextMark.reset( new XclExpString( aTextMark, XclStrFlags::ForceUnicode, 255 ) );
+ }
+
+ // text mark
+ if( !mxTextMark && aUrlObj.HasMark() )
+ mxTextMark.reset( new XclExpString( aUrlObj.GetMark(), XclStrFlags::ForceUnicode, 255 ) );
+
+ if( mxTextMark )
+ {
+ aXclStrm << sal_uInt32( mxTextMark->Len() + 1 ); // string length + 1 trailing zero word
+ mxTextMark->WriteBuffer( aXclStrm ); // NO flags
+ aXclStrm << sal_uInt16( 0 );
+
+ mnFlags |= EXC_HLINK_MARK;
+
+ OUString location = XclXmlUtils::ToOUString(*mxTextMark);
+ if (!location.isEmpty() && msTarget.endsWith(OUStringConcatenation("#" + location)))
+ msTarget = msTarget.copy(0, msTarget.getLength() - location.getLength() - 1);
+ }
+
+ SetRecSize( 32 + mxVarData->Tell() );
+}
+
+XclExpHyperlink::~XclExpHyperlink()
+{
+}
+
+OUString XclExpHyperlink::BuildFileName(
+ sal_uInt16& rnLevel, bool& rbRel, const OUString& rUrl, const XclExpRoot& rRoot, bool bEncoded )
+{
+ INetURLObject aURLObject( rUrl );
+ OUString aDosName(bEncoded ? aURLObject.GetMainURL(INetURLObject::DecodeMechanism::ToIUri)
+ : aURLObject.getFSysPath(FSysStyle::Dos));
+ rnLevel = 0;
+ rbRel = rRoot.IsRelUrl();
+
+ if( rbRel )
+ {
+ // try to convert to relative file name
+ OUString aTmpName( aDosName );
+ aDosName = INetURLObject::GetRelURL( rRoot.GetBasePath(), rUrl,
+ INetURLObject::EncodeMechanism::WasEncoded,
+ (bEncoded ? INetURLObject::DecodeMechanism::ToIUri : INetURLObject::DecodeMechanism::WithCharset));
+
+ if (aDosName.startsWith(INET_FILE_SCHEME))
+ {
+ // not converted to rel -> back to old, return absolute flag
+ aDosName = aTmpName;
+ rbRel = false;
+ }
+ else if (aDosName.startsWith("./"))
+ {
+ aDosName = aDosName.copy(2);
+ }
+ else
+ {
+ while (aDosName.startsWith("../"))
+ {
+ aDosName = aDosName.copy(3);
+ ++rnLevel;
+ }
+ }
+ }
+ return aDosName;
+}
+
+void XclExpHyperlink::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nXclCol = static_cast< sal_uInt16 >( maScPos.Col() );
+ sal_uInt16 nXclRow = static_cast< sal_uInt16 >( maScPos.Row() );
+ rStrm << nXclRow << nXclRow << nXclCol << nXclCol;
+ WriteEmbeddedData( rStrm );
+}
+
+void XclExpHyperlink::WriteEmbeddedData( XclExpStream& rStrm )
+{
+ rStrm << XclTools::maGuidStdLink
+ << sal_uInt32( 2 )
+ << mnFlags;
+
+ mxVarData->Seek( STREAM_SEEK_TO_BEGIN );
+ rStrm.CopyFromStream( *mxVarData );
+}
+
+void XclExpHyperlink::SaveXml( XclExpXmlStream& rStrm )
+{
+ OUString sId = !msTarget.isEmpty() ? rStrm.addRelation( rStrm.GetCurrentStream()->getOutputStream(),
+ oox::getRelationship(Relationship::HYPERLINK),
+ msTarget, true ) : OUString();
+ std::optional<OString> sTextMark;
+ if (mxTextMark)
+ sTextMark = XclXmlUtils::ToOString(*mxTextMark);
+ rStrm.GetCurrentStream()->singleElement( XML_hyperlink,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maScPos),
+ FSNS( XML_r, XML_id ), sax_fastparser::UseIf(sId, !sId.isEmpty()),
+ XML_location, sTextMark,
+ // OOXTODO: XML_tooltip, from record HLinkTooltip 800h wzTooltip
+ XML_display, m_Repr );
+}
+
+// Label ranges ===============================================================
+
+XclExpLabelranges::XclExpLabelranges( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ SCTAB nScTab = GetCurrScTab();
+ // row label ranges
+ FillRangeList( maRowRanges, rRoot.GetDoc().GetRowNameRangesRef(), nScTab );
+ // row labels only over 1 column (restriction of Excel97/2000/XP)
+ for ( size_t i = 0, nRanges = maRowRanges.size(); i < nRanges; ++i )
+ {
+ ScRange & rScRange = maRowRanges[ i ];
+ if( rScRange.aStart.Col() != rScRange.aEnd.Col() )
+ rScRange.aEnd.SetCol( rScRange.aStart.Col() );
+ }
+ // col label ranges
+ FillRangeList( maColRanges, rRoot.GetDoc().GetColNameRangesRef(), nScTab );
+}
+
+void XclExpLabelranges::FillRangeList( ScRangeList& rScRanges,
+ const ScRangePairListRef& xLabelRangesRef, SCTAB nScTab )
+{
+ for ( size_t i = 0, nPairs = xLabelRangesRef->size(); i < nPairs; ++i )
+ {
+ const ScRangePair & rRangePair = (*xLabelRangesRef)[i];
+ const ScRange& rScRange = rRangePair.GetRange( 0 );
+ if( rScRange.aStart.Tab() == nScTab )
+ rScRanges.push_back( rScRange );
+ }
+}
+
+void XclExpLabelranges::Save( XclExpStream& rStrm )
+{
+ XclExpAddressConverter& rAddrConv = GetAddressConverter();
+ XclRangeList aRowXclRanges, aColXclRanges;
+ rAddrConv.ConvertRangeList( aRowXclRanges, maRowRanges, false );
+ rAddrConv.ConvertRangeList( aColXclRanges, maColRanges, false );
+ if( !aRowXclRanges.empty() || !aColXclRanges.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_LABELRANGES, 4 + 8 * (aRowXclRanges.size() + aColXclRanges.size()) );
+ rStrm << aRowXclRanges << aColXclRanges;
+ rStrm.EndRecord();
+ }
+}
+
+// Conditional formatting ====================================================
+
+/** Represents a CF record that contains one condition of a conditional format. */
+class XclExpCFImpl : protected XclExpRoot
+{
+public:
+ explicit XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin );
+
+ /** Writes the body of the CF record. */
+ void WriteBody( XclExpStream& rStrm );
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ const ScCondFormatEntry& mrFormatEntry; /// Calc conditional format entry.
+ ScAddress maOrigin; /// Top left cell of the combined range
+ XclFontData maFontData; /// Font formatting attributes.
+ XclExpCellBorder maBorder; /// Border formatting attributes.
+ XclExpCellArea maArea; /// Pattern formatting attributes.
+ XclTokenArrayRef mxTokArr1; /// Formula for first condition.
+ XclTokenArrayRef mxTokArr2; /// Formula for second condition.
+ sal_uInt32 mnFontColorId; /// Font color ID.
+ sal_uInt8 mnType; /// Type of the condition (cell/formula).
+ sal_uInt8 mnOperator; /// Comparison operator for cell type.
+ sal_Int32 mnPriority; /// Priority of this entry; needed for oox export
+ bool mbFontUsed; /// true = Any font attribute used.
+ bool mbHeightUsed; /// true = Font height used.
+ bool mbWeightUsed; /// true = Font weight used.
+ bool mbColorUsed; /// true = Font color used.
+ bool mbUnderlUsed; /// true = Font underline type used.
+ bool mbItalicUsed; /// true = Font posture used.
+ bool mbStrikeUsed; /// true = Font strikeout used.
+ bool mbBorderUsed; /// true = Border attribute used.
+ bool mbPattUsed; /// true = Pattern attribute used.
+ bool mbFormula2;
+};
+
+XclExpCFImpl::XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin ) :
+ XclExpRoot( rRoot ),
+ mrFormatEntry( rFormatEntry ),
+ maOrigin( aOrigin ),
+ mnFontColorId( 0 ),
+ mnType( EXC_CF_TYPE_CELL ),
+ mnOperator( EXC_CF_CMP_NONE ),
+ mnPriority( nPriority ),
+ mbFontUsed( false ),
+ mbHeightUsed( false ),
+ mbWeightUsed( false ),
+ mbColorUsed( false ),
+ mbUnderlUsed( false ),
+ mbItalicUsed( false ),
+ mbStrikeUsed( false ),
+ mbBorderUsed( false ),
+ mbPattUsed( false ),
+ mbFormula2(false)
+{
+ // Set correct tab for maOrigin from GetValidSrcPos() of the format-entry.
+ ScAddress aValidSrcPos = mrFormatEntry.GetValidSrcPos();
+ maOrigin.SetTab(aValidSrcPos.Tab());
+
+ /* Get formatting attributes here, and not in WriteBody(). This is needed to
+ correctly insert all colors into the palette. */
+
+ if( SfxStyleSheetBase* pStyleSheet = GetDoc().GetStyleSheetPool()->Find( mrFormatEntry.GetStyle(), SfxStyleFamily::Para ) )
+ {
+ const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
+
+ // font
+ mbHeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_HEIGHT, true );
+ mbWeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_WEIGHT, true );
+ mbColorUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_COLOR, true );
+ mbUnderlUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_UNDERLINE, true );
+ mbItalicUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_POSTURE, true );
+ mbStrikeUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_CROSSEDOUT, true );
+ mbFontUsed = mbHeightUsed || mbWeightUsed || mbColorUsed || mbUnderlUsed || mbItalicUsed || mbStrikeUsed;
+ if( mbFontUsed )
+ {
+ vcl::Font aFont;
+ ScPatternAttr::GetFont( aFont, rItemSet, SC_AUTOCOL_RAW );
+ maFontData.FillFromVclFont( aFont );
+ mnFontColorId = GetPalette().InsertColor( maFontData.maColor, EXC_COLOR_CELLTEXT );
+ }
+
+ // border
+ mbBorderUsed = ScfTools::CheckItem( rItemSet, ATTR_BORDER, true );
+ if( mbBorderUsed )
+ maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff() );
+
+ // pattern
+ mbPattUsed = ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true );
+ if( mbPattUsed )
+ maArea.FillFromItemSet( rItemSet, GetPalette(), true );
+ }
+
+ // *** mode and comparison operator ***
+
+ switch( rFormatEntry.GetOperation() )
+ {
+ case ScConditionMode::NONE:
+ mnType = EXC_CF_TYPE_NONE;
+ break;
+ case ScConditionMode::Between:
+ mnOperator = EXC_CF_CMP_BETWEEN;
+ mbFormula2 = true;
+ break;
+ case ScConditionMode::NotBetween:
+ mnOperator = EXC_CF_CMP_NOT_BETWEEN;
+ mbFormula2 = true;
+ break;
+ case ScConditionMode::Equal:
+ mnOperator = EXC_CF_CMP_EQUAL;
+ break;
+ case ScConditionMode::NotEqual:
+ mnOperator = EXC_CF_CMP_NOT_EQUAL;
+ break;
+ case ScConditionMode::Greater:
+ mnOperator = EXC_CF_CMP_GREATER;
+ break;
+ case ScConditionMode::Less:
+ mnOperator = EXC_CF_CMP_LESS;
+ break;
+ case ScConditionMode::EqGreater:
+ mnOperator = EXC_CF_CMP_GREATER_EQUAL;
+ break;
+ case ScConditionMode::EqLess:
+ mnOperator = EXC_CF_CMP_LESS_EQUAL;
+ break;
+ case ScConditionMode::Direct:
+ mnType = EXC_CF_TYPE_FMLA;
+ break;
+ default:
+ mnType = EXC_CF_TYPE_NONE;
+ OSL_FAIL( "XclExpCF::WriteBody - unknown condition type" );
+ }
+}
+
+void XclExpCFImpl::WriteBody( XclExpStream& rStrm )
+{
+
+ // *** formulas ***
+
+ XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
+
+ std::unique_ptr< ScTokenArray > xScTokArr( mrFormatEntry.CreateFlatCopiedTokenArray( 0 ) );
+ mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
+
+ if (mbFormula2)
+ {
+ xScTokArr = mrFormatEntry.CreateFlatCopiedTokenArray( 1 );
+ mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
+ }
+
+ // *** mode and comparison operator ***
+
+ rStrm << mnType << mnOperator;
+
+ // *** formula sizes ***
+
+ sal_uInt16 nFmlaSize1 = mxTokArr1 ? mxTokArr1->GetSize() : 0;
+ sal_uInt16 nFmlaSize2 = mxTokArr2 ? mxTokArr2->GetSize() : 0;
+ rStrm << nFmlaSize1 << nFmlaSize2;
+
+ // *** formatting blocks ***
+
+ if( mbFontUsed || mbBorderUsed || mbPattUsed )
+ {
+ sal_uInt32 nFlags = EXC_CF_ALLDEFAULT;
+
+ ::set_flag( nFlags, EXC_CF_BLOCK_FONT, mbFontUsed );
+ ::set_flag( nFlags, EXC_CF_BLOCK_BORDER, mbBorderUsed );
+ ::set_flag( nFlags, EXC_CF_BLOCK_AREA, mbPattUsed );
+
+ // attributes used -> set flags to 0.
+ ::set_flag( nFlags, EXC_CF_BORDER_ALL, !mbBorderUsed );
+ ::set_flag( nFlags, EXC_CF_AREA_ALL, !mbPattUsed );
+
+ rStrm << nFlags << sal_uInt16( 0 );
+
+ if( mbFontUsed )
+ {
+ // font height, 0xFFFFFFFF indicates unused
+ sal_uInt32 nHeight = mbHeightUsed ? maFontData.mnHeight : 0xFFFFFFFF;
+ // font style: italic and strikeout
+ sal_uInt32 nStyle = 0;
+ ::set_flag( nStyle, EXC_CF_FONT_STYLE, maFontData.mbItalic );
+ ::set_flag( nStyle, EXC_CF_FONT_STRIKEOUT, maFontData.mbStrikeout );
+ // font color, 0xFFFFFFFF indicates unused
+ sal_uInt32 nColor = mbColorUsed ? GetPalette().GetColorIndex( mnFontColorId ) : 0xFFFFFFFF;
+ // font used flags for italic, weight, and strikeout -> 0 = used, 1 = default
+ sal_uInt32 nFontFlags1 = EXC_CF_FONT_ALLDEFAULT;
+ ::set_flag( nFontFlags1, EXC_CF_FONT_STYLE, !(mbItalicUsed || mbWeightUsed) );
+ ::set_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT, !mbStrikeUsed );
+ // font used flag for underline -> 0 = used, 1 = default
+ sal_uInt32 nFontFlags3 = mbUnderlUsed ? 0 : EXC_CF_FONT_UNDERL;
+
+ rStrm.WriteZeroBytesToRecord( 64 );
+ rStrm << nHeight
+ << nStyle
+ << maFontData.mnWeight
+ << EXC_FONTESC_NONE
+ << maFontData.mnUnderline;
+ rStrm.WriteZeroBytesToRecord( 3 );
+ rStrm << nColor
+ << sal_uInt32( 0 )
+ << nFontFlags1
+ << EXC_CF_FONT_ESCAPEM // escapement never used -> set the flag
+ << nFontFlags3;
+ rStrm.WriteZeroBytesToRecord( 16 );
+ rStrm << sal_uInt16( 1 ); // must be 1
+ }
+
+ if( mbBorderUsed )
+ {
+ sal_uInt16 nLineStyle = 0;
+ sal_uInt32 nLineColor = 0;
+ maBorder.SetFinalColors( GetPalette() );
+ maBorder.FillToCF8( nLineStyle, nLineColor );
+ rStrm << nLineStyle << nLineColor << sal_uInt16( 0 );
+ }
+
+ if( mbPattUsed )
+ {
+ sal_uInt16 nPattern = 0, nColor = 0;
+ maArea.SetFinalColors( GetPalette() );
+ maArea.FillToCF8( nPattern, nColor );
+ rStrm << nPattern << nColor;
+ }
+ }
+ else
+ {
+ // no data blocks at all
+ rStrm << sal_uInt32( 0 ) << sal_uInt16( 0 );
+ }
+
+ // *** formulas ***
+
+ if( mxTokArr1 )
+ mxTokArr1->WriteArray( rStrm );
+ if( mxTokArr2 )
+ mxTokArr2->WriteArray( rStrm );
+}
+
+namespace {
+
+const char* GetOperatorString(ScConditionMode eMode, bool& bFrmla2)
+{
+ const char *pRet = nullptr;
+ switch(eMode)
+ {
+ case ScConditionMode::Equal:
+ pRet = "equal";
+ break;
+ case ScConditionMode::Less:
+ pRet = "lessThan";
+ break;
+ case ScConditionMode::Greater:
+ pRet = "greaterThan";
+ break;
+ case ScConditionMode::EqLess:
+ pRet = "lessThanOrEqual";
+ break;
+ case ScConditionMode::EqGreater:
+ pRet = "greaterThanOrEqual";
+ break;
+ case ScConditionMode::NotEqual:
+ pRet = "notEqual";
+ break;
+ case ScConditionMode::Between:
+ bFrmla2 = true;
+ pRet = "between";
+ break;
+ case ScConditionMode::NotBetween:
+ bFrmla2 = true;
+ pRet = "notBetween";
+ break;
+ case ScConditionMode::Duplicate:
+ pRet = nullptr;
+ break;
+ case ScConditionMode::NotDuplicate:
+ pRet = nullptr;
+ break;
+ case ScConditionMode::BeginsWith:
+ pRet = "beginsWith";
+ break;
+ case ScConditionMode::EndsWith:
+ pRet = "endsWith";
+ break;
+ case ScConditionMode::ContainsText:
+ pRet = "containsText";
+ break;
+ case ScConditionMode::NotContainsText:
+ pRet = "notContains";
+ break;
+ case ScConditionMode::Direct:
+ break;
+ case ScConditionMode::NONE:
+ default:
+ break;
+ }
+ return pRet;
+}
+
+const char* GetTypeString(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::Direct:
+ return "expression";
+ case ScConditionMode::Top10:
+ case ScConditionMode::TopPercent:
+ case ScConditionMode::Bottom10:
+ case ScConditionMode::BottomPercent:
+ return "top10";
+ case ScConditionMode::AboveAverage:
+ case ScConditionMode::BelowAverage:
+ case ScConditionMode::AboveEqualAverage:
+ case ScConditionMode::BelowEqualAverage:
+ return "aboveAverage";
+ case ScConditionMode::NotDuplicate:
+ return "uniqueValues";
+ case ScConditionMode::Duplicate:
+ return "duplicateValues";
+ case ScConditionMode::Error:
+ return "containsErrors";
+ case ScConditionMode::NoError:
+ return "notContainsErrors";
+ case ScConditionMode::BeginsWith:
+ return "beginsWith";
+ case ScConditionMode::EndsWith:
+ return "endsWith";
+ case ScConditionMode::ContainsText:
+ return "containsText";
+ case ScConditionMode::NotContainsText:
+ return "notContainsText";
+ default:
+ return "cellIs";
+ }
+}
+
+bool IsTopBottomRule(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::Top10:
+ case ScConditionMode::Bottom10:
+ case ScConditionMode::TopPercent:
+ case ScConditionMode::BottomPercent:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool IsTextRule(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool RequiresFormula(ScConditionMode eMode)
+{
+ if (IsTopBottomRule(eMode))
+ return false;
+ else if (IsTextRule(eMode))
+ return false;
+
+ switch (eMode)
+ {
+ case ScConditionMode::NoError:
+ case ScConditionMode::Error:
+ case ScConditionMode::Duplicate:
+ case ScConditionMode::NotDuplicate:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool RequiresFixedFormula(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::NoError:
+ case ScConditionMode::Error:
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+OString GetFixedFormula(ScConditionMode eMode, const ScAddress& rAddress, std::string_view rText)
+{
+ OStringBuffer aBuffer;
+ XclXmlUtils::ToOString(aBuffer, rAddress);
+ OString aPos = aBuffer.makeStringAndClear();
+ switch (eMode)
+ {
+ case ScConditionMode::Error:
+ return OString("ISERROR(" + aPos + ")") ;
+ case ScConditionMode::NoError:
+ return OString("NOT(ISERROR(" + aPos + "))") ;
+ case ScConditionMode::BeginsWith:
+ return OString("LEFT(" + aPos + ",LEN(\"" + rText + "\"))=\"" + rText + "\"");
+ case ScConditionMode::EndsWith:
+ return OString("RIGHT(" + aPos +",LEN(\"" + rText + "\"))=\"" + rText + "\"");
+ case ScConditionMode::ContainsText:
+ return OString(OString::Concat("NOT(ISERROR(SEARCH(\"") + rText + "\"," + aPos + ")))");
+ case ScConditionMode::NotContainsText:
+ return OString(OString::Concat("ISERROR(SEARCH(\"") + rText + "\"," + aPos + "))");
+ default:
+ break;
+ }
+
+ return "";
+}
+
+}
+
+void XclExpCFImpl::SaveXml( XclExpXmlStream& rStrm )
+{
+ bool bFmla2 = false;
+ ScConditionMode eOperation = mrFormatEntry.GetOperation();
+ bool bAboveAverage = eOperation == ScConditionMode::AboveAverage ||
+ eOperation == ScConditionMode::AboveEqualAverage;
+ bool bEqualAverage = eOperation == ScConditionMode::AboveEqualAverage ||
+ eOperation == ScConditionMode::BelowEqualAverage;
+ bool bBottom = eOperation == ScConditionMode::Bottom10
+ || eOperation == ScConditionMode::BottomPercent;
+ bool bPercent = eOperation == ScConditionMode::TopPercent ||
+ eOperation == ScConditionMode::BottomPercent;
+ OUString aRank("0");
+ if(IsTopBottomRule(eOperation))
+ {
+ // position and formula grammar are not important
+ // we only store a number there
+ aRank = mrFormatEntry.GetExpression(ScAddress(0,0,0), 0);
+ }
+ OString aText;
+ if(IsTextRule(eOperation))
+ {
+ // we need to write the text without quotes
+ // we have to actually get the string from
+ // the token array for that
+ std::unique_ptr<ScTokenArray> pTokenArray(mrFormatEntry.CreateFlatCopiedTokenArray(0));
+ if(pTokenArray->GetLen())
+ aText = pTokenArray->FirstToken()->GetString().getString().toUtf8();
+ }
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, GetTypeString( mrFormatEntry.GetOperation() ),
+ XML_priority, OString::number(mnPriority + 1),
+ XML_operator, GetOperatorString( mrFormatEntry.GetOperation(), bFmla2 ),
+ XML_aboveAverage, ToPsz10(bAboveAverage),
+ XML_equalAverage, ToPsz10(bEqualAverage),
+ XML_bottom, ToPsz10(bBottom),
+ XML_percent, ToPsz10(bPercent),
+ XML_rank, aRank,
+ XML_text, aText,
+ XML_dxfId, OString::number(GetDxfs().GetDxfId(mrFormatEntry.GetStyle())) );
+
+ if (RequiresFixedFormula(eOperation))
+ {
+ rWorksheet->startElement(XML_formula);
+ OString aFormula = GetFixedFormula(eOperation, maOrigin, aText);
+ rWorksheet->writeEscaped(aFormula.getStr());
+ rWorksheet->endElement( XML_formula );
+ }
+ else if(RequiresFormula(eOperation))
+ {
+ rWorksheet->startElement(XML_formula);
+ std::unique_ptr<ScTokenArray> pTokenArray(mrFormatEntry.CreateFlatCopiedTokenArray(0));
+ rWorksheet->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry.GetValidSrcPos(),
+ pTokenArray.get()));
+ rWorksheet->endElement( XML_formula );
+ if (bFmla2)
+ {
+ rWorksheet->startElement(XML_formula);
+ std::unique_ptr<ScTokenArray> pTokenArray2(mrFormatEntry.CreateFlatCopiedTokenArray(1));
+ rWorksheet->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry.GetValidSrcPos(),
+ pTokenArray2.get()));
+ rWorksheet->endElement( XML_formula );
+ }
+ }
+ // OOXTODO: XML_extLst
+ rWorksheet->endElement( XML_cfRule );
+}
+
+XclExpCF::XclExpCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin ) :
+ XclExpRecord( EXC_ID_CF ),
+ XclExpRoot( rRoot ),
+ mxImpl( new XclExpCFImpl( rRoot, rFormatEntry, nPriority, aOrigin ) )
+{
+}
+
+XclExpCF::~XclExpCF()
+{
+}
+
+void XclExpCF::WriteBody( XclExpStream& rStrm )
+{
+ mxImpl->WriteBody( rStrm );
+}
+
+void XclExpCF::SaveXml( XclExpXmlStream& rStrm )
+{
+ mxImpl->SaveXml( rStrm );
+}
+
+XclExpDateFormat::XclExpDateFormat( const XclExpRoot& rRoot, const ScCondDateFormatEntry& rFormatEntry, sal_Int32 nPriority ):
+ XclExpRecord( EXC_ID_CF ),
+ XclExpRoot( rRoot ),
+ mrFormatEntry(rFormatEntry),
+ mnPriority(nPriority)
+{
+}
+
+XclExpDateFormat::~XclExpDateFormat()
+{
+}
+
+namespace {
+
+const char* getTimePeriodString( condformat::ScCondFormatDateType eType )
+{
+ switch(eType)
+ {
+ case condformat::TODAY:
+ return "today";
+ case condformat::YESTERDAY:
+ return "yesterday";
+ case condformat::TOMORROW:
+ return "yesterday";
+ case condformat::THISWEEK:
+ return "thisWeek";
+ case condformat::LASTWEEK:
+ return "lastWeek";
+ case condformat::NEXTWEEK:
+ return "nextWeek";
+ case condformat::THISMONTH:
+ return "thisMonth";
+ case condformat::LASTMONTH:
+ return "lastMonth";
+ case condformat::NEXTMONTH:
+ return "nextMonth";
+ case condformat::LAST7DAYS:
+ return "last7Days";
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+}
+
+void XclExpDateFormat::SaveXml( XclExpXmlStream& rStrm )
+{
+ // only write the supported entries into OOXML
+ const char* sTimePeriod = getTimePeriodString(mrFormatEntry.GetDateType());
+ if(!sTimePeriod)
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, "timePeriod",
+ XML_priority, OString::number(mnPriority + 1),
+ XML_timePeriod, sTimePeriod,
+ XML_dxfId, OString::number(GetDxfs().GetDxfId(mrFormatEntry.GetStyleName())) );
+ rWorksheet->endElement( XML_cfRule);
+}
+
+XclExpCfvo::XclExpCfvo(const XclExpRoot& rRoot, const ScColorScaleEntry& rEntry, const ScAddress& rAddr, bool bFirst):
+ XclExpRoot( rRoot ),
+ mrEntry(rEntry),
+ maSrcPos(rAddr),
+ mbFirst(bFirst)
+{
+}
+
+namespace {
+
+OString getColorScaleType( const ScColorScaleEntry& rEntry, bool bFirst )
+{
+ switch(rEntry.GetType())
+ {
+ case COLORSCALE_MIN:
+ return "min";
+ case COLORSCALE_MAX:
+ return "max";
+ case COLORSCALE_PERCENT:
+ return "percent";
+ case COLORSCALE_FORMULA:
+ return "formula";
+ case COLORSCALE_AUTO:
+ if(bFirst)
+ return "min";
+ else
+ return "max";
+ case COLORSCALE_PERCENTILE:
+ return "percentile";
+ default:
+ break;
+ }
+
+ return "num";
+}
+
+}
+
+void XclExpCfvo::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ OString aValue;
+ if(mrEntry.GetType() == COLORSCALE_FORMULA)
+ {
+ OUString aFormula = XclXmlUtils::ToOUString( GetCompileFormulaContext(), maSrcPos,
+ mrEntry.GetFormula());
+ aValue = OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8 );
+ }
+ else
+ {
+ aValue = OString::number( mrEntry.GetValue() );
+ }
+
+ rWorksheet->startElement( XML_cfvo,
+ XML_type, getColorScaleType(mrEntry, mbFirst),
+ XML_val, aValue );
+
+ rWorksheet->endElement( XML_cfvo );
+}
+
+XclExpColScaleCol::XclExpColScaleCol( const XclExpRoot& rRoot, const Color& rColor ):
+ XclExpRoot( rRoot ),
+ mrColor( rColor )
+{
+}
+
+XclExpColScaleCol::~XclExpColScaleCol()
+{
+}
+
+void XclExpColScaleCol::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(XML_color, XML_rgb, XclXmlUtils::ToOString(mrColor));
+
+ rWorksheet->endElement( XML_color );
+}
+
+namespace {
+
+OString createHexStringFromDigit(sal_uInt8 nDigit)
+{
+ OString aString = OString::number( nDigit, 16 );
+ if(aString.getLength() == 1)
+ aString += OString::number(0);
+ return aString;
+}
+
+OString createGuidStringFromInt(sal_uInt8 nGuid[16])
+{
+ OStringBuffer aBuffer;
+ aBuffer.append('{');
+ for(size_t i = 0; i < 16; ++i)
+ {
+ aBuffer.append(createHexStringFromDigit(nGuid[i]));
+ if(i == 3|| i == 5 || i == 7 || i == 9 )
+ aBuffer.append('-');
+ }
+ aBuffer.append('}');
+ OString aString = aBuffer.makeStringAndClear();
+ return aString.toAsciiUpperCase();
+}
+
+OString generateGUIDString()
+{
+ sal_uInt8 nGuid[16];
+ rtl_createUuid(nGuid, nullptr, true);
+ return createGuidStringFromInt(nGuid);
+}
+
+}
+
+XclExpCondfmt::XclExpCondfmt( const XclExpRoot& rRoot, const ScConditionalFormat& rCondFormat, const XclExtLstRef& xExtLst, sal_Int32& rIndex ) :
+ XclExpRecord( EXC_ID_CONDFMT ),
+ XclExpRoot( rRoot )
+{
+ const ScRangeList& aScRanges = rCondFormat.GetRange();
+ GetAddressConverter().ConvertRangeList( maXclRanges, aScRanges, true );
+ if( maXclRanges.empty() )
+ return;
+
+ std::vector<XclExpExtCondFormatData> aExtEntries;
+ ScAddress aOrigin = aScRanges.Combine().aStart;
+ for( size_t nIndex = 0, nCount = rCondFormat.size(); nIndex < nCount; ++nIndex )
+ if( const ScFormatEntry* pFormatEntry = rCondFormat.GetEntry( nIndex ) )
+ {
+ if(pFormatEntry->GetType() == ScFormatEntry::Type::Condition)
+ maCFList.AppendNewRecord( new XclExpCF( GetRoot(), static_cast<const ScCondFormatEntry&>(*pFormatEntry), ++rIndex, aOrigin ) );
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ {
+ const ScCondFormatEntry& rFormat = static_cast<const ScCondFormatEntry&>(*pFormatEntry);
+ XclExpExtCondFormatData aExtEntry;
+ aExtEntry.nPriority = ++rIndex;
+ aExtEntry.aGUID = generateGUIDString();
+ aExtEntry.pEntry = &rFormat;
+ aExtEntries.push_back(aExtEntry);
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Colorscale)
+ maCFList.AppendNewRecord( new XclExpColorScale( GetRoot(), static_cast<const ScColorScaleFormat&>(*pFormatEntry), ++rIndex ) );
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Databar)
+ {
+ const ScDataBarFormat& rFormat = static_cast<const ScDataBarFormat&>(*pFormatEntry);
+ XclExpExtCondFormatData aExtEntry;
+ aExtEntry.nPriority = -1;
+ aExtEntry.aGUID = generateGUIDString();
+ aExtEntry.pEntry = &rFormat;
+ aExtEntries.push_back(aExtEntry);
+
+ maCFList.AppendNewRecord( new XclExpDataBar( GetRoot(), rFormat, ++rIndex, aExtEntry.aGUID));
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Iconset)
+ {
+ // don't export iconSet entries that are not in OOXML
+ const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(*pFormatEntry);
+ bool bNeedsExt = false;
+ switch (rIconSet.GetIconSetData()->eIconSetType)
+ {
+ case IconSet_3Smilies:
+ case IconSet_3ColorSmilies:
+ case IconSet_3Stars:
+ case IconSet_3Triangles:
+ case IconSet_5Boxes:
+ {
+ bNeedsExt = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ bNeedsExt |= rIconSet.GetIconSetData()->mbCustom;
+
+ if (bNeedsExt)
+ {
+ XclExpExtCondFormatData aExtEntry;
+ aExtEntry.nPriority = ++rIndex;
+ aExtEntry.aGUID = generateGUIDString();
+ aExtEntry.pEntry = &rIconSet;
+ aExtEntries.push_back(aExtEntry);
+ }
+ else
+ maCFList.AppendNewRecord( new XclExpIconSet( GetRoot(), rIconSet, ++rIndex ) );
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Date)
+ maCFList.AppendNewRecord( new XclExpDateFormat( GetRoot(), static_cast<const ScCondDateFormatEntry&>(*pFormatEntry), ++rIndex ) );
+ }
+ aScRanges.Format( msSeqRef, ScRefFlags::VALID, GetDoc(), formula::FormulaGrammar::CONV_XL_OOX, ' ', true );
+
+ if(!aExtEntries.empty() && xExtLst)
+ {
+ XclExpExt* pParent = xExtLst->GetItem( XclExpExtDataBarType );
+ if( !pParent )
+ {
+ xExtLst->AddRecord( new XclExpExtCondFormat( *xExtLst ) );
+ pParent = xExtLst->GetItem( XclExpExtDataBarType );
+ }
+ static_cast<XclExpExtCondFormat*>(xExtLst->GetItem( XclExpExtDataBarType ))->AddRecord(
+ new XclExpExtConditionalFormatting( *pParent, aExtEntries, aScRanges));
+ }
+}
+
+XclExpCondfmt::~XclExpCondfmt()
+{
+}
+
+bool XclExpCondfmt::IsValidForBinary() const
+{
+ // ccf (2 bytes): An unsigned integer that specifies the count of CF records that follow this
+ // record. MUST be greater than or equal to 0x0001, and less than or equal to 0x0003.
+
+ SAL_WARN_IF( maCFList.GetSize() > 3, "sc.filter", "More than 3 conditional filters for cell(s), won't export");
+
+ return !maCFList.IsEmpty() && maCFList.GetSize() <= 3 && !maXclRanges.empty();
+}
+
+bool XclExpCondfmt::IsValidForXml() const
+{
+ return !maCFList.IsEmpty() && !maXclRanges.empty();
+}
+
+void XclExpCondfmt::Save( XclExpStream& rStrm )
+{
+ if( IsValidForBinary() )
+ {
+ XclExpRecord::Save( rStrm );
+ maCFList.Save( rStrm );
+ }
+}
+
+void XclExpCondfmt::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE( !maCFList.IsEmpty(), "XclExpCondfmt::WriteBody - no CF records to write" );
+ OSL_ENSURE( !maXclRanges.empty(), "XclExpCondfmt::WriteBody - no cell ranges found" );
+
+ rStrm << static_cast< sal_uInt16 >( maCFList.GetSize() )
+ << sal_uInt16( 1 )
+ << maXclRanges.GetEnclosingRange()
+ << maXclRanges;
+}
+
+void XclExpCondfmt::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( !IsValidForXml() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_conditionalFormatting,
+ XML_sqref, msSeqRef
+ // OOXTODO: XML_pivot
+ );
+
+ maCFList.SaveXml( rStrm );
+
+ rWorksheet->endElement( XML_conditionalFormatting );
+}
+
+XclExpColorScale::XclExpColorScale( const XclExpRoot& rRoot, const ScColorScaleFormat& rFormat, sal_Int32 nPriority ):
+ XclExpRoot( rRoot ),
+ mnPriority( nPriority )
+{
+ const ScRange & rRange = rFormat.GetRange().front();
+ ScAddress aAddr = rRange.aStart;
+ for(const auto& rxColorScaleEntry : rFormat)
+ {
+ // exact position is not important, we allow only absolute refs
+
+ XclExpCfvoList::RecordRefType xCfvo( new XclExpCfvo( GetRoot(), *rxColorScaleEntry, aAddr ) );
+ maCfvoList.AppendRecord( xCfvo );
+ XclExpColScaleColList::RecordRefType xClo( new XclExpColScaleCol( GetRoot(), rxColorScaleEntry->GetColor() ) );
+ maColList.AppendRecord( xClo );
+ }
+}
+
+void XclExpColorScale::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, "colorScale",
+ XML_priority, OString::number(mnPriority + 1) );
+
+ rWorksheet->startElement(XML_colorScale);
+
+ maCfvoList.SaveXml(rStrm);
+ maColList.SaveXml(rStrm);
+
+ rWorksheet->endElement( XML_colorScale );
+
+ rWorksheet->endElement( XML_cfRule );
+}
+
+XclExpDataBar::XclExpDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, sal_Int32 nPriority, const OString& rGUID):
+ XclExpRoot( rRoot ),
+ mrFormat( rFormat ),
+ mnPriority( nPriority ),
+ maGUID(rGUID)
+{
+ const ScRange & rRange = rFormat.GetRange().front();
+ ScAddress aAddr = rRange.aStart;
+ // exact position is not important, we allow only absolute refs
+ mpCfvoLowerLimit.reset(
+ new XclExpCfvo(GetRoot(), *mrFormat.GetDataBarData()->mpLowerLimit, aAddr, true));
+ mpCfvoUpperLimit.reset(
+ new XclExpCfvo(GetRoot(), *mrFormat.GetDataBarData()->mpUpperLimit, aAddr, false));
+
+ mpCol.reset( new XclExpColScaleCol( GetRoot(), mrFormat.GetDataBarData()->maPositiveColor ) );
+}
+
+void XclExpDataBar::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, "dataBar",
+ XML_priority, OString::number(mnPriority + 1) );
+
+ rWorksheet->startElement( XML_dataBar,
+ XML_showValue, ToPsz10(!mrFormat.GetDataBarData()->mbOnlyBar),
+ XML_minLength, OString::number(sal_uInt32(mrFormat.GetDataBarData()->mnMinLength)),
+ XML_maxLength, OString::number(sal_uInt32(mrFormat.GetDataBarData()->mnMaxLength)) );
+
+ mpCfvoLowerLimit->SaveXml(rStrm);
+ mpCfvoUpperLimit->SaveXml(rStrm);
+ mpCol->SaveXml(rStrm);
+
+ rWorksheet->endElement( XML_dataBar );
+
+ // extLst entries for Excel 2010 and 2013
+ rWorksheet->startElement(XML_extLst);
+ rWorksheet->startElement(XML_ext,
+ FSNS(XML_xmlns, XML_x14), rStrm.getNamespaceURL(OOX_NS(xls14Lst)),
+ XML_uri, "{B025F937-C7B1-47D3-B67F-A62EFF666E3E}");
+
+ rWorksheet->startElementNS( XML_x14, XML_id );
+ rWorksheet->write(maGUID);
+ rWorksheet->endElementNS( XML_x14, XML_id );
+
+ rWorksheet->endElement( XML_ext );
+ rWorksheet->endElement( XML_extLst );
+
+ rWorksheet->endElement( XML_cfRule );
+}
+
+XclExpIconSet::XclExpIconSet( const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, sal_Int32 nPriority ):
+ XclExpRoot( rRoot ),
+ mrFormat( rFormat ),
+ mnPriority( nPriority )
+{
+ const ScRange & rRange = rFormat.GetRange().front();
+ ScAddress aAddr = rRange.aStart;
+ for (auto const& itr : rFormat)
+ {
+ // exact position is not important, we allow only absolute refs
+
+ XclExpCfvoList::RecordRefType xCfvo( new XclExpCfvo( GetRoot(), *itr, aAddr ) );
+ maCfvoList.AppendRecord( xCfvo );
+ }
+}
+
+void XclExpIconSet::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, "iconSet",
+ XML_priority, OString::number(mnPriority + 1) );
+
+ const char* pIconSetName = ScIconSetFormat::getIconSetName(mrFormat.GetIconSetData()->eIconSetType);
+ rWorksheet->startElement( XML_iconSet,
+ XML_iconSet, pIconSetName,
+ XML_showValue, sax_fastparser::UseIf("0", !mrFormat.GetIconSetData()->mbShowValue),
+ XML_reverse, sax_fastparser::UseIf("1", mrFormat.GetIconSetData()->mbReverse));
+
+ maCfvoList.SaveXml( rStrm );
+
+ rWorksheet->endElement( XML_iconSet );
+ rWorksheet->endElement( XML_cfRule );
+}
+
+XclExpCondFormatBuffer::XclExpCondFormatBuffer( const XclExpRoot& rRoot, const XclExtLstRef& xExtLst ) :
+ XclExpRoot( rRoot )
+{
+ if( const ScConditionalFormatList* pCondFmtList = GetDoc().GetCondFormList(GetCurrScTab()) )
+ {
+ sal_Int32 nIndex = 0;
+ for( const auto& rxCondFmt : *pCondFmtList)
+ {
+ XclExpCondfmtList::RecordRefType xCondfmtRec( new XclExpCondfmt( GetRoot(), *rxCondFmt, xExtLst, nIndex ));
+ if( xCondfmtRec->IsValidForXml() )
+ maCondfmtList.AppendRecord( xCondfmtRec );
+ }
+ }
+}
+
+void XclExpCondFormatBuffer::Save( XclExpStream& rStrm )
+{
+ maCondfmtList.Save( rStrm );
+}
+
+void XclExpCondFormatBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ maCondfmtList.SaveXml( rStrm );
+}
+
+// Validation =================================================================
+
+namespace {
+
+/** Writes a formula for the DV record. */
+void lclWriteDvFormula( XclExpStream& rStrm, const XclTokenArray* pXclTokArr )
+{
+ sal_uInt16 nFmlaSize = pXclTokArr ? pXclTokArr->GetSize() : 0;
+ rStrm << nFmlaSize << sal_uInt16( 0 );
+ if( pXclTokArr )
+ pXclTokArr->WriteArray( rStrm );
+}
+
+/** Writes a formula for the DV record, based on a single string. */
+void lclWriteDvFormula( XclExpStream& rStrm, const XclExpString& rString )
+{
+ // fake a formula with a single tStr token
+ rStrm << static_cast< sal_uInt16 >( rString.GetSize() + 1 )
+ << sal_uInt16( 0 )
+ << EXC_TOKID_STR
+ << rString;
+}
+
+const char* lcl_GetValidationType( sal_uInt32 nFlags )
+{
+ switch( nFlags & EXC_DV_MODE_MASK )
+ {
+ case EXC_DV_MODE_ANY: return "none";
+ case EXC_DV_MODE_WHOLE: return "whole";
+ case EXC_DV_MODE_DECIMAL: return "decimal";
+ case EXC_DV_MODE_LIST: return "list";
+ case EXC_DV_MODE_DATE: return "date";
+ case EXC_DV_MODE_TIME: return "time";
+ case EXC_DV_MODE_TEXTLEN: return "textLength";
+ case EXC_DV_MODE_CUSTOM: return "custom";
+ }
+ return nullptr;
+}
+
+const char* lcl_GetOperatorType( sal_uInt32 nFlags )
+{
+ switch( nFlags & EXC_DV_COND_MASK )
+ {
+ case EXC_DV_COND_BETWEEN: return "between";
+ case EXC_DV_COND_NOTBETWEEN: return "notBetween";
+ case EXC_DV_COND_EQUAL: return "equal";
+ case EXC_DV_COND_NOTEQUAL: return "notEqual";
+ case EXC_DV_COND_GREATER: return "greaterThan";
+ case EXC_DV_COND_LESS: return "lessThan";
+ case EXC_DV_COND_EQGREATER: return "greaterThanOrEqual";
+ case EXC_DV_COND_EQLESS: return "lessThanOrEqual";
+ }
+ return nullptr;
+}
+
+const char* lcl_GetErrorType( sal_uInt32 nFlags )
+{
+ switch (nFlags & EXC_DV_ERROR_MASK)
+ {
+ case EXC_DV_ERROR_STOP: return "stop";
+ case EXC_DV_ERROR_WARNING: return "warning";
+ case EXC_DV_ERROR_INFO: return "information";
+ }
+ return nullptr;
+}
+
+void lcl_SetValidationText(const OUString& rText, XclExpString& rValidationText)
+{
+ if ( !rText.isEmpty() )
+ {
+ // maximum length allowed in Excel is 255 characters
+ if ( rText.getLength() > 255 )
+ {
+ OUStringBuffer aBuf( rText );
+ rValidationText.Assign(
+ comphelper::string::truncateToLength(aBuf, 255).makeStringAndClear() );
+ }
+ else
+ rValidationText.Assign( rText );
+ }
+ else
+ rValidationText.Assign( '\0' );
+}
+
+} // namespace
+
+XclExpDV::XclExpDV( const XclExpRoot& rRoot, sal_uLong nScHandle ) :
+ XclExpRecord( EXC_ID_DV ),
+ XclExpRoot( rRoot ),
+ mnFlags( 0 ),
+ mnScHandle( nScHandle )
+{
+ if( const ScValidationData* pValData = GetDoc().GetValidationEntry( mnScHandle ) )
+ {
+ // prompt box - empty string represented by single NUL character
+ OUString aTitle, aText;
+ bool bShowPrompt = pValData->GetInput( aTitle, aText );
+ lcl_SetValidationText(aTitle, maPromptTitle);
+ lcl_SetValidationText(aText, maPromptText);
+
+ // error box - empty string represented by single NUL character
+ ScValidErrorStyle eScErrorStyle;
+ bool bShowError = pValData->GetErrMsg( aTitle, aText, eScErrorStyle );
+ lcl_SetValidationText(aTitle, maErrorTitle);
+ lcl_SetValidationText(aText, maErrorText);
+
+ // flags
+ switch( pValData->GetDataMode() )
+ {
+ case SC_VALID_ANY: mnFlags |= EXC_DV_MODE_ANY; break;
+ case SC_VALID_WHOLE: mnFlags |= EXC_DV_MODE_WHOLE; break;
+ case SC_VALID_DECIMAL: mnFlags |= EXC_DV_MODE_DECIMAL; break;
+ case SC_VALID_LIST: mnFlags |= EXC_DV_MODE_LIST; break;
+ case SC_VALID_DATE: mnFlags |= EXC_DV_MODE_DATE; break;
+ case SC_VALID_TIME: mnFlags |= EXC_DV_MODE_TIME; break;
+ case SC_VALID_TEXTLEN: mnFlags |= EXC_DV_MODE_TEXTLEN; break;
+ case SC_VALID_CUSTOM: mnFlags |= EXC_DV_MODE_CUSTOM; break;
+ default: OSL_FAIL( "XclExpDV::XclExpDV - unknown mode" );
+ }
+
+ switch( pValData->GetOperation() )
+ {
+ case ScConditionMode::NONE:
+ case ScConditionMode::Equal: mnFlags |= EXC_DV_COND_EQUAL; break;
+ case ScConditionMode::Less: mnFlags |= EXC_DV_COND_LESS; break;
+ case ScConditionMode::Greater: mnFlags |= EXC_DV_COND_GREATER; break;
+ case ScConditionMode::EqLess: mnFlags |= EXC_DV_COND_EQLESS; break;
+ case ScConditionMode::EqGreater: mnFlags |= EXC_DV_COND_EQGREATER; break;
+ case ScConditionMode::NotEqual: mnFlags |= EXC_DV_COND_NOTEQUAL; break;
+ case ScConditionMode::Between: mnFlags |= EXC_DV_COND_BETWEEN; break;
+ case ScConditionMode::NotBetween: mnFlags |= EXC_DV_COND_NOTBETWEEN; break;
+ default: OSL_FAIL( "XclExpDV::XclExpDV - unknown condition" );
+ }
+ switch( eScErrorStyle )
+ {
+ case SC_VALERR_STOP: mnFlags |= EXC_DV_ERROR_STOP; break;
+ case SC_VALERR_WARNING: mnFlags |= EXC_DV_ERROR_WARNING; break;
+ case SC_VALERR_INFO: mnFlags |= EXC_DV_ERROR_INFO; break;
+ case SC_VALERR_MACRO:
+ // set INFO for validity with macro call, delete title
+ mnFlags |= EXC_DV_ERROR_INFO;
+ maErrorTitle.Assign( '\0' ); // contains macro name
+ break;
+ default: OSL_FAIL( "XclExpDV::XclExpDV - unknown error style" );
+ }
+ ::set_flag( mnFlags, EXC_DV_IGNOREBLANK, pValData->IsIgnoreBlank() );
+ ::set_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN, pValData->GetListType() == css::sheet::TableValidationVisibility::INVISIBLE );
+ ::set_flag( mnFlags, EXC_DV_SHOWPROMPT, bShowPrompt );
+ ::set_flag( mnFlags, EXC_DV_SHOWERROR, bShowError );
+
+ // formulas
+ XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
+
+ // first formula
+ std::unique_ptr< ScTokenArray > xScTokArr = pValData->CreateFlatCopiedTokenArray( 0 );
+ if (xScTokArr)
+ {
+ if( pValData->GetDataMode() == SC_VALID_LIST )
+ {
+ OUString aString;
+ if( XclTokenArrayHelper::GetStringList( aString, *xScTokArr, '\n' ) )
+ {
+ bool bList = false;
+ OUStringBuffer sListBuf;
+ OUStringBuffer sFormulaBuf;
+ sFormulaBuf.append( '"' );
+ /* Formula is a list of string tokens -> build the Excel string.
+ Data validity is BIFF8 only (important for the XclExpString object).
+ Excel uses the NUL character as string list separator. */
+ mxString1.reset( new XclExpString( XclStrFlags::EightBitLength ) );
+ if (!aString.isEmpty())
+ {
+ sal_Int32 nStringIx = 0;
+ for(;;)
+ {
+ const std::u16string_view aToken( o3tl::getToken(aString, 0, '\n', nStringIx ) );
+ if (aToken.find(',') != std::u16string_view::npos)
+ {
+ sListBuf.append('"');
+ sListBuf.append(aToken);
+ sListBuf.append('"');
+ bList = true;
+ }
+ else
+ sListBuf.append(aToken);
+ mxString1->Append( aToken );
+ sFormulaBuf.append( aToken );
+ if (nStringIx<0)
+ break;
+ sal_Unicode cUnicodeChar = 0;
+ mxString1->Append( std::u16string_view(&cUnicodeChar, 1) );
+ sFormulaBuf.append( ',' );
+ sListBuf.append( ',' );
+ }
+ }
+ ::set_flag( mnFlags, EXC_DV_STRINGLIST );
+
+ // maximum length allowed in Excel is 255 characters, and don't end with an empty token
+ // It should be 8192 but Excel doesn't accept it for unknown reason
+ // See also https://bugs.documentfoundation.org/show_bug.cgi?id=137167#c2 for more details
+ sal_uInt32 nLen = sFormulaBuf.getLength();
+ if( nLen > 256 ) // 255 + beginning quote mark
+ {
+ nLen = 256;
+ if( sFormulaBuf[nLen - 1] == ',' )
+ --nLen;
+ sFormulaBuf.truncate(nLen);
+ }
+
+ sFormulaBuf.append( '"' );
+ msFormula1 = sFormulaBuf.makeStringAndClear();
+ if (bList)
+ msList = sListBuf.makeStringAndClear();
+ else
+ sListBuf.remove(0, sListBuf.getLength());
+ }
+ else
+ {
+ /* All other formulas in validation are stored like conditional
+ formatting formulas (with tRefN/tAreaN tokens as value or
+ array class). But NOT the cell references and defined names
+ in list validation - they are stored as reference class
+ tokens... Example:
+ 1) Cell must be equal to A1 -> formula is =A1 -> writes tRefNV token
+ 2) List is taken from A1 -> formula is =A1 -> writes tRefNR token
+ Formula compiler supports this by offering two different functions
+ CreateDataValFormula() and CreateListValFormula(). */
+ if(GetOutput() == EXC_OUTPUT_BINARY)
+ mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_LISTVAL, *xScTokArr );
+ else
+ msFormula1 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
+ xScTokArr.get());
+ }
+ }
+ else
+ {
+ // no list validation -> convert the formula
+ if(GetOutput() == EXC_OUTPUT_BINARY)
+ mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
+ else
+ msFormula1 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
+ xScTokArr.get());
+ }
+ }
+
+ // second formula
+ xScTokArr = pValData->CreateFlatCopiedTokenArray( 1 );
+ if (xScTokArr)
+ {
+ if(GetOutput() == EXC_OUTPUT_BINARY)
+ mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
+ else
+ msFormula2 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
+ xScTokArr.get());
+ }
+ }
+ else
+ {
+ OSL_FAIL( "XclExpDV::XclExpDV - missing core data" );
+ mnScHandle = ULONG_MAX;
+ }
+}
+
+XclExpDV::~XclExpDV()
+{
+}
+
+void XclExpDV::InsertCellRange( const ScRange& rRange )
+{
+ maScRanges.Join( rRange );
+}
+
+bool XclExpDV::Finalize()
+{
+ GetAddressConverter().ConvertRangeList( maXclRanges, maScRanges, true );
+ return (mnScHandle != ULONG_MAX) && !maXclRanges.empty();
+}
+
+void XclExpDV::WriteBody( XclExpStream& rStrm )
+{
+ // flags and strings
+ rStrm << mnFlags << maPromptTitle << maErrorTitle << maPromptText << maErrorText;
+ // condition formulas
+ if( mxString1 )
+ lclWriteDvFormula( rStrm, *mxString1 );
+ else
+ lclWriteDvFormula( rStrm, mxTokArr1.get() );
+ lclWriteDvFormula( rStrm, mxTokArr2.get() );
+ // cell ranges
+ rStrm << maXclRanges;
+}
+
+void XclExpDV::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_dataValidation,
+ XML_allowBlank, ToPsz( ::get_flag( mnFlags, EXC_DV_IGNOREBLANK ) ),
+ XML_error, XESTRING_TO_PSZ( maErrorText ),
+ XML_errorStyle, lcl_GetErrorType(mnFlags),
+ XML_errorTitle, XESTRING_TO_PSZ( maErrorTitle ),
+ // OOXTODO: XML_imeMode,
+ XML_operator, lcl_GetOperatorType( mnFlags ),
+ XML_prompt, XESTRING_TO_PSZ( maPromptText ),
+ XML_promptTitle, XESTRING_TO_PSZ( maPromptTitle ),
+ // showDropDown should have been showNoDropDown - check oox/xlsx import for details
+ XML_showDropDown, ToPsz( ::get_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN ) ),
+ XML_showErrorMessage, ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWERROR ) ),
+ XML_showInputMessage, ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWPROMPT ) ),
+ XML_sqref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maScRanges),
+ XML_type, lcl_GetValidationType(mnFlags) );
+ if (!msList.isEmpty())
+ {
+ rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent),
+ FSNS(XML_xmlns, XML_x12ac),rStrm.getNamespaceURL(OOX_NS(x12ac)),
+ FSNS(XML_xmlns, XML_mc),rStrm.getNamespaceURL(OOX_NS(mce)));
+ rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x12ac");
+ rWorksheet->startElement(FSNS(XML_x12ac, XML_list));
+ rWorksheet->writeEscaped(msList);
+ rWorksheet->endElement(FSNS(XML_x12ac, XML_list));
+ rWorksheet->endElement(FSNS(XML_mc, XML_Choice));
+ rWorksheet->startElement(FSNS(XML_mc, XML_Fallback));
+ rWorksheet->startElement(XML_formula1);
+ rWorksheet->writeEscaped(msFormula1);
+ rWorksheet->endElement(XML_formula1);
+ rWorksheet->endElement(FSNS(XML_mc, XML_Fallback));
+ rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent));
+ }
+ if (msList.isEmpty() && !msFormula1.isEmpty())
+ {
+ rWorksheet->startElement(XML_formula1);
+ rWorksheet->writeEscaped( msFormula1 );
+ rWorksheet->endElement( XML_formula1 );
+ }
+ if( !msFormula2.isEmpty() )
+ {
+ rWorksheet->startElement(XML_formula2);
+ rWorksheet->writeEscaped( msFormula2 );
+ rWorksheet->endElement( XML_formula2 );
+ }
+ rWorksheet->endElement( XML_dataValidation );
+}
+
+XclExpDval::XclExpDval( const XclExpRoot& rRoot ) :
+ XclExpRecord( EXC_ID_DVAL, 18 ),
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpDval::~XclExpDval()
+{
+}
+
+void XclExpDval::InsertCellRange( const ScRange& rRange, sal_uLong nScHandle )
+{
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ XclExpDV& rDVRec = SearchOrCreateDv( nScHandle );
+ rDVRec.InsertCellRange( rRange );
+ }
+}
+
+void XclExpDval::Save( XclExpStream& rStrm )
+{
+ // check all records
+ size_t nPos = maDVList.GetSize();
+ while( nPos )
+ {
+ --nPos; // backwards to keep nPos valid
+ XclExpDVRef xDVRec = maDVList.GetRecord( nPos );
+ if( !xDVRec->Finalize() )
+ maDVList.RemoveRecord( nPos );
+ }
+
+ // write the DVAL and the DV's
+ if( !maDVList.IsEmpty() )
+ {
+ XclExpRecord::Save( rStrm );
+ maDVList.Save( rStrm );
+ }
+}
+
+void XclExpDval::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maDVList.IsEmpty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_dataValidations,
+ XML_count, OString::number(maDVList.GetSize())
+ // OOXTODO: XML_disablePrompts,
+ // OOXTODO: XML_xWindow,
+ // OOXTODO: XML_yWindow
+ );
+ maDVList.SaveXml( rStrm );
+ rWorksheet->endElement( XML_dataValidations );
+}
+
+XclExpDV& XclExpDval::SearchOrCreateDv( sal_uLong nScHandle )
+{
+ // test last found record
+ if( mxLastFoundDV && (mxLastFoundDV->GetScHandle() == nScHandle) )
+ return *mxLastFoundDV;
+
+ // binary search
+ size_t nCurrPos = 0;
+ if( !maDVList.IsEmpty() )
+ {
+ size_t nFirstPos = 0;
+ size_t nLastPos = maDVList.GetSize() - 1;
+ bool bLoop = true;
+ sal_uLong nCurrScHandle = ::std::numeric_limits< sal_uLong >::max();
+ while( (nFirstPos <= nLastPos) && bLoop )
+ {
+ nCurrPos = (nFirstPos + nLastPos) / 2;
+ mxLastFoundDV = maDVList.GetRecord( nCurrPos );
+ nCurrScHandle = mxLastFoundDV->GetScHandle();
+ if( nCurrScHandle == nScHandle )
+ bLoop = false;
+ else if( nCurrScHandle < nScHandle )
+ nFirstPos = nCurrPos + 1;
+ else if( nCurrPos )
+ nLastPos = nCurrPos - 1;
+ else // special case for nLastPos = -1
+ bLoop = false;
+ }
+ if( nCurrScHandle == nScHandle )
+ return *mxLastFoundDV;
+ else if( nCurrScHandle < nScHandle )
+ ++nCurrPos;
+ }
+
+ // create new DV record
+ mxLastFoundDV = new XclExpDV( *this, nScHandle );
+ maDVList.InsertRecord( mxLastFoundDV, nCurrPos );
+ return *mxLastFoundDV;
+}
+
+void XclExpDval::WriteBody( XclExpStream& rStrm )
+{
+ rStrm.WriteZeroBytes( 10 );
+ rStrm << EXC_DVAL_NOOBJ << static_cast< sal_uInt32 >( maDVList.GetSize() );
+}
+
+// Web Queries ================================================================
+
+XclExpWebQuery::XclExpWebQuery(
+ const OUString& rRangeName,
+ const OUString& rUrl,
+ std::u16string_view rSource,
+ sal_Int32 nRefrSecs ) :
+ maDestRange( rRangeName ),
+ maUrl( rUrl ),
+ // refresh delay time: seconds -> minutes
+ mnRefresh( ulimit_cast< sal_Int16 >( (nRefrSecs + 59) / 60 ) ),
+ mbEntireDoc( false )
+{
+ // comma separated list of HTML table names or indexes
+ OUString aNewTables;
+ OUString aAppendTable;
+ bool bExitLoop = false;
+ if (!rSource.empty())
+ {
+ sal_Int32 nStringIx = 0;
+ do
+ {
+ OUString aToken( o3tl::getToken(rSource, 0, ';', nStringIx ) );
+ mbEntireDoc = ScfTools::IsHTMLDocName( aToken );
+ bExitLoop = mbEntireDoc || ScfTools::IsHTMLTablesName( aToken );
+ if( !bExitLoop && ScfTools::GetHTMLNameFromName( aToken, aAppendTable ) )
+ aNewTables = ScGlobal::addToken( aNewTables, aAppendTable, ',' );
+ }
+ while (nStringIx>0 && !bExitLoop);
+ }
+
+ if( !bExitLoop ) // neither HTML_all nor HTML_tables found
+ {
+ if( !aNewTables.isEmpty() )
+ mxQryTables.reset( new XclExpString( aNewTables ) );
+ else
+ mbEntireDoc = true;
+ }
+}
+
+XclExpWebQuery::~XclExpWebQuery()
+{
+}
+
+void XclExpWebQuery::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( !mbEntireDoc || !mxQryTables, "XclExpWebQuery::Save - illegal mode" );
+ sal_uInt16 nFlags;
+
+ // QSI record
+ rStrm.StartRecord( EXC_ID_QSI, 10 + maDestRange.GetSize() );
+ rStrm << EXC_QSI_DEFAULTFLAGS
+ << sal_uInt16( 0x0010 )
+ << sal_uInt16( 0x0012 )
+ << sal_uInt32( 0x00000000 )
+ << maDestRange;
+ rStrm.EndRecord();
+
+ // PARAMQRY record
+ nFlags = 0;
+ ::insert_value( nFlags, EXC_PQRYTYPE_WEBQUERY, 0, 3 );
+ ::set_flag( nFlags, EXC_PQRY_WEBQUERY );
+ ::set_flag( nFlags, EXC_PQRY_TABLES, !mbEntireDoc );
+ rStrm.StartRecord( EXC_ID_PQRY, 12 );
+ rStrm << nFlags
+ << sal_uInt16( 0x0000 )
+ << sal_uInt16( 0x0001 );
+ rStrm.WriteZeroBytes( 6 );
+ rStrm.EndRecord();
+
+ // WQSTRING record
+ rStrm.StartRecord( EXC_ID_WQSTRING, maUrl.GetSize() );
+ rStrm << maUrl;
+ rStrm.EndRecord();
+
+ // unknown record 0x0802
+ rStrm.StartRecord( EXC_ID_0802, 16 + maDestRange.GetSize() );
+ rStrm << EXC_ID_0802; // repeated record id ?!?
+ rStrm.WriteZeroBytes( 6 );
+ rStrm << sal_uInt16( 0x0003 )
+ << sal_uInt32( 0x00000000 )
+ << sal_uInt16( 0x0010 )
+ << maDestRange;
+ rStrm.EndRecord();
+
+ // WEBQRYSETTINGS record
+ nFlags = mxQryTables ? EXC_WQSETT_SPECTABLES : EXC_WQSETT_ALL;
+ rStrm.StartRecord( EXC_ID_WQSETT, 28 );
+ rStrm << EXC_ID_WQSETT // repeated record id ?!?
+ << sal_uInt16( 0x0000 )
+ << sal_uInt16( 0x0004 )
+ << sal_uInt16( 0x0000 )
+ << EXC_WQSETT_DEFAULTFLAGS
+ << nFlags;
+ rStrm.WriteZeroBytes( 10 );
+ rStrm << mnRefresh // refresh delay in minutes
+ << EXC_WQSETT_FORMATFULL
+ << sal_uInt16( 0x0000 );
+ rStrm.EndRecord();
+
+ // WEBQRYTABLES record
+ if( mxQryTables )
+ {
+ rStrm.StartRecord( EXC_ID_WQTABLES, 4 + mxQryTables->GetSize() );
+ rStrm << EXC_ID_WQTABLES // repeated record id ?!?
+ << sal_uInt16( 0x0000 )
+ << *mxQryTables; // comma separated list of source tables
+ rStrm.EndRecord();
+ }
+}
+
+XclExpWebQueryBuffer::XclExpWebQueryBuffer( const XclExpRoot& rRoot )
+{
+ SCTAB nScTab = rRoot.GetCurrScTab();
+ SfxObjectShell* pShell = rRoot.GetDocShell();
+ if( !pShell ) return;
+ ScfPropertySet aModelProp( pShell->GetModel() );
+ if( !aModelProp.Is() ) return;
+
+ Reference< XAreaLinks > xAreaLinks;
+ aModelProp.GetProperty( xAreaLinks, SC_UNO_AREALINKS );
+ if( !xAreaLinks.is() ) return;
+
+ for( sal_Int32 nIndex = 0, nCount = xAreaLinks->getCount(); nIndex < nCount; ++nIndex )
+ {
+ Reference< XAreaLink > xAreaLink( xAreaLinks->getByIndex( nIndex ), UNO_QUERY );
+ if( xAreaLink.is() )
+ {
+ CellRangeAddress aDestRange( xAreaLink->getDestArea() );
+ if( static_cast< SCTAB >( aDestRange.Sheet ) == nScTab )
+ {
+ ScfPropertySet aLinkProp( xAreaLink );
+ OUString aFilter;
+ if( aLinkProp.GetProperty( aFilter, SC_UNONAME_FILTER ) &&
+ (aFilter == EXC_WEBQRY_FILTER) )
+ {
+ // get properties
+ OUString /*aFilterOpt,*/ aUrl;
+ sal_Int32 nRefresh = 0;
+
+// aLinkProp.GetProperty( aFilterOpt, SC_UNONAME_FILTOPT );
+ aLinkProp.GetProperty( aUrl, SC_UNONAME_LINKURL );
+ aLinkProp.GetProperty( nRefresh, SC_UNONAME_REFDELAY );
+
+ OUString aAbsDoc( ScGlobal::GetAbsDocName( aUrl, pShell ) );
+ INetURLObject aUrlObj( aAbsDoc );
+ OUString aWebQueryUrl( aUrlObj.getFSysPath( FSysStyle::Dos ) );
+ if( aWebQueryUrl.isEmpty() )
+ aWebQueryUrl = aAbsDoc;
+
+ // find range or create a new range
+ OUString aRangeName;
+ ScRange aScDestRange;
+ ScUnoConversion::FillScRange( aScDestRange, aDestRange );
+ if( const ScRangeData* pRangeData = rRoot.GetNamedRanges().findByRange( aScDestRange ) )
+ {
+ aRangeName = pRangeData->GetName();
+ }
+ else
+ {
+ XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
+ XclExpNameManager& rNameMgr = rRoot.GetNameManager();
+
+ // create a new unique defined name containing the range
+ XclTokenArrayRef xTokArr = rFmlaComp.CreateFormula( EXC_FMLATYPE_WQUERY, aScDestRange );
+ sal_uInt16 nNameIdx = rNameMgr.InsertUniqueName( aUrlObj.getBase(), xTokArr, nScTab );
+ aRangeName = rNameMgr.GetOrigName( nNameIdx );
+ }
+
+ // create and store the web query record
+ if( !aRangeName.isEmpty() )
+ AppendNewRecord( new XclExpWebQuery(
+ aRangeName, aWebQueryUrl, xAreaLink->getSourceArea(), nRefresh ) );
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xedbdata.cxx b/sc/source/filter/excel/xedbdata.cxx
new file mode 100644
index 000000000..350f6f70e
--- /dev/null
+++ b/sc/source/filter/excel/xedbdata.cxx
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <xedbdata.hxx>
+#include <excrecds.hxx>
+#include <dbdata.hxx>
+#include <document.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+
+using namespace oox;
+
+namespace {
+
+/** (So far) dummy implementation of table export for BIFF5/BIFF7. */
+class XclExpTablesImpl5 : public XclExpTables
+{
+public:
+ explicit XclExpTablesImpl5( const XclExpRoot& rRoot );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+/** Implementation of table export for OOXML, so far dummy for BIFF8. */
+class XclExpTablesImpl8 : public XclExpTables
+{
+public:
+ explicit XclExpTablesImpl8( const XclExpRoot& rRoot );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+}
+
+XclExpTablesImpl5::XclExpTablesImpl5( const XclExpRoot& rRoot ) :
+ XclExpTables( rRoot )
+{
+}
+
+void XclExpTablesImpl5::Save( XclExpStream& /*rStrm*/ )
+{
+ // not implemented
+}
+
+void XclExpTablesImpl5::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+ // not applicable
+}
+
+
+XclExpTablesImpl8::XclExpTablesImpl8( const XclExpRoot& rRoot ) :
+ XclExpTables( rRoot )
+{
+}
+
+void XclExpTablesImpl8::Save( XclExpStream& /*rStrm*/ )
+{
+ // not implemented
+}
+
+void XclExpTablesImpl8::SaveXml( XclExpXmlStream& rStrm )
+{
+
+ sax_fastparser::FSHelperPtr& pWorksheetStrm = rStrm.GetCurrentStream();
+ pWorksheetStrm->startElement(XML_tableParts);
+ for (auto const& it : maTables)
+ {
+ OUString aRelId;
+ sax_fastparser::FSHelperPtr pTableStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/tables/", "table", it.mnTableId),
+ XclXmlUtils::GetStreamName("../tables/", "table", it.mnTableId),
+ pWorksheetStrm->getOutputStream(),
+ CREATE_XL_CONTENT_TYPE("table"),
+ CREATE_OFFICEDOC_RELATION_TYPE("table"),
+ &aRelId);
+
+ pWorksheetStrm->singleElement(XML_tablePart, FSNS(XML_r, XML_id), aRelId.toUtf8());
+
+ rStrm.PushStream( pTableStrm);
+ SaveTableXml( rStrm, it);
+ rStrm.PopStream();
+ }
+ pWorksheetStrm->endElement( XML_tableParts);
+}
+
+
+XclExpTablesManager::XclExpTablesManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpTablesManager::~XclExpTablesManager()
+{
+}
+
+void XclExpTablesManager::Initialize()
+{
+ // All non-const to be able to call RefreshTableColumnNames().
+ ScDocument& rDoc = GetDoc();
+ ScDBCollection* pDBColl = rDoc.GetDBCollection();
+ if (!pDBColl)
+ return;
+
+ ScDBCollection::NamedDBs& rDBs = pDBColl->getNamedDBs();
+ if (rDBs.empty())
+ return;
+
+ sal_Int32 nTableId = 0;
+ for (const auto& rxDB : rDBs)
+ {
+ ScDBData* pDBData = rxDB.get();
+ pDBData->RefreshTableColumnNames( &rDoc); // currently not in sync, so refresh
+ ScRange aRange( ScAddress::UNINITIALIZED);
+ pDBData->GetArea( aRange);
+ SCTAB nTab = aRange.aStart.Tab();
+ TablesMapType::iterator it = maTablesMap.find( nTab);
+ if (it == maTablesMap.end())
+ {
+ rtl::Reference< XclExpTables > pNew;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5:
+ pNew = new XclExpTablesImpl5( GetRoot());
+ break;
+ case EXC_BIFF8:
+ pNew = new XclExpTablesImpl8( GetRoot());
+ break;
+ default:
+ assert(!"Unknown BIFF type!");
+ continue; // for
+ }
+ ::std::pair< TablesMapType::iterator, bool > ins( maTablesMap.insert( ::std::make_pair( nTab, pNew)));
+ if (!ins.second)
+ {
+ assert(!"XclExpTablesManager::Initialize - XclExpTables insert failed");
+ continue; // for
+ }
+ it = ins.first;
+ }
+ it->second->AppendTable( pDBData, ++nTableId);
+ }
+}
+
+rtl::Reference< XclExpTables > XclExpTablesManager::GetTablesBySheet( SCTAB nTab )
+{
+ TablesMapType::iterator it = maTablesMap.find(nTab);
+ return it == maTablesMap.end() ? nullptr : it->second;
+}
+
+XclExpTables::Entry::Entry( const ScDBData* pData, sal_Int32 nTableId ) :
+ mpData(pData), mnTableId(nTableId)
+{
+}
+
+XclExpTables::XclExpTables( const XclExpRoot& rRoot ) :
+ XclExpRoot(rRoot)
+{
+}
+
+XclExpTables::~XclExpTables()
+{
+}
+
+void XclExpTables::AppendTable( const ScDBData* pData, sal_Int32 nTableId )
+{
+ maTables.emplace_back( pData, nTableId);
+}
+
+void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, const Entry& rEntry )
+{
+ const ScDBData& rData = *rEntry.mpData;
+ ScRange aRange( ScAddress::UNINITIALIZED);
+ rData.GetArea( aRange);
+ sax_fastparser::FSHelperPtr& pTableStrm = rStrm.GetCurrentStream();
+ pTableStrm->startElement( XML_table,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ XML_id, OString::number( rEntry.mnTableId),
+ XML_name, rData.GetName().toUtf8(),
+ XML_displayName, rData.GetName().toUtf8(),
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aRange),
+ XML_headerRowCount, ToPsz10(rData.HasHeader()),
+ XML_totalsRowCount, ToPsz10(rData.HasTotals()),
+ XML_totalsRowShown, ToPsz10(rData.HasTotals()) // we don't support that but if there are totals they are shown
+ // OOXTODO: XML_comment, ...,
+ // OOXTODO: XML_connectionId, ...,
+ // OOXTODO: XML_dataCellStyle, ...,
+ // OOXTODO: XML_dataDxfId, ...,
+ // OOXTODO: XML_headerRowBorderDxfId, ...,
+ // OOXTODO: XML_headerRowCellStyle, ...,
+ // OOXTODO: XML_headerRowDxfId, ...,
+ // OOXTODO: XML_insertRow, ...,
+ // OOXTODO: XML_insertRowShift, ...,
+ // OOXTODO: XML_published, ...,
+ // OOXTODO: XML_tableBorderDxfId, ...,
+ // OOXTODO: XML_tableType, ...,
+ // OOXTODO: XML_totalsRowBorderDxfId, ...,
+ // OOXTODO: XML_totalsRowCellStyle, ...,
+ // OOXTODO: XML_totalsRowDxfId, ...
+ );
+
+ if (rData.HasAutoFilter())
+ {
+ /* TODO: does this need to exclude totals row? */
+
+ /* TODO: in OOXML 12.3.21 Table Definition Part has information
+ * that an applied autoFilter has child elements
+ * <af:filterColumn><af:filters><af:filter>.
+ * When not applied but buttons hidden, Excel writes, for example,
+ * <filterColumn colId="0" hiddenButton="1"/> */
+
+ ExcAutoFilterRecs aAutoFilter( rStrm.GetRoot(), aRange.aStart.Tab(), &rData);
+ aAutoFilter.SaveXml( rStrm);
+ }
+
+ const std::vector< OUString >& rColNames = rData.GetTableColumnNames();
+ if (!rColNames.empty())
+ {
+ pTableStrm->startElement(XML_tableColumns,
+ XML_count, OString::number(aRange.aEnd.Col() - aRange.aStart.Col() + 1));
+
+ for (size_t i=0, n=rColNames.size(); i < n; ++i)
+ {
+ // OOXTODO: write <calculatedColumnFormula> once we support it, in
+ // which case we'd need start/endElement XML_tableColumn for such
+ // column.
+
+ // OOXTODO: write <totalsRowFormula> once we support it.
+
+ pTableStrm->singleElement( XML_tableColumn,
+ XML_id, OString::number(i+1),
+ XML_name, rColNames[i].toUtf8()
+ // OOXTODO: XML_dataCellStyle, ...,
+ // OOXTODO: XML_dataDxfId, ...,
+ // OOXTODO: XML_headerRowCellStyle, ...,
+ // OOXTODO: XML_headerRowDxfId, ...,
+ // OOXTODO: XML_queryTableFieldId, ...,
+ // OOXTODO: XML_totalsRowCellStyle, ...,
+ // OOXTODO: XML_totalsRowDxfId, ...,
+ // OOXTODO: XML_totalsRowFunction, ...,
+ // OOXTODO: XML_totalsRowLabel, ...,
+ // OOXTODO: XML_uniqueName, ...
+ );
+ }
+
+ pTableStrm->endElement( XML_tableColumns);
+ }
+
+ // OOXTODO: write <tableStyleInfo> once we have table styles.
+
+ pTableStrm->endElement( XML_table);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeescher.cxx b/sc/source/filter/excel/xeescher.cxx
new file mode 100644
index 000000000..d166b172a
--- /dev/null
+++ b/sc/source/filter/excel/xeescher.cxx
@@ -0,0 +1,2046 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xeescher.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/awt/VisualEffect.hpp>
+#include <com/sun/star/awt/ScrollBarOrientation.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+
+#include <set>
+#include <vcl/BitmapReadAccess.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdocapt.hxx>
+#include <editeng/outlobj.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <svtools/embedhlp.hxx>
+
+#include <unonames.hxx>
+#include <convuno.hxx>
+#include <postit.hxx>
+
+#include <fapihelper.hxx>
+#include <xcl97esc.hxx>
+#include <xechart.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xestyle.hxx>
+#include <xllink.hxx>
+#include <xltools.hxx>
+#include <userdat.hxx>
+#include <drwlayer.hxx>
+#include <svl/itemset.hxx>
+#include <svx/sdtaitm.hxx>
+#include <document.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/relationship.hxx>
+#include <oox/export/drawingml.hxx>
+#include <oox/export/chartexport.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/export/vmlexport.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::lang::XServiceInfo;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::drawing::XShapes;
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::form::binding::XBindableValue;
+using ::com::sun::star::form::binding::XListEntrySink;
+using ::com::sun::star::script::ScriptEventDescriptor;
+using ::com::sun::star::table::CellAddress;
+using ::com::sun::star::table::CellRangeAddress;
+using ::oox::drawingml::DrawingML;
+using ::oox::drawingml::ChartExport;
+using namespace oox;
+
+namespace
+{
+
+const char *ToHorizAlign( SdrTextHorzAdjust eAdjust )
+{
+ switch( eAdjust )
+ {
+ case SDRTEXTHORZADJUST_CENTER:
+ return "center";
+ case SDRTEXTHORZADJUST_RIGHT:
+ return "right";
+ case SDRTEXTHORZADJUST_BLOCK:
+ return "justify";
+ case SDRTEXTHORZADJUST_LEFT:
+ default:
+ return "left";
+ }
+}
+
+const char *ToVertAlign( SdrTextVertAdjust eAdjust )
+{
+ switch( eAdjust )
+ {
+ case SDRTEXTVERTADJUST_CENTER:
+ return "center";
+ case SDRTEXTVERTADJUST_BOTTOM:
+ return "bottom";
+ case SDRTEXTVERTADJUST_BLOCK:
+ return "justify";
+ case SDRTEXTVERTADJUST_TOP:
+ default:
+ return "top";
+ }
+}
+
+void lcl_WriteAnchorVertex( sax_fastparser::FSHelperPtr const & rComments, const tools::Rectangle &aRect )
+{
+ rComments->startElement(FSNS(XML_xdr, XML_col));
+ rComments->writeEscaped( OUString::number( aRect.Left() ) );
+ rComments->endElement( FSNS( XML_xdr, XML_col ) );
+ rComments->startElement(FSNS(XML_xdr, XML_colOff));
+ rComments->writeEscaped( OUString::number( aRect.Top() ) );
+ rComments->endElement( FSNS( XML_xdr, XML_colOff ) );
+ rComments->startElement(FSNS(XML_xdr, XML_row));
+ rComments->writeEscaped( OUString::number( aRect.Right() ) );
+ rComments->endElement( FSNS( XML_xdr, XML_row ) );
+ rComments->startElement(FSNS(XML_xdr, XML_rowOff));
+ rComments->writeEscaped( OUString::number( aRect.Bottom() ) );
+ rComments->endElement( FSNS( XML_xdr, XML_rowOff ) );
+}
+
+tools::Long lcl_hmm2output(tools::Long value, bool bInEMU)
+{
+ return o3tl::convert(value, o3tl::Length::mm100, bInEMU ? o3tl::Length::emu : o3tl::Length::px);
+}
+
+void lcl_GetFromTo( const XclExpRoot& rRoot, const tools::Rectangle &aRect, sal_Int32 nTab, tools::Rectangle &aFrom, tools::Rectangle &aTo, bool bInEMU = false )
+{
+ sal_Int32 nCol = 0, nRow = 0;
+ sal_Int32 nColOff = 0, nRowOff= 0;
+
+ const bool bRTL = rRoot.GetDoc().IsNegativePage( nTab );
+ if (!bRTL)
+ {
+ while(true)
+ {
+ tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab );
+ if( r.Left() <= aRect.Left() )
+ {
+ nCol++;
+ nColOff = aRect.Left() - r.Left();
+ }
+ if( r.Top() <= aRect.Top() )
+ {
+ nRow++;
+ nRowOff = aRect.Top() - r.Top();
+ }
+ if( r.Left() > aRect.Left() && r.Top() > aRect.Top() )
+ {
+ aFrom = tools::Rectangle( nCol-1, lcl_hmm2output( nColOff, bInEMU ),
+ nRow-1, lcl_hmm2output( nRowOff, bInEMU ) );
+ break;
+ }
+ }
+ }
+ else
+ {
+ while(true)
+ {
+ tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab );
+ if( r.Left() >= aRect.Left() )
+ {
+ nCol++;
+ nColOff = r.Left() - aRect.Left();
+ }
+ if( r.Top() <= aRect.Top() )
+ {
+ nRow++;
+ nRowOff = aRect.Top() - r.Top();
+ }
+ if( r.Left() < aRect.Left() && r.Top() > aRect.Top() )
+ {
+ aFrom = tools::Rectangle( nCol-1, lcl_hmm2output( nColOff, bInEMU ),
+ nRow-1, lcl_hmm2output( nRowOff, bInEMU ) );
+ break;
+ }
+ }
+ }
+ if (!bRTL)
+ {
+ while(true)
+ {
+ tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab );
+ if( r.Right() < aRect.Right() )
+ nCol++;
+ if( r.Bottom() < aRect.Bottom() )
+ nRow++;
+ if( r.Right() >= aRect.Right() && r.Bottom() >= aRect.Bottom() )
+ {
+ aTo = tools::Rectangle( nCol, lcl_hmm2output( aRect.Right() - r.Left(), bInEMU ),
+ nRow, lcl_hmm2output( aRect.Bottom() - r.Top(), bInEMU ));
+ break;
+ }
+ }
+ }
+ else
+ {
+ while(true)
+ {
+ tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab );
+ if( r.Right() >= aRect.Right() )
+ nCol++;
+ if( r.Bottom() < aRect.Bottom() )
+ nRow++;
+ if( r.Right() < aRect.Right() && r.Bottom() >= aRect.Bottom() )
+ {
+ aTo = tools::Rectangle( nCol, lcl_hmm2output( r.Left() - aRect.Right(), bInEMU ),
+ nRow, lcl_hmm2output( aRect.Bottom() - r.Top(), bInEMU ));
+ break;
+ }
+ }
+ }
+}
+
+} // namespace
+
+// Escher client anchor =======================================================
+
+XclExpDffAnchorBase::XclExpDffAnchorBase( const XclExpRoot& rRoot, sal_uInt16 nFlags ) :
+ XclExpRoot( rRoot ),
+ mnFlags( nFlags )
+{
+}
+
+void XclExpDffAnchorBase::SetFlags( const SdrObject& rSdrObj )
+{
+ ImplSetFlags( rSdrObj );
+}
+
+void XclExpDffAnchorBase::SetSdrObject( const SdrObject& rSdrObj )
+{
+ ImplSetFlags( rSdrObj );
+ ImplCalcAnchorRect( rSdrObj.GetCurrentBoundRect(), MapUnit::Map100thMM );
+}
+
+void XclExpDffAnchorBase::WriteDffData( EscherEx& rEscherEx ) const
+{
+ rEscherEx.AddAtom( 18, ESCHER_ClientAnchor );
+ rEscherEx.GetStream().WriteUInt16( mnFlags );
+ WriteXclObjAnchor( rEscherEx.GetStream(), maAnchor );
+}
+
+void XclExpDffAnchorBase::WriteData( EscherEx& rEscherEx, const tools::Rectangle& rRect )
+{
+ // the passed rectangle is in twips
+ ImplCalcAnchorRect( rRect, MapUnit::MapTwip );
+ WriteDffData( rEscherEx );
+}
+
+void XclExpDffAnchorBase::ImplSetFlags( const SdrObject& )
+{
+ OSL_FAIL( "XclExpDffAnchorBase::ImplSetFlags - not implemented" );
+}
+
+void XclExpDffAnchorBase::ImplCalcAnchorRect( const tools::Rectangle&, MapUnit )
+{
+ OSL_FAIL( "XclExpDffAnchorBase::ImplCalcAnchorRect - not implemented" );
+}
+
+XclExpDffSheetAnchor::XclExpDffSheetAnchor( const XclExpRoot& rRoot ) :
+ XclExpDffAnchorBase( rRoot ),
+ mnScTab( rRoot.GetCurrScTab() )
+{
+}
+
+void XclExpDffSheetAnchor::ImplSetFlags( const SdrObject& rSdrObj )
+{
+ // set flags for cell/page anchoring
+ if ( ScDrawLayer::GetAnchorType( rSdrObj ) == SCA_CELL )
+ mnFlags = 0;
+ else
+ mnFlags = EXC_ESC_ANCHOR_LOCKED;
+}
+
+void XclExpDffSheetAnchor::ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit )
+{
+ maAnchor.SetRect( GetRoot(), mnScTab, rRect, eMapUnit );
+}
+
+XclExpDffEmbeddedAnchor::XclExpDffEmbeddedAnchor( const XclExpRoot& rRoot,
+ const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY ) :
+ XclExpDffAnchorBase( rRoot ),
+ maPageSize( rPageSize ),
+ mnScaleX( nScaleX ),
+ mnScaleY( nScaleY )
+{
+}
+
+void XclExpDffEmbeddedAnchor::ImplSetFlags( const SdrObject& /*rSdrObj*/ )
+{
+ // TODO (unsupported feature): fixed size
+}
+
+void XclExpDffEmbeddedAnchor::ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit )
+{
+ maAnchor.SetRect( maPageSize, mnScaleX, mnScaleY, rRect, eMapUnit );
+}
+
+XclExpDffNoteAnchor::XclExpDffNoteAnchor( const XclExpRoot& rRoot, const tools::Rectangle& rRect ) :
+ XclExpDffAnchorBase( rRoot, EXC_ESC_ANCHOR_SIZELOCKED )
+{
+ maAnchor.SetRect( rRoot, rRoot.GetCurrScTab(), rRect, MapUnit::Map100thMM );
+}
+
+XclExpDffDropDownAnchor::XclExpDffDropDownAnchor( const XclExpRoot& rRoot, const ScAddress& rScPos ) :
+ XclExpDffAnchorBase( rRoot, EXC_ESC_ANCHOR_POSLOCKED )
+{
+ GetAddressConverter().ConvertAddress( maAnchor.maFirst, rScPos, true );
+ maAnchor.maLast.mnCol = maAnchor.maFirst.mnCol + 1;
+ maAnchor.maLast.mnRow = maAnchor.maFirst.mnRow + 1;
+ maAnchor.mnLX = maAnchor.mnTY = maAnchor.mnRX = maAnchor.mnBY = 0;
+}
+
+// MSODRAWING* records ========================================================
+
+XclExpMsoDrawingBase::XclExpMsoDrawingBase( XclEscherEx& rEscherEx, sal_uInt16 nRecId ) :
+ XclExpRecord( nRecId ),
+ mrEscherEx( rEscherEx ),
+ mnFragmentKey( rEscherEx.InitNextDffFragment() )
+{
+}
+
+void XclExpMsoDrawingBase::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mrEscherEx.GetStreamPos() == mrEscherEx.GetDffFragmentPos( mnFragmentKey ),
+ "XclExpMsoDrawingBase::WriteBody - DFF stream position mismatch" );
+ rStrm.CopyFromStream( mrEscherEx.GetStream(), mrEscherEx.GetDffFragmentSize( mnFragmentKey ) );
+}
+
+XclExpMsoDrawingGroup::XclExpMsoDrawingGroup( XclEscherEx& rEscherEx ) :
+ XclExpMsoDrawingBase( rEscherEx, EXC_ID_MSODRAWINGGROUP )
+{
+ SvStream& rDffStrm = mrEscherEx.GetStream();
+
+ // write the DGGCONTAINER with some default settings
+ mrEscherEx.OpenContainer( ESCHER_DggContainer );
+
+ // TODO: stuff the OPT atom with our own document defaults?
+ static const sal_uInt8 spnDffOpt[] = {
+ 0xBF, 0x00, 0x08, 0x00, 0x08, 0x00, 0x81, 0x01,
+ 0x09, 0x00, 0x00, 0x08, 0xC0, 0x01, 0x40, 0x00,
+ 0x00, 0x08
+ };
+ mrEscherEx.AddAtom( sizeof( spnDffOpt ), ESCHER_OPT, 3, 3 );
+ rDffStrm.WriteBytes(spnDffOpt, sizeof(spnDffOpt));
+
+ // SPLITMENUCOLORS contains colors in toolbar
+ static const sal_uInt8 spnDffSplitMenuColors[] = {
+ 0x0D, 0x00, 0x00, 0x08, 0x0C, 0x00, 0x00, 0x08,
+ 0x17, 0x00, 0x00, 0x08, 0xF7, 0x00, 0x00, 0x10
+ };
+ mrEscherEx.AddAtom( sizeof( spnDffSplitMenuColors ), ESCHER_SplitMenuColors, 0, 4 );
+ rDffStrm.WriteBytes(spnDffSplitMenuColors, sizeof(spnDffSplitMenuColors));
+
+ // close the DGGCONTAINER
+ mrEscherEx.CloseContainer();
+ mrEscherEx.UpdateDffFragmentEnd();
+}
+
+XclExpMsoDrawing::XclExpMsoDrawing( XclEscherEx& rEscherEx ) :
+ XclExpMsoDrawingBase( rEscherEx, EXC_ID_MSODRAWING )
+{
+}
+
+XclExpImgData::XclExpImgData( const Graphic& rGraphic, sal_uInt16 nRecId ) :
+ maGraphic( rGraphic ),
+ mnRecId( nRecId )
+{
+}
+
+void XclExpImgData::Save( XclExpStream& rStrm )
+{
+ Bitmap aBmp = maGraphic.GetBitmapEx().GetBitmap();
+ if (aBmp.getPixelFormat() != vcl::PixelFormat::N24_BPP)
+ aBmp.Convert( BmpConversion::N24Bit );
+
+ Bitmap::ScopedReadAccess pAccess(aBmp);
+ if( !pAccess )
+ return;
+
+ sal_Int32 nWidth = ::std::min< sal_Int32 >( pAccess->Width(), 0xFFFF );
+ sal_Int32 nHeight = ::std::min< sal_Int32 >( pAccess->Height(), 0xFFFF );
+ if( (nWidth <= 0) || (nHeight <= 0) )
+ return;
+
+ sal_uInt8 nPadding = static_cast< sal_uInt8 >( nWidth & 0x03 );
+ sal_uInt32 nTmpSize = static_cast< sal_uInt32 >( (nWidth * 3 + nPadding) * nHeight + 12 );
+
+ rStrm.StartRecord( mnRecId, nTmpSize + 4 );
+
+ rStrm << EXC_IMGDATA_BMP // BMP format
+ << EXC_IMGDATA_WIN // Windows
+ << nTmpSize // size after _this_ field
+ << sal_uInt32( 12 ) // BITMAPCOREHEADER size
+ << static_cast< sal_uInt16 >( nWidth ) // width
+ << static_cast< sal_uInt16 >( nHeight ) // height
+ << sal_uInt16( 1 ) // planes
+ << sal_uInt16( 24 ); // bits per pixel
+
+ for( sal_Int32 nY = nHeight - 1; nY >= 0; --nY )
+ {
+ Scanline pScanline = pAccess->GetScanline( nY );
+ for( sal_Int32 nX = 0; nX < nWidth; ++nX )
+ {
+ const BitmapColor& rBmpColor = pAccess->GetPixelFromData( pScanline, nX );
+ rStrm << rBmpColor.GetBlue() << rBmpColor.GetGreen() << rBmpColor.GetRed();
+ }
+ rStrm.WriteZeroBytes( nPadding );
+ }
+
+ rStrm.EndRecord();
+}
+
+void XclExpImgData::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pWorksheet = rStrm.GetCurrentStream();
+
+ DrawingML aDML(pWorksheet, &rStrm, drawingml::DOCUMENT_XLSX);
+ OUString rId = aDML.WriteImage( maGraphic );
+ pWorksheet->singleElement(XML_picture, FSNS(XML_r, XML_id), rId);
+}
+
+XclExpControlHelper::XclExpControlHelper( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnEntryCount( 0 )
+{
+}
+
+XclExpControlHelper::~XclExpControlHelper()
+{
+}
+
+void XclExpControlHelper::ConvertSheetLinks( Reference< XShape > const & xShape )
+{
+ mxCellLink.reset();
+ mxCellLinkAddress.SetInvalid();
+ mxSrcRange.reset();
+ mnEntryCount = 0;
+
+ // get control model
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
+ if( !xCtrlModel.is() )
+ return;
+
+ // *** cell link *** ------------------------------------------------------
+
+ Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY );
+ if( xBindable.is() )
+ {
+ Reference< XServiceInfo > xServInfo( xBindable->getValueBinding(), UNO_QUERY );
+ if( xServInfo.is() && xServInfo->supportsService( SC_SERVICENAME_VALBIND ) )
+ {
+ ScfPropertySet aBindProp( xServInfo );
+ CellAddress aApiAddress;
+ if( aBindProp.GetProperty( aApiAddress, SC_UNONAME_BOUNDCELL ) )
+ {
+ ScUnoConversion::FillScAddress( mxCellLinkAddress, aApiAddress );
+ if( GetTabInfo().IsExportTab( mxCellLinkAddress.Tab() ) )
+ mxCellLink = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CONTROL, mxCellLinkAddress );
+ }
+ }
+ }
+
+ // *** source range *** ---------------------------------------------------
+
+ Reference< XListEntrySink > xEntrySink( xCtrlModel, UNO_QUERY );
+ if( !xEntrySink.is() )
+ return;
+
+ Reference< XServiceInfo > xServInfo( xEntrySink->getListEntrySource(), UNO_QUERY );
+ if( !(xServInfo.is() && xServInfo->supportsService( SC_SERVICENAME_LISTSOURCE )) )
+ return;
+
+ ScfPropertySet aSinkProp( xServInfo );
+ CellRangeAddress aApiRange;
+ if( aSinkProp.GetProperty( aApiRange, SC_UNONAME_CELLRANGE ) )
+ {
+ ScRange aSrcRange;
+ ScUnoConversion::FillScRange( aSrcRange, aApiRange );
+ if( (aSrcRange.aStart.Tab() == aSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( aSrcRange.aStart.Tab() ) )
+ mxSrcRange = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CONTROL, aSrcRange );
+ mnEntryCount = static_cast< sal_uInt16 >( aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1 );
+ }
+}
+
+void XclExpControlHelper::WriteFormula( XclExpStream& rStrm, const XclTokenArray& rTokArr )
+{
+ sal_uInt16 nFmlaSize = rTokArr.GetSize();
+ rStrm << nFmlaSize << sal_uInt32( 0 );
+ rTokArr.WriteArray( rStrm );
+ if( nFmlaSize & 1 ) // pad to 16-bit
+ rStrm << sal_uInt8( 0 );
+}
+
+void XclExpControlHelper::WriteFormulaSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId, const XclTokenArray& rTokArr )
+{
+ rStrm.StartRecord( nSubRecId, (rTokArr.GetSize() + 5) & ~1 );
+ WriteFormula( rStrm, rTokArr );
+ rStrm.EndRecord();
+}
+
+//delete for exporting OCX
+//#if EXC_EXP_OCX_CTRL
+
+XclExpOcxControlObj::XclExpOcxControlObj( XclExpObjectManager& rObjMgr, Reference< XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor, const OUString& rClassName, sal_uInt32 nStrmStart, sal_uInt32 nStrmSize ) :
+ XclObj( rObjMgr, EXC_OBJTYPE_PICTURE, true ),
+ XclExpControlHelper( rObjMgr.GetRoot() ),
+ maClassName( rClassName ),
+ mnStrmStart( nStrmStart ),
+ mnStrmSize( nStrmSize )
+{
+ ScfPropertySet aCtrlProp( XclControlHelper::GetControlModel( xShape ) );
+
+ // OBJ record flags
+ SetLocked( true );
+ SetPrintable( aCtrlProp.GetBoolProperty( "Printable" ) );
+ SetAutoFill( false );
+ SetAutoLine( false );
+
+ // fill DFF property set
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_HostControl,
+ ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor | ShapeFlag::OLEShape );
+ tools::Rectangle aDummyRect;
+ EscherPropertyContainer aPropOpt( mrEscherEx.GetGraphicProvider(), mrEscherEx.QueryPictureStream(), aDummyRect );
+ aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x08000040 );
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field
+
+ // #i51348# name of the control, may overwrite shape name
+ OUString aCtrlName;
+ if( aCtrlProp.GetProperty( aCtrlName, "Name" ) && !aCtrlName.isEmpty() )
+ aPropOpt.AddOpt( ESCHER_Prop_wzName, aCtrlName );
+
+ // meta file
+ //TODO - needs check
+ Reference< XPropertySet > xShapePS( xShape, UNO_QUERY );
+ if( xShapePS.is() && aPropOpt.CreateGraphicProperties( xShapePS, "MetaFile", false ) )
+ {
+ sal_uInt32 nBlipId;
+ if( aPropOpt.GetOpt( ESCHER_Prop_pib, nBlipId ) )
+ aPropOpt.AddOpt( ESCHER_Prop_pictureId, nBlipId );
+ }
+
+ // write DFF property set to stream
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ // anchor
+ ImplWriteAnchor( SdrObject::getSdrObjectFromXShape( xShape ), pChildAnchor );
+
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ // spreadsheet links
+ ConvertSheetLinks( xShape );
+}
+
+void XclExpOcxControlObj::WriteSubRecs( XclExpStream& rStrm )
+{
+ // OBJCF - clipboard format
+ rStrm.StartRecord( EXC_ID_OBJCF, 2 );
+ rStrm << sal_uInt16( 2 );
+ rStrm.EndRecord();
+
+ // OBJFLAGS
+ rStrm.StartRecord( EXC_ID_OBJFLAGS, 2 );
+ rStrm << sal_uInt16( 0x0031 );
+ rStrm.EndRecord();
+
+ // OBJPICTFMLA
+ XclExpString aClass( maClassName );
+ sal_uInt16 nClassNameSize = static_cast< sal_uInt16 >( aClass.GetSize() );
+ sal_uInt16 nClassNamePad = nClassNameSize & 1;
+ sal_uInt16 nFirstPartSize = 12 + nClassNameSize + nClassNamePad;
+
+ const XclTokenArray* pCellLink = GetCellLinkTokArr();
+ sal_uInt16 nCellLinkSize = pCellLink ? ((pCellLink->GetSize() + 7) & 0xFFFE) : 0;
+
+ const XclTokenArray* pSrcRange = GetSourceRangeTokArr();
+ sal_uInt16 nSrcRangeSize = pSrcRange ? ((pSrcRange->GetSize() + 7) & 0xFFFE) : 0;
+
+ sal_uInt16 nPictFmlaSize = nFirstPartSize + nCellLinkSize + nSrcRangeSize + 18;
+ rStrm.StartRecord( EXC_ID_OBJPICTFMLA, nPictFmlaSize );
+
+ rStrm << nFirstPartSize // size of first part
+ << sal_uInt16( 5 ) // formula size
+ << sal_uInt32( 0 ) // unknown ID
+ << sal_uInt8( 0x02 ) << sal_uInt32( 0 ) // tTbl token with unknown ID
+ << sal_uInt8( 3 ) // pad to word
+ << aClass; // "Forms.***.1"
+ rStrm.WriteZeroBytes( nClassNamePad ); // pad to word
+ rStrm << mnStrmStart // start in 'Ctls' stream
+ << mnStrmSize // size in 'Ctls' stream
+ << sal_uInt32( 0 ); // class ID size
+ // cell link
+ rStrm << nCellLinkSize;
+ if( pCellLink )
+ WriteFormula( rStrm, *pCellLink );
+ // list source range
+ rStrm << nSrcRangeSize;
+ if( pSrcRange )
+ WriteFormula( rStrm, *pSrcRange );
+
+ rStrm.EndRecord();
+}
+
+//#else
+
+XclExpTbxControlObj::XclExpTbxControlObj( XclExpObjectManager& rRoot, Reference< XShape > const & xShape , const tools::Rectangle* pChildAnchor ) :
+ XclObj( rRoot, EXC_OBJTYPE_UNKNOWN, true ),
+ XclMacroHelper( rRoot ),
+ mxShape( xShape ),
+ meEventType( EXC_TBX_EVENT_ACTION ),
+ mnHeight( 0 ),
+ mnState( 0 ),
+ mnLineCount( 0 ),
+ mnSelEntry( 0 ),
+ mnScrollValue( 0 ),
+ mnScrollMin( 0 ),
+ mnScrollMax( 100 ),
+ mnScrollStep( 1 ),
+ mnScrollPage( 10 ),
+ mbFlatButton( false ),
+ mbFlatBorder( false ),
+ mbMultiSel( false ),
+ mbScrollHor( false ),
+ mbPrint( false ),
+ mbVisible( false ),
+ mnShapeId( 0 ),
+ mrRoot(rRoot)
+{
+ namespace FormCompType = css::form::FormComponentType;
+ namespace AwtVisualEffect = css::awt::VisualEffect;
+ namespace AwtScrollOrient = css::awt::ScrollBarOrientation;
+
+ ScfPropertySet aCtrlProp( XclControlHelper::GetControlModel( xShape ) );
+ if( !xShape.is() || !aCtrlProp.Is() )
+ return;
+
+ mnHeight = xShape->getSize().Height;
+ if( mnHeight <= 0 )
+ return;
+
+ // control type
+ sal_Int16 nClassId = 0;
+ if( aCtrlProp.GetProperty( nClassId, "ClassId" ) )
+ {
+ switch( nClassId )
+ {
+ case FormCompType::COMMANDBUTTON: mnObjType = EXC_OBJTYPE_BUTTON; meEventType = EXC_TBX_EVENT_ACTION; break;
+ case FormCompType::RADIOBUTTON: mnObjType = EXC_OBJTYPE_OPTIONBUTTON; meEventType = EXC_TBX_EVENT_ACTION; break;
+ case FormCompType::CHECKBOX: mnObjType = EXC_OBJTYPE_CHECKBOX; meEventType = EXC_TBX_EVENT_ACTION; break;
+ case FormCompType::LISTBOX: mnObjType = EXC_OBJTYPE_LISTBOX; meEventType = EXC_TBX_EVENT_CHANGE; break;
+ case FormCompType::COMBOBOX: mnObjType = EXC_OBJTYPE_DROPDOWN; meEventType = EXC_TBX_EVENT_CHANGE; break;
+ case FormCompType::GROUPBOX: mnObjType = EXC_OBJTYPE_GROUPBOX; meEventType = EXC_TBX_EVENT_MOUSE; break;
+ case FormCompType::FIXEDTEXT: mnObjType = EXC_OBJTYPE_LABEL; meEventType = EXC_TBX_EVENT_MOUSE; break;
+ case FormCompType::SCROLLBAR: mnObjType = EXC_OBJTYPE_SCROLLBAR; meEventType = EXC_TBX_EVENT_VALUE; break;
+ case FormCompType::SPINBUTTON: mnObjType = EXC_OBJTYPE_SPIN; meEventType = EXC_TBX_EVENT_VALUE; break;
+ }
+ }
+ if( mnObjType == EXC_OBJTYPE_UNKNOWN )
+ return;
+
+ // OBJ record flags
+ SetLocked( true );
+ mbPrint = aCtrlProp.GetBoolProperty( "Printable" );
+ SetPrintable( mbPrint );
+ SetAutoFill( false );
+ SetAutoLine( false );
+
+ // fill DFF property set
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty );
+ EscherPropertyContainer aPropOpt;
+ mbVisible = aCtrlProp.GetBoolProperty( "EnableVisible" );
+ aPropOpt.AddOpt( ESCHER_Prop_fPrint, mbVisible ? 0x00080000 : 0x00080002 ); // visible flag
+
+ aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01000100 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_lTxid, 0 ); // Text ID
+ aPropOpt.AddOpt( ESCHER_Prop_WrapText, 0x00000001 );
+ aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x001A0008 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00100000 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field
+
+ // #i51348# name of the control, may overwrite shape name
+ if( aCtrlProp.GetProperty( msCtrlName, "Name" ) && !msCtrlName.isEmpty() )
+ aPropOpt.AddOpt( ESCHER_Prop_wzName, msCtrlName );
+
+ //Export description as alt text
+ if( SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape ) )
+ {
+ OUString aAltTxt;
+ OUString aDescrText = pSdrObj->GetDescription();
+ if(!aDescrText.isEmpty())
+ aAltTxt = aDescrText.copy( 0, std::min<sal_Int32>(MSPROP_DESCRIPTION_MAX_LEN, aDescrText.getLength()) );
+ aPropOpt.AddOpt( ESCHER_Prop_wzDescription, aAltTxt );
+ }
+
+ // write DFF property set to stream
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ // anchor
+ ImplWriteAnchor( SdrObject::getSdrObjectFromXShape( xShape ), pChildAnchor );
+
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ // control label
+ if( aCtrlProp.GetProperty( msLabel, "Label" ) )
+ {
+ /* Be sure to construct the MSODRAWING record containing the
+ ClientTextbox atom after the base OBJ's MSODRAWING record data is
+ completed. */
+ pClientTextbox.reset( new XclExpMsoDrawing( mrEscherEx ) );
+ mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox ); // TXO record
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ sal_uInt16 nXclFont = EXC_FONT_APP;
+ if( !msLabel.isEmpty() )
+ {
+ XclFontData aFontData;
+ GetFontPropSetHelper().ReadFontProperties( aFontData, aCtrlProp, EXC_FONTPROPSET_CONTROL );
+ if( (!aFontData.maName.isEmpty() ) && (aFontData.mnHeight > 0) )
+ nXclFont = GetFontBuffer().Insert( aFontData, EXC_COLOR_CTRLTEXT );
+ }
+
+ pTxo.reset( new XclTxo( msLabel, nXclFont ) );
+ pTxo->SetHorAlign( (mnObjType == EXC_OBJTYPE_BUTTON) ? EXC_OBJ_HOR_CENTER : EXC_OBJ_HOR_LEFT );
+ pTxo->SetVerAlign( EXC_OBJ_VER_CENTER );
+ }
+
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+
+ // other properties
+ aCtrlProp.GetProperty( mnLineCount, "LineCount" );
+
+ // border style
+ sal_Int16 nApiButton = AwtVisualEffect::LOOK3D;
+ sal_Int16 nApiBorder = AwtVisualEffect::LOOK3D;
+ switch( nClassId )
+ {
+ case FormCompType::LISTBOX:
+ case FormCompType::COMBOBOX:
+ aCtrlProp.GetProperty( nApiBorder, "Border" );
+ break;
+ case FormCompType::CHECKBOX:
+ case FormCompType::RADIOBUTTON:
+ aCtrlProp.GetProperty( nApiButton, "VisualEffect" );
+ nApiBorder = AwtVisualEffect::NONE;
+ break;
+ // Push button cannot be set to flat in Excel
+ case FormCompType::COMMANDBUTTON:
+ nApiBorder = AwtVisualEffect::LOOK3D;
+ break;
+ // Label does not support a border in Excel
+ case FormCompType::FIXEDTEXT:
+ nApiBorder = AwtVisualEffect::NONE;
+ break;
+ /* Scroll bar and spin button have a "Border" property, but it is
+ really used for a border, and not for own 3D/flat look (#i34712#). */
+ case FormCompType::SCROLLBAR:
+ case FormCompType::SPINBUTTON:
+ nApiButton = AwtVisualEffect::LOOK3D;
+ nApiBorder = AwtVisualEffect::NONE;
+ break;
+ // Group box does not support flat style (#i34712#)
+ case FormCompType::GROUPBOX:
+ nApiBorder = AwtVisualEffect::LOOK3D;
+ break;
+ }
+ mbFlatButton = nApiButton != AwtVisualEffect::LOOK3D;
+ mbFlatBorder = nApiBorder != AwtVisualEffect::LOOK3D;
+
+ // control state
+ sal_Int16 nApiState = 0;
+ if( aCtrlProp.GetProperty( nApiState, "State" ) )
+ {
+ switch( nApiState )
+ {
+ case 0: mnState = EXC_OBJ_CHECKBOX_UNCHECKED; break;
+ case 1: mnState = EXC_OBJ_CHECKBOX_CHECKED; break;
+ case 2: mnState = EXC_OBJ_CHECKBOX_TRISTATE; break;
+ }
+ }
+
+ // special control contents
+ switch( nClassId )
+ {
+ case FormCompType::LISTBOX:
+ {
+ mbMultiSel = aCtrlProp.GetBoolProperty( "MultiSelection" );
+ Sequence< sal_Int16 > aSelection;
+ if( aCtrlProp.GetProperty( aSelection, "SelectedItems" ) )
+ {
+ if( aSelection.hasElements() )
+ {
+ mnSelEntry = aSelection[ 0 ] + 1;
+ comphelper::sequenceToContainer(maMultiSel, aSelection);
+ }
+ }
+
+ // convert listbox with dropdown button to Excel dropdown
+ if( aCtrlProp.GetBoolProperty( "Dropdown" ) )
+ mnObjType = EXC_OBJTYPE_DROPDOWN;
+ }
+ break;
+
+ case FormCompType::COMBOBOX:
+ {
+ Sequence< OUString > aStringList;
+ OUString aDefText;
+ if( aCtrlProp.GetProperty( aStringList, "StringItemList" ) &&
+ aCtrlProp.GetProperty( aDefText, "Text" ) &&
+ aStringList.hasElements() && !aDefText.isEmpty() )
+ {
+ auto nIndex = comphelper::findValue(aStringList, aDefText);
+ if( nIndex != -1 )
+ mnSelEntry = static_cast< sal_Int16 >( nIndex + 1 ); // 1-based
+ if( mnSelEntry > 0 )
+ maMultiSel.resize( 1, mnSelEntry - 1 );
+ }
+
+ // convert combobox without dropdown button to Excel listbox
+ if( !aCtrlProp.GetBoolProperty( "Dropdown" ) )
+ mnObjType = EXC_OBJTYPE_LISTBOX;
+ }
+ break;
+
+ case FormCompType::SCROLLBAR:
+ {
+ sal_Int32 nApiValue = 0;
+ if( aCtrlProp.GetProperty( nApiValue, "ScrollValueMin" ) )
+ mnScrollMin = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "ScrollValueMax" ) )
+ mnScrollMax = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "ScrollValue" ) )
+ mnScrollValue = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, mnScrollMax );
+ if( aCtrlProp.GetProperty( nApiValue, "LineIncrement" ) )
+ mnScrollStep = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "BlockIncrement" ) )
+ mnScrollPage = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "Orientation" ) )
+ mbScrollHor = nApiValue == AwtScrollOrient::HORIZONTAL;
+ }
+ break;
+
+ case FormCompType::SPINBUTTON:
+ {
+ sal_Int32 nApiValue = 0;
+ if( aCtrlProp.GetProperty( nApiValue, "SpinValueMin" ) )
+ mnScrollMin = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "SpinValueMax" ) )
+ mnScrollMax = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "SpinValue" ) )
+ mnScrollValue = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, mnScrollMax );
+ if( aCtrlProp.GetProperty( nApiValue, "SpinIncrement" ) )
+ mnScrollStep = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "Orientation" ) )
+ mbScrollHor = nApiValue == AwtScrollOrient::HORIZONTAL;
+ }
+ break;
+ }
+
+ {
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
+ if( xCtrlModel.is() )
+ {
+ Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY );
+ if( xBindable.is() )
+ {
+ Reference< XServiceInfo > xServInfo( xBindable->getValueBinding(), UNO_QUERY );
+ if( xServInfo.is() && xServInfo->supportsService( SC_SERVICENAME_VALBIND ) )
+ {
+ ScfPropertySet aBindProp( xServInfo );
+ CellAddress aApiAddress;
+ if( aBindProp.GetProperty( aApiAddress, SC_UNONAME_BOUNDCELL ) )
+ {
+ ScUnoConversion::FillScAddress( mxCellLinkAddress, aApiAddress );
+ if( SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape ) )
+ lcl_GetFromTo( rRoot, pSdrObj->GetLogicRect(), mxCellLinkAddress.Tab(), maAreaFrom, maAreaTo, true );
+ }
+ }
+ }
+ }
+ }
+
+ // spreadsheet links
+ ConvertSheetLinks( xShape );
+}
+
+bool XclExpTbxControlObj::SetMacroLink( const ScriptEventDescriptor& rEvent )
+{
+ return XclMacroHelper::SetMacroLink( rEvent, meEventType );
+}
+
+void XclExpTbxControlObj::WriteSubRecs( XclExpStream& rStrm )
+{
+ switch( mnObjType )
+ {
+ // *** Push buttons, labels ***
+
+ case EXC_OBJTYPE_BUTTON:
+ case EXC_OBJTYPE_LABEL:
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+ break;
+
+ // *** Check boxes, option buttons ***
+
+ case EXC_OBJTYPE_CHECKBOX:
+ case EXC_OBJTYPE_OPTIONBUTTON:
+ {
+ // ftCbls - box properties
+ sal_uInt16 nStyle = 0;
+ ::set_flag( nStyle, EXC_OBJ_CHECKBOX_FLAT, mbFlatButton );
+
+ rStrm.StartRecord( EXC_ID_OBJCBLS, 12 );
+ rStrm << mnState;
+ rStrm.WriteZeroBytes( 8 );
+ rStrm << nStyle;
+ rStrm.EndRecord();
+
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+ // ftCblsFmla subrecord - cell link
+ WriteCellLinkSubRec( rStrm, EXC_ID_OBJCBLSFMLA );
+
+ // ftCblsData subrecord - box properties, again
+ rStrm.StartRecord( EXC_ID_OBJCBLS, 8 );
+ rStrm << mnState;
+ rStrm.WriteZeroBytes( 4 );
+ rStrm << nStyle;
+ rStrm.EndRecord();
+ }
+ break;
+
+ // *** List boxes, combo boxes ***
+
+ case EXC_OBJTYPE_LISTBOX:
+ case EXC_OBJTYPE_DROPDOWN:
+ {
+ sal_uInt16 nEntryCount = GetSourceEntryCount();
+
+ // ftSbs subrecord - Scroll bars
+ sal_Int32 nLineHeight = XclTools::GetHmmFromTwips( 200 ); // always 10pt
+ if( mnObjType == EXC_OBJTYPE_LISTBOX )
+ mnLineCount = static_cast< sal_uInt16 >( mnHeight / nLineHeight );
+ mnScrollValue = 0;
+ mnScrollMin = 0;
+ sal_uInt16 nInvisLines = (nEntryCount >= mnLineCount) ? (nEntryCount - mnLineCount) : 0;
+ mnScrollMax = limit_cast< sal_uInt16 >( nInvisLines, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ mnScrollStep = 1;
+ mnScrollPage = limit_cast< sal_uInt16 >( mnLineCount, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ mbScrollHor = false;
+ WriteSbs( rStrm );
+
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+ // ftSbsFmla subrecord - cell link
+ WriteCellLinkSubRec( rStrm, EXC_ID_OBJSBSFMLA );
+
+ // ftLbsData - source data range and box properties
+ sal_uInt16 nStyle = 0;
+ ::insert_value( nStyle, mbMultiSel ? EXC_OBJ_LISTBOX_MULTI : EXC_OBJ_LISTBOX_SINGLE, 4, 2 );
+ ::set_flag( nStyle, EXC_OBJ_LISTBOX_FLAT, mbFlatBorder );
+
+ rStrm.StartRecord( EXC_ID_OBJLBSDATA, 0 );
+
+ if( const XclTokenArray* pSrcRange = GetSourceRangeTokArr() )
+ {
+ rStrm << static_cast< sal_uInt16 >( (pSrcRange->GetSize() + 7) & 0xFFFE );
+ WriteFormula( rStrm, *pSrcRange );
+ }
+ else
+ rStrm << sal_uInt16( 0 );
+
+ rStrm << nEntryCount << mnSelEntry << nStyle << sal_uInt16( 0 );
+ if( mnObjType == EXC_OBJTYPE_LISTBOX )
+ {
+ if( nEntryCount )
+ {
+ ScfUInt8Vec aSelEx( nEntryCount, 0 );
+ for( const auto& rItem : maMultiSel )
+ if( rItem < nEntryCount )
+ aSelEx[ rItem ] = 1;
+ rStrm.Write( aSelEx.data(), aSelEx.size() );
+ }
+ }
+ else if( mnObjType == EXC_OBJTYPE_DROPDOWN )
+ {
+ rStrm << sal_uInt16( 0 ) << mnLineCount << sal_uInt16( 0 ) << sal_uInt16( 0 );
+ }
+
+ rStrm.EndRecord();
+ }
+ break;
+
+ // *** Spin buttons, scrollbars ***
+
+ case EXC_OBJTYPE_SPIN:
+ case EXC_OBJTYPE_SCROLLBAR:
+ {
+ // ftSbs subrecord - scroll bars
+ WriteSbs( rStrm );
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+ // ftSbsFmla subrecord - cell link
+ WriteCellLinkSubRec( rStrm, EXC_ID_OBJSBSFMLA );
+ }
+ break;
+
+ // *** Group boxes ***
+
+ case EXC_OBJTYPE_GROUPBOX:
+ {
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+
+ // ftGboData subrecord - group box properties
+ sal_uInt16 nStyle = 0;
+ ::set_flag( nStyle, EXC_OBJ_GROUPBOX_FLAT, mbFlatBorder );
+
+ rStrm.StartRecord( EXC_ID_OBJGBODATA, 6 );
+ rStrm << sal_uInt32( 0 )
+ << nStyle;
+ rStrm.EndRecord();
+ }
+ break;
+ }
+}
+
+void XclExpTbxControlObj::WriteCellLinkSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId )
+{
+ if( const XclTokenArray* pCellLink = GetCellLinkTokArr() )
+ WriteFormulaSubRec( rStrm, nSubRecId, *pCellLink );
+}
+
+void XclExpTbxControlObj::WriteSbs( XclExpStream& rStrm )
+{
+ sal_uInt16 nOrient = 0;
+ ::set_flag( nOrient, EXC_OBJ_SCROLLBAR_HOR, mbScrollHor );
+ sal_uInt16 nStyle = EXC_OBJ_SCROLLBAR_DEFFLAGS;
+ ::set_flag( nStyle, EXC_OBJ_SCROLLBAR_FLAT, mbFlatButton );
+
+ rStrm.StartRecord( EXC_ID_OBJSBS, 20 );
+ rStrm << sal_uInt32( 0 ) // reserved
+ << mnScrollValue // thumb position
+ << mnScrollMin // thumb min pos
+ << mnScrollMax // thumb max pos
+ << mnScrollStep // line increment
+ << mnScrollPage // page increment
+ << nOrient // 0 = vertical, 1 = horizontal
+ << sal_uInt16( 15 ) // thumb width
+ << nStyle; // flags/style
+ rStrm.EndRecord();
+}
+
+void XclExpTbxControlObj::setShapeId(sal_Int32 aShapeId)
+{
+ mnShapeId = aShapeId;
+}
+
+namespace
+{
+/// Handles the VML export of form controls (e.g. checkboxes).
+class VmlFormControlExporter : public oox::vml::VMLExport
+{
+ sal_uInt16 m_nObjType;
+ tools::Rectangle m_aAreaFrom;
+ tools::Rectangle m_aAreaTo;
+ OUString m_aLabel;
+ OUString m_aMacroName;
+
+public:
+ VmlFormControlExporter(const sax_fastparser::FSHelperPtr& p, sal_uInt16 nObjType,
+ const tools::Rectangle& rAreaFrom, const tools::Rectangle& rAreaTo,
+ const OUString& rLabel, const OUString& rMacroName);
+
+protected:
+ using VMLExport::StartShape;
+ sal_Int32 StartShape() override;
+ using VMLExport::EndShape;
+ void EndShape(sal_Int32 nShapeElement) override;
+};
+
+VmlFormControlExporter::VmlFormControlExporter(const sax_fastparser::FSHelperPtr& p,
+ sal_uInt16 nObjType,
+ const tools::Rectangle& rAreaFrom,
+ const tools::Rectangle& rAreaTo,
+ const OUString& rLabel, const OUString& rMacroName)
+ : VMLExport(p)
+ , m_nObjType(nObjType)
+ , m_aAreaFrom(rAreaFrom)
+ , m_aAreaTo(rAreaTo)
+ , m_aLabel(rLabel)
+ , m_aMacroName(rMacroName)
+{
+}
+
+sal_Int32 VmlFormControlExporter::StartShape()
+{
+ // Host control.
+ AddShapeAttribute(XML_type, "#_x0000_t201");
+ return VMLExport::StartShape();
+}
+
+void VmlFormControlExporter::EndShape(sal_Int32 nShapeElement)
+{
+ sax_fastparser::FSHelperPtr pVmlDrawing = GetFS();
+
+ pVmlDrawing->startElement(FSNS(XML_v, XML_textbox));
+ pVmlDrawing->startElement(XML_div);
+ pVmlDrawing->write(m_aLabel);
+ pVmlDrawing->endElement(XML_div);
+ pVmlDrawing->endElement(FSNS(XML_v, XML_textbox));
+
+ OString aObjectType;
+ switch (m_nObjType)
+ {
+ case EXC_OBJTYPE_CHECKBOX:
+ aObjectType = "Checkbox";
+ break;
+ case EXC_OBJTYPE_BUTTON:
+ aObjectType = "Button";
+ break;
+ }
+ pVmlDrawing->startElement(FSNS(XML_x, XML_ClientData), XML_ObjectType, aObjectType);
+ OString aAnchor
+ = OString::number(m_aAreaFrom.Left()) + ", " + OString::number(m_aAreaFrom.Top()) + ", "
+ + OString::number(m_aAreaFrom.Right()) + ", " + OString::number(m_aAreaFrom.Bottom()) + ", "
+ + OString::number(m_aAreaTo.Left()) + ", " + OString::number(m_aAreaTo.Top()) + ", "
+ + OString::number(m_aAreaTo.Right()) + ", " + OString::number(m_aAreaTo.Bottom());
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_Anchor), aAnchor);
+
+ if (!m_aMacroName.isEmpty())
+ {
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_FmlaMacro), m_aMacroName);
+ }
+
+ // XclExpOcxControlObj::WriteSubRecs() has the same fixed values.
+ if (m_nObjType == EXC_OBJTYPE_BUTTON)
+ {
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_TextHAlign), "Center");
+ }
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_TextVAlign), "Center");
+
+ pVmlDrawing->endElement(FSNS(XML_x, XML_ClientData));
+ VMLExport::EndShape(nShapeElement);
+}
+
+}
+
+/// Save into xl/drawings/vmlDrawing1.vml.
+void XclExpTbxControlObj::SaveVml(XclExpXmlStream& rStrm)
+{
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ tools::Rectangle aAreaFrom;
+ tools::Rectangle aAreaTo;
+ // Unlike XclExpTbxControlObj::SaveXml(), this is not calculated in EMUs.
+ lcl_GetFromTo(mrRoot, pObj->GetLogicRect(), GetTab(), aAreaFrom, aAreaTo);
+ VmlFormControlExporter aFormControlExporter(rStrm.GetCurrentStream(), GetObjType(), aAreaFrom,
+ aAreaTo, msLabel, GetMacroName());
+ aFormControlExporter.AddSdrObject(*pObj, /*bIsFollowingTextFlow=*/false, /*eHOri=*/-1,
+ /*eVOri=*/-1, /*eHRel=*/-1, /*eVRel=*/-1,
+ /*pWrapAttrList=*/nullptr, /*bOOxmlExport=*/true);
+}
+
+// save into xl\drawings\drawing1.xml
+void XclExpTbxControlObj::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& pDrawing = rStrm.GetCurrentStream();
+
+ pDrawing->startElement(FSNS(XML_mc, XML_AlternateContent),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)));
+ pDrawing->startElement(FSNS(XML_mc, XML_Choice),
+ FSNS(XML_xmlns, XML_a14), rStrm.getNamespaceURL(OOX_NS(a14)),
+ XML_Requires, "a14");
+
+ pDrawing->startElement(FSNS(XML_xdr, XML_twoCellAnchor), XML_editAs, "oneCell");
+ {
+ pDrawing->startElement(FSNS(XML_xdr, XML_from));
+ lcl_WriteAnchorVertex(pDrawing, maAreaFrom);
+ pDrawing->endElement(FSNS(XML_xdr, XML_from));
+ pDrawing->startElement(FSNS(XML_xdr, XML_to));
+ lcl_WriteAnchorVertex(pDrawing, maAreaTo);
+ pDrawing->endElement(FSNS(XML_xdr, XML_to));
+
+ pDrawing->startElement(FSNS(XML_xdr, XML_sp));
+ {
+ // xdr:nvSpPr
+ pDrawing->startElement(FSNS(XML_xdr, XML_nvSpPr));
+ {
+ pDrawing->singleElement(FSNS(XML_xdr, XML_cNvPr),
+ XML_id, OString::number(mnShapeId).getStr(),
+ XML_name, msCtrlName, // control name
+ XML_descr, msLabel, // description as alt text
+ XML_hidden, mbVisible ? "0" : "1");
+ pDrawing->singleElement(FSNS(XML_xdr, XML_cNvSpPr));
+ }
+ pDrawing->endElement(FSNS(XML_xdr, XML_nvSpPr));
+
+ // xdr:spPr
+ pDrawing->startElement(FSNS(XML_xdr, XML_spPr));
+ {
+ // a:xfrm
+ pDrawing->startElement(FSNS(XML_a, XML_xfrm));
+ {
+ pDrawing->singleElement(FSNS(XML_a, XML_off),
+ XML_x, "0",
+ XML_y, "0");
+ pDrawing->singleElement(FSNS(XML_a, XML_ext),
+ XML_cx, "0",
+ XML_cy, "0");
+ }
+ pDrawing->endElement(FSNS(XML_a, XML_xfrm));
+
+ // a:prstGeom
+ pDrawing->startElement(FSNS(XML_a, XML_prstGeom), XML_prst, "rect");
+ {
+ pDrawing->singleElement(FSNS(XML_a, XML_avLst));
+ }
+ pDrawing->endElement(FSNS(XML_a, XML_prstGeom));
+ }
+ pDrawing->endElement(FSNS(XML_xdr, XML_spPr));
+
+ // xdr:txBody
+ {
+ pDrawing->startElement(FSNS(XML_xdr, XML_txBody));
+
+#define DEFLRINS 254
+#define DEFTBINS 127
+ sal_Int32 nLeft, nRight, nTop, nBottom;
+ nLeft = nRight = DEFLRINS;
+ nTop = nBottom = DEFTBINS;
+
+ // top inset looks a bit different compared to ppt export
+ // check if something related doesn't work as expected
+ Reference< XPropertySet > rXPropSet(mxShape, UNO_QUERY);
+
+ try
+ {
+ css::uno::Any mAny;
+
+ mAny = rXPropSet->getPropertyValue("TextLeftDistance");
+ if (mAny.hasValue())
+ mAny >>= nLeft;
+
+ mAny = rXPropSet->getPropertyValue("TextRightDistance");
+ if (mAny.hasValue())
+ mAny >>= nRight;
+
+ mAny = rXPropSet->getPropertyValue("TextUpperDistance");
+ if (mAny.hasValue())
+ mAny >>= nTop;
+
+ mAny = rXPropSet->getPropertyValue("TextLowerDistance");
+ if (mAny.hasValue())
+ mAny >>= nBottom;
+ }
+ catch (...)
+ {
+ }
+
+ // Specifies the inset of the bounding rectangle.
+ // Insets are used just as internal margins for text boxes within shapes.
+ // If this attribute is omitted, then a value of 45720 or 0.05 inches is implied.
+ pDrawing->startElementNS(XML_a, XML_bodyPr,
+ XML_lIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeft)), nLeft != DEFLRINS),
+ XML_rIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRight)), nRight != DEFLRINS),
+ XML_tIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nTop)), nTop != DEFTBINS),
+ XML_bIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nBottom)), nBottom != DEFTBINS),
+ XML_anchor, "ctr");
+
+ {
+ bool bTextAutoGrowHeight = false;
+
+ try
+ {
+ css::uno::Any mAny;
+
+ mAny = rXPropSet->getPropertyValue("TextAutoGrowHeight");
+ if (mAny.hasValue())
+ mAny >>= bTextAutoGrowHeight;
+ }
+ catch (...)
+ {
+ }
+
+ pDrawing->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
+ }
+
+ pDrawing->endElementNS(XML_a, XML_bodyPr);
+
+ {
+ pDrawing->startElementNS(XML_a, XML_p);
+ pDrawing->startElementNS(XML_a, XML_r);
+ pDrawing->startElementNS(XML_a, XML_t);
+ pDrawing->write(msLabel);
+ pDrawing->endElementNS(XML_a, XML_t);
+ pDrawing->endElementNS(XML_a, XML_r);
+ pDrawing->endElementNS(XML_a, XML_p);
+ }
+
+ pDrawing->endElement(FSNS(XML_xdr, XML_txBody));
+ }
+ }
+ pDrawing->endElement(FSNS(XML_xdr, XML_sp));
+ pDrawing->singleElement(FSNS(XML_xdr, XML_clientData));
+ }
+ pDrawing->endElement(FSNS(XML_xdr, XML_twoCellAnchor));
+ pDrawing->endElement( FSNS( XML_mc, XML_Choice ) );
+ pDrawing->endElement( FSNS( XML_mc, XML_AlternateContent ) );
+}
+
+// output into ctrlProp1.xml
+OUString XclExpTbxControlObj::SaveControlPropertiesXml(XclExpXmlStream& rStrm) const
+{
+ OUString sIdFormControlPr;
+
+ switch (mnObjType)
+ {
+ case EXC_OBJTYPE_CHECKBOX:
+ {
+ const sal_Int32 nDrawing = DrawingML::getNewDrawingUniqueId();
+ sax_fastparser::FSHelperPtr pFormControl = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "ctrlProps/ctrlProps", nDrawing ),
+ XclXmlUtils::GetStreamName( "../", "ctrlProps/ctrlProps", nDrawing ),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.ms-excel.controlproperties+xml",
+ oox::getRelationship(Relationship::CTRLPROP),
+ &sIdFormControlPr );
+
+ rStrm.PushStream( pFormControl );
+ // checkbox
+ // <formControlPr
+ // xmlns="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
+ // objectType="CheckBox" checked="Checked" lockText="1" noThreeD="1"/>
+ //
+ pFormControl->write("<formControlPr xmlns=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main\" objectType=\"CheckBox\"");
+ if (mnState == EXC_OBJ_CHECKBOX_CHECKED)
+ pFormControl->write(" checked=\"Checked\"");
+
+ pFormControl->write(" autoLine=\"false\"");
+
+ if (mbPrint)
+ pFormControl->write(" print=\"true\"");
+ else
+ pFormControl->write(" print=\"false\"");
+
+ if (mxCellLinkAddress.IsValid())
+ {
+ OUString aCellLink = mxCellLinkAddress.Format(ScRefFlags::ADDR_ABS,
+ &GetDoc(),
+ ScAddress::Details(::formula::FormulaGrammar::CONV_XL_A1));
+
+ // "Sheet1!$C$5"
+ pFormControl->write(" fmlaLink=\"");
+ if (aCellLink.indexOf('!') < 0)
+ {
+ pFormControl->write(GetTabInfo().GetScTabName(mxCellLinkAddress.Tab()));
+ pFormControl->write("!");
+ }
+ pFormControl->write(aCellLink);
+ pFormControl->write("\"");
+ }
+
+ pFormControl->write(" lockText=\"1\" noThreeD=\"1\"/>");
+ rStrm.PopStream();
+
+ break;
+ }
+ case EXC_OBJTYPE_BUTTON:
+ {
+ sal_Int32 nDrawing = DrawingML::getNewDrawingUniqueId();
+ sax_fastparser::FSHelperPtr pFormControl = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/", "ctrlProps/ctrlProps", nDrawing),
+ XclXmlUtils::GetStreamName("../", "ctrlProps/ctrlProps", nDrawing),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.ms-excel.controlproperties+xml",
+ oox::getRelationship(Relationship::CTRLPROP), &sIdFormControlPr);
+
+ pFormControl->singleElement(XML_formControlPr, XML_xmlns,
+ rStrm.getNamespaceURL(OOX_NS(xls14Lst)), XML_objectType,
+ "Button", XML_lockText, "1");
+ break;
+ }
+ }
+
+ return sIdFormControlPr;
+}
+
+// output into sheet1.xml
+void XclExpTbxControlObj::SaveSheetXml(XclExpXmlStream& rStrm, const OUString& aIdFormControlPr) const
+{
+ switch (mnObjType)
+ {
+ case EXC_OBJTYPE_CHECKBOX:
+ {
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)));
+ rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x14");
+
+ rWorksheet->startElement(
+ XML_control,
+ XML_shapeId, OString::number(mnShapeId).getStr(),
+ FSNS(XML_r, XML_id), aIdFormControlPr,
+ XML_name, msLabel); // text to display with checkbox button
+
+ rWorksheet->write("<controlPr defaultSize=\"0\" locked=\"1\" autoFill=\"0\" autoLine=\"0\" autoPict=\"0\"");
+
+ if (mbPrint)
+ rWorksheet->write(" print=\"true\"");
+ else
+ rWorksheet->write(" print=\"false\"");
+
+ if (!msCtrlName.isEmpty())
+ {
+ rWorksheet->write(" altText=\"");
+ rWorksheet->write(msCtrlName); // alt text
+ rWorksheet->write("\"");
+ }
+
+ rWorksheet->write(">");
+
+ rWorksheet->startElement(XML_anchor, XML_moveWithCells, "true", XML_sizeWithCells, "false");
+ rWorksheet->startElement(XML_from);
+ lcl_WriteAnchorVertex(rWorksheet, maAreaFrom);
+ rWorksheet->endElement(XML_from);
+ rWorksheet->startElement(XML_to);
+ lcl_WriteAnchorVertex(rWorksheet, maAreaTo);
+ rWorksheet->endElement(XML_to);
+ rWorksheet->endElement( XML_anchor );
+
+ rWorksheet->write("</controlPr>");
+
+ rWorksheet->endElement(XML_control);
+ rWorksheet->endElement( FSNS( XML_mc, XML_Choice ) );
+ rWorksheet->endElement( FSNS( XML_mc, XML_AlternateContent ) );
+
+ break;
+ }
+ case EXC_OBJTYPE_BUTTON:
+ {
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent), FSNS(XML_xmlns, XML_mc),
+ rStrm.getNamespaceURL(OOX_NS(mce)));
+ rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x14");
+
+ rWorksheet->startElement(XML_control, XML_shapeId, OString::number(mnShapeId).getStr(),
+ FSNS(XML_r, XML_id), aIdFormControlPr, XML_name, msCtrlName);
+
+ OString aMacroName = GetMacroName().toUtf8();
+ // Omit the macro attribute if it would be empty.
+ const char* pMacroName = aMacroName.isEmpty() ? nullptr : aMacroName.getStr();
+ rWorksheet->startElement(XML_controlPr, XML_defaultSize, "0", XML_print,
+ mbPrint ? "true" : "false", XML_autoFill, "0", XML_autoPict,
+ "0", XML_macro, pMacroName);
+
+ rWorksheet->startElement(XML_anchor, XML_moveWithCells, "true", XML_sizeWithCells,
+ "false");
+
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ tools::Rectangle aAreaFrom;
+ tools::Rectangle aAreaTo;
+ lcl_GetFromTo(mrRoot, pObj->GetLogicRect(), GetTab(), aAreaFrom, aAreaTo,
+ /*bInEMU=*/true);
+
+ rWorksheet->startElement(XML_from);
+ lcl_WriteAnchorVertex(rWorksheet, aAreaFrom);
+ rWorksheet->endElement(XML_from);
+ rWorksheet->startElement(XML_to);
+ lcl_WriteAnchorVertex(rWorksheet, aAreaTo);
+ rWorksheet->endElement(XML_to);
+ rWorksheet->endElement(XML_anchor);
+
+ rWorksheet->endElement(XML_controlPr);
+
+ rWorksheet->endElement(XML_control);
+ rWorksheet->endElement(FSNS(XML_mc, XML_Choice));
+ rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent));
+ break;
+ }
+ }
+}
+
+//#endif
+
+XclExpChartObj::XclExpChartObj( XclExpObjectManager& rObjMgr, Reference< XShape > const & xShape, const tools::Rectangle* pChildAnchor, ScDocument* pDoc ) :
+ XclObj( rObjMgr, EXC_OBJTYPE_CHART ),
+ XclExpRoot( rObjMgr.GetRoot() ), mxShape( xShape ),
+ mpDoc(pDoc)
+{
+ // create the MSODRAWING record contents for the chart object
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty );
+ EscherPropertyContainer aPropOpt;
+ aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01040104 );
+ aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 );
+ aPropOpt.AddOpt( ESCHER_Prop_fillColor, 0x0800004E );
+ aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x0800004D );
+ aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00110010 );
+ aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x0800004D );
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080008 );
+ aPropOpt.AddOpt( ESCHER_Prop_fshadowObscured, 0x00020000 );
+ aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x00080000 );
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ // anchor
+ SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape );
+ ImplWriteAnchor( pSdrObj, pChildAnchor );
+
+ // client data (the following OBJ record)
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData );
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ // load the chart OLE object
+ if( SdrOle2Obj* pSdrOleObj = dynamic_cast< SdrOle2Obj* >( pSdrObj ) )
+ svt::EmbeddedObjectRef::TryRunningState( pSdrOleObj->GetObjRef() );
+
+ // create the chart substream object
+ ScfPropertySet aShapeProp( xShape );
+ css::awt::Rectangle aBoundRect;
+ aShapeProp.GetProperty( aBoundRect, "BoundRect" );
+ tools::Rectangle aChartRect( Point( aBoundRect.X, aBoundRect.Y ), Size( aBoundRect.Width, aBoundRect.Height ) );
+ mxChart = std::make_shared<XclExpChart>(GetRoot(), GetChartDoc(), aChartRect);
+}
+
+XclExpChartObj::~XclExpChartObj()
+{
+}
+
+void XclExpChartObj::Save( XclExpStream& rStrm )
+{
+ // content of OBJ record
+ XclObj::Save( rStrm );
+ // chart substream
+ mxChart->Save( rStrm );
+}
+
+void XclExpChartObj::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pDrawing = rStrm.GetCurrentStream();
+
+ // FIXME: two cell? it seems the two cell anchor is incorrect.
+ pDrawing->startElement( FSNS( XML_xdr, XML_twoCellAnchor ), // OOXTODO: oneCellAnchor, absoluteAnchor
+ XML_editAs, "oneCell" );
+ Reference< XPropertySet > xPropSet( mxShape, UNO_QUERY );
+ if (xPropSet.is())
+ {
+ XclObjAny::WriteFromTo( rStrm, mxShape, GetTab() );
+ ChartExport aChartExport(XML_xdr, pDrawing, GetChartDoc(), &rStrm, drawingml::DOCUMENT_XLSX);
+ auto pURLTransformer = std::make_shared<ScURLTransformer>(*mpDoc);
+ aChartExport.SetURLTranslator(pURLTransformer);
+ static sal_Int32 nChartCount = 0;
+ nChartCount++;
+ sal_Int32 nID = rStrm.GetUniqueId();
+ aChartExport.WriteChartObj( mxShape, nID, nChartCount );
+ // TODO: get the correcto chart number
+ }
+
+ pDrawing->singleElement( FSNS( XML_xdr, XML_clientData)
+ // OOXTODO: XML_fLocksWithSheet
+ // OOXTODO: XML_fPrintsWithSheet
+ );
+ pDrawing->endElement( FSNS( XML_xdr, XML_twoCellAnchor ) );
+}
+
+css::uno::Reference<css::chart::XChartDocument> XclExpChartObj::GetChartDoc() const
+{
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ if (!pObj || pObj->GetObjIdentifier() != SdrObjKind::OLE2)
+ return {};
+ // May load here - makes sure that we are working with actually loaded OLE object
+ return css::uno::Reference<css::chart::XChartDocument>(
+ static_cast<SdrOle2Obj*>(pObj)->getXModel(), css::uno::UNO_QUERY);
+}
+
+XclExpNote::XclExpNote(const XclExpRoot& rRoot, const ScAddress& rScPos,
+ const ScPostIt* pScNote, std::u16string_view rAddText)
+ : XclExpRecord(EXC_ID_NOTE)
+ , mrRoot(rRoot)
+ , maScPos(rScPos)
+ , mnObjId(EXC_OBJ_INVALID_ID)
+ , mbVisible(pScNote && pScNote->IsCaptionShown())
+ , meTHA(SDRTEXTHORZADJUST_LEFT)
+ , meTVA(SDRTEXTVERTADJUST_TOP)
+ , mbAutoScale(false)
+ , mbLocked(false)
+ , mbAutoFill(false)
+ , mbColHidden(false)
+ , mbRowHidden(false)
+{
+ // get the main note text
+ OUString aNoteText;
+ if( pScNote )
+ {
+ aNoteText = pScNote->GetText();
+ const EditTextObject *pEditObj = pScNote->GetEditTextObject();
+ if( pEditObj )
+ mpNoteContents = XclExpStringHelper::CreateString( rRoot, *pEditObj );
+ }
+ // append additional text
+ aNoteText = ScGlobal::addToken( aNoteText, rAddText, '\n', 2 );
+
+ // initialize record dependent on BIFF type
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF5:
+ maNoteText = OUStringToOString(aNoteText, rRoot.GetTextEncoding());
+ break;
+
+ case EXC_BIFF8:
+ {
+ // TODO: additional text
+ if( pScNote )
+ {
+ if( SdrCaptionObj* pCaption = pScNote->GetOrCreateCaption( maScPos ) )
+ {
+ lcl_GetFromTo( rRoot, pCaption->GetLogicRect(), maScPos.Tab(), maCommentFrom, maCommentTo );
+ if( const OutlinerParaObject* pOPO = pCaption->GetOutlinerParaObject() )
+ mnObjId = rRoot.GetObjectManager().AddObj( std::make_unique<XclObjComment>( rRoot.GetObjectManager(), pCaption->GetLogicRect(), pOPO->GetTextObject(), pCaption, mbVisible, maScPos, maCommentFrom, maCommentTo ) );
+
+ SfxItemSet aItemSet = pCaption->GetMergedItemSet();
+ meTVA = pCaption->GetTextVerticalAdjust();
+ meTHA = pCaption->GetTextHorizontalAdjust();
+ mbAutoScale = pCaption->GetFitToSize() != drawing::TextFitToSizeType_NONE;
+ mbLocked = pCaption->IsMoveProtect() || pCaption->IsResizeProtect();
+
+ // AutoFill style would change if Postit.cxx object creation values are changed
+ OUString aCol(aItemSet.Get(XATTR_FILLCOLOR).GetValue());
+ mbAutoFill = aCol.isEmpty() && (aItemSet.Get(XATTR_FILLSTYLE).GetValue() == drawing::FillStyle_SOLID);
+ mbRowHidden = (rRoot.GetDoc().RowHidden(maScPos.Row(),maScPos.Tab()));
+ mbColHidden = (rRoot.GetDoc().ColHidden(maScPos.Col(),maScPos.Tab()));
+ }
+ // stAuthor (variable): An XLUnicodeString that specifies the name of the comment
+ // author. String length MUST be greater than or equal to 1 and less than or equal
+ // to 54.
+ if( pScNote->GetAuthor().isEmpty() )
+ maAuthor = XclExpString( " " );
+ else
+ maAuthor = XclExpString( pScNote->GetAuthor(), XclStrFlags::NONE, 54 );
+ }
+
+ SetRecSize( 9 + maAuthor.GetSize() );
+ }
+ break;
+
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpNote::Save( XclExpStream& rStrm )
+{
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF5:
+ {
+ // write the NOTE record directly, there may be the need to create more than one
+ const char* pcBuffer = maNoteText.getStr();
+ sal_uInt16 nCharsLeft = static_cast< sal_uInt16 >( maNoteText.getLength() );
+
+ while( nCharsLeft )
+ {
+ sal_uInt16 nWriteChars = ::std::min( nCharsLeft, EXC_NOTE5_MAXLEN );
+
+ rStrm.StartRecord( EXC_ID_NOTE, 6 + nWriteChars );
+ if( pcBuffer == maNoteText.getStr() )
+ {
+ // first record: row, col, length of complete text
+ rStrm << static_cast< sal_uInt16 >( maScPos.Row() )
+ << static_cast< sal_uInt16 >( maScPos.Col() )
+ << nCharsLeft; // still contains full length
+ }
+ else
+ {
+ // next records: -1, 0, length of current text segment
+ rStrm << sal_uInt16( 0xFFFF )
+ << sal_uInt16( 0 )
+ << nWriteChars;
+ }
+ rStrm.Write( pcBuffer, nWriteChars );
+ rStrm.EndRecord();
+
+ pcBuffer += nWriteChars;
+ nCharsLeft = nCharsLeft - nWriteChars;
+ }
+ }
+ break;
+
+ case EXC_BIFF8:
+ if( mnObjId != EXC_OBJ_INVALID_ID )
+ XclExpRecord::Save( rStrm );
+ break;
+
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpNote::WriteBody( XclExpStream& rStrm )
+{
+ // BIFF5/BIFF7 is written separately
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() == EXC_BIFF8 );
+
+ sal_uInt16 nFlags = 0;
+ ::set_flag( nFlags, EXC_NOTE_VISIBLE, mbVisible );
+
+ rStrm << static_cast< sal_uInt16 >( maScPos.Row() )
+ << static_cast< sal_uInt16 >( maScPos.Col() )
+ << nFlags
+ << mnObjId
+ << maAuthor
+ << sal_uInt8( 0 );
+}
+
+void XclExpNote::WriteXml( sal_Int32 nAuthorId, XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr rComments = rStrm.GetCurrentStream();
+
+ rComments->startElement( XML_comment,
+ XML_ref, XclXmlUtils::ToOString(mrRoot.GetDoc(), maScPos),
+ XML_authorId, OString::number(nAuthorId)
+ // OOXTODO: XML_guid
+ );
+ rComments->startElement(XML_text);
+ // OOXTODO: phoneticPr, rPh, r
+ if( mpNoteContents )
+ mpNoteContents->WriteXml( rStrm );
+ rComments->endElement( XML_text );
+
+/*
+ Export of commentPr is disabled, since the current (Oct 2010)
+ version of MSO 2010 doesn't yet support commentPr
+ */
+#if 1//def XLSX_OOXML_FUTURE
+ if( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 )
+ {
+ rComments->startElement(FSNS(XML_mc, XML_AlternateContent));
+ rComments->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "v2");
+ rComments->startElement( XML_commentPr,
+ XML_autoFill, ToPsz( mbAutoFill ),
+ XML_autoScale, ToPsz( mbAutoScale ),
+ XML_colHidden, ToPsz( mbColHidden ),
+ XML_locked, ToPsz( mbLocked ),
+ XML_rowHidden, ToPsz( mbRowHidden ),
+ XML_textHAlign, ToHorizAlign( meTHA ),
+ XML_textVAlign, ToVertAlign( meTVA ) );
+ rComments->startElement(XML_anchor, XML_moveWithCells, "false", XML_sizeWithCells, "false");
+ rComments->startElement(FSNS(XML_xdr, XML_from));
+ lcl_WriteAnchorVertex( rComments, maCommentFrom );
+ rComments->endElement( FSNS( XML_xdr, XML_from ) );
+ rComments->startElement(FSNS(XML_xdr, XML_to));
+ lcl_WriteAnchorVertex( rComments, maCommentTo );
+ rComments->endElement( FSNS( XML_xdr, XML_to ) );
+ rComments->endElement( XML_anchor );
+ rComments->endElement( XML_commentPr );
+
+ rComments->endElement( FSNS( XML_mc, XML_Choice ) );
+ rComments->startElement(FSNS(XML_mc, XML_Fallback));
+ // Any fallback code ?
+ rComments->endElement( FSNS( XML_mc, XML_Fallback ) );
+ rComments->endElement( FSNS( XML_mc, XML_AlternateContent ) );
+ }
+#endif
+ rComments->endElement( XML_comment );
+}
+
+XclMacroHelper::XclMacroHelper( const XclExpRoot& rRoot ) :
+ XclExpControlHelper( rRoot )
+{
+}
+
+XclMacroHelper::~XclMacroHelper()
+{
+}
+
+void XclMacroHelper::WriteMacroSubRec( XclExpStream& rStrm )
+{
+ if( mxMacroLink )
+ WriteFormulaSubRec( rStrm, EXC_ID_OBJMACRO, *mxMacroLink );
+}
+
+const OUString& XclMacroHelper::GetMacroName() const { return maMacroName; }
+
+bool
+XclMacroHelper::SetMacroLink( const ScriptEventDescriptor& rEvent, const XclTbxEventType& nEventType )
+{
+ maMacroName = XclControlHelper::ExtractFromMacroDescriptor(rEvent, nEventType);
+ if (!maMacroName.isEmpty())
+ {
+ return SetMacroLink(maMacroName);
+ }
+ return false;
+}
+
+bool
+XclMacroHelper::SetMacroLink( const OUString& rMacroName )
+{
+ // OOXML documents do not store any defined name for VBA macros (while BIFF documents do).
+ bool bOOXML = GetOutput() == EXC_OUTPUT_XML_2007;
+ if (!rMacroName.isEmpty() && !bOOXML)
+ {
+ sal_uInt16 nExtSheet = GetLocalLinkManager().FindExtSheet( EXC_EXTSH_OWNDOC );
+ sal_uInt16 nNameIdx
+ = GetNameManager().InsertMacroCall(rMacroName, /*bVBasic=*/true, /*bFunc=*/false);
+ mxMacroLink = GetFormulaCompiler().CreateNameXFormula( nExtSheet, nNameIdx );
+ return true;
+ }
+ return false;
+}
+
+XclExpShapeObj::XclExpShapeObj( XclExpObjectManager& rRoot, css::uno::Reference< css::drawing::XShape > const & xShape, ScDocument* pDoc ) :
+ XclObjAny( rRoot, xShape, pDoc ),
+ XclMacroHelper( rRoot )
+{
+ if (SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(xShape))
+ {
+ ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( pSdrObj );
+ if ( pInfo && !pInfo->GetMacro().isEmpty() )
+// FIXME ooo330-m2: XclControlHelper::GetXclMacroName was removed in upstream sources; they started to call XclTools::GetXclMacroName instead; is this enough? it has only one parameter
+// SetMacroLink( XclControlHelper::GetXclMacroName( pInfo->GetMacro(), rRoot.GetDocShell() ) );
+ SetMacroLink( XclTools::GetXclMacroName( pInfo->GetMacro() ) );
+ }
+}
+
+XclExpShapeObj::~XclExpShapeObj()
+{
+}
+
+void XclExpShapeObj::WriteSubRecs( XclExpStream& rStrm )
+{
+ XclObjAny::WriteSubRecs( rStrm );
+ WriteMacroSubRec( rStrm );
+}
+
+XclExpComments::XclExpComments( SCTAB nTab, XclExpRecordList< XclExpNote >& rNotes )
+ : mnTab( nTab ), mrNotes( rNotes )
+{
+}
+
+void XclExpComments::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mrNotes.IsEmpty() )
+ return;
+
+ sax_fastparser::FSHelperPtr rComments = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "comments", mnTab + 1 ),
+ XclXmlUtils::GetStreamName( "../", "comments", mnTab + 1 ),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
+ oox::getRelationship(Relationship::COMMENTS));
+ rStrm.PushStream( rComments );
+
+ if( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 )
+ rComments->startElement( XML_comments,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)),
+ FSNS(XML_xmlns, XML_xdr), rStrm.getNamespaceURL(OOX_NS(dmlSpreadDr)),
+ FSNS(XML_xmlns, XML_v2), rStrm.getNamespaceURL(OOX_NS(mceTest)),
+ FSNS( XML_mc, XML_Ignorable ), "v2" );
+ else
+ rComments->startElement( XML_comments,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ FSNS(XML_xmlns, XML_xdr), rStrm.getNamespaceURL(OOX_NS(dmlSpreadDr)) );
+
+ rComments->startElement(XML_authors);
+
+ typedef std::set<OUString> Authors;
+ Authors aAuthors;
+
+ size_t nNotes = mrNotes.GetSize();
+ for( size_t i = 0; i < nNotes; ++i )
+ {
+ aAuthors.insert( XclXmlUtils::ToOUString( mrNotes.GetRecord( i )->GetAuthor() ) );
+ }
+
+ for( const auto& rAuthor : aAuthors )
+ {
+ rComments->startElement(XML_author);
+ rComments->writeEscaped( rAuthor );
+ rComments->endElement( XML_author );
+ }
+
+ rComments->endElement( XML_authors );
+ rComments->startElement(XML_commentList);
+
+ Authors::const_iterator aAuthorsBegin = aAuthors.begin();
+ for( size_t i = 0; i < nNotes; ++i )
+ {
+ XclExpRecordList< XclExpNote >::RecordRefType xNote = mrNotes.GetRecord( i );
+ Authors::const_iterator aAuthor = aAuthors.find(
+ XclXmlUtils::ToOUString( xNote->GetAuthor() ) );
+ sal_Int32 nAuthorId = distance( aAuthorsBegin, aAuthor );
+ xNote->WriteXml( nAuthorId, rStrm );
+ }
+
+ rComments->endElement( XML_commentList );
+ rComments->endElement( XML_comments );
+
+ rStrm.PopStream();
+}
+
+// object manager =============================================================
+
+XclExpObjectManager::XclExpObjectManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ InitStream( true );
+ mxEscherEx = std::make_shared<XclEscherEx>( GetRoot(), *this, *mxDffStrm );
+}
+
+XclExpObjectManager::XclExpObjectManager( const XclExpObjectManager& rParent ) :
+ XclExpRoot( rParent.GetRoot() )
+{
+ InitStream( false );
+ mxEscherEx = std::make_shared<XclEscherEx>( GetRoot(), *this, *mxDffStrm, rParent.mxEscherEx.get() );
+}
+
+XclExpObjectManager::~XclExpObjectManager()
+{
+}
+
+XclExpDffAnchorBase* XclExpObjectManager::CreateDffAnchor() const
+{
+ return new XclExpDffSheetAnchor( GetRoot() );
+}
+
+rtl::Reference< XclExpRecordBase > XclExpObjectManager::CreateDrawingGroup()
+{
+ return new XclExpMsoDrawingGroup( *mxEscherEx );
+}
+
+void XclExpObjectManager::StartSheet()
+{
+ mxObjList = new XclExpObjList( GetRoot(), *mxEscherEx );
+}
+
+rtl::Reference< XclExpRecordBase > XclExpObjectManager::ProcessDrawing( const SdrPage* pSdrPage )
+{
+ if( pSdrPage )
+ mxEscherEx->AddSdrPage( *pSdrPage, GetOutput() != EXC_OUTPUT_BINARY );
+ // the first dummy object may still be open
+ OSL_ENSURE( mxEscherEx->GetGroupLevel() <= 1, "XclExpObjectManager::ProcessDrawing - still groups open?" );
+ while( mxEscherEx->GetGroupLevel() )
+ mxEscherEx->LeaveGroup();
+ mxObjList->EndSheet();
+ return mxObjList;
+}
+
+rtl::Reference< XclExpRecordBase > XclExpObjectManager::ProcessDrawing( const Reference< XShapes >& rxShapes )
+{
+ if( rxShapes.is() )
+ mxEscherEx->AddUnoShapes( rxShapes, GetOutput() != EXC_OUTPUT_BINARY );
+ // the first dummy object may still be open
+ OSL_ENSURE( mxEscherEx->GetGroupLevel() <= 1, "XclExpObjectManager::ProcessDrawing - still groups open?" );
+ while( mxEscherEx->GetGroupLevel() )
+ mxEscherEx->LeaveGroup();
+ mxObjList->EndSheet();
+ return mxObjList;
+}
+
+void XclExpObjectManager::EndDocument()
+{
+ mxEscherEx->EndDocument();
+}
+
+XclExpMsoDrawing* XclExpObjectManager::GetMsodrawingPerSheet()
+{
+ return mxObjList->GetMsodrawingPerSheet();
+}
+
+bool XclExpObjectManager::HasObj() const
+{
+ return !mxObjList->empty();
+}
+
+sal_uInt16 XclExpObjectManager::AddObj( std::unique_ptr<XclObj> pObjRec )
+{
+ return mxObjList->Add( std::move(pObjRec) );
+}
+
+std::unique_ptr<XclObj> XclExpObjectManager::RemoveLastObj()
+{
+ return mxObjList->pop_back();
+}
+
+void XclExpObjectManager::InitStream( bool bTempFile )
+{
+ if( bTempFile )
+ {
+ mxTempFile = std::make_shared<::utl::TempFile>();
+ if( mxTempFile->IsValid() )
+ {
+ mxTempFile->EnableKillingFile();
+ mxDffStrm = ::utl::UcbStreamHelper::CreateStream( mxTempFile->GetURL(), StreamMode::STD_READWRITE );
+ }
+ }
+
+ if( !mxDffStrm )
+ mxDffStrm = std::make_unique<SvMemoryStream>();
+
+ mxDffStrm->SetEndian( SvStreamEndian::LITTLE );
+}
+
+XclExpEmbeddedObjectManager::XclExpEmbeddedObjectManager(
+ const XclExpObjectManager& rParent, const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY ) :
+ XclExpObjectManager( rParent ),
+ maPageSize( rPageSize ),
+ mnScaleX( nScaleX ),
+ mnScaleY( nScaleY )
+{
+}
+
+XclExpDffAnchorBase* XclExpEmbeddedObjectManager::CreateDffAnchor() const
+{
+ return new XclExpDffEmbeddedAnchor( GetRoot(), maPageSize, mnScaleX, mnScaleY );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeextlst.cxx b/sc/source/filter/excel/xeextlst.cxx
new file mode 100644
index 000000000..242f21dbb
--- /dev/null
+++ b/sc/source/filter/excel/xeextlst.cxx
@@ -0,0 +1,652 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <xeextlst.hxx>
+#include <xeroot.hxx>
+#include <xestyle.hxx>
+#include <stlpool.hxx>
+#include <scitems.hxx>
+#include <svl/itemset.hxx>
+#include <svl/intitem.hxx>
+
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+
+using namespace ::oox;
+
+XclExpExt::XclExpExt( const XclExpRoot& rRoot ):
+ XclExpRoot(rRoot)
+{
+}
+
+XclExtLst::XclExtLst( const XclExpRoot& rRoot ):
+ XclExpRoot(rRoot)
+{
+}
+
+XclExpExtNegativeColor::XclExpExtNegativeColor( const Color& rColor ):
+ maColor(rColor)
+{
+}
+
+void XclExpExtNegativeColor::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->singleElementNS( XML_x14, XML_negativeFillColor,
+ XML_rgb, XclXmlUtils::ToOString(maColor) );
+}
+
+XclExpExtAxisColor::XclExpExtAxisColor( const Color& rColor ):
+ maAxisColor(rColor)
+{
+}
+
+void XclExpExtAxisColor::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->singleElementNS( XML_x14, XML_axisColor,
+ XML_rgb, XclXmlUtils::ToOString(maAxisColor) );
+}
+
+XclExpExtIcon::XclExpExtIcon(const XclExpRoot& rRoot, const std::pair<ScIconSetType, sal_Int32>& rCustomEntry):
+ XclExpRoot(rRoot),
+ nIndex(rCustomEntry.second)
+{
+ pIconSetName = ScIconSetFormat::getIconSetName(rCustomEntry.first);
+}
+
+void XclExpExtIcon::SaveXml(XclExpXmlStream& rStrm)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ if (nIndex == -1)
+ {
+ nIndex = 0;
+ pIconSetName = "NoIcons";
+ }
+
+ rWorksheet->singleElementNS(XML_x14, XML_cfIcon,
+ XML_iconSet, pIconSetName,
+ XML_iconId, OString::number(nIndex));
+}
+
+XclExpExtCfvo::XclExpExtCfvo( const XclExpRoot& rRoot, const ScColorScaleEntry& rEntry, const ScAddress& rSrcPos, bool bFirst ):
+ XclExpRoot(rRoot),
+ meType(rEntry.GetType()),
+ mbFirst(bFirst)
+{
+ if( rEntry.GetType() == COLORSCALE_FORMULA )
+ {
+ const ScTokenArray* pArr = rEntry.GetFormula();
+ OUString aFormula;
+ if(pArr)
+ {
+ aFormula = XclXmlUtils::ToOUString( GetCompileFormulaContext(), rSrcPos, pArr);
+ }
+ maValue = OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8 );
+ }
+ else
+ maValue = OString::number(rEntry.GetValue());
+}
+
+namespace {
+
+const char* getColorScaleType( ScColorScaleEntryType eType, bool bFirst )
+{
+ switch(eType)
+ {
+ case COLORSCALE_MIN:
+ return "min";
+ case COLORSCALE_MAX:
+ return "max";
+ case COLORSCALE_PERCENT:
+ return "percent";
+ case COLORSCALE_FORMULA:
+ return "formula";
+ case COLORSCALE_AUTO:
+ if(bFirst)
+ return "autoMin";
+ else
+ return "autoMax";
+ case COLORSCALE_PERCENTILE:
+ return "percentile";
+ default:
+ break;
+ }
+ return "num";
+}
+
+}
+
+void XclExpExtCfvo::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElementNS(XML_x14, XML_cfvo, XML_type, getColorScaleType(meType, mbFirst));
+
+ if (meType == COLORSCALE_FORMULA ||
+ meType == COLORSCALE_PERCENT ||
+ meType == COLORSCALE_PERCENTILE ||
+ meType == COLORSCALE_VALUE)
+ {
+ rWorksheet->startElementNS(XML_xm, XML_f);
+ rWorksheet->writeEscaped(maValue.getStr());
+ rWorksheet->endElementNS(XML_xm, XML_f);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_cfvo);
+}
+
+XclExpExtCF::XclExpExtCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormat ):
+ XclExpRoot(rRoot),
+ mrFormat(rFormat)
+{
+}
+
+namespace {
+
+bool RequiresFixedFormula(ScConditionMode eMode)
+{
+ switch (eMode)
+ {
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+OString GetFixedFormula(ScConditionMode eMode, const ScAddress& rAddress, std::string_view rText)
+{
+ OStringBuffer aBuffer;
+ XclXmlUtils::ToOString(aBuffer, rAddress);
+ OString aPos = aBuffer.makeStringAndClear();
+ switch (eMode)
+ {
+ case ScConditionMode::BeginsWith:
+ return OString("LEFT(" + aPos + ",LEN(" + rText + "))=" + rText);
+ case ScConditionMode::EndsWith:
+ return OString("RIGHT(" + aPos + ",LEN(" + rText + "))=" + rText);
+ case ScConditionMode::ContainsText:
+ return OString(OString::Concat("NOT(ISERROR(SEARCH(") + rText + "," + aPos + ")))");
+ case ScConditionMode::NotContainsText:
+ return OString(OString::Concat("ISERROR(SEARCH(") + rText + "," + aPos + "))");
+ default:
+ break;
+ }
+
+ return "";
+}
+
+}
+
+void XclExpExtCF::SaveXml( XclExpXmlStream& rStrm )
+{
+ OUString aStyleName = mrFormat.GetStyle();
+ SfxStyleSheetBasePool* pPool = GetDoc().GetStyleSheetPool();
+ SfxStyleSheetBase* pStyle = pPool->Find(aStyleName, SfxStyleFamily::Para);
+ SfxItemSet& rSet = pStyle->GetItemSet();
+
+ std::unique_ptr<ScTokenArray> pTokenArray(mrFormat.CreateFlatCopiedTokenArray(0));
+ aFormula = XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormat.GetValidSrcPos(), pTokenArray.get());
+
+ std::unique_ptr<XclExpColor> pColor(new XclExpColor);
+ if(!pColor->FillFromItemSet( rSet ))
+ pColor.reset();
+
+ std::unique_ptr<XclExpCellBorder> pBorder(new XclExpCellBorder);
+ if (!pBorder->FillFromItemSet( rSet, GetPalette(), GetBiff()) )
+ pBorder.reset();
+
+ std::unique_ptr<XclExpCellAlign> pAlign(new XclExpCellAlign);
+ if (!pAlign->FillFromItemSet(*this, rSet, false, GetBiff()))
+ pAlign.reset();
+
+ std::unique_ptr<XclExpCellProt> pCellProt(new XclExpCellProt);
+ if (!pCellProt->FillFromItemSet( rSet ))
+ pCellProt.reset();
+
+ std::unique_ptr<XclExpDxfFont> pFont(new XclExpDxfFont(GetRoot(), rSet));
+
+ std::unique_ptr<XclExpNumFmt> pNumFormat;
+ if( const SfxUInt32Item* pPoolItem = rSet.GetItemIfSet( ATTR_VALUE_FORMAT ) )
+ {
+ sal_uInt32 nScNumFmt = pPoolItem->GetValue();
+ XclExpNumFmtBuffer& rNumFmtBuffer = GetRoot().GetNumFmtBuffer();
+ sal_uInt32 nXclNumFmt = rNumFmtBuffer.Insert(nScNumFmt);
+ pNumFormat.reset(new XclExpNumFmt(nScNumFmt, nXclNumFmt, rNumFmtBuffer.GetFormatCode(nScNumFmt)));
+ }
+
+ XclExpDxf rDxf( GetRoot(),
+ std::move(pAlign),
+ std::move(pBorder),
+ std::move(pFont),
+ std::move(pNumFormat),
+ std::move(pCellProt),
+ std::move(pColor) );
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ ScConditionMode eOperation = mrFormat.GetOperation();
+ if (RequiresFixedFormula(eOperation))
+ {
+ ScAddress aFixedFormulaPos = mrFormat.GetValidSrcPos();
+ OString aFixedFormulaText = aFormula.toUtf8();
+ OString aFixedFormula = GetFixedFormula(eOperation, aFixedFormulaPos, aFixedFormulaText);
+ rWorksheet->startElementNS( XML_xm, XML_f );
+ rWorksheet->writeEscaped(aFixedFormula.getStr());
+ rWorksheet->endElementNS( XML_xm, XML_f );
+
+ rWorksheet->startElementNS( XML_xm, XML_f );
+ rWorksheet->writeEscaped( aFormula );
+ rWorksheet->endElementNS( XML_xm, XML_f );
+ rDxf.SaveXmlExt(rStrm);
+ }
+ else
+ {
+ rWorksheet->startElementNS(XML_xm, XML_f);
+ rWorksheet->writeEscaped(aFormula);
+ rWorksheet->endElementNS(XML_xm, XML_f);
+ rDxf.SaveXmlExt(rStrm);
+ }
+}
+
+XclExpExtDataBar::XclExpExtDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, const ScAddress& rPos ):
+ XclExpRoot(rRoot)
+{
+ const ScDataBarFormatData& rFormatData = *rFormat.GetDataBarData();
+ mpLowerLimit.reset(new XclExpExtCfvo(*this, *rFormatData.mpLowerLimit, rPos, true));
+ mpUpperLimit.reset(new XclExpExtCfvo(*this, *rFormatData.mpUpperLimit, rPos, false));
+ if (rFormatData.mxNegativeColor)
+ mpNegativeColor.reset(new XclExpExtNegativeColor(*rFormatData.mxNegativeColor));
+ else
+ mpNegativeColor.reset( new XclExpExtNegativeColor( rFormatData.maPositiveColor ) );
+ mpAxisColor.reset( new XclExpExtAxisColor( rFormatData.maAxisColor ) );
+
+ meAxisPosition = rFormatData.meAxisPosition;
+ mbGradient = rFormatData.mbGradient;
+ mnMinLength = rFormatData.mnMinLength;
+ mnMaxLength = rFormatData.mnMaxLength;
+}
+
+namespace {
+
+const char* getAxisPosition(databar::ScAxisPosition eAxisPosition)
+{
+ switch(eAxisPosition)
+ {
+ case databar::NONE:
+ return "none";
+ case databar::AUTOMATIC:
+ return "automatic";
+ case databar::MIDDLE:
+ return "middle";
+ }
+ return "";
+}
+
+const char* GetOperatorString(ScConditionMode eMode)
+{
+ const char* pRet = nullptr;
+ switch(eMode)
+ {
+ case ScConditionMode::Equal:
+ pRet = "equal";
+ break;
+ case ScConditionMode::Less:
+ pRet = "lessThan";
+ break;
+ case ScConditionMode::Greater:
+ pRet = "greaterThan";
+ break;
+ case ScConditionMode::EqLess:
+ pRet = "lessThanOrEqual";
+ break;
+ case ScConditionMode::EqGreater:
+ pRet = "greaterThanOrEqual";
+ break;
+ case ScConditionMode::NotEqual:
+ pRet = "notEqual";
+ break;
+ case ScConditionMode::Between:
+ pRet = "between";
+ break;
+ case ScConditionMode::NotBetween:
+ pRet = "notBetween";
+ break;
+ case ScConditionMode::Duplicate:
+ pRet = nullptr;
+ break;
+ case ScConditionMode::NotDuplicate:
+ pRet = nullptr;
+ break;
+ case ScConditionMode::BeginsWith:
+ pRet = "beginsWith";
+ break;
+ case ScConditionMode::EndsWith:
+ pRet = "endsWith";
+ break;
+ case ScConditionMode::ContainsText:
+ pRet = "containsText";
+ break;
+ case ScConditionMode::NotContainsText:
+ pRet = "notContains";
+ break;
+ case ScConditionMode::Direct:
+ break;
+ case ScConditionMode::NONE:
+ default:
+ break;
+ }
+ return pRet;
+}
+
+const char* GetTypeString(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::Direct:
+ return "expression";
+ case ScConditionMode::BeginsWith:
+ return "beginsWith";
+ case ScConditionMode::EndsWith:
+ return "endsWith";
+ case ScConditionMode::ContainsText:
+ return "containsText";
+ case ScConditionMode::NotContainsText:
+ return "notContainsText";
+ default:
+ return "cellIs";
+ }
+}
+
+}
+
+void XclExpExtDataBar::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElementNS( XML_x14, XML_dataBar,
+ XML_minLength, OString::number(mnMinLength),
+ XML_maxLength, OString::number(mnMaxLength),
+ XML_axisPosition, getAxisPosition(meAxisPosition),
+ XML_gradient, ToPsz(mbGradient) );
+
+ mpLowerLimit->SaveXml( rStrm );
+ mpUpperLimit->SaveXml( rStrm );
+ mpNegativeColor->SaveXml( rStrm );
+ mpAxisColor->SaveXml( rStrm );
+
+ rWorksheet->endElementNS( XML_x14, XML_dataBar );
+}
+
+XclExpExtIconSet::XclExpExtIconSet(const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, const ScAddress& rPos):
+ XclExpRoot(rRoot)
+{
+ const ScIconSetFormatData& rData = *rFormat.GetIconSetData();
+ for (auto const& itr : rData.m_Entries)
+ {
+ maCfvos.AppendNewRecord(new XclExpExtCfvo(*this, *itr, rPos, false));
+ }
+ mbCustom = rData.mbCustom;
+ mbReverse = rData.mbReverse;
+ mbShowValue = rData.mbShowValue;
+ mpIconSetName = ScIconSetFormat::getIconSetName(rData.eIconSetType);
+
+ if (mbCustom)
+ {
+ for (const auto& rItem : rData.maCustomVector)
+ {
+ maCustom.AppendNewRecord(new XclExpExtIcon(*this, rItem));
+ }
+ }
+}
+
+void XclExpExtIconSet::SaveXml(XclExpXmlStream& rStrm)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElementNS(XML_x14, XML_iconSet,
+ XML_iconSet, mpIconSetName,
+ XML_custom, sax_fastparser::UseIf(ToPsz10(mbCustom), mbCustom),
+ XML_reverse, ToPsz10(mbReverse),
+ XML_showValue, ToPsz10(mbShowValue));
+
+ maCfvos.SaveXml(rStrm);
+
+ if (mbCustom)
+ {
+ maCustom.SaveXml(rStrm);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_iconSet);
+}
+
+XclExpExtCfRule::XclExpExtCfRule( const XclExpRoot& rRoot, const ScFormatEntry& rFormat, const ScAddress& rPos, const OString& rId, sal_Int32 nPriority ):
+ XclExpRoot(rRoot),
+ maId(rId),
+ pType(nullptr),
+ mnPriority(nPriority),
+ mOperator(nullptr)
+{
+ switch (rFormat.GetType())
+ {
+ case ScFormatEntry::Type::Databar:
+ {
+ const ScDataBarFormat& rDataBar = static_cast<const ScDataBarFormat&>(rFormat);
+ mxEntry = new XclExpExtDataBar( *this, rDataBar, rPos );
+ pType = "dataBar";
+ }
+ break;
+ case ScFormatEntry::Type::Iconset:
+ {
+ const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(rFormat);
+ mxEntry = new XclExpExtIconSet(*this, rIconSet, rPos);
+ pType = "iconSet";
+ }
+ break;
+ case ScFormatEntry::Type::ExtCondition:
+ {
+ const ScCondFormatEntry& rCondFormat = static_cast<const ScCondFormatEntry&>(rFormat);
+ mxEntry = new XclExpExtCF(*this, rCondFormat);
+ pType = GetTypeString(rCondFormat.GetOperation());
+ mOperator = GetOperatorString( rCondFormat.GetOperation() );
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void XclExpExtCfRule::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (!mxEntry)
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElementNS( XML_x14, XML_cfRule,
+ XML_type, pType,
+ XML_priority, sax_fastparser::UseIf(OString::number(mnPriority + 1), mnPriority != -1),
+ XML_operator, mOperator,
+ XML_id, maId );
+
+ mxEntry->SaveXml( rStrm );
+
+ rWorksheet->endElementNS( XML_x14, XML_cfRule );
+
+}
+
+XclExpExtConditionalFormatting::XclExpExtConditionalFormatting( const XclExpRoot& rRoot,
+ std::vector<XclExpExtCondFormatData>& rData, const ScRangeList& rRange):
+ XclExpRoot(rRoot),
+ maRange(rRange)
+{
+ ScAddress aAddr = maRange.front().aStart;
+ for (const auto& rItem : rData)
+ {
+ const ScFormatEntry* pEntry = rItem.pEntry;
+ switch (pEntry->GetType())
+ {
+ case ScFormatEntry::Type::Iconset:
+ {
+ const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(*pEntry);
+ bool bNeedsExt = false;
+ switch (rIconSet.GetIconSetData()->eIconSetType)
+ {
+ case IconSet_3Triangles:
+ case IconSet_3Smilies:
+ case IconSet_3ColorSmilies:
+ case IconSet_5Boxes:
+ case IconSet_3Stars:
+ bNeedsExt = true;
+ break;
+ default:
+ break;
+ }
+
+ if (rIconSet.GetIconSetData()->mbCustom)
+ bNeedsExt = true;
+
+ if (bNeedsExt)
+ {
+ maCfRules.AppendNewRecord(new XclExpExtCfRule(*this, *pEntry, aAddr, rItem.aGUID, rItem.nPriority));
+ }
+ }
+ break;
+ case ScFormatEntry::Type::Databar:
+ maCfRules.AppendNewRecord(new XclExpExtCfRule( *this, *pEntry, aAddr, rItem.aGUID, rItem.nPriority));
+ break;
+ case ScFormatEntry::Type::ExtCondition:
+ maCfRules.AppendNewRecord(new XclExpExtCfRule( *this, *pEntry, aAddr, rItem.aGUID, rItem.nPriority));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void XclExpExtConditionalFormatting::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElementNS( XML_x14, XML_conditionalFormatting,
+ FSNS( XML_xmlns, XML_xm ), rStrm.getNamespaceURL(OOX_NS(xm)) );
+
+ maCfRules.SaveXml( rStrm );
+ rWorksheet->startElementNS(XML_xm, XML_sqref);
+ rWorksheet->write(XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maRange));
+
+ rWorksheet->endElementNS( XML_xm, XML_sqref );
+
+ rWorksheet->endElementNS( XML_x14, XML_conditionalFormatting );
+}
+
+XclExpExtCalcPr::XclExpExtCalcPr( const XclExpRoot& rRoot, formula::FormulaGrammar::AddressConvention eConv ):
+ XclExpExt( rRoot )
+{
+ maURI = OString("{7626C862-2A13-11E5-B345-FEFF819CDC9F}");
+
+ switch (eConv)
+ {
+ case formula::FormulaGrammar::CONV_OOO:
+ maSyntax = OString("CalcA1");
+ break;
+ case formula::FormulaGrammar::CONV_XL_A1:
+ maSyntax = OString("ExcelA1");
+ break;
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ maSyntax = OString("ExcelR1C1");
+ break;
+ case formula::FormulaGrammar::CONV_A1_XL_A1:
+ maSyntax = OString("CalcA1ExcelA1");
+ break;
+ case formula::FormulaGrammar::CONV_UNSPECIFIED:
+ case formula::FormulaGrammar::CONV_ODF:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ case formula::FormulaGrammar::CONV_LOTUS_A1:
+ case formula::FormulaGrammar::CONV_LAST:
+ maSyntax = OString("Unspecified");
+ break;
+ }
+}
+
+void XclExpExtCalcPr::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_ext,
+ FSNS(XML_xmlns, XML_loext), rStrm.getNamespaceURL(OOX_NS(loext)),
+ XML_uri, maURI );
+
+ rWorksheet->singleElementNS(XML_loext, XML_extCalcPr, XML_stringRefSyntax, maSyntax);
+
+ rWorksheet->endElement( XML_ext );
+}
+
+XclExpExtCondFormat::XclExpExtCondFormat( const XclExpRoot& rRoot ):
+ XclExpExt( rRoot )
+{
+ maURI = OString("{78C0D931-6437-407d-A8EE-F0AAD7539E65}");
+}
+
+void XclExpExtCondFormat::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_ext,
+ FSNS(XML_xmlns, XML_x14), rStrm.getNamespaceURL(OOX_NS(xls14Lst)),
+ XML_uri, maURI );
+
+ rWorksheet->startElementNS(XML_x14, XML_conditionalFormattings);
+
+ maCF.SaveXml( rStrm );
+
+ rWorksheet->endElementNS( XML_x14, XML_conditionalFormattings );
+ rWorksheet->endElement( XML_ext );
+}
+
+void XclExpExtCondFormat::AddRecord( XclExpExtConditionalFormatting* pEntry )
+{
+ maCF.AppendRecord( pEntry );
+}
+
+void XclExtLst::SaveXml( XclExpXmlStream& rStrm )
+{
+ if(maExtEntries.IsEmpty())
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_extLst);
+
+ maExtEntries.SaveXml(rStrm);
+
+ rWorksheet->endElement( XML_extLst );
+}
+
+void XclExtLst::AddRecord( XclExpExt* pEntry )
+{
+ maExtEntries.AppendRecord( pEntry );
+}
+
+XclExpExt* XclExtLst::GetItem( XclExpExtType eType )
+{
+ size_t n = maExtEntries.GetSize();
+ for( size_t i = 0; i < n; ++i )
+ {
+ if (maExtEntries.GetRecord( i )->GetType() == eType)
+ return maExtEntries.GetRecord( i );
+ }
+
+ return nullptr;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeformula.cxx b/sc/source/filter/excel/xeformula.cxx
new file mode 100644
index 000000000..e6eabd69c
--- /dev/null
+++ b/sc/source/filter/excel/xeformula.cxx
@@ -0,0 +1,2709 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <map>
+#include <addincol.hxx>
+#include <compiler.hxx>
+#include <document.hxx>
+#include <externalrefmgr.hxx>
+#include <rangelst.hxx>
+#include <tokenarray.hxx>
+#include <scmatrix.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xestring.hxx>
+#include <xllink.hxx>
+#include <xltools.hxx>
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+
+using namespace ::formula;
+
+// External reference log =====================================================
+
+XclExpRefLogEntry::XclExpRefLogEntry() :
+ mpUrl( nullptr ),
+ mpFirstTab( nullptr ),
+ mpLastTab( nullptr ),
+ mnFirstXclTab( EXC_TAB_DELETED ),
+ mnLastXclTab( EXC_TAB_DELETED )
+{
+}
+
+// Formula compiler ===========================================================
+
+namespace {
+
+/** Wrapper structure for a processed Calc formula token with additional
+ settings (whitespaces). */
+struct XclExpScToken
+{
+ const FormulaToken* mpScToken; /// Currently processed Calc token.
+ sal_uInt8 mnSpaces; /// Number of spaces before the Calc token.
+
+ explicit XclExpScToken() : mpScToken( nullptr ), mnSpaces( 0 ) {}
+ bool Is() const { return mpScToken != nullptr; }
+ StackVar GetType() const { return mpScToken ? mpScToken->GetType() : svUnknown; }
+ OpCode GetOpCode() const { return mpScToken ? mpScToken->GetOpCode() : ocNone; }
+};
+
+/** Effective token class conversion types. */
+enum XclExpClassConv
+{
+ EXC_CLASSCONV_ORG, /// Keep original class of the token.
+ EXC_CLASSCONV_VAL, /// Convert ARR tokens to VAL class (REF remains unchanged).
+ EXC_CLASSCONV_ARR /// Convert VAL tokens to ARR class (REF remains unchanged).
+};
+
+/** Token class conversion and position of a token in the token array. */
+struct XclExpTokenConvInfo
+{
+ sal_uInt16 mnTokPos; /// Position of the token in the token array.
+ XclFuncParamConv meConv; /// Token class conversion type.
+ bool mbValType; /// Data type (false = REFTYPE, true = VALTYPE).
+};
+
+/** Vector of token position and conversion for all operands of an operator,
+ or for all parameters of a function. */
+struct XclExpOperandList : public std::vector< XclExpTokenConvInfo >
+{
+ explicit XclExpOperandList() { reserve( 2 ); }
+ void AppendOperand( sal_uInt16 nTokPos, XclFuncParamConv eConv, bool bValType );
+};
+
+void XclExpOperandList::AppendOperand( sal_uInt16 nTokPos, XclFuncParamConv eConv, bool bValType )
+{
+ resize( size() + 1 );
+ XclExpTokenConvInfo& rConvInfo = back();
+ rConvInfo.mnTokPos = nTokPos;
+ rConvInfo.meConv = eConv;
+ rConvInfo.mbValType = bValType;
+}
+
+typedef std::shared_ptr< XclExpOperandList > XclExpOperandListRef;
+
+/** Encapsulates all data needed for a call to an external function (macro, add-in). */
+struct XclExpExtFuncData
+{
+ OUString maFuncName; /// Name of the function.
+ bool mbVBasic; /// True = Visual Basic macro call.
+ bool mbHidden; /// True = Create hidden defined name.
+
+ explicit XclExpExtFuncData() : mbVBasic( false ), mbHidden( false ) {}
+ void Set( const OUString& rFuncName, bool bVBasic, bool bHidden );
+};
+
+void XclExpExtFuncData::Set( const OUString& rFuncName, bool bVBasic, bool bHidden )
+{
+ maFuncName = rFuncName;
+ mbVBasic = bVBasic;
+ mbHidden = bHidden;
+}
+
+/** Encapsulates all data needed to process an entire function. */
+class XclExpFuncData
+{
+public:
+ explicit XclExpFuncData(
+ const XclExpScToken& rTokData,
+ const XclFunctionInfo& rFuncInfo,
+ const XclExpExtFuncData& rExtFuncData );
+
+ const FormulaToken& GetScToken() const { return *mrTokData.mpScToken; }
+ OpCode GetOpCode() const { return mrFuncInfo.meOpCode; }
+ sal_uInt16 GetXclFuncIdx() const { return mrFuncInfo.mnXclFunc; }
+ bool IsVolatile() const { return mrFuncInfo.IsVolatile(); }
+ bool IsFixedParamCount() const { return mrFuncInfo.IsFixedParamCount(); }
+ bool IsAddInEquivalent() const { return mrFuncInfo.IsAddInEquivalent(); }
+ bool IsMacroFunc() const { return mrFuncInfo.IsMacroFunc(); }
+ sal_uInt8 GetSpaces() const { return mrTokData.mnSpaces; }
+ const XclExpExtFuncData& GetExtFuncData() const { return maExtFuncData; }
+ sal_uInt8 GetReturnClass() const { return mrFuncInfo.mnRetClass; }
+
+ const XclFuncParamInfo& GetParamInfo() const;
+ bool IsCalcOnlyParam() const;
+ bool IsExcelOnlyParam() const;
+ void IncParamInfoIdx();
+
+ sal_uInt8 GetMinParamCount() const { return mrFuncInfo.mnMinParamCount; }
+ sal_uInt8 GetMaxParamCount() const { return mrFuncInfo.mnMaxParamCount; }
+ sal_uInt8 GetParamCount() const { return static_cast< sal_uInt8 >( mxOperands->size() ); }
+ void FinishParam( sal_uInt16 nTokPos );
+ const XclExpOperandListRef& GetOperandList() const { return mxOperands; }
+
+ ScfUInt16Vec& GetAttrPosVec() { return maAttrPosVec; }
+ void AppendAttrPos( sal_uInt16 nPos ) { maAttrPosVec.push_back( nPos ); }
+
+private:
+ ScfUInt16Vec maAttrPosVec; /// Token array positions of tAttr tokens.
+ const XclExpScToken& mrTokData; /// Data about processed function name token.
+ const XclFunctionInfo& mrFuncInfo; /// Constant data about processed function.
+ XclExpExtFuncData maExtFuncData; /// Data for external functions (macro, add-in).
+ XclExpOperandListRef mxOperands; /// Class conversion and position of all parameters.
+ const XclFuncParamInfo* mpParamInfo; /// Information for current parameter.
+};
+
+XclExpFuncData::XclExpFuncData( const XclExpScToken& rTokData,
+ const XclFunctionInfo& rFuncInfo, const XclExpExtFuncData& rExtFuncData ) :
+ mrTokData( rTokData ),
+ mrFuncInfo( rFuncInfo ),
+ maExtFuncData( rExtFuncData ),
+ mxOperands( std::make_shared<XclExpOperandList>() ),
+ mpParamInfo( rFuncInfo.mpParamInfos )
+{
+ OSL_ENSURE( mrTokData.mpScToken, "XclExpFuncData::XclExpFuncData - missing core token" );
+ // set name of an add-in function
+ if( (maExtFuncData.maFuncName.isEmpty()) && dynamic_cast< const FormulaExternalToken* >( mrTokData.mpScToken ) )
+ maExtFuncData.Set( GetScToken().GetExternal(), true, false );
+}
+
+const XclFuncParamInfo& XclExpFuncData::GetParamInfo() const
+{
+ static const XclFuncParamInfo saInvalidInfo = { EXC_PARAM_NONE, EXC_PARAMCONV_ORG, false };
+ return mpParamInfo ? *mpParamInfo : saInvalidInfo;
+}
+
+bool XclExpFuncData::IsCalcOnlyParam() const
+{
+ return mpParamInfo && (mpParamInfo->meValid == EXC_PARAM_CALCONLY);
+}
+
+bool XclExpFuncData::IsExcelOnlyParam() const
+{
+ return mpParamInfo && (mpParamInfo->meValid == EXC_PARAM_EXCELONLY);
+}
+
+void XclExpFuncData::IncParamInfoIdx()
+{
+ if( !mpParamInfo )
+ return;
+
+ // move pointer to next entry, if something explicit follows
+ if( (o3tl::make_unsigned( mpParamInfo - mrFuncInfo.mpParamInfos + 1 ) < EXC_FUNCINFO_PARAMINFO_COUNT) && (mpParamInfo[ 1 ].meValid != EXC_PARAM_NONE) )
+ ++mpParamInfo;
+ // if last parameter type is 'Excel-only' or 'Calc-only', do not repeat it
+ else if( IsExcelOnlyParam() || IsCalcOnlyParam() )
+ mpParamInfo = nullptr;
+ // points to last info, but parameter pairs expected, move to previous info
+ else if( mrFuncInfo.IsParamPairs() )
+ --mpParamInfo;
+ // otherwise: repeat last parameter class
+}
+
+void XclExpFuncData::FinishParam( sal_uInt16 nTokPos )
+{
+ // write token class conversion info for this parameter
+ const XclFuncParamInfo& rParamInfo = GetParamInfo();
+ mxOperands->AppendOperand( nTokPos, rParamInfo.meConv, rParamInfo.mbValType );
+ // move to next parameter info structure
+ IncParamInfoIdx();
+}
+
+// compiler configuration -----------------------------------------------------
+
+/** Type of token class handling. */
+enum XclExpFmlaClassType
+{
+ EXC_CLASSTYPE_CELL, /// Cell formula, shared formula.
+ EXC_CLASSTYPE_ARRAY, /// Array formula, conditional formatting, data validation.
+ EXC_CLASSTYPE_NAME /// Defined name, range list.
+};
+
+/** Configuration data of the formula compiler. */
+struct XclExpCompConfig
+{
+ XclFormulaType meType; /// Type of the formula to be created.
+ XclExpFmlaClassType meClassType; /// Token class handling type.
+ bool mbLocalLinkMgr; /// True = local (per-sheet) link manager, false = global.
+ bool mbFromCell; /// True = Any kind of cell formula (cell, array, shared).
+ bool mb3DRefOnly; /// True = Only 3D references allowed (e.g. names).
+ bool mbAllowArrays; /// True = Allow inline arrays.
+};
+
+/** The table containing configuration data for all formula types. */
+const XclExpCompConfig spConfigTable[] =
+{
+ // formula type token class type lclLM inCell 3dOnly allowArray
+ { EXC_FMLATYPE_CELL, EXC_CLASSTYPE_CELL, true, true, false, true },
+ { EXC_FMLATYPE_SHARED, EXC_CLASSTYPE_CELL, true, true, false, true },
+ { EXC_FMLATYPE_MATRIX, EXC_CLASSTYPE_ARRAY, true, true, false, true },
+ { EXC_FMLATYPE_CONDFMT, EXC_CLASSTYPE_ARRAY, true, false, false, false },
+ { EXC_FMLATYPE_DATAVAL, EXC_CLASSTYPE_ARRAY, true, false, false, false },
+ { EXC_FMLATYPE_NAME, EXC_CLASSTYPE_NAME, false, false, true, true },
+ { EXC_FMLATYPE_CHART, EXC_CLASSTYPE_NAME, true, false, true, true },
+ { EXC_FMLATYPE_CONTROL, EXC_CLASSTYPE_NAME, true, false, false, false },
+ { EXC_FMLATYPE_WQUERY, EXC_CLASSTYPE_NAME, true, false, true, false },
+ { EXC_FMLATYPE_LISTVAL, EXC_CLASSTYPE_NAME, true, false, false, false }
+};
+
+/** Working data of the formula compiler. Used to push onto a stack for recursive calls. */
+struct XclExpCompData
+{
+ typedef std::shared_ptr< ScTokenArray > ScTokenArrayRef;
+
+ const XclExpCompConfig& mrCfg; /// Configuration for current formula type.
+ ScTokenArrayRef mxOwnScTokArr; /// Own clone of a Calc token array.
+ XclTokenArrayIterator maTokArrIt; /// Iterator in Calc token array.
+ XclExpLinkManager* mpLinkMgr; /// Link manager for current context (local/global).
+ XclExpRefLog* mpRefLog; /// Log for external references.
+ const ScAddress* mpScBasePos; /// Current cell position of the formula.
+
+ ScfUInt8Vec maTokVec; /// Byte vector containing token data.
+ ScfUInt8Vec maExtDataVec; /// Byte vector containing extended data (arrays, stacked NLRs).
+ std::vector< XclExpOperandListRef >
+ maOpListVec; /// Formula structure, maps operators to their operands.
+ ScfUInt16Vec maOpPosStack; /// Stack with positions of operand tokens waiting for an operator.
+ bool mbStopAtSep; /// True = Stop subexpression creation at an ocSep token.
+ bool mbVolatile; /// True = Formula contains volatile function.
+ bool mbOk; /// Current state of the compiler.
+
+ explicit XclExpCompData( const XclExpCompConfig* pCfg );
+};
+
+XclExpCompData::XclExpCompData( const XclExpCompConfig* pCfg ) :
+ mrCfg( pCfg ? *pCfg : spConfigTable[ 0 ] ),
+ mpLinkMgr( nullptr ),
+ mpRefLog( nullptr ),
+ mpScBasePos( nullptr ),
+ mbStopAtSep( false ),
+ mbVolatile( false ),
+ mbOk( pCfg != nullptr )
+{
+ OSL_ENSURE( pCfg, "XclExpFmlaCompImpl::Init - unknown formula type" );
+}
+
+} // namespace
+
+/** Implementation class of the export formula compiler. */
+class XclExpFmlaCompImpl : protected XclExpRoot, protected XclTokenArrayHelper
+{
+public:
+ explicit XclExpFmlaCompImpl( const XclExpRoot& rRoot );
+
+ /** Creates an Excel token array from the passed Calc token array. */
+ XclTokenArrayRef CreateFormula(
+ XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos = nullptr, XclExpRefLog* pRefLog = nullptr );
+ /** Creates a single error token containing the passed error code. */
+ XclTokenArrayRef CreateErrorFormula( sal_uInt8 nErrCode );
+ /** Creates a single token for a special cell reference. */
+ XclTokenArrayRef CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos );
+ /** Creates a single tNameXR token for a reference to an external name. */
+ XclTokenArrayRef CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName );
+
+ /** Returns true, if the passed formula type allows 3D references only. */
+ bool Is3DRefOnly( XclFormulaType eType ) const;
+
+ bool IsRef2D( const ScSingleRefData& rRefData, bool bCheck3DFlag ) const;
+ bool IsRef2D( const ScComplexRefData& rRefData, bool bCheck3DFlag ) const;
+
+private:
+ const XclExpCompConfig* GetConfigForType( XclFormulaType eType ) const;
+ sal_uInt16 GetSize() const { return static_cast< sal_uInt16 >( mxData->maTokVec.size() ); }
+
+ void Init( XclFormulaType eType );
+ void Init( XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos, XclExpRefLog* pRefLog );
+
+ void RecalcTokenClasses();
+ void RecalcTokenClass( const XclExpTokenConvInfo& rConvInfo, XclFuncParamConv ePrevConv, XclExpClassConv ePrevClassConv, bool bWasRefClass );
+
+ void FinalizeFormula();
+ XclTokenArrayRef CreateTokenArray();
+
+ // compiler ---------------------------------------------------------------
+ // XclExpScToken: pass-by-value and return-by-value is intended
+
+ const FormulaToken* GetNextRawToken();
+ const FormulaToken* PeekNextRawToken() const;
+
+ bool GetNextToken( XclExpScToken& rTokData );
+ XclExpScToken GetNextToken();
+
+ XclExpScToken Expression( XclExpScToken aTokData, bool bInParentheses, bool bStopAtSep );
+ XclExpScToken SkipExpression( XclExpScToken aTokData, bool bStopAtSep );
+
+ XclExpScToken OrTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken AndTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken CompareTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken ConcatTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken AddSubTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken MulDivTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken PowTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken UnaryPostTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken UnaryPreTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken ListTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken IntersectTerm( XclExpScToken aTokData, bool& rbHasRefOp );
+ XclExpScToken RangeTerm( XclExpScToken aTokData, bool& rbHasRefOp );
+ XclExpScToken Factor( XclExpScToken aTokData );
+
+ // formula structure ------------------------------------------------------
+
+ void ProcessDouble( const XclExpScToken& rTokData );
+ void ProcessString( const XclExpScToken& rTokData );
+ void ProcessMissing( const XclExpScToken& rTokData );
+ void ProcessBad( const XclExpScToken& rTokData );
+ void ProcessParentheses( const XclExpScToken& rTokData );
+ void ProcessBoolean( const XclExpScToken& rTokData );
+ void ProcessDdeLink( const XclExpScToken& rTokData );
+ void ProcessExternal( const XclExpScToken& rTokData );
+ void ProcessMatrix( const XclExpScToken& rTokData );
+
+ void ProcessFunction( const XclExpScToken& rTokData );
+ void PrepareFunction( const XclExpFuncData& rFuncData );
+ void FinishFunction( XclExpFuncData& rFuncData, sal_uInt8 nCloseSpaces );
+ void FinishIfFunction( XclExpFuncData& rFuncData );
+ void FinishChooseFunction( XclExpFuncData& rFuncData );
+
+ XclExpScToken ProcessParam( XclExpScToken aTokData, XclExpFuncData& rFuncData );
+ void PrepareParam( XclExpFuncData& rFuncData );
+ void FinishParam( XclExpFuncData& rFuncData );
+ void AppendDefaultParam( XclExpFuncData& rFuncData );
+ void AppendTrailingParam( XclExpFuncData& rFuncData );
+
+ // reference handling -----------------------------------------------------
+
+ SCTAB GetScTab( const ScSingleRefData& rRefData ) const;
+
+ void ConvertRefData( ScSingleRefData& rRefData, XclAddress& rXclPos,
+ bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const;
+ void ConvertRefData( ScComplexRefData& rRefData, XclRange& rXclRange,
+ bool bNatLangRef ) const;
+
+ XclExpRefLogEntry* GetNewRefLogEntry();
+ void ProcessCellRef( const XclExpScToken& rTokData );
+ void ProcessRangeRef( const XclExpScToken& rTokData );
+ void ProcessExternalCellRef( const XclExpScToken& rTokData );
+ void ProcessExternalRangeRef( const XclExpScToken& rTokData );
+ void ProcessDefinedName( const XclExpScToken& rTokData );
+ void ProcessExternalName( const XclExpScToken& rTokData );
+
+ // token vector -----------------------------------------------------------
+
+ void PushOperandPos( sal_uInt16 nTokPos );
+ void PushOperatorPos( sal_uInt16 nTokPos, const XclExpOperandListRef& rxOperands );
+ sal_uInt16 PopOperandPos();
+
+ void Append( sal_uInt8 nData );
+ void Append( sal_uInt8 nData, size_t nCount );
+ void Append( sal_uInt16 nData );
+ void Append( sal_uInt32 nData );
+ void Append( double fData );
+ void Append( const OUString& rString );
+
+ void AppendAddress( const XclAddress& rXclPos );
+ void AppendRange( const XclRange& rXclRange );
+
+ void AppendSpaceToken( sal_uInt8 nType, sal_uInt8 nCount );
+
+ void AppendOperandTokenId( sal_uInt8 nTokenId, sal_uInt8 nSpaces = 0 );
+ void AppendIntToken( sal_uInt16 nValue, sal_uInt8 nSpaces = 0 );
+ void AppendNumToken( double fValue, sal_uInt8 nSpaces = 0 );
+ void AppendBoolToken( bool bValue, sal_uInt8 nSpaces = 0 );
+ void AppendErrorToken( sal_uInt8 nErrCode, sal_uInt8 nSpaces = 0 );
+ void AppendMissingToken( sal_uInt8 nSpaces = 0 );
+ void AppendNameToken( sal_uInt16 nNameIdx, sal_uInt8 nSpaces = 0 );
+ void AppendMissingNameToken( const OUString& rName, sal_uInt8 nSpaces = 0 );
+ void AppendNameXToken( sal_uInt16 nExtSheet, sal_uInt16 nExtName, sal_uInt8 nSpaces = 0 );
+ void AppendMacroCallToken( const XclExpExtFuncData& rExtFuncData );
+ void AppendAddInCallToken( const XclExpExtFuncData& rExtFuncData );
+ void AppendEuroToolCallToken( const XclExpExtFuncData& rExtFuncData );
+
+ void AppendOperatorTokenId( sal_uInt8 nTokenId, const XclExpOperandListRef& rxOperands, sal_uInt8 nSpaces = 0 );
+ void AppendUnaryOperatorToken( sal_uInt8 nTokenId, sal_uInt8 nSpaces = 0 );
+ void AppendBinaryOperatorToken( sal_uInt8 nTokenId, bool bValType, sal_uInt8 nSpaces = 0 );
+ void AppendLogicalOperatorToken( sal_uInt16 nXclFuncIdx, sal_uInt8 nOpCount );
+ void AppendFuncToken( const XclExpFuncData& rFuncData );
+
+ void AppendParenToken( sal_uInt8 nOpenSpaces = 0, sal_uInt8 nCloseSpaces = 0 );
+ void AppendJumpToken( XclExpFuncData& rFuncData, sal_uInt8 nAttrType );
+
+ void InsertZeros( sal_uInt16 nInsertPos, sal_uInt16 nInsertSize );
+ void Overwrite( sal_uInt16 nWriteToPos, sal_uInt16 nOffset );
+
+ void UpdateAttrGoto( sal_uInt16 nAttrPos );
+
+ bool IsSpaceToken( sal_uInt16 nPos ) const;
+ void RemoveTrailingParen();
+
+ void AppendExt( sal_uInt8 nData );
+ void AppendExt( sal_uInt8 nData, size_t nCount );
+ void AppendExt( sal_uInt16 nData );
+ void AppendExt( double fData );
+ void AppendExt( const OUString& rString );
+
+private:
+ typedef std::map< XclFormulaType, XclExpCompConfig > XclExpCompConfigMap;
+ typedef std::shared_ptr< XclExpCompData > XclExpCompDataRef;
+
+ XclExpCompConfigMap maCfgMap; /// Compiler configuration map for all formula types.
+ XclFunctionProvider maFuncProv; /// Excel function data provider.
+ XclExpCompDataRef mxData; /// Working data for current formula.
+ std::vector< XclExpCompDataRef >
+ maDataStack; /// Stack for working data, when compiler is called recursively.
+ const XclBiff meBiff; /// Cached BIFF version to save GetBiff() calls.
+ const SCCOL mnMaxAbsCol; /// Maximum column index.
+ const SCROW mnMaxAbsRow; /// Maximum row index.
+ const SCCOL mnMaxScCol; /// Maximum column index in Calc itself.
+ const SCROW mnMaxScRow; /// Maximum row index in Calc itself.
+ const sal_uInt16 mnMaxColMask; /// Mask to delete invalid bits in column fields.
+ const sal_uInt32 mnMaxRowMask; /// Mask to delete invalid bits in row fields.
+};
+
+XclExpFmlaCompImpl::XclExpFmlaCompImpl( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ maFuncProv( rRoot ),
+ meBiff( rRoot.GetBiff() ),
+ mnMaxAbsCol( rRoot.GetXclMaxPos().Col() ),
+ mnMaxAbsRow( rRoot.GetXclMaxPos().Row() ),
+ mnMaxScCol( rRoot.GetScMaxPos().Col() ),
+ mnMaxScRow( rRoot.GetScMaxPos().Row() ),
+ mnMaxColMask( static_cast< sal_uInt16 >( rRoot.GetXclMaxPos().Col() ) ),
+ mnMaxRowMask( static_cast< sal_uInt32 >( rRoot.GetXclMaxPos().Row() ) )
+{
+ // build the configuration map
+ for(auto const &rEntry : spConfigTable)
+ maCfgMap[ rEntry.meType ] = rEntry;
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateFormula( XclFormulaType eType,
+ const ScTokenArray& rScTokArr, const ScAddress* pScBasePos, XclExpRefLog* pRefLog )
+{
+ // initialize the compiler
+ Init( eType, rScTokArr, pScBasePos, pRefLog );
+
+ // start compilation, if initialization didn't fail
+ if( mxData->mbOk )
+ {
+ XclExpScToken aTokData( GetNextToken() );
+ FormulaError nScError = rScTokArr.GetCodeError();
+ if( (nScError != FormulaError::NONE) && (!aTokData.Is() || (aTokData.GetOpCode() == ocStop)) )
+ {
+ // #i50253# convert simple ocStop token to error code formula (e.g. =#VALUE!)
+ AppendErrorToken( XclTools::GetXclErrorCode( nScError ), aTokData.mnSpaces );
+ }
+ else if( aTokData.Is() )
+ {
+ aTokData = Expression( aTokData, false, false );
+ }
+ else
+ {
+ OSL_FAIL( "XclExpFmlaCompImpl::CreateFormula - empty token array" );
+ mxData->mbOk = false;
+ }
+
+ if( mxData->mbOk )
+ {
+ // #i44907# auto-generated SUBTOTAL formula cells have trailing ocStop token
+ mxData->mbOk = !aTokData.Is() || (aTokData.GetOpCode() == ocStop);
+ OSL_ENSURE( mxData->mbOk, "XclExpFmlaCompImpl::CreateFormula - unknown garbage behind formula" );
+ }
+ }
+
+ // finalize (add tAttrVolatile token, calculate all token classes)
+ RecalcTokenClasses();
+ FinalizeFormula();
+
+ // leave recursive call, create and return the final token array
+ return CreateTokenArray();
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateErrorFormula( sal_uInt8 nErrCode )
+{
+ Init( EXC_FMLATYPE_NAME );
+ AppendErrorToken( nErrCode );
+ return CreateTokenArray();
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos )
+{
+ Init( EXC_FMLATYPE_NAME );
+ AppendOperandTokenId( nTokenId );
+ Append( static_cast<sal_uInt16>(rXclPos.mnRow) );
+ Append( rXclPos.mnCol ); // do not use AppendAddress(), we always need 16-bit column here
+ return CreateTokenArray();
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName )
+{
+ Init( EXC_FMLATYPE_NAME );
+ AppendNameXToken( nExtSheet, nExtName );
+ return CreateTokenArray();
+}
+
+bool XclExpFmlaCompImpl::Is3DRefOnly( XclFormulaType eType ) const
+{
+ const XclExpCompConfig* pCfg = GetConfigForType( eType );
+ return pCfg && pCfg->mb3DRefOnly;
+}
+
+// private --------------------------------------------------------------------
+
+const XclExpCompConfig* XclExpFmlaCompImpl::GetConfigForType( XclFormulaType eType ) const
+{
+ XclExpCompConfigMap::const_iterator aIt = maCfgMap.find( eType );
+ OSL_ENSURE( aIt != maCfgMap.end(), "XclExpFmlaCompImpl::GetConfigForType - unknown formula type" );
+ return (aIt == maCfgMap.end()) ? nullptr : &aIt->second;
+}
+
+void XclExpFmlaCompImpl::Init( XclFormulaType eType )
+{
+ // compiler invoked recursively? - store old working data
+ if( mxData )
+ maDataStack.push_back( mxData );
+ // new compiler working data structure
+ mxData = std::make_shared<XclExpCompData>( GetConfigForType( eType ) );
+}
+
+void XclExpFmlaCompImpl::Init( XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos, XclExpRefLog* pRefLog )
+{
+ // common initialization
+ Init( eType );
+
+ // special initialization
+ if( mxData->mbOk ) switch( mxData->mrCfg.meType )
+ {
+ case EXC_FMLATYPE_CELL:
+ case EXC_FMLATYPE_MATRIX:
+ case EXC_FMLATYPE_CHART:
+ mxData->mbOk = pScBasePos != nullptr;
+ OSL_ENSURE( mxData->mbOk, "XclExpFmlaCompImpl::Init - missing cell address" );
+ mxData->mpScBasePos = pScBasePos;
+ break;
+ case EXC_FMLATYPE_SHARED:
+ mxData->mbOk = pScBasePos != nullptr;
+ assert(mxData->mbOk && "XclExpFmlaCompImpl::Init - missing cell address");
+ if (mxData->mbOk)
+ {
+ // clone the passed token array, convert references relative to current cell position
+ mxData->mxOwnScTokArr = rScTokArr.Clone();
+ ScCompiler::MoveRelWrap( *mxData->mxOwnScTokArr, GetDoc(), *pScBasePos, GetDoc().MaxCol(), GetDoc().MaxRow() );
+ // don't remember pScBasePos in mxData->mpScBasePos, shared formulas use real relative refs
+ }
+ break;
+ default:;
+ }
+
+ if( mxData->mbOk )
+ {
+ // link manager to be used
+ mxData->mpLinkMgr = mxData->mrCfg.mbLocalLinkMgr ? &GetLocalLinkManager() : &GetGlobalLinkManager();
+
+ // token array iterator (use cloned token array if present)
+ mxData->maTokArrIt.Init( mxData->mxOwnScTokArr ? *mxData->mxOwnScTokArr : rScTokArr, false );
+ mxData->mpRefLog = pRefLog;
+ // Only for OOXML
+ if (GetOutput() == EXC_OUTPUT_XML_2007)
+ mxData->mpScBasePos = pScBasePos;
+ }
+}
+
+void XclExpFmlaCompImpl::RecalcTokenClasses()
+{
+ if( !mxData->mbOk )
+ return;
+
+ mxData->mbOk = mxData->maOpPosStack.size() == 1;
+ OSL_ENSURE( mxData->mbOk, "XclExpFmlaCompImpl::RecalcTokenClasses - position of root token expected on stack" );
+ if( mxData->mbOk )
+ {
+ /* Cell and array formulas start with VAL conversion and VALTYPE
+ parameter type, defined names start with ARR conversion and
+ REFTYPE parameter type for the root token. */
+ bool bNameFmla = mxData->mrCfg.meClassType == EXC_CLASSTYPE_NAME;
+ XclFuncParamConv eParamConv = bNameFmla ? EXC_PARAMCONV_ARR : EXC_PARAMCONV_VAL;
+ XclExpClassConv eClassConv = bNameFmla ? EXC_CLASSCONV_ARR : EXC_CLASSCONV_VAL;
+ XclExpTokenConvInfo aConvInfo = { PopOperandPos(), eParamConv, !bNameFmla };
+ RecalcTokenClass( aConvInfo, eParamConv, eClassConv, bNameFmla );
+ }
+
+ // clear operand vectors (calls to the expensive InsertZeros() may follow)
+ mxData->maOpListVec.clear();
+ mxData->maOpPosStack.clear();
+}
+
+void XclExpFmlaCompImpl::RecalcTokenClass( const XclExpTokenConvInfo& rConvInfo,
+ XclFuncParamConv ePrevConv, XclExpClassConv ePrevClassConv, bool bWasRefClass )
+{
+ OSL_ENSURE( rConvInfo.mnTokPos < GetSize(), "XclExpFmlaCompImpl::RecalcTokenClass - invalid token position" );
+ sal_uInt8& rnTokenId = mxData->maTokVec[ rConvInfo.mnTokPos ];
+ sal_uInt8 nTokClass = GetTokenClass( rnTokenId );
+
+ // REF tokens in VALTYPE parameters behave like VAL tokens
+ if( rConvInfo.mbValType && (nTokClass == EXC_TOKCLASS_REF) )
+ {
+ nTokClass = EXC_TOKCLASS_VAL;
+ ChangeTokenClass( rnTokenId, nTokClass );
+ }
+
+ // replace RPO conversion of operator with parent conversion
+ XclFuncParamConv eConv = (rConvInfo.meConv == EXC_PARAMCONV_RPO) ? ePrevConv : rConvInfo.meConv;
+
+ // find the effective token class conversion to be performed for this token
+ XclExpClassConv eClassConv = EXC_CLASSCONV_ORG;
+ switch( eConv )
+ {
+ case EXC_PARAMCONV_ORG:
+ // conversion is forced independent of parent conversion
+ eClassConv = EXC_CLASSCONV_ORG;
+ break;
+ case EXC_PARAMCONV_VAL:
+ // conversion is forced independent of parent conversion
+ eClassConv = EXC_CLASSCONV_VAL;
+ break;
+ case EXC_PARAMCONV_ARR:
+ // conversion is forced independent of parent conversion
+ eClassConv = EXC_CLASSCONV_ARR;
+ break;
+ case EXC_PARAMCONV_RPT:
+ switch( ePrevConv )
+ {
+ case EXC_PARAMCONV_ORG:
+ case EXC_PARAMCONV_VAL:
+ case EXC_PARAMCONV_ARR:
+ /* If parent token has REF class (REF token in REFTYPE
+ function parameter), then RPT does not repeat the
+ previous explicit ORG or ARR conversion, but always
+ falls back to VAL conversion. */
+ eClassConv = bWasRefClass ? EXC_CLASSCONV_VAL : ePrevClassConv;
+ break;
+ case EXC_PARAMCONV_RPT:
+ // nested RPT repeats the previous effective conversion
+ eClassConv = ePrevClassConv;
+ break;
+ case EXC_PARAMCONV_RPX:
+ /* If parent token has REF class (REF token in REFTYPE
+ function parameter), then RPX repeats the previous
+ effective conversion (which will be either ORG or ARR,
+ but never VAL), otherwise falls back to ORG conversion. */
+ eClassConv = bWasRefClass ? ePrevClassConv : EXC_CLASSCONV_ORG;
+ break;
+ case EXC_PARAMCONV_RPO: // does not occur
+ break;
+ }
+ break;
+ case EXC_PARAMCONV_RPX:
+ /* If current token still has REF class, set previous effective
+ conversion as current conversion. This will not have an effect
+ on the REF token but is needed for RPT parameters of this
+ function that want to repeat this conversion type. If current
+ token is VAL or ARR class, the previous ARR conversion will be
+ repeated on the token, but VAL conversion will not. */
+ eClassConv = ((nTokClass == EXC_TOKCLASS_REF) || (ePrevClassConv == EXC_CLASSCONV_ARR)) ?
+ ePrevClassConv : EXC_CLASSCONV_ORG;
+ break;
+ case EXC_PARAMCONV_RPO: // does not occur (see above)
+ break;
+ }
+
+ // do the token class conversion
+ switch( eClassConv )
+ {
+ case EXC_CLASSCONV_ORG:
+ /* Cell formulas: leave the current token class. Cell formulas
+ are the only type of formulas where all tokens can keep
+ their original token class.
+ Array and defined name formulas: convert VAL to ARR. */
+ if( (mxData->mrCfg.meClassType != EXC_CLASSTYPE_CELL) && (nTokClass == EXC_TOKCLASS_VAL) )
+ {
+ nTokClass = EXC_TOKCLASS_ARR;
+ ChangeTokenClass( rnTokenId, nTokClass );
+ }
+ break;
+ case EXC_CLASSCONV_VAL:
+ // convert ARR to VAL
+ if( nTokClass == EXC_TOKCLASS_ARR )
+ {
+ nTokClass = EXC_TOKCLASS_VAL;
+ ChangeTokenClass( rnTokenId, nTokClass );
+ }
+ break;
+ case EXC_CLASSCONV_ARR:
+ // convert VAL to ARR
+ if( nTokClass == EXC_TOKCLASS_VAL )
+ {
+ nTokClass = EXC_TOKCLASS_ARR;
+ ChangeTokenClass( rnTokenId, nTokClass );
+ }
+ break;
+ }
+
+ // do conversion for nested operands, if token is an operator or function
+ if( rConvInfo.mnTokPos < mxData->maOpListVec.size() )
+ if( const XclExpOperandList* pOperands = mxData->maOpListVec[ rConvInfo.mnTokPos ].get() )
+ for( const auto& rOperand : *pOperands )
+ RecalcTokenClass( rOperand, eConv, eClassConv, nTokClass == EXC_TOKCLASS_REF );
+}
+
+void XclExpFmlaCompImpl::FinalizeFormula()
+{
+ if( mxData->mbOk )
+ {
+ // Volatile? Add a tAttrVolatile token at the beginning of the token array.
+ if( mxData->mbVolatile )
+ {
+ // tAttrSpace token can be extended with volatile flag
+ if( !IsSpaceToken( 0 ) )
+ {
+ InsertZeros( 0, 4 );
+ mxData->maTokVec[ 0 ] = EXC_TOKID_ATTR;
+ }
+ mxData->maTokVec[ 1 ] |= EXC_TOK_ATTR_VOLATILE;
+ }
+
+ // Token array too long? -> error
+ mxData->mbOk = mxData->maTokVec.size() <= EXC_TOKARR_MAXLEN;
+ }
+
+ if( !mxData->mbOk )
+ {
+ // Any unrecoverable error? -> Create a =#NA formula.
+ mxData->maTokVec.clear();
+ mxData->maExtDataVec.clear();
+ mxData->mbVolatile = false;
+ AppendErrorToken( EXC_ERR_NA );
+ }
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateTokenArray()
+{
+ // create the Excel token array from working data before resetting mxData
+ OSL_ENSURE( mxData->mrCfg.mbAllowArrays || mxData->maExtDataVec.empty(), "XclExpFmlaCompImpl::CreateTokenArray - unexpected extended data" );
+ if( !mxData->mrCfg.mbAllowArrays )
+ mxData->maExtDataVec.clear();
+ XclTokenArrayRef xTokArr = std::make_shared<XclTokenArray>( mxData->maTokVec, mxData->maExtDataVec, mxData->mbVolatile );
+ mxData.reset();
+
+ // compiler invoked recursively? - restore old working data
+ if( !maDataStack.empty() )
+ {
+ mxData = maDataStack.back();
+ maDataStack.pop_back();
+ }
+
+ return xTokArr;
+}
+
+// compiler -------------------------------------------------------------------
+
+const FormulaToken* XclExpFmlaCompImpl::GetNextRawToken()
+{
+ const FormulaToken* pScToken = mxData->maTokArrIt.Get();
+ ++mxData->maTokArrIt;
+ return pScToken;
+}
+
+const FormulaToken* XclExpFmlaCompImpl::PeekNextRawToken() const
+{
+ /* Returns pointer to next raw token in the token array. The token array
+ iterator already points to the next token (A call to GetNextToken()
+ always increases the iterator), so this function just returns the token
+ the iterator points to. To skip space tokens, a copy of the iterator is
+ created and set to the passed skip-spaces mode. If spaces have to be
+ skipped, and the iterator currently points to a space token, the
+ constructor will move it to the next non-space token. */
+ XclTokenArrayIterator aTempIt( mxData->maTokArrIt, true/*bSkipSpaces*/ );
+ return aTempIt.Get();
+}
+
+bool XclExpFmlaCompImpl::GetNextToken( XclExpScToken& rTokData )
+{
+ rTokData.mpScToken = GetNextRawToken();
+ rTokData.mnSpaces = 0;
+ /* TODO: handle ocWhitespace characters? */
+ while (rTokData.GetOpCode() == ocSpaces || rTokData.GetOpCode() == ocWhitespace)
+ {
+ rTokData.mnSpaces += rTokData.mpScToken->GetByte();
+ rTokData.mpScToken = GetNextRawToken();
+ }
+ return rTokData.Is();
+}
+
+XclExpScToken XclExpFmlaCompImpl::GetNextToken()
+{
+ XclExpScToken aTokData;
+ GetNextToken( aTokData );
+ return aTokData;
+}
+
+namespace {
+
+/** Returns the Excel token ID of a comparison operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetCompareTokenId( OpCode eOpCode )
+{
+ switch( eOpCode )
+ {
+ case ocLess: return EXC_TOKID_LT;
+ case ocLessEqual: return EXC_TOKID_LE;
+ case ocEqual: return EXC_TOKID_EQ;
+ case ocGreaterEqual: return EXC_TOKID_GE;
+ case ocGreater: return EXC_TOKID_GT;
+ case ocNotEqual: return EXC_TOKID_NE;
+ default:;
+ }
+ return EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a string concatenation operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetConcatTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocAmpersand) ? EXC_TOKID_CONCAT : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of an addition/subtraction operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetAddSubTokenId( OpCode eOpCode )
+{
+ switch( eOpCode )
+ {
+ case ocAdd: return EXC_TOKID_ADD;
+ case ocSub: return EXC_TOKID_SUB;
+ default:;
+ }
+ return EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a multiplication/division operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetMulDivTokenId( OpCode eOpCode )
+{
+ switch( eOpCode )
+ {
+ case ocMul: return EXC_TOKID_MUL;
+ case ocDiv: return EXC_TOKID_DIV;
+ default:;
+ }
+ return EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a power operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetPowTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocPow) ? EXC_TOKID_POWER : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a trailing unary operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetUnaryPostTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocPercentSign) ? EXC_TOKID_PERCENT : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a leading unary operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetUnaryPreTokenId( OpCode eOpCode )
+{
+ switch( eOpCode )
+ {
+ case ocAdd: return EXC_TOKID_UPLUS; // +(1)
+ case ocNeg: return EXC_TOKID_UMINUS; // NEG(1)
+ case ocNegSub: return EXC_TOKID_UMINUS; // -(1)
+ default:;
+ }
+ return EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a reference list operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetListTokenId( OpCode eOpCode, bool bStopAtSep )
+{
+ return ((eOpCode == ocUnion) || (!bStopAtSep && (eOpCode == ocSep))) ? EXC_TOKID_LIST : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a reference intersection operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetIntersectTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocIntersect) ? EXC_TOKID_ISECT : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a reference range operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetRangeTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocRange) ? EXC_TOKID_RANGE : EXC_TOKID_NONE;
+}
+
+} // namespace
+
+XclExpScToken XclExpFmlaCompImpl::Expression( XclExpScToken aTokData, bool bInParentheses, bool bStopAtSep )
+{
+ if( mxData->mbOk && aTokData.Is() )
+ {
+ // remember old stop-at-ocSep mode, restored below
+ bool bOldStopAtSep = mxData->mbStopAtSep;
+ mxData->mbStopAtSep = bStopAtSep;
+ // start compilation of the subexpression
+ aTokData = OrTerm( aTokData, bInParentheses );
+ // restore old stop-at-ocSep mode
+ mxData->mbStopAtSep = bOldStopAtSep;
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::SkipExpression( XclExpScToken aTokData, bool bStopAtSep )
+{
+ while( mxData->mbOk && aTokData.Is() && (aTokData.GetOpCode() != ocClose) && (!bStopAtSep || (aTokData.GetOpCode() != ocSep)) )
+ {
+ if( aTokData.GetOpCode() == ocOpen )
+ {
+ aTokData = SkipExpression( GetNextToken(), false );
+ if( mxData->mbOk ) mxData->mbOk = aTokData.GetOpCode() == ocClose;
+ }
+ aTokData = GetNextToken();
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::OrTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = AndTerm( aTokData, bInParentheses );
+ sal_uInt8 nParamCount = 1;
+ while( mxData->mbOk && (aTokData.GetOpCode() == ocOr) )
+ {
+ RemoveTrailingParen();
+ aTokData = AndTerm( GetNextToken(), bInParentheses );
+ RemoveTrailingParen();
+ ++nParamCount;
+ if( mxData->mbOk ) mxData->mbOk = nParamCount <= EXC_FUNC_MAXPARAM;
+ }
+ if( mxData->mbOk && (nParamCount > 1) )
+ AppendLogicalOperatorToken( EXC_FUNCID_OR, nParamCount );
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::AndTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = CompareTerm( aTokData, bInParentheses );
+ sal_uInt8 nParamCount = 1;
+ while( mxData->mbOk && (aTokData.GetOpCode() == ocAnd) )
+ {
+ RemoveTrailingParen();
+ aTokData = CompareTerm( GetNextToken(), bInParentheses );
+ RemoveTrailingParen();
+ ++nParamCount;
+ if( mxData->mbOk ) mxData->mbOk = nParamCount <= EXC_FUNC_MAXPARAM;
+ }
+ if( mxData->mbOk && (nParamCount > 1) )
+ AppendLogicalOperatorToken( EXC_FUNCID_AND, nParamCount );
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::CompareTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = ConcatTerm( aTokData, bInParentheses );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetConcatTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = ConcatTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::ConcatTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = AddSubTerm( aTokData, bInParentheses );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetCompareTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = AddSubTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::AddSubTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = MulDivTerm( aTokData, bInParentheses );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetAddSubTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = MulDivTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::MulDivTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = PowTerm( aTokData, bInParentheses );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetMulDivTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = PowTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::PowTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = UnaryPostTerm( aTokData, bInParentheses );
+ sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
+ while( mxData->mbOk )
+ {
+ nOpTokenId = lclGetPowTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = UnaryPostTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::UnaryPostTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = UnaryPreTerm( aTokData, bInParentheses );
+ sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
+ while( mxData->mbOk )
+ {
+ nOpTokenId = lclGetUnaryPostTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ AppendUnaryOperatorToken( nOpTokenId, aTokData.mnSpaces );
+ GetNextToken( aTokData );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::UnaryPreTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ sal_uInt8 nOpTokenId = mxData->mbOk ? lclGetUnaryPreTokenId( aTokData.GetOpCode() ) : EXC_TOKID_NONE;
+ if( nOpTokenId != EXC_TOKID_NONE )
+ {
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = UnaryPreTerm( GetNextToken(), bInParentheses );
+ AppendUnaryOperatorToken( nOpTokenId, nSpaces );
+ }
+ else
+ {
+ aTokData = ListTerm( aTokData, bInParentheses );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::ListTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ sal_uInt16 nSubExprPos = GetSize();
+ bool bHasAnyRefOp = false;
+ bool bHasListOp = false;
+ aTokData = IntersectTerm( aTokData, bHasAnyRefOp );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetListTokenId( aTokData.GetOpCode(), mxData->mbStopAtSep );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = IntersectTerm( GetNextToken(), bHasAnyRefOp );
+ AppendBinaryOperatorToken( nOpTokenId, false, nSpaces );
+ bHasAnyRefOp = bHasListOp = true;
+ }
+ if( bHasAnyRefOp )
+ {
+ // add a tMemFunc token enclosing the entire reference subexpression
+ sal_uInt16 nSubExprSize = GetSize() - nSubExprPos;
+ InsertZeros( nSubExprPos, 3 );
+ mxData->maTokVec[ nSubExprPos ] = GetTokenId( EXC_TOKID_MEMFUNC, EXC_TOKCLASS_REF );
+ Overwrite( nSubExprPos + 1, nSubExprSize );
+ // update the operand/operator stack (set the list expression as operand of the tMemFunc)
+ XclExpOperandListRef xOperands = std::make_shared<XclExpOperandList>();
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_VAL, false );
+ PushOperatorPos( nSubExprPos, xOperands );
+ }
+ // #i86439# enclose list operator into parentheses, e.g. Calc's =AREAS(A1~A2) to Excel's =AREAS((A1;A2))
+ if( bHasListOp && !bInParentheses )
+ AppendParenToken();
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::IntersectTerm( XclExpScToken aTokData, bool& rbHasRefOp )
+{
+ aTokData = RangeTerm( aTokData, rbHasRefOp );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetIntersectTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId ==EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = RangeTerm( GetNextToken(), rbHasRefOp );
+ AppendBinaryOperatorToken( nOpTokenId, false, nSpaces );
+ rbHasRefOp = true;
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::RangeTerm( XclExpScToken aTokData, bool& rbHasRefOp )
+{
+ aTokData = Factor( aTokData );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetRangeTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = Factor( GetNextToken() );
+ AppendBinaryOperatorToken( nOpTokenId, false, nSpaces );
+ rbHasRefOp = true;
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::Factor( XclExpScToken aTokData )
+{
+ if( !mxData->mbOk || !aTokData.Is() ) return XclExpScToken();
+
+ switch( aTokData.GetType() )
+ {
+ case svUnknown: mxData->mbOk = false; break;
+ case svDouble: ProcessDouble( aTokData ); break;
+ case svString: ProcessString( aTokData ); break;
+ case svSingleRef: ProcessCellRef( aTokData ); break;
+ case svDoubleRef: ProcessRangeRef( aTokData ); break;
+ case svExternalSingleRef: ProcessExternalCellRef( aTokData ); break;
+ case svExternalDoubleRef: ProcessExternalRangeRef( aTokData ); break;
+ case svExternalName: ProcessExternalName( aTokData ); break;
+ case svMatrix: ProcessMatrix( aTokData ); break;
+ case svExternal: ProcessExternal( aTokData ); break;
+
+ default: switch( aTokData.GetOpCode() )
+ {
+ case ocNone: /* do nothing */ break;
+ case ocMissing: ProcessMissing( aTokData ); break;
+ case ocBad: ProcessBad( aTokData ); break;
+ case ocOpen: ProcessParentheses( aTokData ); break;
+ case ocName: ProcessDefinedName( aTokData ); break;
+ case ocFalse:
+ case ocTrue: ProcessBoolean( aTokData ); break;
+ case ocDde: ProcessDdeLink( aTokData ); break;
+ default: ProcessFunction( aTokData );
+ }
+ }
+
+ return GetNextToken();
+}
+
+// formula structure ----------------------------------------------------------
+
+void XclExpFmlaCompImpl::ProcessDouble( const XclExpScToken& rTokData )
+{
+ double fValue = rTokData.mpScToken->GetDouble();
+ double fInt;
+ double fFrac = modf( fValue, &fInt );
+ if( (fFrac == 0.0) && (0.0 <= fInt) && (fInt <= 65535.0) )
+ AppendIntToken( static_cast< sal_uInt16 >( fInt ), rTokData.mnSpaces );
+ else
+ AppendNumToken( fValue, rTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessString( const XclExpScToken& rTokData )
+{
+ AppendOperandTokenId( EXC_TOKID_STR, rTokData.mnSpaces );
+ Append( rTokData.mpScToken->GetString().getString() );
+}
+
+void XclExpFmlaCompImpl::ProcessMissing( const XclExpScToken& rTokData )
+{
+ AppendMissingToken( rTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessBad( const XclExpScToken& rTokData )
+{
+ AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessParentheses( const XclExpScToken& rTokData )
+{
+ XclExpScToken aTokData = Expression( GetNextToken(), true, false );
+ mxData->mbOk = aTokData.GetOpCode() == ocClose;
+ AppendParenToken( rTokData.mnSpaces, aTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessBoolean( const XclExpScToken& rTokData )
+{
+ mxData->mbOk = GetNextToken().GetOpCode() == ocOpen;
+ if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocClose;
+ if( mxData->mbOk )
+ AppendBoolToken( rTokData.GetOpCode() == ocTrue, rTokData.mnSpaces );
+}
+
+namespace {
+
+bool lclGetTokenString( OUString& rString, const XclExpScToken& rTokData )
+{
+ bool bIsStr = (rTokData.GetType() == svString) && (rTokData.GetOpCode() == ocPush);
+ if( bIsStr )
+ rString = rTokData.mpScToken->GetString().getString();
+ return bIsStr;
+}
+
+} // namespace
+
+void XclExpFmlaCompImpl::ProcessDdeLink( const XclExpScToken& rTokData )
+{
+ OUString aApplic, aTopic, aItem;
+
+ mxData->mbOk = GetNextToken().GetOpCode() == ocOpen;
+ if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aApplic, GetNextToken() );
+ if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocSep;
+ if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aTopic, GetNextToken() );
+ if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocSep;
+ if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aItem, GetNextToken() );
+ if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocClose;
+ if( mxData->mbOk ) mxData->mbOk = !aApplic.isEmpty() && !aTopic.isEmpty() && !aItem.isEmpty();
+ if( mxData->mbOk )
+ {
+ sal_uInt16 nExtSheet(0), nExtName(0);
+ if( mxData->mpLinkMgr && mxData->mpLinkMgr->InsertDde( nExtSheet, nExtName, aApplic, aTopic, aItem ) )
+ AppendNameXToken( nExtSheet, nExtName, rTokData.mnSpaces );
+ else
+ AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessExternal( const XclExpScToken& rTokData )
+{
+ /* #i47228# Excel import generates svExternal/ocMacro tokens for invalid
+ names and for external/invalid function calls. This function looks for
+ the next token in the token array. If it is an opening parenthesis, the
+ token is processed as external function call, otherwise as undefined name. */
+ const FormulaToken* pNextScToken = PeekNextRawToken();
+ if( !pNextScToken || (pNextScToken->GetOpCode() != ocOpen) )
+ AppendMissingNameToken( rTokData.mpScToken->GetExternal(), rTokData.mnSpaces );
+ else
+ ProcessFunction( rTokData );
+}
+
+void XclExpFmlaCompImpl::ProcessMatrix( const XclExpScToken& rTokData )
+{
+ const ScMatrix* pMatrix = rTokData.mpScToken->GetMatrix();
+ if( pMatrix && mxData->mrCfg.mbAllowArrays )
+ {
+ SCSIZE nScCols, nScRows;
+ pMatrix->GetDimensions( nScCols, nScRows );
+ OSL_ENSURE( (nScCols > 0) && (nScRows > 0), "XclExpFmlaCompImpl::ProcessMatrix - invalid matrix size" );
+ sal_uInt16 nCols = ::limit_cast< sal_uInt16 >( nScCols, 0, 256 );
+ sal_uInt16 nRows = ::limit_cast< sal_uInt16 >( nScRows, 0, 1024 );
+
+ // create the tArray token
+ AppendOperandTokenId( GetTokenId( EXC_TOKID_ARRAY, EXC_TOKCLASS_ARR ), rTokData.mnSpaces );
+ Append( static_cast< sal_uInt8 >( (meBiff == EXC_BIFF8) ? (nCols - 1) : nCols ) );
+ Append( static_cast< sal_uInt16 >( (meBiff == EXC_BIFF8) ? (nRows - 1) : nRows ) );
+ Append( static_cast< sal_uInt32 >( 0 ) );
+
+ // create the extended data containing the array values
+ AppendExt( static_cast< sal_uInt8 >( (meBiff == EXC_BIFF8) ? (nCols - 1) : nCols ) );
+ AppendExt( static_cast< sal_uInt16 >( (meBiff == EXC_BIFF8) ? (nRows - 1) : nRows ) );
+ for( SCSIZE nScRow = 0; nScRow < nScRows; ++nScRow )
+ {
+ for( SCSIZE nScCol = 0; nScCol < nScCols; ++nScCol )
+ {
+ ScMatrixValue nMatVal = pMatrix->Get( nScCol, nScRow );
+ if( ScMatrix::IsValueType( nMatVal.nType ) ) // value, boolean, or error
+ {
+ FormulaError nErr;
+ if( ScMatrix::IsBooleanType( nMatVal.nType ) )
+ {
+ AppendExt( EXC_CACHEDVAL_BOOL );
+ AppendExt( static_cast< sal_uInt8 >( nMatVal.GetBoolean() ? 1 : 0 ) );
+ AppendExt( 0, 7 );
+ }
+ else if( (nErr = nMatVal.GetError()) != FormulaError::NONE )
+ {
+ AppendExt( EXC_CACHEDVAL_ERROR );
+ AppendExt( XclTools::GetXclErrorCode( nErr ) );
+ AppendExt( 0, 7 );
+ }
+ else
+ {
+ AppendExt( EXC_CACHEDVAL_DOUBLE );
+ AppendExt( nMatVal.fVal );
+ }
+ }
+ else // string or empty
+ {
+ const OUString aStr( nMatVal.GetString().getString());
+ if( aStr.isEmpty() )
+ {
+ AppendExt( EXC_CACHEDVAL_EMPTY );
+ AppendExt( 0, 8 );
+ }
+ else
+ {
+ AppendExt( EXC_CACHEDVAL_STRING );
+ AppendExt( aStr );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // array in places that do not allow it (cond fmts, data validation)
+ AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessFunction( const XclExpScToken& rTokData )
+{
+ OpCode eOpCode = rTokData.GetOpCode();
+ const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromOpCode( eOpCode );
+
+ XclExpExtFuncData aExtFuncData;
+
+ // no exportable function found - try to create an external macro call
+ if( !pFuncInfo && (eOpCode >= SC_OPCODE_START_NO_PAR) )
+ {
+ const OUString& rFuncName = ScCompiler::GetNativeSymbol( eOpCode );
+ if( !rFuncName.isEmpty() )
+ {
+ aExtFuncData.Set( rFuncName, true, false );
+ pFuncInfo = maFuncProv.GetFuncInfoFromOpCode( ocMacro );
+ }
+ }
+
+ mxData->mbOk = pFuncInfo != nullptr;
+ if( !mxData->mbOk ) return;
+
+ // internal functions equivalent to an existing add-in
+ if( pFuncInfo->IsAddInEquivalent() )
+ aExtFuncData.Set( pFuncInfo->GetAddInEquivalentFuncName(), true, false );
+ // functions simulated by a macro call in file format
+ else if( pFuncInfo->IsMacroFunc() )
+ aExtFuncData.Set( pFuncInfo->GetMacroFuncName(), false, true );
+
+ XclExpFuncData aFuncData( rTokData, *pFuncInfo, aExtFuncData );
+ XclExpScToken aTokData;
+
+ // preparations for special functions, before function processing starts
+ PrepareFunction( aFuncData );
+
+ enum { STATE_START, STATE_OPEN, STATE_PARAM, STATE_SEP, STATE_CLOSE, STATE_END }
+ eState = STATE_START;
+ while( eState != STATE_END ) switch( eState )
+ {
+ case STATE_START:
+ mxData->mbOk = GetNextToken( aTokData ) && (aTokData.GetOpCode() == ocOpen);
+ eState = mxData->mbOk ? STATE_OPEN : STATE_END;
+ break;
+ case STATE_OPEN:
+ mxData->mbOk = GetNextToken( aTokData );
+ eState = mxData->mbOk ? ((aTokData.GetOpCode() == ocClose) ? STATE_CLOSE : STATE_PARAM) : STATE_END;
+ break;
+ case STATE_PARAM:
+ aTokData = ProcessParam( aTokData, aFuncData );
+ switch( aTokData.GetOpCode() )
+ {
+ case ocSep: eState = STATE_SEP; break;
+ case ocClose: eState = STATE_CLOSE; break;
+ default: mxData->mbOk = false;
+ }
+ if( !mxData->mbOk ) eState = STATE_END;
+ break;
+ case STATE_SEP:
+ mxData->mbOk = (aFuncData.GetParamCount() < EXC_FUNC_MAXPARAM) && GetNextToken( aTokData );
+ eState = mxData->mbOk ? STATE_PARAM : STATE_END;
+ break;
+ case STATE_CLOSE:
+ FinishFunction( aFuncData, aTokData.mnSpaces );
+ eState = STATE_END;
+ break;
+ default:;
+ }
+}
+
+void XclExpFmlaCompImpl::PrepareFunction( const XclExpFuncData& rFuncData )
+{
+ // For OOXML these are not rewritten anymore.
+ if (GetOutput() == EXC_OUTPUT_XML_2007)
+ return;
+
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocCosecant: // simulate CSC(x) by (1/SIN(x))
+ case ocSecant: // simulate SEC(x) by (1/COS(x))
+ case ocCot: // simulate COT(x) by (1/TAN(x))
+ case ocCosecantHyp: // simulate CSCH(x) by (1/SINH(x))
+ case ocSecantHyp: // simulate SECH(x) by (1/COSH(x))
+ case ocCotHyp: // simulate COTH(x) by (1/TANH(x))
+ AppendIntToken( 1 );
+ break;
+ case ocArcCot: // simulate ACOT(x) by (PI/2-ATAN(x))
+ AppendNumToken( M_PI_2 );
+ break;
+ default:;
+ }
+}
+
+void XclExpFmlaCompImpl::FinishFunction( XclExpFuncData& rFuncData, sal_uInt8 nCloseSpaces )
+{
+ // append missing parameters required in Excel, may modify param count
+ AppendTrailingParam( rFuncData );
+
+ // check if parameter count fits into the limits of the function
+ sal_uInt8 nParamCount = rFuncData.GetParamCount();
+ if( (rFuncData.GetMinParamCount() <= nParamCount) && (nParamCount <= rFuncData.GetMaxParamCount()) )
+ {
+ // first put the tAttrSpace tokens, they must not be included in tAttrGoto handling
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_CLOSE, nCloseSpaces );
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, rFuncData.GetSpaces() );
+
+ // add tAttrGoto tokens for IF or CHOOSE functions
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocIf:
+ case ocChoose:
+ AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO );
+ break;
+ default:;
+ }
+
+ // put the tFunc or tFuncVar token (or another special token, e.g. tAttrSum)
+ AppendFuncToken( rFuncData );
+
+ // update volatile flag - is set if at least one used function is volatile
+ mxData->mbVolatile |= rFuncData.IsVolatile();
+
+ // update jump tokens for specific functions, add additional tokens
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocIf:
+ FinishIfFunction( rFuncData );
+ break;
+ case ocChoose:
+ FinishChooseFunction( rFuncData );
+ break;
+
+ case ocCosecant: // simulate CSC(x) by (1/SIN(x))
+ case ocSecant: // simulate SEC(x) by (1/COS(x))
+ case ocCot: // simulate COT(x) by (1/TAN(x))
+ case ocCosecantHyp: // simulate CSCH(x) by (1/SINH(x))
+ case ocSecantHyp: // simulate SECH(x) by (1/COSH(x))
+ case ocCotHyp: // simulate COTH(x) by (1/TANH(x))
+ // For OOXML not rewritten anymore.
+ if (GetOutput() != EXC_OUTPUT_XML_2007)
+ {
+ AppendBinaryOperatorToken( EXC_TOKID_DIV, true );
+ AppendParenToken();
+ }
+ break;
+ case ocArcCot: // simulate ACOT(x) by (PI/2-ATAN(x))
+ // For OOXML not rewritten anymore.
+ if (GetOutput() != EXC_OUTPUT_XML_2007)
+ {
+ AppendBinaryOperatorToken( EXC_TOKID_SUB, true );
+ AppendParenToken();
+ }
+ break;
+
+ default:;
+ }
+ }
+ else
+ mxData->mbOk = false;
+}
+
+void XclExpFmlaCompImpl::FinishIfFunction( XclExpFuncData& rFuncData )
+{
+ sal_uInt16 nParamCount = rFuncData.GetParamCount();
+ OSL_ENSURE( (nParamCount == 2) || (nParamCount == 3), "XclExpFmlaCompImpl::FinishIfFunction - wrong parameter count" );
+ const ScfUInt16Vec& rAttrPos = rFuncData.GetAttrPosVec();
+ OSL_ENSURE( nParamCount == rAttrPos.size(), "XclExpFmlaCompImpl::FinishIfFunction - wrong number of tAttr tokens" );
+ // update tAttrIf token following the condition parameter
+ Overwrite( rAttrPos[ 0 ] + 2, static_cast< sal_uInt16 >( rAttrPos[ 1 ] - rAttrPos[ 0 ] ) );
+ // update the tAttrGoto tokens following true and false parameters
+ UpdateAttrGoto( rAttrPos[ 1 ] );
+ if( nParamCount == 3 )
+ UpdateAttrGoto( rAttrPos[ 2 ] );
+}
+
+void XclExpFmlaCompImpl::FinishChooseFunction( XclExpFuncData& rFuncData )
+{
+ sal_uInt16 nParamCount = rFuncData.GetParamCount();
+ ScfUInt16Vec& rAttrPos = rFuncData.GetAttrPosVec();
+ OSL_ENSURE( nParamCount == rAttrPos.size(), "XclExpFmlaCompImpl::FinishChooseFunction - wrong number of tAttr tokens" );
+ // number of choices is parameter count minus 1
+ sal_uInt16 nChoices = nParamCount - 1;
+ // tAttrChoose token contains number of choices
+ Overwrite( rAttrPos[ 0 ] + 2, nChoices );
+ // cache position of the jump table (follows number of choices in tAttrChoose token)
+ sal_uInt16 nJumpArrPos = rAttrPos[ 0 ] + 4;
+ // size of jump table: number of choices, plus 1 for error position
+ sal_uInt16 nJumpArrSize = 2 * (nChoices + 1);
+ // insert the jump table into the tAttrChoose token
+ InsertZeros( nJumpArrPos, nJumpArrSize );
+ // update positions of tAttrGoto tokens after jump table insertion
+ sal_uInt16 nIdx;
+ for( nIdx = 1; nIdx < nParamCount; ++nIdx )
+ rAttrPos[ nIdx ] = rAttrPos[ nIdx ] + nJumpArrSize;
+ // update the tAttrGoto tokens (they contain a value one-less to real distance)
+ for( nIdx = 1; nIdx < nParamCount; ++nIdx )
+ UpdateAttrGoto( rAttrPos[ nIdx ] );
+ // update the distances in the jump table
+ Overwrite( nJumpArrPos, nJumpArrSize );
+ for( nIdx = 1; nIdx < nParamCount; ++nIdx )
+ Overwrite( nJumpArrPos + 2 * nIdx, static_cast< sal_uInt16 >( rAttrPos[ nIdx ] + 4 - nJumpArrPos ) );
+}
+
+XclExpScToken XclExpFmlaCompImpl::ProcessParam( XclExpScToken aTokData, XclExpFuncData& rFuncData )
+{
+ if( rFuncData.IsCalcOnlyParam() )
+ {
+ // skip Calc-only parameter, stop at next ocClose or ocSep
+ aTokData = SkipExpression( aTokData, true );
+ rFuncData.IncParamInfoIdx();
+ }
+ else
+ {
+ // insert Excel-only parameters, modifies param count and class in rFuncData
+ while( rFuncData.IsExcelOnlyParam() )
+ AppendDefaultParam( rFuncData );
+
+ // process the parameter, stop at next ocClose or ocSep
+ PrepareParam( rFuncData );
+ /* #i37355# insert tMissArg token for missing parameters --
+ Excel import filter adds ocMissing token (handled in Factor()),
+ but Calc itself does not do this if a new formula is entered. */
+ switch( aTokData.GetOpCode() )
+ {
+ case ocSep:
+ case ocClose: AppendMissingToken(); break; // empty parameter
+ default: aTokData = Expression( aTokData, false, true );
+ }
+ // finalize the parameter and add special tokens, e.g. for IF or CHOOSE parameters
+ if( mxData->mbOk ) FinishParam( rFuncData );
+ }
+ return aTokData;
+}
+
+void XclExpFmlaCompImpl::PrepareParam( XclExpFuncData& rFuncData )
+{
+ // index of this parameter is equal to number of already finished parameters
+ sal_uInt8 nParamIdx = rFuncData.GetParamCount();
+
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocIf:
+ switch( nParamIdx )
+ {
+ // add a tAttrIf token before true-parameter (second parameter)
+ case 1: AppendJumpToken( rFuncData, EXC_TOK_ATTR_IF ); break;
+ // add a tAttrGoto token before false-parameter (third parameter)
+ case 2: AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO ); break;
+ }
+ break;
+
+ case ocChoose:
+ switch( nParamIdx )
+ {
+ // do nothing for first parameter
+ case 0: break;
+ // add a tAttrChoose token before first value parameter (second parameter)
+ case 1: AppendJumpToken( rFuncData, EXC_TOK_ATTR_CHOOSE ); break;
+ // add a tAttrGoto token before other value parameters
+ default: AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO );
+ }
+ break;
+
+ case ocArcCotHyp: // simulate ACOTH(x) by ATANH(1/(x))
+ if( nParamIdx == 0 )
+ AppendIntToken( 1 );
+ break;
+ default:;
+ }
+}
+
+void XclExpFmlaCompImpl::FinishParam( XclExpFuncData& rFuncData )
+{
+ // increase parameter count, update operand stack
+ rFuncData.FinishParam( PopOperandPos() );
+
+ // append more tokens for parameters of some special functions
+ sal_uInt8 nParamIdx = rFuncData.GetParamCount() - 1;
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocArcCotHyp: // simulate ACOTH(x) by ATANH(1/(x))
+ if( nParamIdx == 0 )
+ {
+ AppendParenToken();
+ AppendBinaryOperatorToken( EXC_TOKID_DIV, true );
+ }
+ break;
+ default:;
+ }
+}
+
+void XclExpFmlaCompImpl::AppendDefaultParam( XclExpFuncData& rFuncData )
+{
+ // prepare parameters of some special functions
+ PrepareParam( rFuncData );
+
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocExternal:
+ AppendAddInCallToken( rFuncData.GetExtFuncData() );
+ break;
+ case ocEuroConvert:
+ AppendEuroToolCallToken( rFuncData.GetExtFuncData() );
+ break;
+ case ocMacro:
+ // Do not write the OOXML <definedName> element.
+ if (GetOutput() == EXC_OUTPUT_XML_2007)
+ AppendNameToken( 0 ); // dummy to keep parameter count valid
+ else
+ AppendMacroCallToken( rFuncData.GetExtFuncData() );
+ break;
+ default:
+ {
+ if( rFuncData.IsAddInEquivalent() )
+ {
+ AppendAddInCallToken( rFuncData.GetExtFuncData() );
+ }
+ else if( rFuncData.IsMacroFunc() )
+ {
+ // Do not write the OOXML <definedName> element for new _xlfn.
+ // prefixed functions.
+ if (GetOutput() == EXC_OUTPUT_XML_2007)
+ AppendNameToken( 0 ); // dummy to keep parameter count valid
+ else
+ AppendMacroCallToken( rFuncData.GetExtFuncData() );
+ }
+ else
+ {
+ SAL_WARN( "sc.filter", "XclExpFmlaCompImpl::AppendDefaultParam - unknown opcode" );
+ AppendMissingToken(); // to keep parameter count valid
+ }
+ }
+ }
+
+ // update parameter count, add special parameter tokens
+ FinishParam( rFuncData );
+}
+
+void XclExpFmlaCompImpl::AppendTrailingParam( XclExpFuncData& rFuncData )
+{
+ sal_uInt8 nParamCount = rFuncData.GetParamCount();
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocIf:
+ if( nParamCount == 1 )
+ {
+ // Excel needs at least two parameters in IF function
+ PrepareParam( rFuncData );
+ AppendBoolToken( true );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocRound:
+ case ocRoundUp:
+ case ocRoundDown:
+ if( nParamCount == 1 )
+ {
+ // ROUND, ROUNDUP, ROUNDDOWN functions are fixed to 2 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendIntToken( 0 );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocIndex:
+ if( nParamCount == 1 )
+ {
+ // INDEX function needs at least 2 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendMissingToken();
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocExternal:
+ case ocMacro:
+ // external or macro call without parameters needs the external name reference
+ if( nParamCount == 0 )
+ AppendDefaultParam( rFuncData );
+ break;
+
+ case ocGammaDist:
+ if( nParamCount == 3 )
+ {
+ // GAMMADIST function needs 4 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendIntToken( 1 );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocPoissonDist:
+ if( nParamCount == 2 )
+ {
+ // POISSON function needs 3 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendIntToken( 1 );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocNormDist:
+ if( nParamCount == 3 )
+ {
+ // NORMDIST function needs 4 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendBoolToken( true );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocLogNormDist:
+ case ocLogInv:
+ switch( nParamCount )
+ {
+ // LOGNORMDIST function needs 3 parameters in Excel
+ case 1:
+ PrepareParam( rFuncData );
+ AppendIntToken( 0 );
+ FinishParam( rFuncData );
+ [[fallthrough]]; // add next default parameter
+ case 2:
+ PrepareParam( rFuncData );
+ AppendIntToken( 1 );
+ FinishParam( rFuncData );
+ break;
+ default:;
+ }
+
+ break;
+
+ default:
+ // #i108420# function without parameters stored as macro call needs the external name reference
+ if( (nParamCount == 0) && rFuncData.IsMacroFunc() )
+ AppendDefaultParam( rFuncData );
+
+ }
+}
+
+// reference handling ---------------------------------------------------------
+
+namespace {
+
+bool lclIsRefRel2D( const ScSingleRefData& rRefData )
+{
+ return rRefData.IsColRel() || rRefData.IsRowRel();
+}
+
+bool lclIsRefDel2D( const ScSingleRefData& rRefData )
+{
+ return rRefData.IsColDeleted() || rRefData.IsRowDeleted();
+}
+
+bool lclIsRefRel2D( const ScComplexRefData& rRefData )
+{
+ return lclIsRefRel2D( rRefData.Ref1 ) || lclIsRefRel2D( rRefData.Ref2 );
+}
+
+bool lclIsRefDel2D( const ScComplexRefData& rRefData )
+{
+ return lclIsRefDel2D( rRefData.Ref1 ) || lclIsRefDel2D( rRefData.Ref2 );
+}
+
+} // namespace
+
+SCTAB XclExpFmlaCompImpl::GetScTab( const ScSingleRefData& rRefData ) const
+{
+ if (rRefData.IsTabDeleted())
+ return SCTAB_INVALID;
+
+ if (!rRefData.IsTabRel())
+ // absolute address
+ return rRefData.Tab();
+
+ if (!mxData->mpScBasePos)
+ return SCTAB_INVALID;
+
+ return rRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos).Tab();
+}
+
+bool XclExpFmlaCompImpl::IsRef2D( const ScSingleRefData& rRefData, bool bCheck3DFlag ) const
+{
+ /* rRefData.IsFlag3D() determines if sheet name is always visible, even on
+ the own sheet. If 3D references are allowed, the passed reference does
+ not count as 2D reference. */
+
+ // conditional formatting does not allow 3D refs in xls
+ if (mxData && mxData->mrCfg.meType == EXC_FMLATYPE_CONDFMT)
+ return true;
+
+ if (bCheck3DFlag && rRefData.IsFlag3D())
+ return false;
+
+ if (rRefData.IsTabDeleted())
+ return false;
+
+ if (rRefData.IsTabRel())
+ return rRefData.Tab() == 0;
+ else
+ return rRefData.Tab() == GetCurrScTab();
+}
+
+bool XclExpFmlaCompImpl::IsRef2D( const ScComplexRefData& rRefData, bool bCheck3DFlag ) const
+{
+ return IsRef2D(rRefData.Ref1, bCheck3DFlag) && IsRef2D(rRefData.Ref2, bCheck3DFlag);
+}
+
+void XclExpFmlaCompImpl::ConvertRefData(
+ ScSingleRefData& rRefData, XclAddress& rXclPos,
+ bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const
+{
+ if( mxData->mpScBasePos )
+ {
+ // *** reference position exists (cell, matrix) - convert to absolute ***
+ ScAddress aAbs = rRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos);
+
+ // convert column index
+ if (bTruncMaxCol && (aAbs.Col() == mnMaxScCol))
+ aAbs.SetCol(mnMaxAbsCol);
+ else if ((aAbs.Col() < 0) || (aAbs.Col() > mnMaxAbsCol))
+ rRefData.SetColDeleted(true);
+ rXclPos.mnCol = static_cast<sal_uInt16>(aAbs.Col()) & mnMaxColMask;
+
+ // convert row index
+ if (bTruncMaxRow && (aAbs.Row() == mnMaxScRow))
+ aAbs.SetRow(mnMaxAbsRow);
+ else if ((aAbs.Row() < 0) || (aAbs.Row() > mnMaxAbsRow))
+ rRefData.SetRowDeleted(true);
+ rXclPos.mnRow = static_cast<sal_uInt32>(aAbs.Row()) & mnMaxRowMask;
+
+ // Update the reference.
+ rRefData.SetAddress(GetRoot().GetDoc().GetSheetLimits(), aAbs, *mxData->mpScBasePos);
+ }
+ else
+ {
+ // *** no reference position (shared, names, condfmt) - use relative values ***
+
+ // convert column index (2-step-cast ScsCOL->sal_Int16->sal_uInt16 to get all bits correctly)
+ sal_Int16 nXclRelCol = static_cast<sal_Int16>(rRefData.Col());
+ rXclPos.mnCol = static_cast< sal_uInt16 >( nXclRelCol ) & mnMaxColMask;
+
+ // convert row index (2-step-cast ScsROW->sal_Int32->sal_uInt32 to get all bits correctly)
+ sal_Int32 nXclRelRow = static_cast<sal_Int32>(rRefData.Row());
+ rXclPos.mnRow = static_cast< sal_uInt32 >( nXclRelRow ) & mnMaxRowMask;
+ }
+
+ // flags for relative column and row
+ if( bNatLangRef )
+ {
+ OSL_ENSURE( meBiff == EXC_BIFF8, "XclExpFmlaCompImpl::ConvertRefData - NLRs only for BIFF8" );
+ // Calc does not support absolute reference mode in natural language references
+ ::set_flag( rXclPos.mnCol, EXC_TOK_NLR_REL );
+ }
+ else
+ {
+ sal_uInt16 rnRelRow = rXclPos.mnRow;
+ sal_uInt16& rnRelField = (meBiff <= EXC_BIFF5) ? rnRelRow : rXclPos.mnCol;
+ ::set_flag( rnRelField, EXC_TOK_REF_COLREL, rRefData.IsColRel() );
+ ::set_flag( rnRelField, EXC_TOK_REF_ROWREL, rRefData.IsRowRel() );
+ }
+}
+
+void XclExpFmlaCompImpl::ConvertRefData(
+ ScComplexRefData& rRefData, XclRange& rXclRange, bool bNatLangRef ) const
+{
+ // convert start and end of the range
+ ConvertRefData( rRefData.Ref1, rXclRange.maFirst, bNatLangRef, false, false );
+ bool bTruncMaxCol = !rRefData.Ref1.IsColDeleted() && (rXclRange.maFirst.mnCol == 0);
+ bool bTruncMaxRow = !rRefData.Ref1.IsRowDeleted() && (rXclRange.maFirst.mnRow == 0);
+ ConvertRefData( rRefData.Ref2, rXclRange.maLast, bNatLangRef, bTruncMaxCol, bTruncMaxRow );
+}
+
+XclExpRefLogEntry* XclExpFmlaCompImpl::GetNewRefLogEntry()
+{
+ if( mxData->mpRefLog )
+ {
+ mxData->mpRefLog->emplace_back();
+ return &mxData->mpRefLog->back();
+ }
+ return nullptr;
+}
+
+void XclExpFmlaCompImpl::ProcessCellRef( const XclExpScToken& rTokData )
+{
+ // get the Excel address components, adjust internal data in aRefData
+ bool bNatLangRef = (meBiff == EXC_BIFF8) && mxData->mpScBasePos && (rTokData.GetOpCode() == ocColRowName);
+ ScSingleRefData aRefData = *rTokData.mpScToken->GetSingleRef();
+ XclAddress aXclPos( ScAddress::UNINITIALIZED );
+ ConvertRefData( aRefData, aXclPos, bNatLangRef, false, false );
+
+ if( bNatLangRef )
+ {
+ OSL_ENSURE( aRefData.IsColRel() != aRefData.IsRowRel(),
+ "XclExpFmlaCompImpl::ProcessCellRef - broken natural language reference" );
+ // create tNlr token for natural language reference
+ sal_uInt8 nSubId = aRefData.IsColRel() ? EXC_TOK_NLR_COLV : EXC_TOK_NLR_ROWV;
+ AppendOperandTokenId( EXC_TOKID_NLR, rTokData.mnSpaces );
+ Append( nSubId );
+ AppendAddress( aXclPos );
+ }
+ else
+ {
+ // store external cell contents in CRN records
+ if( mxData->mrCfg.mbFromCell && mxData->mpLinkMgr && mxData->mpScBasePos )
+ mxData->mpLinkMgr->StoreCell(aRefData, *mxData->mpScBasePos);
+
+ // create the tRef, tRefErr, tRefN, tRef3d, or tRefErr3d token
+ if (!mxData->mrCfg.mb3DRefOnly && IsRef2D(aRefData, mxData->mpLinkMgr != nullptr))
+ {
+ // 2D reference (not in defined names, but allowed in range lists)
+ sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_REFN :
+ (lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR : EXC_TOKID_REF);
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ AppendAddress( aXclPos );
+ }
+ else if( mxData->mpLinkMgr ) // 3D reference
+ {
+ // 1-based EXTERNSHEET index and 0-based Excel sheet index
+ sal_uInt16 nExtSheet, nXclTab;
+ mxData->mpLinkMgr->FindExtSheet( nExtSheet, nXclTab, GetScTab( aRefData ), GetNewRefLogEntry() );
+ // write the token
+ sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR3D : EXC_TOKID_REF3D;
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( 0, 8 );
+ Append( nXclTab );
+ Append( nXclTab );
+ }
+ AppendAddress( aXclPos );
+ }
+ else
+ {
+ // 3D ref in cond. format, or 2D ref in name
+ AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
+ }
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessRangeRef( const XclExpScToken& rTokData )
+{
+ // get the Excel address components, adjust internal data in aRefData
+ ScComplexRefData aRefData = *rTokData.mpScToken->GetDoubleRef();
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ ConvertRefData( aRefData, aXclRange, false );
+
+ // store external cell contents in CRN records
+ if( mxData->mrCfg.mbFromCell && mxData->mpLinkMgr && mxData->mpScBasePos )
+ mxData->mpLinkMgr->StoreCellRange(aRefData, *mxData->mpScBasePos);
+
+ // create the tArea, tAreaErr, tAreaN, tArea3d, or tAreaErr3d token
+ if (!mxData->mrCfg.mb3DRefOnly && IsRef2D(aRefData, mxData->mpLinkMgr != nullptr))
+ {
+ // 2D reference (not in name formulas, but allowed in range lists)
+ sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_AREAN :
+ (lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR : EXC_TOKID_AREA);
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ AppendRange( aXclRange );
+ }
+ else if( mxData->mpLinkMgr ) // 3D reference
+ {
+ // 1-based EXTERNSHEET index and 0-based Excel sheet indexes
+ sal_uInt16 nExtSheet, nFirstXclTab, nLastXclTab;
+ mxData->mpLinkMgr->FindExtSheet( nExtSheet, nFirstXclTab, nLastXclTab,
+ GetScTab( aRefData.Ref1 ), GetScTab( aRefData.Ref2 ), GetNewRefLogEntry() );
+ // write the token
+ sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR3D : EXC_TOKID_AREA3D;
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( 0, 8 );
+ Append( nFirstXclTab );
+ Append( nLastXclTab );
+ }
+ AppendRange( aXclRange );
+ }
+ else
+ {
+ // 3D ref in cond. format, or 2D ref in name
+ AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessExternalCellRef( const XclExpScToken& rTokData )
+{
+ if( mxData->mpLinkMgr )
+ {
+ // get the Excel address components, adjust internal data in aRefData
+ ScSingleRefData aRefData = *rTokData.mpScToken->GetSingleRef();
+ XclAddress aXclPos( ScAddress::UNINITIALIZED );
+ ConvertRefData( aRefData, aXclPos, false, false, false );
+
+ // store external cell contents in CRN records
+ sal_uInt16 nFileId = rTokData.mpScToken->GetIndex();
+ OUString aTabName = rTokData.mpScToken->GetString().getString();
+ if( mxData->mrCfg.mbFromCell && mxData->mpScBasePos )
+ mxData->mpLinkMgr->StoreCell(nFileId, aTabName, aRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos));
+
+ // 1-based EXTERNSHEET index and 0-based Excel sheet indexes
+ sal_uInt16 nExtSheet, nFirstSBTab, nLastSBTab;
+ mxData->mpLinkMgr->FindExtSheet( nFileId, aTabName, 1, nExtSheet, nFirstSBTab, nLastSBTab, GetNewRefLogEntry() );
+ // write the token
+ sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR3D : EXC_TOKID_REF3D;
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( 0, 8 );
+ Append( nFirstSBTab );
+ Append( nLastSBTab );
+ }
+ AppendAddress( aXclPos );
+ }
+ else
+ {
+ AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessExternalRangeRef( const XclExpScToken& rTokData )
+{
+ if( mxData->mpLinkMgr )
+ {
+ // get the Excel address components, adjust internal data in aRefData
+ ScComplexRefData aRefData = *rTokData.mpScToken->GetDoubleRef();
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ ConvertRefData( aRefData, aXclRange, false );
+
+ // store external cell contents in CRN records
+ sal_uInt16 nFileId = rTokData.mpScToken->GetIndex();
+ OUString aTabName = rTokData.mpScToken->GetString().getString();
+ if( mxData->mrCfg.mbFromCell && mxData->mpScBasePos )
+ mxData->mpLinkMgr->StoreCellRange(nFileId, aTabName, aRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos));
+
+ // 1-based EXTERNSHEET index and 0-based Excel sheet indexes
+ sal_uInt16 nExtSheet, nFirstSBTab, nLastSBTab;
+ sal_uInt16 nTabSpan = static_cast<sal_uInt16>(aRefData.Ref2.Tab() - aRefData.Ref1.Tab() + 1);
+ mxData->mpLinkMgr->FindExtSheet(
+ nFileId, aTabName, nTabSpan, nExtSheet, nFirstSBTab, nLastSBTab, GetNewRefLogEntry());
+ // write the token
+ sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR3D : EXC_TOKID_AREA3D;
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( 0, 8 );
+ Append( nFirstSBTab );
+ Append( nLastSBTab );
+ }
+ AppendRange( aXclRange );
+ }
+ else
+ {
+ AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessDefinedName( const XclExpScToken& rTokData )
+{
+ sal_Int16 nSheet = rTokData.mpScToken->GetSheet();
+ SCTAB nTab = (nSheet < 0 ? SCTAB_GLOBAL : nSheet);
+
+ XclExpNameManager& rNameMgr = GetNameManager();
+ sal_uInt16 nNameIdx = rNameMgr.InsertName(nTab, rTokData.mpScToken->GetIndex(), GetCurrScTab());
+ if( nNameIdx != 0 )
+ {
+ // global names always with tName token, local names dependent on config
+ SCTAB nScTab = rNameMgr.GetScTab( nNameIdx );
+ if( (nScTab == SCTAB_GLOBAL) || (!mxData->mrCfg.mb3DRefOnly && (nScTab == GetCurrScTab())) )
+ {
+ AppendNameToken( nNameIdx, rTokData.mnSpaces );
+ }
+ else if( mxData->mpLinkMgr )
+ {
+ // use the same special EXTERNNAME to refer to any local name
+ sal_uInt16 nExtSheet = mxData->mpLinkMgr->FindExtSheet( EXC_EXTSH_OWNDOC );
+ AppendNameXToken( nExtSheet, nNameIdx, rTokData.mnSpaces );
+ }
+ else
+ AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces );
+ // volatile names (containing volatile functions)
+ mxData->mbVolatile |= rNameMgr.IsVolatile( nNameIdx );
+ }
+ else
+ AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessExternalName( const XclExpScToken& rTokData )
+{
+ if( mxData->mpLinkMgr )
+ {
+ ScExternalRefManager& rExtRefMgr = *GetDoc().GetExternalRefManager();
+ sal_uInt16 nFileId = rTokData.mpScToken->GetIndex();
+ OUString aName = rTokData.mpScToken->GetString().getString();
+ ScExternalRefCache::TokenArrayRef xArray = rExtRefMgr.getRangeNameTokens( nFileId, aName );
+ if( xArray )
+ {
+ // store external cell contents in CRN records
+ if( mxData->mpScBasePos )
+ {
+ FormulaTokenArrayPlainIterator aIter(*xArray);
+ for( FormulaToken* pScToken = aIter.First(); pScToken; pScToken = aIter.Next() )
+ {
+ if( pScToken->IsExternalRef() )
+ {
+ switch( pScToken->GetType() )
+ {
+ case svExternalSingleRef:
+ {
+ ScSingleRefData aRefData = *pScToken->GetSingleRef();
+ mxData->mpLinkMgr->StoreCell(
+ nFileId, pScToken->GetString().getString(), aRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos));
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ ScComplexRefData aRefData = *pScToken->GetDoubleRef();
+ mxData->mpLinkMgr->StoreCellRange(
+ nFileId, pScToken->GetString().getString(), aRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos));
+ }
+ break;
+ default:
+ ; // nothing, avoid compiler warning
+ }
+ }
+ }
+ }
+
+ // insert the new external name and create the tNameX token
+ sal_uInt16 nExtSheet = 0, nExtName = 0;
+ const OUString* pFile = rExtRefMgr.getExternalFileName( nFileId );
+ if( pFile && mxData->mpLinkMgr->InsertExtName( nExtSheet, nExtName, *pFile, aName, xArray ) )
+ {
+ AppendNameXToken( nExtSheet, nExtName, rTokData.mnSpaces );
+ return;
+ }
+ }
+ }
+
+ // on any error: create a #NAME? error
+ AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces );
+}
+
+// token vector ---------------------------------------------------------------
+
+void XclExpFmlaCompImpl::PushOperandPos( sal_uInt16 nTokPos )
+{
+ mxData->maOpPosStack.push_back( nTokPos );
+}
+
+void XclExpFmlaCompImpl::PushOperatorPos( sal_uInt16 nTokPos, const XclExpOperandListRef& rxOperands )
+{
+ PushOperandPos( nTokPos );
+ OSL_ENSURE( rxOperands, "XclExpFmlaCompImpl::AppendOperatorTokenId - missing operand list" );
+ if( mxData->maOpListVec.size() <= nTokPos )
+ mxData->maOpListVec.resize( nTokPos + 1, XclExpOperandListRef() );
+ mxData->maOpListVec[ nTokPos ] = rxOperands;
+}
+
+sal_uInt16 XclExpFmlaCompImpl::PopOperandPos()
+{
+ OSL_ENSURE( !mxData->mbOk || !mxData->maOpPosStack.empty(), "XclExpFmlaCompImpl::PopOperandPos - token stack broken" );
+ mxData->mbOk &= !mxData->maOpPosStack.empty();
+ if( mxData->mbOk )
+ {
+ sal_uInt16 nTokPos = mxData->maOpPosStack.back();
+ mxData->maOpPosStack.pop_back();
+ return nTokPos;
+ }
+ return 0;
+}
+
+namespace {
+
+void lclAppend( ScfUInt8Vec& orVector, sal_uInt16 nData )
+{
+ orVector.resize( orVector.size() + 2 );
+ ShortToSVBT16( nData, &*(orVector.end() - 2) );
+}
+
+void lclAppend( ScfUInt8Vec& orVector, sal_uInt32 nData )
+{
+ orVector.resize( orVector.size() + 4 );
+ UInt32ToSVBT32( nData, &*(orVector.end() - 4) );
+}
+
+void lclAppend( ScfUInt8Vec& orVector, double fData )
+{
+ orVector.resize( orVector.size() + 8 );
+ DoubleToSVBT64( fData, &*(orVector.end() - 8) );
+}
+
+void lclAppend( ScfUInt8Vec& orVector, const XclExpRoot& rRoot, const OUString& rString, XclStrFlags nStrFlags )
+{
+ XclExpStringRef xXclStr = XclExpStringHelper::CreateString( rRoot, rString, nStrFlags, EXC_TOK_STR_MAXLEN );
+ size_t nSize = orVector.size();
+ orVector.resize( nSize + xXclStr->GetSize() );
+ xXclStr->WriteToMem( &orVector[ nSize ] );
+}
+
+} // namespace
+
+void XclExpFmlaCompImpl::Append( sal_uInt8 nData )
+{
+ mxData->maTokVec.push_back( nData );
+}
+
+void XclExpFmlaCompImpl::Append( sal_uInt8 nData, size_t nCount )
+{
+ mxData->maTokVec.resize( mxData->maTokVec.size() + nCount, nData );
+}
+
+void XclExpFmlaCompImpl::Append( sal_uInt16 nData )
+{
+ lclAppend( mxData->maTokVec, nData );
+}
+
+void XclExpFmlaCompImpl::Append( sal_uInt32 nData )
+{
+ lclAppend( mxData->maTokVec, nData );
+}
+
+void XclExpFmlaCompImpl::Append( double fData )
+{
+ lclAppend( mxData->maTokVec, fData );
+}
+
+void XclExpFmlaCompImpl::Append( const OUString& rString )
+{
+ lclAppend( mxData->maTokVec, GetRoot(), rString, XclStrFlags::EightBitLength );
+}
+
+void XclExpFmlaCompImpl::AppendAddress( const XclAddress& rXclPos )
+{
+ Append( static_cast<sal_uInt16>(rXclPos.mnRow) );
+ if( meBiff <= EXC_BIFF5 )
+ Append( static_cast< sal_uInt8 >( rXclPos.mnCol ) );
+ else
+ Append( rXclPos.mnCol );
+}
+
+void XclExpFmlaCompImpl::AppendRange( const XclRange& rXclRange )
+{
+ Append( static_cast<sal_uInt16>(rXclRange.maFirst.mnRow) );
+ Append( static_cast<sal_uInt16>(rXclRange.maLast.mnRow) );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( static_cast< sal_uInt8 >( rXclRange.maFirst.mnCol ) );
+ Append( static_cast< sal_uInt8 >( rXclRange.maLast.mnCol ) );
+ }
+ else
+ {
+ Append( rXclRange.maFirst.mnCol );
+ Append( rXclRange.maLast.mnCol );
+ }
+}
+
+void XclExpFmlaCompImpl::AppendSpaceToken( sal_uInt8 nType, sal_uInt8 nCount )
+{
+ if( nCount > 0 )
+ {
+ Append( EXC_TOKID_ATTR );
+ Append( EXC_TOK_ATTR_SPACE );
+ Append( nType );
+ Append( nCount );
+ }
+}
+
+void XclExpFmlaCompImpl::AppendOperandTokenId( sal_uInt8 nTokenId, sal_uInt8 nSpaces )
+{
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, nSpaces );
+ PushOperandPos( GetSize() );
+ Append( nTokenId );
+}
+
+void XclExpFmlaCompImpl::AppendIntToken( sal_uInt16 nValue, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_INT, nSpaces );
+ Append( nValue );
+}
+
+void XclExpFmlaCompImpl::AppendNumToken( double fValue, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_NUM, nSpaces );
+ Append( fValue );
+}
+
+void XclExpFmlaCompImpl::AppendBoolToken( bool bValue, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_BOOL, nSpaces );
+ Append( bValue ? EXC_TOK_BOOL_TRUE : EXC_TOK_BOOL_FALSE );
+}
+
+void XclExpFmlaCompImpl::AppendErrorToken( sal_uInt8 nErrCode, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_ERR, nSpaces );
+ Append( nErrCode );
+}
+
+void XclExpFmlaCompImpl::AppendMissingToken( sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_MISSARG, nSpaces );
+}
+
+void XclExpFmlaCompImpl::AppendNameToken( sal_uInt16 nNameIdx, sal_uInt8 nSpaces )
+{
+ if( nNameIdx > 0 )
+ {
+ AppendOperandTokenId( GetTokenId( EXC_TOKID_NAME, EXC_TOKCLASS_REF ), nSpaces );
+ Append( nNameIdx );
+ Append( 0, (meBiff <= EXC_BIFF5) ? 12 : 2 );
+ }
+ else
+ AppendErrorToken( EXC_ERR_NAME );
+}
+
+void XclExpFmlaCompImpl::AppendMissingNameToken( const OUString& rName, sal_uInt8 nSpaces )
+{
+ sal_uInt16 nNameIdx = GetNameManager().InsertRawName( rName );
+ AppendNameToken( nNameIdx, nSpaces );
+}
+
+void XclExpFmlaCompImpl::AppendNameXToken( sal_uInt16 nExtSheet, sal_uInt16 nExtName, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ), nSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ Append( 0, 8 );
+ Append( nExtName );
+ Append( 0, (meBiff <= EXC_BIFF5) ? 12 : 2 );
+}
+
+void XclExpFmlaCompImpl::AppendMacroCallToken( const XclExpExtFuncData& rExtFuncData )
+{
+ sal_uInt16 nNameIdx = GetNameManager().InsertMacroCall( rExtFuncData.maFuncName, rExtFuncData.mbVBasic, true, rExtFuncData.mbHidden );
+ AppendNameToken( nNameIdx );
+}
+
+void XclExpFmlaCompImpl::AppendAddInCallToken( const XclExpExtFuncData& rExtFuncData )
+{
+ OUString aXclFuncName;
+ if( mxData->mpLinkMgr && ScGlobal::GetAddInCollection()->GetExcelName( rExtFuncData.maFuncName, GetUILanguage(), aXclFuncName ) )
+ {
+ sal_uInt16 nExtSheet, nExtName;
+ if( mxData->mpLinkMgr->InsertAddIn( nExtSheet, nExtName, aXclFuncName ) )
+ {
+ AppendNameXToken( nExtSheet, nExtName );
+ return;
+ }
+ }
+ AppendMacroCallToken( rExtFuncData );
+}
+
+void XclExpFmlaCompImpl::AppendEuroToolCallToken( const XclExpExtFuncData& rExtFuncData )
+{
+ sal_uInt16 nExtSheet(0), nExtName(0);
+ if( mxData->mpLinkMgr && mxData->mpLinkMgr->InsertEuroTool( nExtSheet, nExtName, rExtFuncData.maFuncName ) )
+ AppendNameXToken( nExtSheet, nExtName );
+ else
+ AppendMacroCallToken( rExtFuncData );
+}
+
+void XclExpFmlaCompImpl::AppendOperatorTokenId( sal_uInt8 nTokenId, const XclExpOperandListRef& rxOperands, sal_uInt8 nSpaces )
+{
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, nSpaces );
+ PushOperatorPos( GetSize(), rxOperands );
+ Append( nTokenId );
+}
+
+void XclExpFmlaCompImpl::AppendUnaryOperatorToken( sal_uInt8 nTokenId, sal_uInt8 nSpaces )
+{
+ XclExpOperandListRef xOperands = std::make_shared<XclExpOperandList>();
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, true );
+ AppendOperatorTokenId( nTokenId, xOperands, nSpaces );
+}
+
+void XclExpFmlaCompImpl::AppendBinaryOperatorToken( sal_uInt8 nTokenId, bool bValType, sal_uInt8 nSpaces )
+{
+ XclExpOperandListRef xOperands = std::make_shared<XclExpOperandList>();
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, bValType );
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, bValType );
+ AppendOperatorTokenId( nTokenId, xOperands, nSpaces );
+}
+
+void XclExpFmlaCompImpl::AppendLogicalOperatorToken( sal_uInt16 nXclFuncIdx, sal_uInt8 nOpCount )
+{
+ XclExpOperandListRef xOperands = std::make_shared<XclExpOperandList>();
+ for( sal_uInt8 nOpIdx = 0; nOpIdx < nOpCount; ++nOpIdx )
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPX, false );
+ AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNCVAR, EXC_TOKCLASS_VAL ), xOperands );
+ Append( nOpCount );
+ Append( nXclFuncIdx );
+}
+
+void XclExpFmlaCompImpl::AppendFuncToken( const XclExpFuncData& rFuncData )
+{
+ sal_uInt16 nXclFuncIdx = rFuncData.GetXclFuncIdx();
+ sal_uInt8 nParamCount = rFuncData.GetParamCount();
+ sal_uInt8 nRetClass = rFuncData.GetReturnClass();
+
+ if( (nXclFuncIdx == EXC_FUNCID_SUM) && (nParamCount == 1) )
+ {
+ // SUM with only one parameter
+ AppendOperatorTokenId( EXC_TOKID_ATTR, rFuncData.GetOperandList() );
+ Append( EXC_TOK_ATTR_SUM );
+ Append( sal_uInt16( 0 ) );
+ }
+ else if( rFuncData.IsFixedParamCount() )
+ {
+ // fixed number of parameters
+ AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNC, nRetClass ), rFuncData.GetOperandList() );
+ Append( nXclFuncIdx );
+ }
+ else
+ {
+ // variable number of parameters
+ AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNCVAR, nRetClass ), rFuncData.GetOperandList() );
+ Append( nParamCount );
+ Append( nXclFuncIdx );
+ }
+}
+
+void XclExpFmlaCompImpl::AppendParenToken( sal_uInt8 nOpenSpaces, sal_uInt8 nCloseSpaces )
+{
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_OPEN, nOpenSpaces );
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_CLOSE, nCloseSpaces );
+ Append( EXC_TOKID_PAREN );
+}
+
+void XclExpFmlaCompImpl::AppendJumpToken( XclExpFuncData& rFuncData, sal_uInt8 nAttrType )
+{
+ // store the start position of the token
+ rFuncData.AppendAttrPos( GetSize() );
+ // create the tAttr token
+ Append( EXC_TOKID_ATTR );
+ Append( nAttrType );
+ Append( sal_uInt16( 0 ) ); // placeholder that will be updated later
+}
+
+void XclExpFmlaCompImpl::InsertZeros( sal_uInt16 nInsertPos, sal_uInt16 nInsertSize )
+{
+ // insert zeros into the token array
+ OSL_ENSURE( nInsertPos < mxData->maTokVec.size(), "XclExpFmlaCompImpl::Insert - invalid position" );
+ mxData->maTokVec.insert( mxData->maTokVec.begin() + nInsertPos, nInsertSize, 0 );
+
+ // update positions of operands waiting for an operator
+ for( auto& rOpPos : mxData->maOpPosStack )
+ if( nInsertPos <= rOpPos )
+ rOpPos += nInsertSize;
+
+ // update operand lists of all operator tokens
+ if( nInsertPos < mxData->maOpListVec.size() )
+ mxData->maOpListVec.insert( mxData->maOpListVec.begin() + nInsertPos, nInsertSize, XclExpOperandListRef() );
+ for( auto& rxOpList : mxData->maOpListVec )
+ if( rxOpList )
+ for( auto& rOp : *rxOpList )
+ if( nInsertPos <= rOp.mnTokPos )
+ rOp.mnTokPos += nInsertSize;
+}
+
+void XclExpFmlaCompImpl::Overwrite( sal_uInt16 nWriteToPos, sal_uInt16 nOffset )
+{
+ OSL_ENSURE( o3tl::make_unsigned( nWriteToPos + 1 ) < mxData->maTokVec.size(), "XclExpFmlaCompImpl::Overwrite - invalid position" );
+ ShortToSVBT16( nOffset, &mxData->maTokVec[ nWriteToPos ] );
+}
+
+void XclExpFmlaCompImpl::UpdateAttrGoto( sal_uInt16 nAttrPos )
+{
+ /* tAttrGoto contains distance from end of tAttr token to position behind
+ the function token (for IF or CHOOSE function), which is currently at
+ the end of the token array. Additionally this distance is decreased by
+ one, for whatever reason. So we have to subtract 4 and 1 from the
+ distance between the tAttr token start and the end of the token array. */
+ Overwrite( nAttrPos + 2, static_cast< sal_uInt16 >( GetSize() - nAttrPos - 5 ) );
+}
+
+bool XclExpFmlaCompImpl::IsSpaceToken( sal_uInt16 nPos ) const
+{
+ return
+ (o3tl::make_unsigned( nPos + 4 ) <= mxData->maTokVec.size()) &&
+ (mxData->maTokVec[ nPos ] == EXC_TOKID_ATTR) &&
+ (mxData->maTokVec[ nPos + 1 ] == EXC_TOK_ATTR_SPACE);
+}
+
+void XclExpFmlaCompImpl::RemoveTrailingParen()
+{
+ // remove trailing tParen token
+ if( !mxData->maTokVec.empty() && (mxData->maTokVec.back() == EXC_TOKID_PAREN) )
+ mxData->maTokVec.pop_back();
+ // remove remaining tAttrSpace tokens
+ while( (mxData->maTokVec.size() >= 4) && IsSpaceToken( GetSize() - 4 ) )
+ mxData->maTokVec.erase( mxData->maTokVec.end() - 4, mxData->maTokVec.end() );
+}
+
+void XclExpFmlaCompImpl::AppendExt( sal_uInt8 nData )
+{
+ mxData->maExtDataVec.push_back( nData );
+}
+
+void XclExpFmlaCompImpl::AppendExt( sal_uInt8 nData, size_t nCount )
+{
+ mxData->maExtDataVec.resize( mxData->maExtDataVec.size() + nCount, nData );
+}
+
+void XclExpFmlaCompImpl::AppendExt( sal_uInt16 nData )
+{
+ lclAppend( mxData->maExtDataVec, nData );
+}
+
+void XclExpFmlaCompImpl::AppendExt( double fData )
+{
+ lclAppend( mxData->maExtDataVec, fData );
+}
+
+void XclExpFmlaCompImpl::AppendExt( const OUString& rString )
+{
+ lclAppend( mxData->maExtDataVec, GetRoot(), rString, (meBiff == EXC_BIFF8) ? XclStrFlags::NONE : XclStrFlags::EightBitLength );
+}
+
+namespace {
+
+void lclInitOwnTab( ScSingleRefData& rRef, const ScAddress& rScPos, SCTAB nCurrScTab, bool b3DRefOnly )
+{
+ if( b3DRefOnly )
+ {
+ // no reduction to 2D reference, if global link manager is used
+ rRef.SetFlag3D(true);
+ }
+ else if( rScPos.Tab() == nCurrScTab )
+ {
+ rRef.SetRelTab(0);
+ }
+}
+
+void lclPutCellToTokenArray( ScTokenArray& rScTokArr, const ScAddress& rScPos, SCTAB nCurrScTab, bool b3DRefOnly )
+{
+ ScSingleRefData aRef;
+ aRef.InitAddress( rScPos );
+ lclInitOwnTab( aRef, rScPos, nCurrScTab, b3DRefOnly );
+ rScTokArr.AddSingleReference( aRef );
+}
+
+void lclPutRangeToTokenArray( ScTokenArray& rScTokArr, const ScRange& rScRange, SCTAB nCurrScTab, bool b3DRefOnly )
+{
+ if( rScRange.aStart == rScRange.aEnd )
+ {
+ lclPutCellToTokenArray( rScTokArr, rScRange.aStart, nCurrScTab, b3DRefOnly );
+ }
+ else
+ {
+ ScComplexRefData aRef;
+ aRef.InitRange( rScRange );
+ lclInitOwnTab( aRef.Ref1, rScRange.aStart, nCurrScTab, b3DRefOnly );
+ lclInitOwnTab( aRef.Ref2, rScRange.aEnd, nCurrScTab, b3DRefOnly );
+ rScTokArr.AddDoubleReference( aRef );
+ }
+}
+
+} // namespace
+
+XclExpFormulaCompiler::XclExpFormulaCompiler( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mxImpl( std::make_shared<XclExpFmlaCompImpl>( rRoot ) )
+{
+}
+
+XclExpFormulaCompiler::~XclExpFormulaCompiler()
+{
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateFormula(
+ XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos, XclExpRefLog* pRefLog )
+{
+ return mxImpl->CreateFormula( eType, rScTokArr, pScBasePos, pRefLog );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScAddress& rScPos )
+{
+ ScTokenArray aScTokArr(GetRoot().GetDoc());
+ lclPutCellToTokenArray( aScTokArr, rScPos, GetCurrScTab(), mxImpl->Is3DRefOnly( eType ) );
+ return mxImpl->CreateFormula( eType, aScTokArr );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScRange& rScRange )
+{
+ ScTokenArray aScTokArr(GetRoot().GetDoc());
+ lclPutRangeToTokenArray( aScTokArr, rScRange, GetCurrScTab(), mxImpl->Is3DRefOnly( eType ) );
+ return mxImpl->CreateFormula( eType, aScTokArr );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScRangeList& rScRanges )
+{
+ size_t nCount = rScRanges.size();
+ if( nCount == 0 )
+ return XclTokenArrayRef();
+
+ ScTokenArray aScTokArr(GetRoot().GetDoc());
+ SCTAB nCurrScTab = GetCurrScTab();
+ bool b3DRefOnly = mxImpl->Is3DRefOnly( eType );
+ for( size_t nIdx = 0; nIdx < nCount; ++nIdx )
+ {
+ if( nIdx > 0 )
+ aScTokArr.AddOpCode( ocUnion );
+ lclPutRangeToTokenArray( aScTokArr, rScRanges[ nIdx ], nCurrScTab, b3DRefOnly );
+ }
+ return mxImpl->CreateFormula( eType, aScTokArr );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateErrorFormula( sal_uInt8 nErrCode )
+{
+ return mxImpl->CreateErrorFormula( nErrCode );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateSpecialRefFormula(
+ sal_uInt8 nTokenId, const XclAddress& rXclPos )
+{
+ return mxImpl->CreateSpecialRefFormula( nTokenId, rXclPos );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateNameXFormula(
+ sal_uInt16 nExtSheet, sal_uInt16 nExtName )
+{
+ return mxImpl->CreateNameXFormula( nExtSheet, nExtName );
+}
+
+bool XclExpFormulaCompiler::IsRef2D( const ScSingleRefData& rRefData ) const
+{
+ return mxImpl->IsRef2D(rRefData, true);
+}
+
+bool XclExpFormulaCompiler::IsRef2D( const ScComplexRefData& rRefData ) const
+{
+ return mxImpl->IsRef2D(rRefData, true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xehelper.cxx b/sc/source/filter/excel/xehelper.cxx
new file mode 100644
index 000000000..038ec8f71
--- /dev/null
+++ b/sc/source/filter/excel/xehelper.cxx
@@ -0,0 +1,1071 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <sfx2/objsh.hxx>
+#include <vcl/font.hxx>
+#include <tools/urlobj.hxx>
+#include <svl/itemset.hxx>
+#include <svtools/ctrltool.hxx>
+#include <svx/svdotext.hxx>
+#include <editeng/outlobj.hxx>
+#include <scitems.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/svxfont.hxx>
+#include <editeng/editids.hrc>
+
+#include <document.hxx>
+#include <docpool.hxx>
+#include <editutil.hxx>
+#include <patattr.hxx>
+#include <scmatrix.hxx>
+#include <xestyle.hxx>
+#include <fprogressbar.hxx>
+#include <globstr.hrc>
+#include <xltracer.hxx>
+#include <xltools.hxx>
+#include <xecontent.hxx>
+#include <xelink.hxx>
+#include <xehelper.hxx>
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::i18n::XBreakIterator;
+
+// Export progress bar ========================================================
+
+XclExpProgressBar::XclExpProgressBar( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mxProgress( new ScfProgressBar( rRoot.GetDocShell(), STR_SAVE_DOC ) ),
+ mpSubProgress( nullptr ),
+ mpSubRowCreate( nullptr ),
+ mpSubRowFinal( nullptr ),
+ mnSegRowFinal( SCF_INV_SEGMENT ),
+ mnRowCount( 0 )
+{
+}
+
+XclExpProgressBar::~XclExpProgressBar()
+{
+}
+
+void XclExpProgressBar::Initialize()
+{
+ const ScDocument& rDoc = GetDoc();
+ const XclExpTabInfo& rTabInfo = GetTabInfo();
+ SCTAB nScTabCount = rTabInfo.GetScTabCount();
+
+ // *** segment: creation of ROW records *** -------------------------------
+
+ sal_Int32 nSegRowCreate = mxProgress->AddSegment( 2000 );
+ mpSubRowCreate = &mxProgress->GetSegmentProgressBar( nSegRowCreate );
+ maSubSegRowCreate.resize( nScTabCount, SCF_INV_SEGMENT );
+
+ for( SCTAB nScTab = 0; nScTab < nScTabCount; ++nScTab )
+ {
+ if( rTabInfo.IsExportTab( nScTab ) )
+ {
+ SCCOL nLastUsedScCol;
+ SCROW nLastUsedScRow;
+ rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
+ std::size_t nSegSize = static_cast< std::size_t >( nLastUsedScRow + 1 );
+ maSubSegRowCreate[ nScTab ] = mpSubRowCreate->AddSegment( nSegSize );
+ }
+ }
+
+ // *** segment: writing all ROW records *** -------------------------------
+
+ mnSegRowFinal = mxProgress->AddSegment( 1000 );
+ // sub progress bar and segment are created later in ActivateFinalRowsSegment()
+}
+
+void XclExpProgressBar::IncRowRecordCount()
+{
+ ++mnRowCount;
+}
+
+void XclExpProgressBar::ActivateCreateRowsSegment()
+{
+ OSL_ENSURE( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()),
+ "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" );
+ sal_Int32 nSeg = maSubSegRowCreate[ GetCurrScTab() ];
+ OSL_ENSURE( nSeg != SCF_INV_SEGMENT, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" );
+ if( nSeg != SCF_INV_SEGMENT )
+ {
+ mpSubProgress = mpSubRowCreate;
+ mpSubProgress->ActivateSegment( nSeg );
+ }
+ else
+ mpSubProgress = nullptr;
+}
+
+void XclExpProgressBar::ActivateFinalRowsSegment()
+{
+ if( !mpSubRowFinal && (mnRowCount > 0) )
+ {
+ mpSubRowFinal = &mxProgress->GetSegmentProgressBar( mnSegRowFinal );
+ mpSubRowFinal->AddSegment( mnRowCount );
+ }
+ mpSubProgress = mpSubRowFinal;
+ if( mpSubProgress )
+ mpSubProgress->Activate();
+}
+
+void XclExpProgressBar::Progress()
+{
+ if( mpSubProgress && !mpSubProgress->IsFull() )
+ mpSubProgress->Progress();
+}
+
+// Calc->Excel cell address/range conversion ==================================
+
+namespace {
+
+/** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */
+void lclFillAddress( XclAddress& rXclPos, SCCOL nScCol, SCROW nScRow )
+{
+ rXclPos.mnCol = static_cast< sal_uInt16 >( nScCol );
+ rXclPos.mnRow = static_cast< sal_uInt32 >( nScRow );
+}
+
+} // namespace
+
+XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot& rRoot ) :
+ XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetXclMaxPos() )
+{
+}
+
+// cell address ---------------------------------------------------------------
+
+bool XclExpAddressConverter::CheckAddress( const ScAddress& rScPos, bool bWarn )
+{
+ // ScAddress::operator<=() doesn't do what we want here
+ bool bValidCol = (0 <= rScPos.Col()) && (rScPos.Col() <= maMaxPos.Col());
+ bool bValidRow = (0 <= rScPos.Row()) && (rScPos.Row() <= maMaxPos.Row());
+ bool bValidTab = (0 <= rScPos.Tab()) && (rScPos.Tab() <= maMaxPos.Tab());
+
+ bool bValid = bValidCol && bValidRow && bValidTab;
+ if( !bValid )
+ {
+ mbColTrunc |= !bValidCol;
+ mbRowTrunc |= !bValidRow;
+ }
+ if( !bValid && bWarn )
+ {
+ mbTabTrunc |= (rScPos.Tab() > maMaxPos.Tab()); // do not warn for deleted refs
+ mrTracer.TraceInvalidAddress( rScPos, maMaxPos );
+ }
+ return bValid;
+}
+
+bool XclExpAddressConverter::ConvertAddress( XclAddress& rXclPos,
+ const ScAddress& rScPos, bool bWarn )
+{
+ bool bValid = CheckAddress( rScPos, bWarn );
+ if( bValid )
+ lclFillAddress( rXclPos, rScPos.Col(), rScPos.Row() );
+ return bValid;
+}
+
+XclAddress XclExpAddressConverter::CreateValidAddress( const ScAddress& rScPos, bool bWarn )
+{
+ XclAddress aXclPos( ScAddress::UNINITIALIZED );
+ if( !ConvertAddress( aXclPos, rScPos, bWarn ) )
+ lclFillAddress( aXclPos, ::std::min( rScPos.Col(), maMaxPos.Col() ), ::std::min( rScPos.Row(), maMaxPos.Row() ) );
+ return aXclPos;
+}
+
+// cell range -----------------------------------------------------------------
+
+bool XclExpAddressConverter::CheckRange( const ScRange& rScRange, bool bWarn )
+{
+ return CheckAddress( rScRange.aStart, bWarn ) && CheckAddress( rScRange.aEnd, bWarn );
+}
+
+bool XclExpAddressConverter::ValidateRange( ScRange& rScRange, bool bWarn )
+{
+ rScRange.PutInOrder();
+
+ // check start position
+ bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
+ if( bValidStart )
+ {
+ // check & correct end position
+ ScAddress& rScEnd = rScRange.aEnd;
+ if( !CheckAddress( rScEnd, bWarn ) )
+ {
+ rScEnd.SetCol( ::std::min( rScEnd.Col(), maMaxPos.Col() ) );
+ rScEnd.SetRow( ::std::min( rScEnd.Row(), maMaxPos.Row() ) );
+ rScEnd.SetTab( ::std::min( rScEnd.Tab(), maMaxPos.Tab() ) );
+ }
+ }
+
+ return bValidStart;
+}
+
+bool XclExpAddressConverter::ConvertRange( XclRange& rXclRange,
+ const ScRange& rScRange, bool bWarn )
+{
+ // check start position
+ bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
+ if( bValidStart )
+ {
+ lclFillAddress( rXclRange.maFirst, rScRange.aStart.Col(), rScRange.aStart.Row() );
+
+ // check & correct end position
+ SCCOL nScCol2 = rScRange.aEnd.Col();
+ SCROW nScRow2 = rScRange.aEnd.Row();
+ if( !CheckAddress( rScRange.aEnd, bWarn ) )
+ {
+ nScCol2 = ::std::min( nScCol2, maMaxPos.Col() );
+ nScRow2 = ::std::min( nScRow2, maMaxPos.Row() );
+ }
+ lclFillAddress( rXclRange.maLast, nScCol2, nScRow2 );
+ }
+ return bValidStart;
+}
+
+// cell range list ------------------------------------------------------------
+
+void XclExpAddressConverter::ValidateRangeList( ScRangeList& rScRanges, bool bWarn )
+{
+ for ( size_t nRange = rScRanges.size(); nRange > 0; )
+ {
+ ScRange & rScRange = rScRanges[ --nRange ];
+ if( !CheckRange( rScRange, bWarn ) )
+ rScRanges.Remove(nRange);
+ }
+}
+
+void XclExpAddressConverter::ConvertRangeList( XclRangeList& rXclRanges,
+ const ScRangeList& rScRanges, bool bWarn )
+{
+ rXclRanges.clear();
+ for( size_t nPos = 0, nCount = rScRanges.size(); nPos < nCount; ++nPos )
+ {
+ const ScRange & rScRange = rScRanges[ nPos ];
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ if( ConvertRange( aXclRange, rScRange, bWarn ) )
+ rXclRanges.push_back( aXclRange );
+ }
+}
+
+// EditEngine->String conversion ==============================================
+
+namespace {
+
+OUString lclGetUrlRepresentation( const SvxURLField& rUrlField )
+{
+ const OUString& aRepr = rUrlField.GetRepresentation();
+ // no representation -> use URL
+ return aRepr.isEmpty() ? rUrlField.GetURL() : aRepr;
+}
+
+} // namespace
+
+XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos ) :
+ XclExpRoot( rRoot ),
+ maScPos( rScPos ),
+ mbMultipleUrls( false )
+{
+}
+
+XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
+{
+}
+
+OUString XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField& rUrlField )
+{
+ OUString aUrlRepr;
+
+ if( GetBiff() == EXC_BIFF8 ) // no HLINK records in BIFF2-BIFF7
+ {
+ // there was/is already a HLINK record
+ mbMultipleUrls = static_cast< bool >(mxLinkRec);
+
+ mxLinkRec = new XclExpHyperlink( GetRoot(), rUrlField, maScPos );
+
+ if( const OUString* pRepr = mxLinkRec->GetRepr() )
+ aUrlRepr = *pRepr;
+
+ // add URL to note text
+ maUrlList = ScGlobal::addToken( maUrlList, rUrlField.GetURL(), '\n' );
+ }
+
+ // no hyperlink representation from Excel HLINK record -> use it from text field
+ return aUrlRepr.isEmpty() ? lclGetUrlRepresentation(rUrlField) : aUrlRepr;
+}
+
+bool XclExpHyperlinkHelper::HasLinkRecord() const
+{
+ return !mbMultipleUrls && mxLinkRec;
+}
+
+XclExpHyperlinkHelper::XclExpHyperlinkRef XclExpHyperlinkHelper::GetLinkRecord() const
+{
+ if( HasLinkRecord() )
+ return mxLinkRec;
+ return XclExpHyperlinkRef();
+}
+
+namespace {
+
+/** Creates a new formatted string from the passed unformatted string.
+
+ Creates a Unicode string or a byte string, depending on the current BIFF
+ version contained in the passed XclExpRoot object. May create a formatted
+ string object, if the text contains different script types.
+
+ @param pCellAttr
+ Cell attributes used for font formatting.
+ @param nFlags
+ Modifiers for string export.
+ @param nMaxLen
+ The maximum number of characters to store in this string.
+ @return
+ The new string object.
+ */
+XclExpStringRef lclCreateFormattedString(
+ const XclExpRoot& rRoot, const OUString& rText, const ScPatternAttr* pCellAttr,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ /* Create an empty Excel string object with correctly initialized BIFF mode,
+ because this function only uses Append() functions that require this. */
+ XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, OUString(), nFlags, nMaxLen );
+
+ // script type handling
+ Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+ // #i63255# get script type for leading weak characters
+ sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rText );
+
+ // font buffer and cell item set
+ XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
+ const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
+
+ // process all script portions
+ sal_Int32 nPortionPos = 0;
+ sal_Int32 nTextLen = rText.getLength();
+ while( nPortionPos < nTextLen )
+ {
+ // get script type and end position of next script portion
+ sal_Int16 nScript = xBreakIt->getScriptType( rText, nPortionPos );
+ sal_Int32 nPortionEnd = xBreakIt->endOfScript( rText, nPortionPos, nScript );
+
+ // reuse previous script for following weak portions
+ if( nScript == ApiScriptType::WEAK )
+ nScript = nLastScript;
+
+ // construct font from current text portion
+ SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, rItemSet, nScript ) );
+
+ // Excel start position of this portion
+ sal_Int32 nXclPortionStart = xString->Len();
+ // add portion text to Excel string
+ XclExpStringHelper::AppendString( *xString, rRoot, rText.subView( nPortionPos, nPortionEnd - nPortionPos ) );
+ if( nXclPortionStart < xString->Len() )
+ {
+ // insert font into buffer
+ sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
+ // insert font index into format run vector
+ xString->AppendFormat( nXclPortionStart, nFontIdx );
+ }
+
+ // go to next script portion
+ nLastScript = nScript;
+ nPortionPos = nPortionEnd;
+ }
+
+ return xString;
+}
+
+/** Creates a new formatted string from an edit engine text object.
+
+ Creates a Unicode string or a byte string, depending on the current BIFF
+ version contained in the passed XclExpRoot object.
+
+ @param rEE
+ The edit engine in use. The text object must already be set.
+ @param nFlags
+ Modifiers for string export.
+ @param nMaxLen
+ The maximum number of characters to store in this string.
+ @return
+ The new string object.
+ */
+XclExpStringRef lclCreateFormattedString(
+ const XclExpRoot& rRoot, EditEngine& rEE, XclExpHyperlinkHelper* pLinkHelper,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ /* Create an empty Excel string object with correctly initialized BIFF mode,
+ because this function only uses Append() functions that require this. */
+ XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, OUString(), nFlags, nMaxLen );
+
+ // font buffer and helper item set for edit engine -> Calc item conversion
+ XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aItemSet( *rRoot.GetDoc().GetPool() );
+
+ // script type handling
+ Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+ // #i63255# get script type for leading weak characters
+ sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rEE.GetText() );
+
+ // process all paragraphs
+ sal_Int32 nParaCount = rEE.GetParagraphCount();
+ for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
+ {
+ ESelection aSel( nPara, 0 );
+ OUString aParaText( rEE.GetText( nPara ) );
+
+ std::vector<sal_Int32> aPosList;
+ rEE.GetPortions( nPara, aPosList );
+
+ // process all portions in the paragraph
+ for( const auto& rPos : aPosList )
+ {
+ aSel.nEndPos = rPos;
+ OUString aXclPortionText = aParaText.copy( aSel.nStartPos, aSel.nEndPos - aSel.nStartPos );
+
+ aItemSet.ClearItem();
+ SfxItemSet aEditSet( rEE.GetAttribs( aSel ) );
+ ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
+
+ // get escapement value
+ short nEsc = aEditSet.Get( EE_CHAR_ESCAPEMENT ).GetEsc();
+
+ // process text fields
+ bool bIsHyperlink = false;
+ if( aSel.nStartPos + 1 == aSel.nEndPos )
+ {
+ // test if the character is a text field
+ if( const SvxFieldItem* pItem = aEditSet.GetItemIfSet( EE_FEATURE_FIELD, false ) )
+ {
+ const SvxFieldData* pField = pItem->GetField();
+ if( const SvxURLField* pUrlField = dynamic_cast<const SvxURLField*>( pField ) )
+ {
+ // convert URL field to string representation
+ aXclPortionText = pLinkHelper ?
+ pLinkHelper->ProcessUrlField( *pUrlField ) :
+ lclGetUrlRepresentation( *pUrlField );
+ bIsHyperlink = true;
+ }
+ else
+ {
+ OSL_FAIL( "lclCreateFormattedString - unknown text field" );
+ aXclPortionText.clear();
+ }
+ }
+ }
+
+ // Excel start position of this portion
+ sal_Int32 nXclPortionStart = xString->Len();
+ // add portion text to Excel string
+ XclExpStringHelper::AppendString( *xString, rRoot, aXclPortionText );
+ if( (nXclPortionStart < xString->Len()) || (aParaText.isEmpty()) )
+ {
+ /* Construct font from current edit engine text portion. Edit engine
+ creates different portions for different script types, no need to loop. */
+ sal_Int16 nScript = xBreakIt->getScriptType( aXclPortionText, 0 );
+ if( nScript == ApiScriptType::WEAK )
+ nScript = nLastScript;
+ SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, aItemSet, nScript ) );
+ nLastScript = nScript;
+
+ // add escapement
+ aFont.SetEscapement( nEsc );
+ // modify automatic font color for hyperlinks
+ if( bIsHyperlink && aItemSet.Get( ATTR_FONT_COLOR ).GetValue() == COL_AUTO)
+ aFont.SetColor( COL_LIGHTBLUE );
+
+ // insert font into buffer
+ sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
+ // insert font index into format run vector
+ xString->AppendFormat( nXclPortionStart, nFontIdx );
+ }
+
+ aSel.nStartPos = aSel.nEndPos;
+ }
+
+ // add trailing newline (important for correct character index calculation)
+ if( nPara + 1 < nParaCount )
+ XclExpStringHelper::AppendChar( *xString, rRoot, '\n' );
+ }
+
+ return xString;
+}
+
+} // namespace
+
+XclExpStringRef XclExpStringHelper::CreateString(
+ const XclExpRoot& rRoot, const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ XclExpStringRef xString = std::make_shared<XclExpString>();
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ xString->Assign( rString, nFlags, nMaxLen );
+ else
+ xString->AssignByte( rString, rRoot.GetTextEncoding(), nFlags, nMaxLen );
+ return xString;
+}
+
+XclExpStringRef XclExpStringHelper::CreateString(
+ const XclExpRoot& rRoot, sal_Unicode cChar, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ XclExpStringRef xString = CreateString( rRoot, OUString(), nFlags, nMaxLen );
+ AppendChar( *xString, rRoot, cChar );
+ return xString;
+}
+
+void XclExpStringHelper::AppendString( XclExpString& rXclString, const XclExpRoot& rRoot, std::u16string_view rString )
+{
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ rXclString.Append( rString );
+ else
+ rXclString.AppendByte( rString, rRoot.GetTextEncoding() );
+}
+
+void XclExpStringHelper::AppendChar( XclExpString& rXclString, const XclExpRoot& rRoot, sal_Unicode cChar )
+{
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ rXclString.Append( rtl::OUStringChar(cChar) );
+ else
+ rXclString.AppendByte( cChar, rRoot.GetTextEncoding() );
+}
+
+XclExpStringRef XclExpStringHelper::CreateCellString(
+ const XclExpRoot& rRoot, const OUString& rString, const ScPatternAttr* pCellAttr,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ return lclCreateFormattedString(rRoot, rString, pCellAttr, nFlags, nMaxLen);
+}
+
+XclExpStringRef XclExpStringHelper::CreateCellString(
+ const XclExpRoot& rRoot, const EditTextObject& rEditText, const ScPatternAttr* pCellAttr,
+ XclExpHyperlinkHelper& rLinkHelper, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ XclExpStringRef xString;
+
+ // formatted cell
+ ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
+ bool bOldUpdateMode = rEE.SetUpdateLayout( true );
+
+ // default items
+ const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
+ auto pEEItemSet = std::make_unique<SfxItemSet>( rEE.GetEmptyItemSet() );
+ ScPatternAttr::FillToEditItemSet( *pEEItemSet, rItemSet );
+ rEE.SetDefaults( std::move(pEEItemSet) ); // edit engine takes ownership
+
+ // create the string
+ rEE.SetTextCurrentDefaults(rEditText);
+ xString = lclCreateFormattedString( rRoot, rEE, &rLinkHelper, nFlags, nMaxLen );
+ rEE.SetUpdateLayout( bOldUpdateMode );
+
+ return xString;
+}
+
+XclExpStringRef XclExpStringHelper::CreateString(
+ const XclExpRoot& rRoot, const SdrTextObj& rTextObj,
+ XclStrFlags nFlags )
+{
+ XclExpStringRef xString;
+ if( const OutlinerParaObject* pParaObj = rTextObj.GetOutlinerParaObject() )
+ {
+ EditEngine& rEE = rRoot.GetDrawEditEngine();
+ bool bOldUpdateMode = rEE.SetUpdateLayout( true );
+ // create the string
+ rEE.SetText( pParaObj->GetTextObject() );
+ xString = lclCreateFormattedString( rRoot, rEE, nullptr, nFlags, EXC_STR_MAXLEN );
+ rEE.SetUpdateLayout( bOldUpdateMode );
+ // limit formats - TODO: BIFF dependent
+ if( !xString->IsEmpty() )
+ {
+ xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
+ xString->AppendTrailingFormat( EXC_FONT_APP );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "XclExpStringHelper::CreateString - textbox without para object" );
+ // create BIFF dependent empty Excel string
+ xString = CreateString( rRoot, OUString(), nFlags );
+ }
+ return xString;
+}
+
+XclExpStringRef XclExpStringHelper::CreateString(
+ const XclExpRoot& rRoot, const EditTextObject& rEditObj,
+ XclStrFlags nFlags )
+{
+ XclExpStringRef xString;
+ EditEngine& rEE = rRoot.GetDrawEditEngine();
+ bool bOldUpdateMode = rEE.SetUpdateLayout( true );
+ rEE.SetText( rEditObj );
+ xString = lclCreateFormattedString( rRoot, rEE, nullptr, nFlags, EXC_STR_MAXLEN );
+ rEE.SetUpdateLayout( bOldUpdateMode );
+ // limit formats - TODO: BIFF dependent
+ if( !xString->IsEmpty() )
+ {
+ xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
+ xString->AppendTrailingFormat( EXC_FONT_APP );
+ }
+ return xString;
+}
+
+sal_Int16 XclExpStringHelper::GetLeadingScriptType( const XclExpRoot& rRoot, const OUString& rString )
+{
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+ Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
+ sal_Int32 nStrPos = 0;
+ sal_Int32 nStrLen = rString.getLength();
+ sal_Int16 nScript = ApiScriptType::WEAK;
+ while( (nStrPos < nStrLen) && (nScript == ApiScriptType::WEAK) )
+ {
+ nScript = xBreakIt->getScriptType( rString, nStrPos );
+ nStrPos = xBreakIt->endOfScript( rString, nStrPos, nScript );
+ }
+ return (nScript == ApiScriptType::WEAK) ? rRoot.GetDefApiScript() : nScript;
+}
+
+// Header/footer conversion ===================================================
+
+XclExpHFConverter::XclExpHFConverter( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mrEE( rRoot.GetHFEditEngine() ),
+ mnTotalHeight( 0 )
+{
+}
+
+void XclExpHFConverter::GenerateString(
+ const EditTextObject* pLeftObj,
+ const EditTextObject* pCenterObj,
+ const EditTextObject* pRightObj )
+{
+ maHFString.clear();
+ mnTotalHeight = 0;
+ AppendPortion( pLeftObj, 'L' );
+ AppendPortion( pCenterObj, 'C' );
+ AppendPortion( pRightObj, 'R' );
+}
+
+void XclExpHFConverter::AppendPortion( const EditTextObject* pTextObj, sal_Unicode cPortionCode )
+{
+ if( !pTextObj ) return;
+
+ OUString aText;
+ sal_Int32 nHeight = 0;
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aItemSet( *GetDoc().GetPool() );
+
+ // edit engine
+ bool bOldUpdateMode = mrEE.SetUpdateLayout( true );
+ mrEE.SetText( *pTextObj );
+
+ // font information
+ XclFontData aFontData, aNewData;
+ if( const XclExpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
+ {
+ aFontData = pFirstFont->GetFontData();
+ aFontData.mnHeight = (aFontData.mnHeight + 10) / 20; // using pt here, not twips
+ }
+ else
+ aFontData.mnHeight = 10;
+
+ const FontList* pFontList = nullptr;
+ if( SfxObjectShell* pDocShell = GetDocShell() )
+ {
+ if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
+ pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
+ pFontList = pInfoItem->GetFontList();
+ }
+
+ sal_Int32 nParaCount = mrEE.GetParagraphCount();
+ for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
+ {
+ ESelection aSel( nPara, 0 );
+ OUStringBuffer aParaText;
+ sal_Int32 nParaHeight = 0;
+ std::vector<sal_Int32> aPosList;
+ mrEE.GetPortions( nPara, aPosList );
+
+ for( const auto& rPos : aPosList )
+ {
+ aSel.nEndPos = rPos;
+ if( aSel.nStartPos < aSel.nEndPos )
+ {
+
+// --- font attributes ---
+
+ vcl::Font aFont;
+ aItemSet.ClearItem();
+ SfxItemSet aEditSet( mrEE.GetAttribs( aSel ) );
+ ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
+ ScPatternAttr::GetFont( aFont, aItemSet, SC_AUTOCOL_RAW );
+
+ // font name and style
+ aNewData.maName = XclTools::GetXclFontName( aFont.GetFamilyName() );
+ aNewData.mnWeight = (aFont.GetWeight() > WEIGHT_NORMAL) ? EXC_FONTWGHT_BOLD : EXC_FONTWGHT_NORMAL;
+ aNewData.mbItalic = (aFont.GetItalic() != ITALIC_NONE);
+ bool bNewFont = (aFontData.maName != aNewData.maName);
+ bool bNewStyle = (aFontData.mnWeight != aNewData.mnWeight) ||
+ (aFontData.mbItalic != aNewData.mbItalic);
+ if( bNewFont || (bNewStyle && pFontList) )
+ {
+ aParaText.append("&\"" + aNewData.maName);
+ if( pFontList )
+ {
+ FontMetric aFontMetric( pFontList->Get(
+ aNewData.maName,
+ (aNewData.mnWeight > EXC_FONTWGHT_NORMAL) ? WEIGHT_BOLD : WEIGHT_NORMAL,
+ aNewData.mbItalic ? ITALIC_NORMAL : ITALIC_NONE ) );
+ aNewData.maStyle = pFontList->GetStyleName( aFontMetric );
+ if( !aNewData.maStyle.isEmpty() )
+ aParaText.append("," + aNewData.maStyle);
+ }
+ aParaText.append("\"");
+ }
+
+ // height
+ // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
+ // -> get it directly from edit engine item set
+ aNewData.mnHeight = ulimit_cast< sal_uInt16 >( aEditSet.Get( EE_CHAR_FONTHEIGHT ).GetHeight() );
+ aNewData.mnHeight = (aNewData.mnHeight + 10) / 20;
+ bool bFontHtChanged = (aFontData.mnHeight != aNewData.mnHeight);
+ if( bFontHtChanged )
+ aParaText.append("&" + OUString::number(aNewData.mnHeight));
+ // update maximum paragraph height, convert to twips
+ nParaHeight = ::std::max< sal_Int32 >( nParaHeight, aNewData.mnHeight * 20 );
+
+ // underline
+ aNewData.mnUnderline = EXC_FONTUNDERL_NONE;
+ switch( aFont.GetUnderline() )
+ {
+ case LINESTYLE_NONE: aNewData.mnUnderline = EXC_FONTUNDERL_NONE; break;
+ case LINESTYLE_SINGLE: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; break;
+ case LINESTYLE_DOUBLE: aNewData.mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
+ default: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE;
+ }
+ if( aFontData.mnUnderline != aNewData.mnUnderline )
+ {
+ sal_uInt8 nTmpUnderl = (aNewData.mnUnderline == EXC_FONTUNDERL_NONE) ?
+ aFontData.mnUnderline : aNewData.mnUnderline;
+ (nTmpUnderl == EXC_FONTUNDERL_SINGLE)? aParaText.append("&U") : aParaText.append("&E");
+ }
+
+ // font color
+ aNewData.maColor = aFont.GetColor();
+ if ( !aFontData.maColor.IsRGBEqual( aNewData.maColor ) )
+ {
+ aParaText.append("&K" + aNewData.maColor.AsRGBHexString());
+ }
+
+ // strikeout
+ aNewData.mbStrikeout = (aFont.GetStrikeout() != STRIKEOUT_NONE);
+ if( aFontData.mbStrikeout != aNewData.mbStrikeout )
+ aParaText.append("&S");
+
+ // super/sub script
+ const SvxEscapementItem& rEscapeItem = aEditSet.Get( EE_CHAR_ESCAPEMENT );
+ aNewData.SetScEscapement( rEscapeItem.GetEsc() );
+ if( aFontData.mnEscapem != aNewData.mnEscapem )
+ {
+ switch(aNewData.mnEscapem)
+ {
+ // close the previous super/sub script.
+ case EXC_FONTESC_NONE: (aFontData.mnEscapem == EXC_FONTESC_SUPER) ? aParaText.append("&X") : aParaText.append("&Y"); break;
+ case EXC_FONTESC_SUPER: aParaText.append("&X"); break;
+ case EXC_FONTESC_SUB: aParaText.append("&Y"); break;
+ default: break;
+ }
+ }
+
+ aFontData = aNewData;
+
+// --- text content or text fields ---
+
+ const SvxFieldItem* pItem;
+ if( (aSel.nStartPos + 1 == aSel.nEndPos) && // fields are single characters
+ (pItem = aEditSet.GetItemIfSet( EE_FEATURE_FIELD, false )) )
+ {
+ if( const SvxFieldData* pFieldData = pItem->GetField() )
+ {
+ if( dynamic_cast<const SvxPageField*>( pFieldData) != nullptr )
+ aParaText.append("&P");
+ else if( dynamic_cast<const SvxPagesField*>( pFieldData) != nullptr )
+ aParaText.append("&N");
+ else if( dynamic_cast<const SvxDateField*>( pFieldData) != nullptr )
+ aParaText.append("&D");
+ else if( dynamic_cast<const SvxTimeField*>( pFieldData) != nullptr || dynamic_cast<const SvxExtTimeField*>( pFieldData) != nullptr )
+ aParaText.append("&T");
+ else if( dynamic_cast<const SvxTableField*>( pFieldData) != nullptr )
+ aParaText.append("&A");
+ else if( dynamic_cast<const SvxFileField*>( pFieldData) != nullptr ) // title -> file name
+ aParaText.append("&F");
+ else if( const SvxExtFileField* pFileField = dynamic_cast<const SvxExtFileField*>( pFieldData ) )
+ {
+ switch( pFileField->GetFormat() )
+ {
+ case SvxFileFormat::NameAndExt:
+ case SvxFileFormat::NameOnly:
+ aParaText.append("&F");
+ break;
+ case SvxFileFormat::PathOnly:
+ aParaText.append("&Z");
+ break;
+ case SvxFileFormat::PathFull:
+ aParaText.append("&Z&F");
+ break;
+ default:
+ OSL_FAIL( "XclExpHFConverter::AppendPortion - unknown file field" );
+ }
+ }
+ }
+ }
+ else
+ {
+ OUString aPortionText( mrEE.GetText( aSel ) );
+ aPortionText = aPortionText.replaceAll( "&", "&&" );
+ // #i17440# space between font height and numbers in text
+ if( bFontHtChanged && aParaText.getLength() && !aPortionText.isEmpty() )
+ {
+ sal_Unicode cLast = aParaText[ aParaText.getLength() - 1 ];
+ sal_Unicode cFirst = aPortionText[0];
+ if( ('0' <= cLast) && (cLast <= '9') && ('0' <= cFirst) && (cFirst <= '9') )
+ aParaText.append(" ");
+ }
+ aParaText.append(aPortionText);
+ }
+ }
+
+ aSel.nStartPos = aSel.nEndPos;
+ }
+
+ aText = ScGlobal::addToken( aText, aParaText, '\n' );
+ aParaText.setLength(0);
+ if( nParaHeight == 0 )
+ nParaHeight = aFontData.mnHeight * 20; // points -> twips
+ nHeight += nParaHeight;
+ }
+
+ mrEE.SetUpdateLayout( bOldUpdateMode );
+
+ if( !aText.isEmpty() )
+ {
+ maHFString += "&" + OUStringChar(cPortionCode) + aText;
+ mnTotalHeight = ::std::max( mnTotalHeight, nHeight );
+ }
+}
+
+// URL conversion =============================================================
+
+namespace {
+
+/** Encodes special parts of the URL, i.e. directory separators and volume names.
+ @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
+OUString lclEncodeDosUrl(
+ XclBiff eBiff, const OUString& rUrl, std::u16string_view rBase, const OUString* pTableName)
+{
+ OUStringBuffer aBuf;
+
+ if (!rUrl.isEmpty())
+ {
+ OUString aOldUrl = rUrl;
+ aBuf.append(EXC_URLSTART_ENCODED);
+
+ if ( aOldUrl.getLength() > 2 && aOldUrl.startsWith("\\\\") )
+ {
+ // UNC
+ aBuf.append(EXC_URL_DOSDRIVE).append('@');
+ aOldUrl = aOldUrl.copy(2);
+ }
+ else if ( aOldUrl.getLength() > 2 && aOldUrl.match(":\\", 1) )
+ {
+ // drive letter
+ sal_Unicode cThisDrive = rBase.empty() ? ' ' : rBase[0];
+ sal_Unicode cDrive = aOldUrl[0];
+ if (cThisDrive == cDrive)
+ // This document and the referenced document are under the same drive.
+ aBuf.append(EXC_URL_DRIVEROOT);
+ else
+ aBuf.append(EXC_URL_DOSDRIVE).append(cDrive);
+ aOldUrl = aOldUrl.copy(3);
+ }
+ else
+ {
+ // URL probably points to a document on a Unix-like file system
+ aBuf.append(EXC_URL_DRIVEROOT);
+ }
+
+ // directories
+ sal_Int32 nPos = -1;
+ while((nPos = aOldUrl.indexOf('\\')) != -1)
+ {
+ if ( aOldUrl.startsWith("..") )
+ // parent dir (NOTE: the MS-XLS spec doesn't mention this, and
+ // Excel seems confused by this token).
+ aBuf.append(EXC_URL_PARENTDIR);
+ else
+ aBuf.append(aOldUrl.subView(0,nPos)).append(EXC_URL_SUBDIR);
+
+ aOldUrl = aOldUrl.copy(nPos + 1);
+ }
+
+ // file name
+ if (pTableName) // enclose file name in brackets if table name follows
+ aBuf.append('[').append(aOldUrl).append(']');
+ else
+ aBuf.append(aOldUrl);
+ }
+ else // empty URL -> self reference
+ {
+ switch( eBiff )
+ {
+ case EXC_BIFF5:
+ aBuf.append(pTableName ? EXC_URLSTART_SELFENCODED : EXC_URLSTART_SELF);
+ break;
+ case EXC_BIFF8:
+ DBG_ASSERT( pTableName, "lclEncodeDosUrl - sheet name required for BIFF8" );
+ aBuf.append(EXC_URLSTART_SELF);
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+ }
+
+ // table name
+ if (pTableName)
+ aBuf.append(*pTableName);
+
+ // VirtualPath must be shorter than 255 chars ([MS-XLS].pdf 2.5.277)
+ // It's better to truncate, than generate invalid file that Excel cannot open.
+ if (aBuf.getLength() > 255)
+ aBuf.setLength(255);
+
+ return aBuf.makeStringAndClear();
+}
+
+} // namespace
+
+OUString XclExpUrlHelper::EncodeUrl( const XclExpRoot& rRoot, std::u16string_view rAbsUrl, const OUString* pTableName )
+{
+ OUString aDosUrl = INetURLObject(rAbsUrl).getFSysPath(FSysStyle::Dos);
+ OUString aDosBase = INetURLObject(rRoot.GetBasePath()).getFSysPath(FSysStyle::Dos);
+ return lclEncodeDosUrl(rRoot.GetBiff(), aDosUrl, aDosBase, pTableName);
+}
+
+OUString XclExpUrlHelper::EncodeDde( std::u16string_view rApplic, std::u16string_view rTopic )
+{
+ return rApplic + OUStringChar(EXC_DDE_DELIM) + rTopic;
+}
+
+// Cached Value Lists =========================================================
+
+XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix& rMatrix )
+ : mrMatrix( rMatrix )
+{
+ mrMatrix.IncRef();
+}
+XclExpCachedMatrix::~XclExpCachedMatrix()
+{
+ mrMatrix.DecRef();
+}
+
+void XclExpCachedMatrix::GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const
+{
+ mrMatrix.GetDimensions( nCols, nRows );
+
+ OSL_ENSURE( nCols && nRows, "XclExpCachedMatrix::GetDimensions - empty matrix" );
+ OSL_ENSURE( nCols <= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
+}
+
+std::size_t XclExpCachedMatrix::GetSize() const
+{
+ SCSIZE nCols, nRows;
+
+ GetDimensions( nCols, nRows );
+
+ /* The returned size may be wrong if the matrix contains strings. The only
+ effect is that the export stream has to update a wrong record size which is
+ faster than to iterate through all cached values and calculate their sizes. */
+ return 3 + 9 * (nCols * nRows);
+}
+
+void XclExpCachedMatrix::Save( XclExpStream& rStrm ) const
+{
+ SCSIZE nCols, nRows;
+
+ GetDimensions( nCols, nRows );
+
+ if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
+ // in BIFF2-BIFF7: 256 columns represented by 0 columns
+ rStrm << static_cast< sal_uInt8 >( nCols ) << static_cast< sal_uInt16 >( nRows );
+ else
+ // in BIFF8: columns and rows decreased by 1
+ rStrm << static_cast< sal_uInt8 >( nCols - 1 ) << static_cast< sal_uInt16 >( nRows - 1 );
+
+ for( SCSIZE nRow = 0; nRow < nRows; ++nRow )
+ {
+ for( SCSIZE nCol = 0; nCol < nCols; ++nCol )
+ {
+ ScMatrixValue nMatVal = mrMatrix.Get( nCol, nRow );
+
+ FormulaError nScError;
+ if( ScMatValType::Empty == nMatVal.nType )
+ {
+ rStrm.SetSliceSize( 9 );
+ rStrm << EXC_CACHEDVAL_EMPTY;
+ rStrm.WriteZeroBytes( 8 );
+ }
+ else if( ScMatrix::IsNonValueType( nMatVal.nType ) )
+ {
+ XclExpString aStr( nMatVal.GetString().getString(), XclStrFlags::NONE );
+ rStrm.SetSliceSize( 6 );
+ rStrm << EXC_CACHEDVAL_STRING << aStr;
+ }
+ else if( ScMatValType::Boolean == nMatVal.nType )
+ {
+ sal_Int8 nBool = sal_Int8(nMatVal.GetBoolean());
+ rStrm.SetSliceSize( 9 );
+ rStrm << EXC_CACHEDVAL_BOOL << nBool;
+ rStrm.WriteZeroBytes( 7 );
+ }
+ else if( (nScError = nMatVal.GetError()) != FormulaError::NONE )
+ {
+ sal_Int8 nError ( XclTools::GetXclErrorCode( nScError ) );
+ rStrm.SetSliceSize( 9 );
+ rStrm << EXC_CACHEDVAL_ERROR << nError;
+ rStrm.WriteZeroBytes( 7 );
+ }
+ else
+ {
+ rStrm.SetSliceSize( 9 );
+ rStrm << EXC_CACHEDVAL_DOUBLE << nMatVal.fVal;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xelink.cxx b/sc/source/filter/excel/xelink.cxx
new file mode 100644
index 000000000..4bdc24335
--- /dev/null
+++ b/sc/source/filter/excel/xelink.cxx
@@ -0,0 +1,2660 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xelink.hxx>
+
+#include <algorithm>
+#include <formula/errorcodes.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <sal/log.hxx>
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <externalrefmgr.hxx>
+#include <tokenarray.hxx>
+#include <xecontent.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xllink.hxx>
+#include <xltools.hxx>
+
+#include <vector>
+#include <memory>
+#include <string_view>
+
+using ::std::unique_ptr;
+using ::std::vector;
+using ::com::sun::star::uno::Any;
+
+using namespace oox;
+
+// *** Helper classes ***
+
+// External names =============================================================
+
+namespace {
+
+/** This is a base class for any external name (i.e. add-in names or DDE links).
+ @descr Derived classes implement creation and export of the external names. */
+class XclExpExtNameBase : public XclExpRecord, protected XclExpRoot
+{
+public:
+ /** @param nFlags The flags to export. */
+ explicit XclExpExtNameBase( const XclExpRoot& rRoot,
+ const OUString& rName, sal_uInt16 nFlags = 0 );
+
+ /** Returns the name string of the external name. */
+ const OUString& GetName() const { return maName; }
+
+private:
+ /** Writes the start of the record that is equal in all EXTERNNAME records and calls WriteAddData(). */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+ /** Called to write additional data following the common record contents.
+ @descr Derived classes should overwrite this function to write their data. */
+ virtual void WriteAddData( XclExpStream& rStrm );
+
+protected:
+ OUString maName; /// Calc name (title) of the external name.
+ XclExpStringRef mxName; /// Excel name (title) of the external name.
+ sal_uInt16 mnFlags; /// Flags for record export.
+};
+
+/** Represents an EXTERNNAME record for an add-in function name. */
+class XclExpExtNameAddIn : public XclExpExtNameBase
+{
+public:
+ explicit XclExpExtNameAddIn( const XclExpRoot& rRoot, const OUString& rName );
+
+private:
+ /** Writes additional record contents. */
+ virtual void WriteAddData( XclExpStream& rStrm ) override;
+};
+
+/** Represents an EXTERNNAME record for a DDE link. */
+class XclExpExtNameDde : public XclExpExtNameBase
+{
+public:
+ explicit XclExpExtNameDde( const XclExpRoot& rRoot, const OUString& rName,
+ sal_uInt16 nFlags, const ScMatrix* pResults = nullptr );
+
+private:
+ /** Writes additional record contents. */
+ virtual void WriteAddData( XclExpStream& rStrm ) override;
+
+private:
+ typedef std::shared_ptr< XclExpCachedMatrix > XclExpCachedMatRef;
+ XclExpCachedMatRef mxMatrix; /// Cached results of the DDE link.
+};
+
+class XclExpSupbook;
+
+class XclExpExtName : public XclExpExtNameBase
+{
+public:
+ explicit XclExpExtName( const XclExpRoot& rRoot, const XclExpSupbook& rSupbook, const OUString& rName,
+ const ScExternalRefCache::TokenArrayRef& rArray );
+
+ virtual void SaveXml(XclExpXmlStream& rStrm) override;
+
+private:
+ /** Writes additional record contents. */
+ virtual void WriteAddData( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpSupbook& mrSupbook;
+ unique_ptr<ScTokenArray> mpArray;
+};
+
+// List of external names =====================================================
+
+/** List of all external names of a sheet. */
+class XclExpExtNameBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtNameBuffer( const XclExpRoot& rRoot );
+
+ /** Inserts an add-in function name
+ @return The 1-based (Excel-like) list index of the name. */
+ sal_uInt16 InsertAddIn( const OUString& rName );
+ /** InsertEuroTool */
+ sal_uInt16 InsertEuroTool( const OUString& rName );
+ /** Inserts a DDE link.
+ @return The 1-based (Excel-like) list index of the DDE link. */
+ sal_uInt16 InsertDde( std::u16string_view rApplic, std::u16string_view rTopic, const OUString& rItem );
+
+ sal_uInt16 InsertExtName( const XclExpSupbook& rSupbook, const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
+
+ /** Writes the EXTERNNAME record list. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual void SaveXml(XclExpXmlStream& rStrm) override;
+
+private:
+ /** Returns the 1-based (Excel-like) list index of the external name or 0, if not found. */
+ sal_uInt16 GetIndex( std::u16string_view rName ) const;
+ /** Appends the passed newly crested external name.
+ @return The 1-based (Excel-like) list index of the appended name. */
+ sal_uInt16 AppendNew( XclExpExtNameBase* pExtName );
+
+ XclExpRecordList< XclExpExtNameBase > maNameList; /// The list with all EXTERNNAME records.
+};
+
+// Cached external cells ======================================================
+
+/** Stores the contents of a consecutive row of external cells (record CRN). */
+class XclExpCrn : public XclExpRecord
+{
+public:
+ explicit XclExpCrn( SCCOL nScCol, SCROW nScRow, const Any& rValue );
+
+ /** Returns true, if the passed value could be appended to this record. */
+ bool InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue );
+
+ /** Writes the row and child elements. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+ static void WriteBool( XclExpStream& rStrm, bool bValue );
+ static void WriteDouble( XclExpStream& rStrm, double fValue );
+ static void WriteString( XclExpStream& rStrm, const OUString& rValue );
+ static void WriteError( XclExpStream& rStrm, sal_uInt8 nErrCode );
+ static void WriteEmpty( XclExpStream& rStrm );
+
+private:
+ typedef ::std::vector< Any > CachedValues;
+
+ CachedValues maValues; /// All cached values.
+ SCCOL mnScCol; /// Column index of the first external cell.
+ SCROW mnScRow; /// Row index of the external cells.
+};
+
+class XclExpCrnList;
+
+/** Represents the record XCT which is the header record of a CRN record list.
+ */
+class XclExpXct : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpXct( const XclExpRoot& rRoot,
+ const OUString& rTabName, sal_uInt16 nSBTab,
+ ScExternalRefCache::TableTypeRef const & xCacheTable );
+
+ /** Returns the external sheet name. */
+ const XclExpString& GetTabName() const { return maTabName; }
+
+ /** Stores all cells in the given range in the CRN list. */
+ void StoreCellRange( const ScRange& rRange );
+
+ void StoreCell_( const ScAddress& rCell );
+ void StoreCellRange_( const ScRange& rRange );
+
+ /** Writes the XCT and all CRN records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ /** Writes the sheetDataSet and child elements. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ ScExternalRefCache::TableTypeRef mxCacheTable;
+ ScMarkData maUsedCells; /// Contains addresses of all stored cells.
+ ScRange maBoundRange; /// Bounding box of maUsedCells.
+ XclExpString maTabName; /// Sheet name of the external sheet.
+ sal_uInt16 mnSBTab; /// Referred sheet index in SUPBOOK record.
+
+ /** Build the internal representation of records to be saved as BIFF or OOXML. */
+ bool BuildCrnList( XclExpCrnList& rCrnRecs );
+};
+
+// External documents (EXTERNSHEET/SUPBOOK), base class =======================
+
+/** Base class for records representing external sheets/documents.
+
+ In BIFF5/BIFF7, this record is the EXTERNSHEET record containing one sheet
+ of the own or an external document. In BIFF8, this record is the SUPBOOK
+ record representing the entire own or external document with all referenced
+ sheets.
+ */
+class XclExpExternSheetBase : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpExternSheetBase( const XclExpRoot& rRoot,
+ sal_uInt16 nRecId, sal_uInt32 nRecSize = 0 );
+
+protected:
+ /** Creates and returns the list of EXTERNNAME records. */
+ XclExpExtNameBuffer& GetExtNameBuffer();
+ /** Writes the list of EXTERNNAME records. */
+ void WriteExtNameBuffer( XclExpStream& rStrm );
+ /** Writes the list of externalName elements. */
+ void WriteExtNameBufferXml( XclExpXmlStream& rStrm );
+
+protected:
+ typedef std::shared_ptr< XclExpExtNameBuffer > XclExpExtNameBfrRef;
+ XclExpExtNameBfrRef mxExtNameBfr; /// List of EXTERNNAME records.
+};
+
+// External documents (EXTERNSHEET, BIFF5/BIFF7) ==============================
+
+/** Represents an EXTERNSHEET record containing the URL and sheet name of a sheet.
+ @descr This class is used up to BIFF7 only, writing a BIFF8 EXTERNSHEET
+ record is implemented directly in the link manager. */
+class XclExpExternSheet : public XclExpExternSheetBase
+{
+public:
+ /** Creates an EXTERNSHEET record containing a special code (i.e. own document or sheet). */
+ explicit XclExpExternSheet( const XclExpRoot& rRoot, sal_Unicode cCode );
+ /** Creates an EXTERNSHEET record referring to an internal sheet. */
+ explicit XclExpExternSheet( const XclExpRoot& rRoot, std::u16string_view rTabName );
+
+ /** Finds or inserts an EXTERNNAME record for add-ins.
+ @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */
+ sal_uInt16 InsertAddIn( const OUString& rName );
+
+ /** Writes the EXTERNSHEET and all EXTERNNAME, XCT and CRN records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ /** Initializes the record data with the passed encoded URL. */
+ void Init( std::u16string_view rEncUrl );
+ /** Writes the contents of the EXTERNSHEET record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpString maTabName; /// The name of the sheet.
+};
+
+// External documents (SUPBOOK, BIFF8) ========================================
+
+/** The SUPBOOK record contains data for an external document (URL, sheet names, external values). */
+class XclExpSupbook : public XclExpExternSheetBase
+{
+public:
+ /** Creates a SUPBOOK record for internal references. */
+ explicit XclExpSupbook( const XclExpRoot& rRoot, sal_uInt16 nXclTabCount );
+ /** Creates a SUPBOOK record for add-in functions. */
+ explicit XclExpSupbook( const XclExpRoot& rRoot );
+ /** EUROTOOL SUPBOOK */
+ explicit XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl, XclSupbookType );
+ /** Creates a SUPBOOK record for an external document. */
+ explicit XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl );
+ /** Creates a SUPBOOK record for a DDE link. */
+ explicit XclExpSupbook( const XclExpRoot& rRoot, const OUString& rApplic, const OUString& rTopic );
+
+ /** Returns true, if this SUPBOOK contains the passed URL of an external document. */
+ bool IsUrlLink( std::u16string_view rUrl ) const;
+ /** Returns true, if this SUPBOOK contains the passed DDE link. */
+ bool IsDdeLink( std::u16string_view rApplic, std::u16string_view rTopic ) const;
+ /** Fills the passed reference log entry with the URL and sheet names. */
+ void FillRefLogEntry( XclExpRefLogEntry& rRefLogEntry,
+ sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) const;
+
+ /** Stores all cells in the given range in the CRN list of the specified SUPBOOK sheet. */
+ void StoreCellRange( const ScRange& rRange, sal_uInt16 nSBTab );
+
+ void StoreCell_( sal_uInt16 nSBTab, const ScAddress& rCell );
+ void StoreCellRange_( sal_uInt16 nSBTab, const ScRange& rRange );
+
+ sal_uInt16 GetTabIndex( const OUString& rTabName ) const;
+ sal_uInt16 GetTabCount() const;
+
+ /** Inserts a new sheet name into the SUPBOOK and returns the SUPBOOK internal sheet index. */
+ sal_uInt16 InsertTabName( const OUString& rTabName, ScExternalRefCache::TableTypeRef const & xCacheTable );
+ /** Finds or inserts an EXTERNNAME record for add-ins.
+ @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */
+ sal_uInt16 InsertAddIn( const OUString& rName );
+ /** InsertEuroTool */
+ sal_uInt16 InsertEuroTool( const OUString& rName );
+ /** Finds or inserts an EXTERNNAME record for DDE links.
+ @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */
+ sal_uInt16 InsertDde( const OUString& rItem );
+
+ sal_uInt16 InsertExtName( const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
+
+ /** Get the type of record. */
+ XclSupbookType GetType() const;
+
+ /** For references to an external document, 1-based OOXML file ID. */
+ sal_uInt16 GetFileId() const;
+
+ /** For references to an external document. */
+ const OUString& GetUrl() const;
+
+ /** Writes the SUPBOOK and all EXTERNNAME, XCT and CRN records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ /** Writes the externalBook and all child elements. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Returns the sheet name inside of this SUPBOOK. */
+ const XclExpString* GetTabName( sal_uInt16 nSBTab ) const;
+
+ /** Writes the SUPBOOK record contents. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpXct > XclExpXctList;
+ typedef XclExpXctList::RecordRefType XclExpXctRef;
+
+ XclExpXctList maXctList; /// List of XCT records (which contain CRN records).
+ OUString maUrl; /// URL of the external document or application name for DDE.
+ OUString maDdeTopic; /// Topic of a DDE link.
+ XclExpString maUrlEncoded; /// Document name encoded for Excel.
+ XclSupbookType meType; /// Type of this SUPBOOK record.
+ sal_uInt16 mnXclTabCount; /// Number of internal sheets.
+ sal_uInt16 mnFileId; /// 1-based external reference file ID for OOXML
+};
+
+// All SUPBOOKS in a document =================================================
+
+/** This struct contains a sheet index range for 3D references.
+ @descr This reference consists of an index to a SUPBOOK record and indexes
+ to SUPBOOK sheet names. */
+struct XclExpXti
+{
+ sal_uInt16 mnSupbook; /// Index to SUPBOOK record.
+ sal_uInt16 mnFirstSBTab; /// Index to the first sheet of the range in the SUPBOOK.
+ sal_uInt16 mnLastSBTab; /// Index to the last sheet of the range in the SUPBOOK.
+
+ explicit XclExpXti() : mnSupbook( 0 ), mnFirstSBTab( 0 ), mnLastSBTab( 0 ) {}
+ explicit XclExpXti( sal_uInt16 nSupbook, sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) :
+ mnSupbook( nSupbook ), mnFirstSBTab( nFirstSBTab ), mnLastSBTab( nLastSBTab ) {}
+
+ /** Writes this XTI structure (inside of the EXTERNSHEET record). */
+ void Save( XclExpStream& rStrm ) const
+ { rStrm << mnSupbook << mnFirstSBTab << mnLastSBTab; }
+};
+
+bool operator==( const XclExpXti& rLeft, const XclExpXti& rRight )
+{
+ return
+ (rLeft.mnSupbook == rRight.mnSupbook) &&
+ (rLeft.mnFirstSBTab == rRight.mnFirstSBTab) &&
+ (rLeft.mnLastSBTab == rRight.mnLastSBTab);
+}
+
+/** Contains a list of all SUPBOOK records and index arrays of external sheets. */
+class XclExpSupbookBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpSupbookBuffer( const XclExpRoot& rRoot );
+
+ /** Finds SUPBOOK index and SUPBOOK sheet range from given Excel sheet range.
+ @return An XTI structure containing SUPBOOK and sheet indexes. */
+ XclExpXti GetXti( sal_uInt16 nFirstXclTab, sal_uInt16 nLastXclTab,
+ XclExpRefLogEntry* pRefLogEntry = nullptr ) const;
+
+ /** Stores all cells in the given range in a CRN record list. */
+ void StoreCellRange( const ScRange& rRange );
+
+ void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell );
+ void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange );
+
+ /** Finds or inserts an EXTERNNAME record for an add-in function name.
+ @param rnSupbook Returns the index of the SUPBOOK record which contains the add-in function name.
+ @param rnExtName Returns the 1-based EXTERNNAME record index. */
+ bool InsertAddIn(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName,
+ const OUString& rName );
+ /** InsertEuroTool */
+ bool InsertEuroTool(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName,
+ const OUString& rName );
+ /** Finds or inserts an EXTERNNAME record for DDE links.
+ @param rnSupbook Returns the index of the SUPBOOK record which contains the DDE link.
+ @param rnExtName Returns the 1-based EXTERNNAME record index. */
+ bool InsertDde(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem );
+
+ bool InsertExtName(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
+
+ XclExpXti GetXti( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ XclExpRefLogEntry* pRefLogEntry );
+
+ /** Writes all SUPBOOK records with their sub records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ /** Writes all externalBook elements with their child elements to OOXML. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ /** Whether we need to write externalReferences or not. */
+ bool HasExternalReferences() const;
+
+ struct XclExpSBIndex
+ {
+ sal_uInt16 mnSupbook; /// SUPBOOK index for an Excel sheet.
+ sal_uInt16 mnSBTab; /// Sheet name index in SUPBOOK for an Excel sheet.
+ void Set( sal_uInt16 nSupbook, sal_uInt16 nSBTab )
+ { mnSupbook = nSupbook; mnSBTab = nSBTab; }
+ };
+
+private:
+ typedef XclExpRecordList< XclExpSupbook > XclExpSupbookList;
+ typedef XclExpSupbookList::RecordRefType XclExpSupbookRef;
+
+private:
+ /** Searches for the SUPBOOK record containing the passed document URL.
+ @param rxSupbook (out-param) Returns a reference to the SUPBOOK record, or 0.
+ @param rnIndex (out-param) Returns the list index, if the SUPBOOK exists.
+ @return True, if the SUPBOOK record exists (out-parameters are valid). */
+ bool GetSupbookUrl( XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex,
+ std::u16string_view rUrl ) const;
+ /** Searches for the SUPBOOK record containing the passed DDE link.
+ @param rxSupbook (out-param) Returns a reference to the SUPBOOK record, or 0.
+ @param rnIndex (out-param) Returns the list index, if the SUPBOOK exists.
+ @return True, if the SUPBOOK record exists (out-parameters are valid). */
+ bool GetSupbookDde( XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex,
+ std::u16string_view rApplic, std::u16string_view rTopic ) const;
+
+ /** Appends a new SUPBOOK to the list.
+ @return The list index of the SUPBOOK record. */
+ sal_uInt16 Append( XclExpSupbookRef const & xSupbook );
+
+private:
+ XclExpSupbookList maSupbookList; /// List of all SUPBOOK records.
+ std::vector< XclExpSBIndex >
+ maSBIndexVec; /// SUPBOOK and sheet name index for each Excel sheet.
+ sal_uInt16 mnOwnDocSB; /// Index to SUPBOOK for own document.
+ sal_uInt16 mnAddInSB; /// Index to add-in SUPBOOK.
+};
+
+}
+
+// Export link manager ========================================================
+
+/** Abstract base class for implementation classes of the link manager. */
+class XclExpLinkManagerImpl : protected XclExpRoot
+{
+public:
+ /** Derived classes search for an EXTSHEET structure for the given Calc sheet range. */
+ virtual void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab,
+ XclExpRefLogEntry* pRefLogEntry ) = 0;
+ /** Derived classes search for a special EXTERNSHEET index for the own document. */
+ virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) = 0;
+
+ virtual void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry ) = 0;
+
+ /** Derived classes store all cells in the given range in a CRN record list. */
+ virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos ) = 0;
+
+ virtual void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos ) = 0;
+ virtual void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange ) = 0;
+
+ /** Derived classes find or insert an EXTERNNAME record for an add-in function name. */
+ virtual bool InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) = 0;
+ /** InsertEuroTool */
+ virtual bool InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) = 0;
+
+ /** Derived classes find or insert an EXTERNNAME record for DDE links. */
+ virtual bool InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem ) = 0;
+
+ virtual bool InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) = 0;
+
+ /** Derived classes write the entire link table to the passed stream. */
+ virtual void Save( XclExpStream& rStrm ) = 0;
+
+ /** Derived classes write the entire link table to the passed OOXML stream. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) = 0;
+
+protected:
+ explicit XclExpLinkManagerImpl( const XclExpRoot& rRoot );
+};
+
+namespace {
+
+/** Implementation of the link manager for BIFF5/BIFF7. */
+class XclExpLinkManagerImpl5 : public XclExpLinkManagerImpl
+{
+public:
+ explicit XclExpLinkManagerImpl5( const XclExpRoot& rRoot );
+
+ virtual void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab,
+ XclExpRefLogEntry* pRefLogEntry ) override;
+ virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) override;
+
+ virtual void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry ) override;
+
+ virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos ) override;
+
+ virtual void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos ) override;
+ virtual void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange ) override;
+
+ virtual bool InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) override;
+
+ /** InsertEuroTool */
+ virtual bool InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) override;
+
+ virtual bool InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem ) override;
+
+ virtual bool InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) override;
+
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpExternSheet > XclExpExtSheetList;
+ typedef XclExpExtSheetList::RecordRefType XclExpExtSheetRef;
+ typedef ::std::map< SCTAB, sal_uInt16 > XclExpIntTabMap;
+ typedef ::std::map< sal_Unicode, sal_uInt16 > XclExpCodeMap;
+
+private:
+ /** Returns the number of EXTERNSHEET records. */
+ sal_uInt16 GetExtSheetCount() const;
+
+ /** Appends an internal EXTERNSHEET record and returns the one-based index. */
+ sal_uInt16 AppendInternal( XclExpExtSheetRef const & xExtSheet );
+ /** Creates all EXTERNSHEET records for internal sheets on first call. */
+ void CreateInternal();
+
+ /** Returns the specified internal EXTERNSHEET record. */
+ XclExpExtSheetRef GetInternal( sal_uInt16 nExtSheet );
+ /** Returns the EXTERNSHEET index of an internal Calc sheet, or a deleted reference. */
+ XclExpExtSheetRef FindInternal( sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab, SCTAB nScTab );
+ /** Finds or creates the EXTERNSHEET index of an internal special EXTERNSHEET. */
+ XclExpExtSheetRef FindInternal( sal_uInt16& rnExtSheet, sal_Unicode cCode );
+
+private:
+ XclExpExtSheetList maExtSheetList; /// List with EXTERNSHEET records.
+ XclExpIntTabMap maIntTabMap; /// Maps internal Calc sheets to EXTERNSHEET records.
+ XclExpCodeMap maCodeMap; /// Maps special external codes to EXTERNSHEET records.
+};
+
+/** Implementation of the link manager for BIFF8 and OOXML. */
+class XclExpLinkManagerImpl8 : public XclExpLinkManagerImpl
+{
+public:
+ explicit XclExpLinkManagerImpl8( const XclExpRoot& rRoot );
+
+ virtual void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab,
+ XclExpRefLogEntry* pRefLogEntry ) override;
+ virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) override;
+
+ virtual void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry ) override;
+
+ virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos ) override;
+
+ virtual void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos ) override;
+ virtual void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange ) override;
+
+ virtual bool InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) override;
+ /** InsertEuroTool */
+ virtual bool InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) override;
+
+ virtual bool InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem ) override;
+
+ virtual bool InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) override;
+
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Searches for or inserts a new XTI structure.
+ @return The 0-based list index of the XTI structure. */
+ sal_uInt16 InsertXti( const XclExpXti& rXti );
+
+private:
+
+ XclExpSupbookBuffer maSBBuffer; /// List of all SUPBOOK records.
+ std::vector< XclExpXti > maXtiVec; /// List of XTI structures for the EXTERNSHEET record.
+};
+
+}
+
+// *** Implementation ***
+
+// Excel sheet indexes ========================================================
+
+
+XclExpTabInfo::XclExpTabInfo( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnScCnt( 0 ),
+ mnXclCnt( 0 ),
+ mnXclExtCnt( 0 ),
+ mnXclSelCnt( 0 ),
+ mnDisplXclTab( 0 ),
+ mnFirstVisXclTab( 0 )
+{
+ ScDocument& rDoc = GetDoc();
+ ScExtDocOptions& rDocOpt = GetExtDocOptions();
+
+ mnScCnt = rDoc.GetTableCount();
+
+ SCTAB nScTab;
+ SCTAB nFirstVisScTab = SCTAB_INVALID; // first visible sheet
+ SCTAB nFirstExpScTab = SCTAB_INVALID; // first exported sheet
+
+ // --- initialize the flags in the index buffer ---
+
+ maTabInfoVec.resize( mnScCnt );
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ // ignored sheets (skipped by export, with invalid Excel sheet index)
+ if( rDoc.IsScenario( nScTab ) )
+ {
+ SetFlag( nScTab, ExcTabBufFlags::Ignore );
+ }
+
+ // external sheets (skipped, but with valid Excel sheet index for ref's)
+ else if( rDoc.GetLinkMode( nScTab ) == ScLinkMode::VALUE )
+ {
+ SetFlag( nScTab, ExcTabBufFlags::Extern );
+ }
+
+ // exported sheets
+ else
+ {
+ // sheet name
+ rDoc.GetName( nScTab, maTabInfoVec[ nScTab ].maScName );
+
+ // remember first exported sheet
+ if( nFirstExpScTab == SCTAB_INVALID )
+ nFirstExpScTab = nScTab;
+ // remember first visible exported sheet
+ if( (nFirstVisScTab == SCTAB_INVALID) && rDoc.IsVisible( nScTab ) )
+ nFirstVisScTab = nScTab;
+
+ // sheet visible (only exported sheets)
+ SetFlag( nScTab, ExcTabBufFlags::Visible, rDoc.IsVisible( nScTab ) );
+
+ // sheet selected (only exported sheets)
+ if( const ScExtTabSettings* pTabSett = rDocOpt.GetTabSettings( nScTab ) )
+ SetFlag( nScTab, ExcTabBufFlags::Selected, pTabSett->mbSelected );
+
+ // sheet mirrored (only exported sheets)
+ SetFlag( nScTab, ExcTabBufFlags::Mirrored, rDoc.IsLayoutRTL( nScTab ) );
+ }
+ }
+
+ // --- visible/selected sheets ---
+
+ SCTAB nDisplScTab = rDocOpt.GetDocSettings().mnDisplTab;
+
+ // missing viewdata at embedded XLSX OLE objects
+ if (nDisplScTab == -1 )
+ nDisplScTab = rDoc.GetVisibleTab();
+
+ // find first visible exported sheet
+ if( (nFirstVisScTab == SCTAB_INVALID) || !IsExportTab( nFirstVisScTab ) )
+ {
+ // no exportable visible sheet -> use first exportable sheet
+ nFirstVisScTab = nFirstExpScTab;
+ if( (nFirstVisScTab == SCTAB_INVALID) || !IsExportTab( nFirstVisScTab ) )
+ {
+ // no exportable sheet at all -> use active sheet and export it
+ nFirstVisScTab = nDisplScTab;
+ SetFlag( nFirstVisScTab, ExcTabBufFlags::SkipMask, false ); // clear skip flags
+ }
+ SetFlag( nFirstVisScTab, ExcTabBufFlags::Visible ); // must be visible, even if originally hidden
+ }
+
+ // find currently displayed sheet
+ if( !IsExportTab( nDisplScTab ) ) // selected sheet not exported (i.e. scenario) -> use first visible
+ nDisplScTab = nFirstVisScTab;
+ SetFlag( nDisplScTab, ExcTabBufFlags::Visible | ExcTabBufFlags::Selected );
+
+ // number of selected sheets
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ if( IsSelectedTab( nScTab ) )
+ ++mnXclSelCnt;
+
+ // --- calculate resulting Excel sheet indexes ---
+
+ CalcXclIndexes();
+ mnFirstVisXclTab = GetXclTab( nFirstVisScTab );
+ mnDisplXclTab = GetXclTab( nDisplScTab );
+
+ // --- sorted vectors for index lookup ---
+
+ CalcSortedIndexes();
+}
+
+bool XclExpTabInfo::IsExportTab( SCTAB nScTab ) const
+{
+ /* Check sheet index before to avoid assertion in GetFlag(). */
+ return (nScTab < mnScCnt && nScTab >= 0) && !GetFlag( nScTab, ExcTabBufFlags::SkipMask );
+}
+
+bool XclExpTabInfo::IsExternalTab( SCTAB nScTab ) const
+{
+ /* Check sheet index before to avoid assertion (called from formula
+ compiler also for deleted references). */
+ return (nScTab < mnScCnt && nScTab >= 0) && GetFlag( nScTab, ExcTabBufFlags::Extern );
+}
+
+bool XclExpTabInfo::IsVisibleTab( SCTAB nScTab ) const
+{
+ return GetFlag( nScTab, ExcTabBufFlags::Visible );
+}
+
+bool XclExpTabInfo::IsSelectedTab( SCTAB nScTab ) const
+{
+ return GetFlag( nScTab, ExcTabBufFlags::Selected );
+}
+
+bool XclExpTabInfo::IsDisplayedTab( SCTAB nScTab ) const
+{
+ OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::IsActiveTab - sheet out of range" );
+ return GetXclTab( nScTab ) == mnDisplXclTab;
+}
+
+bool XclExpTabInfo::IsMirroredTab( SCTAB nScTab ) const
+{
+ return GetFlag( nScTab, ExcTabBufFlags::Mirrored );
+}
+
+OUString XclExpTabInfo::GetScTabName( SCTAB nScTab ) const
+{
+ OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::IsActiveTab - sheet out of range" );
+ return (nScTab < mnScCnt && nScTab >= 0) ? maTabInfoVec[ nScTab ].maScName : OUString();
+}
+
+sal_uInt16 XclExpTabInfo::GetXclTab( SCTAB nScTab ) const
+{
+ return (nScTab < mnScCnt && nScTab >= 0) ? maTabInfoVec[ nScTab ].mnXclTab : EXC_TAB_DELETED;
+}
+
+SCTAB XclExpTabInfo::GetRealScTab( SCTAB nSortedScTab ) const
+{
+ OSL_ENSURE( nSortedScTab < mnScCnt && nSortedScTab >= 0, "XclExpTabInfo::GetRealScTab - sheet out of range" );
+ return (nSortedScTab < mnScCnt && nSortedScTab >= 0) ? maFromSortedVec[ nSortedScTab ] : SCTAB_INVALID;
+}
+
+bool XclExpTabInfo::GetFlag( SCTAB nScTab, ExcTabBufFlags nFlags ) const
+{
+ OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::GetFlag - sheet out of range" );
+ return (nScTab < mnScCnt && nScTab >= 0) && (maTabInfoVec[ nScTab ].mnFlags & nFlags);
+}
+
+void XclExpTabInfo::SetFlag( SCTAB nScTab, ExcTabBufFlags nFlags, bool bSet )
+{
+ OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::SetFlag - sheet out of range" );
+ if( nScTab < mnScCnt && nScTab >= 0 )
+ {
+ if (bSet)
+ maTabInfoVec[ nScTab ].mnFlags |= nFlags;
+ else
+ maTabInfoVec[ nScTab ].mnFlags &= ~nFlags;
+ }
+}
+
+void XclExpTabInfo::CalcXclIndexes()
+{
+ sal_uInt16 nXclTab = 0;
+ SCTAB nScTab = 0;
+
+ // --- pass 1: process regular sheets ---
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ if( IsExportTab( nScTab ) )
+ {
+ maTabInfoVec[ nScTab ].mnXclTab = nXclTab;
+ ++nXclTab;
+ }
+ else
+ maTabInfoVec[ nScTab ].mnXclTab = EXC_TAB_DELETED;
+ }
+ mnXclCnt = nXclTab;
+
+ // --- pass 2: process external sheets (nXclTab continues) ---
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ if( IsExternalTab( nScTab ) )
+ {
+ maTabInfoVec[ nScTab ].mnXclTab = nXclTab;
+ ++nXclTab;
+ ++mnXclExtCnt;
+ }
+ }
+
+ // result: first occur all exported sheets, followed by all external sheets
+}
+
+typedef ::std::pair< OUString, SCTAB > XclExpTabName;
+
+namespace {
+
+struct XclExpTabNameSort {
+ bool operator ()( const XclExpTabName& rArg1, const XclExpTabName& rArg2 )
+ {
+ // compare the sheet names only
+ return ScGlobal::GetCollator().compareString( rArg1.first, rArg2.first ) < 0;
+ }
+};
+
+}
+
+void XclExpTabInfo::CalcSortedIndexes()
+{
+ ScDocument& rDoc = GetDoc();
+ ::std::vector< XclExpTabName > aVec( mnScCnt );
+ SCTAB nScTab;
+
+ // fill with sheet names
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ rDoc.GetName( nScTab, aVec[ nScTab ].first );
+ aVec[ nScTab ].second = nScTab;
+ }
+ ::std::sort( aVec.begin(), aVec.end(), XclExpTabNameSort() );
+
+ // fill index vectors from sorted sheet name vector
+ maFromSortedVec.resize( mnScCnt );
+ maToSortedVec.resize( mnScCnt );
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ maFromSortedVec[ nScTab ] = aVec[ nScTab ].second;
+ maToSortedVec[ aVec[ nScTab ].second ] = nScTab;
+ }
+}
+
+// External names =============================================================
+
+XclExpExtNameBase::XclExpExtNameBase(
+ const XclExpRoot& rRoot, const OUString& rName, sal_uInt16 nFlags ) :
+ XclExpRecord( EXC_ID_EXTERNNAME ),
+ XclExpRoot( rRoot ),
+ maName( rName ),
+ mxName( XclExpStringHelper::CreateString( rRoot, rName, XclStrFlags::EightBitLength ) ),
+ mnFlags( nFlags )
+{
+ OSL_ENSURE( maName.getLength() <= 255, "XclExpExtNameBase::XclExpExtNameBase - string too long" );
+ SetRecSize( 6 + mxName->GetSize() );
+}
+
+void XclExpExtNameBase::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnFlags
+ << sal_uInt32( 0 )
+ << *mxName;
+ WriteAddData( rStrm );
+}
+
+void XclExpExtNameBase::WriteAddData( XclExpStream& /*rStrm*/ )
+{
+}
+
+XclExpExtNameAddIn::XclExpExtNameAddIn( const XclExpRoot& rRoot, const OUString& rName ) :
+ XclExpExtNameBase( rRoot, rName )
+{
+ AddRecSize( 4 );
+}
+
+void XclExpExtNameAddIn::WriteAddData( XclExpStream& rStrm )
+{
+ // write a #REF! error formula
+ rStrm << sal_uInt16( 2 ) << EXC_TOKID_ERR << EXC_ERR_REF;
+}
+
+XclExpExtNameDde::XclExpExtNameDde( const XclExpRoot& rRoot,
+ const OUString& rName, sal_uInt16 nFlags, const ScMatrix* pResults ) :
+ XclExpExtNameBase( rRoot, rName, nFlags )
+{
+ if( pResults )
+ {
+ mxMatrix = std::make_shared<XclExpCachedMatrix>( *pResults );
+ AddRecSize( mxMatrix->GetSize() );
+ }
+}
+
+void XclExpExtNameDde::WriteAddData( XclExpStream& rStrm )
+{
+ if( mxMatrix )
+ mxMatrix->Save( rStrm );
+}
+
+XclExpExtName::XclExpExtName( const XclExpRoot& rRoot, const XclExpSupbook& rSupbook,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) :
+ XclExpExtNameBase( rRoot, rName ),
+ mrSupbook(rSupbook),
+ mpArray(rArray->Clone())
+{
+}
+
+void XclExpExtName::WriteAddData( XclExpStream& rStrm )
+{
+ // Write only if it only has a single token that is either a cell or cell
+ // range address. Excel just writes '02 00 1C 17' for all the other types
+ // of external names.
+
+ using namespace ::formula;
+ do
+ {
+ if (mpArray->GetLen() != 1)
+ break;
+
+ const formula::FormulaToken* p = mpArray->FirstToken();
+ if (!p->IsExternalRef())
+ break;
+
+ switch (p->GetType())
+ {
+ case svExternalSingleRef:
+ {
+ const ScSingleRefData& rRef = *p->GetSingleRef();
+ if (rRef.IsTabRel())
+ break;
+
+ bool bColRel = rRef.IsColRel();
+ bool bRowRel = rRef.IsRowRel();
+ sal_uInt16 nCol = static_cast<sal_uInt16>(rRef.Col());
+ sal_uInt16 nRow = static_cast<sal_uInt16>(rRef.Row());
+ if (bColRel) nCol |= 0x4000;
+ if (bRowRel) nCol |= 0x8000;
+
+ OUString aTabName = p->GetString().getString();
+ sal_uInt16 nSBTab = mrSupbook.GetTabIndex(aTabName);
+
+ // size is always 9
+ rStrm << static_cast<sal_uInt16>(9);
+ // operator token (3A for cell reference)
+ rStrm << static_cast<sal_uInt8>(0x3A);
+ // cell address (Excel's address has 2 sheet IDs.)
+ rStrm << nSBTab << nSBTab << nRow << nCol;
+ return;
+ }
+ case svExternalDoubleRef:
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ const ScSingleRefData& r1 = rRef.Ref1;
+ const ScSingleRefData& r2 = rRef.Ref2;
+ if (r1.IsTabRel() || r2.IsTabRel())
+ break;
+
+ sal_uInt16 nTab1 = r1.Tab();
+ sal_uInt16 nTab2 = r2.Tab();
+ bool bCol1Rel = r1.IsColRel();
+ bool bRow1Rel = r1.IsRowRel();
+ bool bCol2Rel = r2.IsColRel();
+ bool bRow2Rel = r2.IsRowRel();
+
+ sal_uInt16 nCol1 = static_cast<sal_uInt16>(r1.Col());
+ sal_uInt16 nCol2 = static_cast<sal_uInt16>(r2.Col());
+ sal_uInt16 nRow1 = static_cast<sal_uInt16>(r1.Row());
+ sal_uInt16 nRow2 = static_cast<sal_uInt16>(r2.Row());
+ if (bCol1Rel) nCol1 |= 0x4000;
+ if (bRow1Rel) nCol1 |= 0x8000;
+ if (bCol2Rel) nCol2 |= 0x4000;
+ if (bRow2Rel) nCol2 |= 0x8000;
+
+ OUString aTabName = p->GetString().getString();
+ sal_uInt16 nSBTab = mrSupbook.GetTabIndex(aTabName);
+
+ // size is always 13 (0x0D)
+ rStrm << static_cast<sal_uInt16>(13);
+ // operator token (3B for area reference)
+ rStrm << static_cast<sal_uInt8>(0x3B);
+ // range (area) address
+ sal_uInt16 nSBTab2 = nSBTab + nTab2 - nTab1;
+ rStrm << nSBTab << nSBTab2 << nRow1 << nRow2 << nCol1 << nCol2;
+ return;
+ }
+ default:
+ ; // nothing
+ }
+ }
+ while (false);
+
+ // special value for #REF! (02 00 1C 17)
+ rStrm << static_cast<sal_uInt16>(2) << EXC_TOKID_ERR << EXC_ERR_REF;
+}
+
+void XclExpExtName::SaveXml(XclExpXmlStream& rStrm)
+{
+ sax_fastparser::FSHelperPtr pExternalLink = rStrm.GetCurrentStream();
+
+ /* TODO: mpArray contains external references. It doesn't cause any problems, but it's enough
+ to export it without the external document identifier. */
+ if (mpArray->GetLen())
+ {
+ const OUString aFormula = XclXmlUtils::ToOUString(GetCompileFormulaContext(), ScAddress(0, 0, 0), mpArray.get());
+ pExternalLink->startElement(XML_definedName,
+ XML_name, maName.toUtf8(),
+ XML_refersTo, aFormula.toUtf8(),
+ XML_sheetId, nullptr);
+ }
+ else
+ {
+ pExternalLink->startElement(XML_definedName,
+ XML_name, maName.toUtf8(),
+ XML_refersTo, nullptr,
+ XML_sheetId, nullptr);
+ }
+
+ pExternalLink->endElement(XML_definedName);
+}
+
+// List of external names =====================================================
+
+XclExpExtNameBuffer::XclExpExtNameBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+sal_uInt16 XclExpExtNameBuffer::InsertAddIn( const OUString& rName )
+{
+ sal_uInt16 nIndex = GetIndex( rName );
+ return nIndex ? nIndex : AppendNew( new XclExpExtNameAddIn( GetRoot(), rName ) );
+}
+
+sal_uInt16 XclExpExtNameBuffer::InsertEuroTool( const OUString& rName )
+{
+ sal_uInt16 nIndex = GetIndex( rName );
+ return nIndex ? nIndex : AppendNew( new XclExpExtNameBase( GetRoot(), rName ) );
+}
+
+sal_uInt16 XclExpExtNameBuffer::InsertDde(
+ std::u16string_view rApplic, std::u16string_view rTopic, const OUString& rItem )
+{
+ sal_uInt16 nIndex = GetIndex( rItem );
+ if( nIndex == 0 )
+ {
+ size_t nPos;
+ if( GetDoc().FindDdeLink( rApplic, rTopic, rItem, SC_DDE_IGNOREMODE, nPos ) )
+ {
+ // create the leading 'StdDocumentName' EXTERNNAME record
+ if( maNameList.IsEmpty() )
+ AppendNew( new XclExpExtNameDde(
+ GetRoot(), "StdDocumentName", EXC_EXTN_EXPDDE_STDDOC ) );
+
+ // try to find DDE result array, but create EXTERNNAME record without them too
+ const ScMatrix* pScMatrix = GetDoc().GetDdeLinkResultMatrix( nPos );
+ nIndex = AppendNew( new XclExpExtNameDde( GetRoot(), rItem, EXC_EXTN_EXPDDE, pScMatrix ) );
+ }
+ }
+ return nIndex;
+}
+
+sal_uInt16 XclExpExtNameBuffer::InsertExtName( const XclExpSupbook& rSupbook,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ sal_uInt16 nIndex = GetIndex( rName );
+ return nIndex ? nIndex : AppendNew( new XclExpExtName( GetRoot(), rSupbook, rName, rArray ) );
+}
+
+void XclExpExtNameBuffer::Save( XclExpStream& rStrm )
+{
+ maNameList.Save( rStrm );
+}
+
+void XclExpExtNameBuffer::SaveXml(XclExpXmlStream& rStrm)
+{
+ maNameList.SaveXml(rStrm);
+}
+
+sal_uInt16 XclExpExtNameBuffer::GetIndex( std::u16string_view rName ) const
+{
+ for( size_t nPos = 0, nSize = maNameList.GetSize(); nPos < nSize; ++nPos )
+ if( maNameList.GetRecord( nPos )->GetName() == rName )
+ return static_cast< sal_uInt16 >( nPos + 1 );
+ return 0;
+}
+
+sal_uInt16 XclExpExtNameBuffer::AppendNew( XclExpExtNameBase* pExtName )
+{
+ size_t nSize = maNameList.GetSize();
+ if( nSize < 0x7FFF )
+ {
+ maNameList.AppendRecord( pExtName );
+ return static_cast< sal_uInt16 >( nSize + 1 );
+ }
+ return 0;
+}
+
+// Cached external cells ======================================================
+
+XclExpCrn::XclExpCrn( SCCOL nScCol, SCROW nScRow, const Any& rValue ) :
+ XclExpRecord( EXC_ID_CRN, 4 ),
+ mnScCol( nScCol ),
+ mnScRow( nScRow )
+{
+ maValues.push_back( rValue );
+}
+
+bool XclExpCrn::InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue )
+{
+ if( (nScRow != mnScRow) || (nScCol != static_cast< SCCOL >( mnScCol + maValues.size() )) )
+ return false;
+ maValues.push_back( rValue );
+ return true;
+}
+
+void XclExpCrn::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast< sal_uInt8 >( mnScCol + maValues.size() - 1 )
+ << static_cast< sal_uInt8 >( mnScCol )
+ << static_cast< sal_uInt16 >( mnScRow );
+ for( const auto& rValue : maValues )
+ {
+ if( rValue.has< bool >() )
+ WriteBool( rStrm, rValue.get< bool >() );
+ else if( rValue.has< double >() )
+ WriteDouble( rStrm, rValue.get< double >() );
+ else if( rValue.has< OUString >() )
+ WriteString( rStrm, rValue.get< OUString >() );
+ else
+ WriteEmpty( rStrm );
+ }
+}
+
+void XclExpCrn::WriteBool( XclExpStream& rStrm, bool bValue )
+{
+ rStrm << EXC_CACHEDVAL_BOOL << sal_uInt8( bValue ? 1 : 0);
+ rStrm.WriteZeroBytes( 7 );
+}
+
+void XclExpCrn::WriteDouble( XclExpStream& rStrm, double fValue )
+{
+ if( !std::isfinite( fValue ) )
+ {
+ FormulaError nScError = GetDoubleErrorValue(fValue);
+ WriteError( rStrm, XclTools::GetXclErrorCode( nScError ) );
+ }
+ else
+ {
+ rStrm << EXC_CACHEDVAL_DOUBLE << fValue;
+ }
+}
+
+void XclExpCrn::WriteString( XclExpStream& rStrm, const OUString& rValue )
+{
+ rStrm << EXC_CACHEDVAL_STRING << XclExpString( rValue );
+}
+
+void XclExpCrn::WriteError( XclExpStream& rStrm, sal_uInt8 nErrCode )
+{
+ rStrm << EXC_CACHEDVAL_ERROR << nErrCode;
+ rStrm.WriteZeroBytes( 7 );
+}
+
+void XclExpCrn::WriteEmpty( XclExpStream& rStrm )
+{
+ rStrm << EXC_CACHEDVAL_EMPTY;
+ rStrm.WriteZeroBytes( 8 );
+}
+
+void XclExpCrn::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pFS = rStrm.GetCurrentStream();
+
+ pFS->startElement(XML_row, XML_r, OString::number(mnScRow + 1));
+
+ ScAddress aAdr( mnScCol, mnScRow, 0); // Tab number doesn't matter
+ for( const auto& rValue : maValues )
+ {
+ bool bCloseCell = true;
+ if( rValue.has< double >() )
+ {
+ double fVal = rValue.get< double >();
+ if (std::isfinite( fVal))
+ {
+ // t='n' is omitted
+ pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aAdr));
+ pFS->startElement(XML_v);
+ pFS->write( fVal );
+ }
+ else
+ {
+ pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aAdr), XML_t, "e");
+ pFS->startElement(XML_v);
+ pFS->write( "#VALUE!" ); // OOXTODO: support other error values
+ }
+ }
+ else if( rValue.has< OUString >() )
+ {
+ pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aAdr), XML_t, "str");
+ pFS->startElement(XML_v);
+ pFS->write( rValue.get< OUString >() );
+ }
+ else if( rValue.has< bool >() )
+ {
+ pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aAdr), XML_t, "b");
+ pFS->startElement(XML_v);
+ pFS->write( rValue.get< bool >() ? "1" : "0" );
+ }
+ // OOXTODO: error type cell t='e'
+ else
+ {
+ // Empty/blank cell not stored, only aAdr is incremented.
+ bCloseCell = false;
+ }
+ if (bCloseCell)
+ {
+ pFS->endElement(XML_v);
+ pFS->endElement(XML_cell);
+ }
+ aAdr.IncCol();
+ }
+
+ pFS->endElement( XML_row);
+}
+
+// Cached cells of a sheet ====================================================
+
+XclExpXct::XclExpXct( const XclExpRoot& rRoot, const OUString& rTabName,
+ sal_uInt16 nSBTab, ScExternalRefCache::TableTypeRef const & xCacheTable ) :
+ XclExpRoot( rRoot ),
+ mxCacheTable( xCacheTable ),
+ maUsedCells( rRoot.GetDoc().GetSheetLimits() ),
+ maBoundRange( ScAddress::INITIALIZE_INVALID ),
+ maTabName( rTabName ),
+ mnSBTab( nSBTab )
+{
+}
+
+void XclExpXct::StoreCellRange( const ScRange& rRange )
+{
+ // #i70418# restrict size of external range to prevent memory overflow
+ if( (rRange.aEnd.Col() - rRange.aStart.Col()) * (rRange.aEnd.Row() - rRange.aStart.Row()) > 1024 )
+ return;
+
+ maUsedCells.SetMultiMarkArea( rRange );
+ maBoundRange.ExtendTo( rRange );
+}
+
+void XclExpXct::StoreCell_( const ScAddress& rCell )
+{
+ maUsedCells.SetMultiMarkArea( ScRange( rCell ) );
+ maBoundRange.ExtendTo( ScRange( rCell ) );
+}
+
+void XclExpXct::StoreCellRange_( const ScRange& rRange )
+{
+ maUsedCells.SetMultiMarkArea( rRange );
+ maBoundRange.ExtendTo( rRange );
+}
+
+namespace {
+
+class XclExpCrnList : public XclExpRecordList< XclExpCrn >
+{
+public:
+ /** Inserts the passed value into an existing or new CRN record.
+ @return True = value inserted successfully, false = CRN list is full. */
+ bool InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue );
+};
+
+bool XclExpCrnList::InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue )
+{
+ RecordRefType xLastRec = GetLastRecord();
+ if( xLastRec && xLastRec->InsertValue( nScCol, nScRow, rValue ) )
+ return true;
+ if( GetSize() == SAL_MAX_UINT16 )
+ return false;
+ AppendNewRecord( new XclExpCrn( nScCol, nScRow, rValue ) );
+ return true;
+}
+
+} // namespace
+
+bool XclExpXct::BuildCrnList( XclExpCrnList& rCrnRecs )
+{
+ if( !mxCacheTable )
+ return false;
+
+ /* Get the range of used rows in the cache table. This may help to
+ optimize building the CRN record list if the cache table does not
+ contain all referred cells, e.g. if big empty ranges are used in the
+ formulas. */
+ ::std::pair< SCROW, SCROW > aRowRange = mxCacheTable->getRowRange();
+ if( aRowRange.first >= aRowRange.second )
+ return false;
+
+ /* Crop the bounding range of used cells in this table to Excel limits.
+ Return if there is no external cell inside these limits. */
+ if( !GetAddressConverter().ValidateRange( maBoundRange, false ) )
+ return false;
+
+ /* Find the resulting row range that needs to be processed. */
+ SCROW nScRow1 = ::std::max( aRowRange.first, maBoundRange.aStart.Row() );
+ SCROW nScRow2 = ::std::min( aRowRange.second - 1, maBoundRange.aEnd.Row() );
+ if( nScRow1 > nScRow2 )
+ return false;
+
+ /* Build and collect all CRN records before writing the XCT record. This
+ is needed to determine the total number of CRN records which must be
+ known when writing the XCT record (possibly encrypted, so seeking the
+ output stream back after writing the CRN records is not an option). */
+ SvNumberFormatter& rFormatter = GetFormatter();
+ bool bValid = true;
+ for( SCROW nScRow = nScRow1; bValid && (nScRow <= nScRow2); ++nScRow )
+ {
+ ::std::pair< SCCOL, SCCOL > aColRange = mxCacheTable->getColRange( nScRow );
+ const SCCOL nScEnd = ::std::min( aColRange.second, GetDoc().GetSheetLimits().GetMaxColCount() );
+ for( SCCOL nScCol = aColRange.first; bValid && (nScCol < nScEnd); ++nScCol )
+ {
+ if( maUsedCells.IsCellMarked( nScCol, nScRow, true ) )
+ {
+ sal_uInt32 nScNumFmt = 0;
+ ScExternalRefCache::TokenRef xToken = mxCacheTable->getCell( nScCol, nScRow, &nScNumFmt );
+ using namespace ::formula;
+ if( xToken )
+ switch( xToken->GetType() )
+ {
+ case svDouble:
+ bValid = (rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL) ?
+ rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetDouble() != 0 ) ) :
+ rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetDouble() ) );
+ break;
+ case svString:
+ // do not save empty strings (empty cells) to cache
+ if( !xToken->GetString().isEmpty() )
+ bValid = rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetString().getString() ) );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void XclExpXct::Save( XclExpStream& rStrm )
+{
+ XclExpCrnList aCrnRecs;
+ if (!BuildCrnList( aCrnRecs))
+ return;
+
+ // write the XCT record and the list of CRN records
+ rStrm.StartRecord( EXC_ID_XCT, 4 );
+ rStrm << static_cast< sal_uInt16 >( aCrnRecs.GetSize() ) << mnSBTab;
+ rStrm.EndRecord();
+ aCrnRecs.Save( rStrm );
+}
+
+void XclExpXct::SaveXml( XclExpXmlStream& rStrm )
+{
+ XclExpCrnList aCrnRecs;
+
+ sax_fastparser::FSHelperPtr pFS = rStrm.GetCurrentStream();
+
+ bool bValid = BuildCrnList( aCrnRecs);
+ pFS->startElement(XML_sheetData, XML_sheetId, OString::number(mnSBTab));
+ if (bValid)
+ {
+ // row elements
+ aCrnRecs.SaveXml( rStrm );
+ }
+ pFS->endElement( XML_sheetData);
+}
+
+// External documents (EXTERNSHEET/SUPBOOK), base class =======================
+
+XclExpExternSheetBase::XclExpExternSheetBase( const XclExpRoot& rRoot, sal_uInt16 nRecId, sal_uInt32 nRecSize ) :
+ XclExpRecord( nRecId, nRecSize ),
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpExtNameBuffer& XclExpExternSheetBase::GetExtNameBuffer()
+{
+ if( !mxExtNameBfr )
+ mxExtNameBfr = std::make_shared<XclExpExtNameBuffer>( GetRoot() );
+ return *mxExtNameBfr;
+}
+
+void XclExpExternSheetBase::WriteExtNameBuffer( XclExpStream& rStrm )
+{
+ if( mxExtNameBfr )
+ mxExtNameBfr->Save( rStrm );
+}
+
+void XclExpExternSheetBase::WriteExtNameBufferXml( XclExpXmlStream& rStrm )
+{
+ if( mxExtNameBfr )
+ mxExtNameBfr->SaveXml( rStrm );
+}
+
+// External documents (EXTERNSHEET, BIFF5/BIFF7) ==============================
+
+XclExpExternSheet::XclExpExternSheet( const XclExpRoot& rRoot, sal_Unicode cCode ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_EXTERNSHEET )
+{
+ Init( OUStringChar(cCode) );
+}
+
+XclExpExternSheet::XclExpExternSheet( const XclExpRoot& rRoot, std::u16string_view rTabName ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_EXTERNSHEET )
+{
+ // reference to own sheet: \03<sheetname>
+ Init(OUStringConcatenation(OUStringChar(EXC_EXTSH_TABNAME) + rTabName));
+}
+
+void XclExpExternSheet::Save( XclExpStream& rStrm )
+{
+ // EXTERNSHEET record
+ XclExpRecord::Save( rStrm );
+ // EXTERNNAME records
+ WriteExtNameBuffer( rStrm );
+}
+
+void XclExpExternSheet::Init( std::u16string_view rEncUrl )
+{
+ OSL_ENSURE_BIFF( GetBiff() <= EXC_BIFF5 );
+ maTabName.AssignByte( rEncUrl, GetTextEncoding(), XclStrFlags::EightBitLength );
+ SetRecSize( maTabName.GetSize() );
+}
+
+sal_uInt16 XclExpExternSheet::InsertAddIn( const OUString& rName )
+{
+ return GetExtNameBuffer().InsertAddIn( rName );
+}
+
+void XclExpExternSheet::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt8 nNameSize = static_cast< sal_uInt8 >( maTabName.Len() );
+ // special case: reference to own sheet (starting with '\03') needs wrong string length
+ if( maTabName.GetChar( 0 ) == EXC_EXTSH_TABNAME )
+ --nNameSize;
+ rStrm << nNameSize;
+ maTabName.WriteBuffer( rStrm );
+}
+
+// External document (SUPBOOK, BIFF8) =========================================
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, sal_uInt16 nXclTabCount ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ),
+ meType( XclSupbookType::Self ),
+ mnXclTabCount( nXclTabCount ),
+ mnFileId( 0 )
+{
+}
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ),
+ meType( XclSupbookType::Addin ),
+ mnXclTabCount( 1 ),
+ mnFileId( 0 )
+{
+}
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl, XclSupbookType ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK ),
+ maUrl( rUrl ),
+ maUrlEncoded( rUrl ),
+ meType( XclSupbookType::Eurotool ),
+ mnXclTabCount( 0 ),
+ mnFileId( 0 )
+{
+ SetRecSize( 2 + maUrlEncoded.GetSize() );
+}
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK ),
+ maUrl( rUrl ),
+ maUrlEncoded( XclExpUrlHelper::EncodeUrl( rRoot, rUrl ) ),
+ meType( XclSupbookType::Extern ),
+ mnXclTabCount( 0 ),
+ mnFileId( 0 )
+{
+ SetRecSize( 2 + maUrlEncoded.GetSize() );
+
+ // We need to create all tables up front to ensure the correct table order.
+ ScExternalRefManager* pRefMgr = rRoot.GetDoc().GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId( rUrl );
+ mnFileId = nFileId + 1;
+ ScfStringVec aTabNames;
+ pRefMgr->getAllCachedTableNames( nFileId, aTabNames );
+ size_t nTabIndex = 0;
+ for( const auto& rTabName : aTabNames )
+ {
+ InsertTabName( rTabName, pRefMgr->getCacheTable( nFileId, nTabIndex ) );
+ ++nTabIndex;
+ }
+}
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rApplic, const OUString& rTopic ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ),
+ maUrl( rApplic ),
+ maDdeTopic( rTopic ),
+ maUrlEncoded( XclExpUrlHelper::EncodeDde( rApplic, rTopic ) ),
+ meType( XclSupbookType::Special ),
+ mnXclTabCount( 0 ),
+ mnFileId( 0 )
+{
+ SetRecSize( 2 + maUrlEncoded.GetSize() );
+}
+
+bool XclExpSupbook::IsUrlLink( std::u16string_view rUrl ) const
+{
+ return (meType == XclSupbookType::Extern || meType == XclSupbookType::Eurotool) && (maUrl == rUrl);
+}
+
+bool XclExpSupbook::IsDdeLink( std::u16string_view rApplic, std::u16string_view rTopic ) const
+{
+ return (meType == XclSupbookType::Special) && (maUrl == rApplic) && (maDdeTopic == rTopic);
+}
+
+void XclExpSupbook::FillRefLogEntry( XclExpRefLogEntry& rRefLogEntry,
+ sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) const
+{
+ rRefLogEntry.mpUrl = maUrlEncoded.IsEmpty() ? nullptr : &maUrlEncoded;
+ rRefLogEntry.mpFirstTab = GetTabName( nFirstSBTab );
+ rRefLogEntry.mpLastTab = GetTabName( nLastSBTab );
+}
+
+void XclExpSupbook::StoreCellRange( const ScRange& rRange, sal_uInt16 nSBTab )
+{
+ if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ) )
+ pXct->StoreCellRange( rRange );
+}
+
+void XclExpSupbook::StoreCell_( sal_uInt16 nSBTab, const ScAddress& rCell )
+{
+ if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ) )
+ pXct->StoreCell_( rCell );
+}
+
+void XclExpSupbook::StoreCellRange_( sal_uInt16 nSBTab, const ScRange& rRange )
+{
+ // multi-table range is not allowed!
+ if( rRange.aStart.Tab() == rRange.aEnd.Tab() )
+ if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ) )
+ pXct->StoreCellRange_( rRange );
+}
+
+sal_uInt16 XclExpSupbook::GetTabIndex( const OUString& rTabName ) const
+{
+ XclExpString aXclName(rTabName);
+ size_t nSize = maXctList.GetSize();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ XclExpXctRef aRec = maXctList.GetRecord(i);
+ if (aXclName == aRec->GetTabName())
+ return ulimit_cast<sal_uInt16>(i);
+ }
+ return EXC_NOTAB;
+}
+
+sal_uInt16 XclExpSupbook::GetTabCount() const
+{
+ return ulimit_cast<sal_uInt16>(maXctList.GetSize());
+}
+
+sal_uInt16 XclExpSupbook::InsertTabName( const OUString& rTabName, ScExternalRefCache::TableTypeRef const & xCacheTable )
+{
+ SAL_WARN_IF( meType != XclSupbookType::Extern, "sc.filter", "Don't insert sheet names here" );
+ sal_uInt16 nSBTab = ulimit_cast< sal_uInt16 >( maXctList.GetSize() );
+ XclExpXctRef xXct = new XclExpXct( GetRoot(), rTabName, nSBTab, xCacheTable );
+ AddRecSize( xXct->GetTabName().GetSize() );
+ maXctList.AppendRecord( xXct );
+ return nSBTab;
+}
+
+sal_uInt16 XclExpSupbook::InsertAddIn( const OUString& rName )
+{
+ return GetExtNameBuffer().InsertAddIn( rName );
+}
+
+sal_uInt16 XclExpSupbook::InsertEuroTool( const OUString& rName )
+{
+ return GetExtNameBuffer().InsertEuroTool( rName );
+}
+
+sal_uInt16 XclExpSupbook::InsertDde( const OUString& rItem )
+{
+ return GetExtNameBuffer().InsertDde( maUrl, maDdeTopic, rItem );
+}
+
+sal_uInt16 XclExpSupbook::InsertExtName( const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ return GetExtNameBuffer().InsertExtName(*this, rName, rArray);
+}
+
+XclSupbookType XclExpSupbook::GetType() const
+{
+ return meType;
+}
+
+sal_uInt16 XclExpSupbook::GetFileId() const
+{
+ return mnFileId;
+}
+
+const OUString& XclExpSupbook::GetUrl() const
+{
+ return maUrl;
+}
+
+void XclExpSupbook::Save( XclExpStream& rStrm )
+{
+ // SUPBOOK record
+ XclExpRecord::Save( rStrm );
+ // XCT record, CRN records
+ maXctList.Save( rStrm );
+ // EXTERNNAME records
+ WriteExtNameBuffer( rStrm );
+}
+
+void XclExpSupbook::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pExternalLink = rStrm.GetCurrentStream();
+
+ // Add relation for this stream, e.g. xl/externalLinks/_rels/externalLink1.xml.rels
+ sal_uInt16 nLevel = 0;
+ bool bRel = true;
+
+ // BuildFileName delete ../ and convert them to nLevel
+ // but addrelation needs ../ instead of nLevel, so we have to convert it back
+ OUString sFile = XclExpHyperlink::BuildFileName(nLevel, bRel, maUrl, GetRoot(), true);
+ while (nLevel-- > 0)
+ sFile = "../" + sFile;
+
+ OUString sId = rStrm.addRelation( pExternalLink->getOutputStream(),
+ oox::getRelationship(Relationship::EXTERNALLINKPATH), sFile, true );
+
+ pExternalLink->startElement( XML_externalLink,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8());
+
+ pExternalLink->startElement( XML_externalBook,
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ FSNS(XML_r, XML_id), sId.toUtf8());
+
+ if (!maXctList.IsEmpty())
+ {
+ pExternalLink->startElement(XML_sheetNames);
+ for (size_t nPos = 0, nSize = maXctList.GetSize(); nPos < nSize; ++nPos)
+ {
+ pExternalLink->singleElement(XML_sheetName,
+ XML_val, XclXmlUtils::ToOString(maXctList.GetRecord(nPos)->GetTabName()));
+ }
+ pExternalLink->endElement( XML_sheetNames);
+
+ }
+
+ if (mxExtNameBfr)
+ {
+ pExternalLink->startElement(XML_definedNames);
+ // externalName elements
+ WriteExtNameBufferXml( rStrm );
+ pExternalLink->endElement(XML_definedNames);
+ }
+
+ if (!maXctList.IsEmpty())
+ {
+ pExternalLink->startElement(XML_sheetDataSet);
+
+ // sheetData elements
+ maXctList.SaveXml( rStrm );
+
+ pExternalLink->endElement( XML_sheetDataSet);
+
+ }
+ pExternalLink->endElement( XML_externalBook);
+ pExternalLink->endElement( XML_externalLink);
+}
+
+const XclExpString* XclExpSupbook::GetTabName( sal_uInt16 nSBTab ) const
+{
+ XclExpXctRef xXct = maXctList.GetRecord( nSBTab );
+ return xXct ? &xXct->GetTabName() : nullptr;
+}
+
+void XclExpSupbook::WriteBody( XclExpStream& rStrm )
+{
+ switch( meType )
+ {
+ case XclSupbookType::Self:
+ rStrm << mnXclTabCount << EXC_SUPB_SELF;
+ break;
+ case XclSupbookType::Extern:
+ case XclSupbookType::Special:
+ case XclSupbookType::Eurotool:
+ {
+ sal_uInt16 nCount = ulimit_cast< sal_uInt16 >( maXctList.GetSize() );
+ rStrm << nCount << maUrlEncoded;
+
+ for( size_t nPos = 0, nSize = maXctList.GetSize(); nPos < nSize; ++nPos )
+ rStrm << maXctList.GetRecord( nPos )->GetTabName();
+ }
+ break;
+ case XclSupbookType::Addin:
+ rStrm << mnXclTabCount << EXC_SUPB_ADDIN;
+ break;
+ default:
+ SAL_WARN( "sc.filter", "Unhandled SUPBOOK type " << meType);
+ }
+}
+
+// All SUPBOOKS in a document =================================================
+
+XclExpSupbookBuffer::XclExpSupbookBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnOwnDocSB( SAL_MAX_UINT16 ),
+ mnAddInSB( SAL_MAX_UINT16 )
+{
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+ sal_uInt16 nXclCnt = rTabInfo.GetXclTabCount();
+ sal_uInt16 nCodeCnt = static_cast< sal_uInt16 >( GetExtDocOptions().GetCodeNameCount() );
+ size_t nCount = nXclCnt + rTabInfo.GetXclExtTabCount();
+
+ OSL_ENSURE( nCount > 0, "XclExpSupbookBuffer::XclExpSupbookBuffer - no sheets to export" );
+ if( nCount )
+ {
+ maSBIndexVec.resize( nCount );
+
+ // self-ref SUPBOOK first of list
+ XclExpSupbookRef xSupbook = new XclExpSupbook( GetRoot(), ::std::max( nXclCnt, nCodeCnt ) );
+ mnOwnDocSB = Append( xSupbook );
+ for( sal_uInt16 nXclTab = 0; nXclTab < nXclCnt; ++nXclTab )
+ maSBIndexVec[ nXclTab ].Set( mnOwnDocSB, nXclTab );
+ }
+}
+
+XclExpXti XclExpSupbookBuffer::GetXti( sal_uInt16 nFirstXclTab, sal_uInt16 nLastXclTab,
+ XclExpRefLogEntry* pRefLogEntry ) const
+{
+ XclExpXti aXti;
+ size_t nSize = maSBIndexVec.size();
+ if( (nFirstXclTab < nSize) && (nLastXclTab < nSize) )
+ {
+ // index of the SUPBOOK record
+ aXti.mnSupbook = maSBIndexVec[ nFirstXclTab ].mnSupbook;
+
+ // all sheets in the same supbook?
+ bool bSameSB = true;
+ for( sal_uInt16 nXclTab = nFirstXclTab + 1; bSameSB && (nXclTab <= nLastXclTab); ++nXclTab )
+ {
+ bSameSB = maSBIndexVec[ nXclTab ].mnSupbook == aXti.mnSupbook;
+ if( !bSameSB )
+ nLastXclTab = nXclTab - 1;
+ }
+ aXti.mnFirstSBTab = maSBIndexVec[ nFirstXclTab ].mnSBTab;
+ aXti.mnLastSBTab = maSBIndexVec[ nLastXclTab ].mnSBTab;
+
+ // fill external reference log entry (for change tracking)
+ if( pRefLogEntry )
+ {
+ pRefLogEntry->mnFirstXclTab = nFirstXclTab;
+ pRefLogEntry->mnLastXclTab = nLastXclTab;
+ XclExpSupbookRef xSupbook = maSupbookList.GetRecord( aXti.mnSupbook );
+ if( xSupbook )
+ xSupbook->FillRefLogEntry( *pRefLogEntry, aXti.mnFirstSBTab, aXti.mnLastSBTab );
+ }
+ }
+ else
+ {
+ // special range, i.e. for deleted sheets or add-ins
+ aXti.mnSupbook = mnOwnDocSB;
+ aXti.mnFirstSBTab = nFirstXclTab;
+ aXti.mnLastSBTab = nLastXclTab;
+ }
+
+ return aXti;
+}
+
+void XclExpSupbookBuffer::StoreCellRange( const ScRange& rRange )
+{
+ sal_uInt16 nXclTab = GetTabInfo().GetXclTab( rRange.aStart.Tab() );
+ if( nXclTab < maSBIndexVec.size() )
+ {
+ const XclExpSBIndex& rSBIndex = maSBIndexVec[ nXclTab ];
+ XclExpSupbookRef xSupbook = maSupbookList.GetRecord( rSBIndex.mnSupbook );
+ OSL_ENSURE( xSupbook , "XclExpSupbookBuffer::StoreCellRange - missing SUPBOOK record" );
+ if( xSupbook )
+ xSupbook->StoreCellRange( rRange, rSBIndex.mnSBTab );
+ }
+}
+
+namespace {
+
+class FindSBIndexEntry
+{
+public:
+ explicit FindSBIndexEntry(sal_uInt16 nSupbookId, sal_uInt16 nTabId) :
+ mnSupbookId(nSupbookId), mnTabId(nTabId) {}
+
+ bool operator()(const XclExpSupbookBuffer::XclExpSBIndex& r) const
+ {
+ return mnSupbookId == r.mnSupbook && mnTabId == r.mnSBTab;
+ }
+
+private:
+ sal_uInt16 mnSupbookId;
+ sal_uInt16 mnTabId;
+};
+
+}
+
+void XclExpSupbookBuffer::StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell )
+{
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
+ if (!pUrl)
+ return;
+
+ XclExpSupbookRef xSupbook;
+ sal_uInt16 nSupbookId;
+ if (!GetSupbookUrl(xSupbook, nSupbookId, *pUrl))
+ {
+ xSupbook = new XclExpSupbook(GetRoot(), *pUrl);
+ nSupbookId = Append(xSupbook);
+ }
+
+ sal_uInt16 nSheetId = xSupbook->GetTabIndex(rTabName);
+ if (nSheetId == EXC_NOTAB)
+ // specified table name not found in this SUPBOOK.
+ return;
+
+ FindSBIndexEntry f(nSupbookId, nSheetId);
+ if (::std::none_of(maSBIndexVec.begin(), maSBIndexVec.end(), f))
+ {
+ maSBIndexVec.emplace_back();
+ XclExpSBIndex& r = maSBIndexVec.back();
+ r.mnSupbook = nSupbookId;
+ r.mnSBTab = nSheetId;
+ }
+
+ xSupbook->StoreCell_(nSheetId, rCell);
+}
+
+void XclExpSupbookBuffer::StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange )
+{
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
+ if (!pUrl)
+ return;
+
+ XclExpSupbookRef xSupbook;
+ sal_uInt16 nSupbookId;
+ if (!GetSupbookUrl(xSupbook, nSupbookId, *pUrl))
+ {
+ xSupbook = new XclExpSupbook(GetRoot(), *pUrl);
+ nSupbookId = Append(xSupbook);
+ }
+
+ SCTAB nTabCount = rRange.aEnd.Tab() - rRange.aStart.Tab() + 1;
+
+ // If this is a multi-table range, get token for each table.
+ using namespace ::formula;
+ SCTAB aMatrixListSize = 0;
+
+ // This is a new'ed instance, so we must manage its life cycle here.
+ ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(nFileId, rTabName, rRange, nullptr);
+ if (!pArray)
+ return;
+
+ FormulaTokenArrayPlainIterator aIter(*pArray);
+ for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ if (p->GetType() == svMatrix)
+ ++aMatrixListSize;
+ else if (p->GetOpCode() != ocSep)
+ {
+ // This is supposed to be ocSep!!!
+ return;
+ }
+ }
+
+ if (aMatrixListSize != nTabCount)
+ {
+ // matrix size mismatch!
+ return;
+ }
+
+ sal_uInt16 nFirstSheetId = xSupbook->GetTabIndex(rTabName);
+
+ ScRange aRange(rRange);
+ aRange.aStart.SetTab(0);
+ aRange.aEnd.SetTab(0);
+ for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
+ {
+ sal_uInt16 nSheetId = nFirstSheetId + static_cast<sal_uInt16>(nTab);
+ FindSBIndexEntry f(nSupbookId, nSheetId);
+ if (::std::none_of(maSBIndexVec.begin(), maSBIndexVec.end(), f))
+ {
+ maSBIndexVec.emplace_back();
+ XclExpSBIndex& r = maSBIndexVec.back();
+ r.mnSupbook = nSupbookId;
+ r.mnSBTab = nSheetId;
+ }
+
+ xSupbook->StoreCellRange_(nSheetId, aRange);
+ }
+}
+
+bool XclExpSupbookBuffer::InsertAddIn(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const OUString& rName )
+{
+ XclExpSupbookRef xSupbook;
+ if( mnAddInSB == SAL_MAX_UINT16 )
+ {
+ xSupbook = new XclExpSupbook( GetRoot() );
+ mnAddInSB = Append( xSupbook );
+ }
+ else
+ xSupbook = maSupbookList.GetRecord( mnAddInSB );
+ OSL_ENSURE( xSupbook, "XclExpSupbookBuffer::InsertAddin - missing add-in supbook" );
+ rnSupbook = mnAddInSB;
+ rnExtName = xSupbook->InsertAddIn( rName );
+ return rnExtName > 0;
+}
+
+bool XclExpSupbookBuffer::InsertEuroTool(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const OUString& rName )
+{
+ XclExpSupbookRef xSupbook;
+ OUString aUrl( "\001\010EUROTOOL.XLA" );
+ if( !GetSupbookUrl( xSupbook, rnSupbook, aUrl ) )
+ {
+ xSupbook = new XclExpSupbook( GetRoot(), aUrl, XclSupbookType::Eurotool );
+ rnSupbook = Append( xSupbook );
+ }
+ rnExtName = xSupbook->InsertEuroTool( rName );
+ return rnExtName > 0;
+}
+
+bool XclExpSupbookBuffer::InsertDde(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem )
+{
+ XclExpSupbookRef xSupbook;
+ if( !GetSupbookDde( xSupbook, rnSupbook, rApplic, rTopic ) )
+ {
+ xSupbook = new XclExpSupbook( GetRoot(), rApplic, rTopic );
+ rnSupbook = Append( xSupbook );
+ }
+ rnExtName = xSupbook->InsertDde( rItem );
+ return rnExtName > 0;
+}
+
+bool XclExpSupbookBuffer::InsertExtName(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ XclExpSupbookRef xSupbook;
+ if (!GetSupbookUrl(xSupbook, rnSupbook, rUrl))
+ {
+ xSupbook = new XclExpSupbook(GetRoot(), rUrl);
+ rnSupbook = Append(xSupbook);
+ }
+ rnExtName = xSupbook->InsertExtName(rName, rArray);
+ return rnExtName > 0;
+}
+
+XclExpXti XclExpSupbookBuffer::GetXti( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ XclExpRefLogEntry* pRefLogEntry )
+{
+ XclExpXti aXti(0, EXC_NOTAB, EXC_NOTAB);
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
+ if (!pUrl)
+ return aXti;
+
+ XclExpSupbookRef xSupbook;
+ sal_uInt16 nSupbookId;
+ if (!GetSupbookUrl(xSupbook, nSupbookId, *pUrl))
+ {
+ xSupbook = new XclExpSupbook(GetRoot(), *pUrl);
+ nSupbookId = Append(xSupbook);
+ }
+ aXti.mnSupbook = nSupbookId;
+
+ sal_uInt16 nFirstSheetId = xSupbook->GetTabIndex(rTabName);
+ if (nFirstSheetId == EXC_NOTAB)
+ {
+ // first sheet not found in SUPBOOK.
+ return aXti;
+ }
+ sal_uInt16 nSheetCount = xSupbook->GetTabCount();
+ for (sal_uInt16 i = 0; i < nXclTabSpan; ++i)
+ {
+ sal_uInt16 nSheetId = nFirstSheetId + i;
+ if (nSheetId >= nSheetCount)
+ return aXti;
+
+ FindSBIndexEntry f(nSupbookId, nSheetId);
+ if (::std::none_of(maSBIndexVec.begin(), maSBIndexVec.end(), f))
+ {
+ maSBIndexVec.emplace_back();
+ XclExpSBIndex& r = maSBIndexVec.back();
+ r.mnSupbook = nSupbookId;
+ r.mnSBTab = nSheetId;
+ }
+ if (i == 0)
+ aXti.mnFirstSBTab = nSheetId;
+ if (i == nXclTabSpan - 1)
+ aXti.mnLastSBTab = nSheetId;
+ }
+
+ if (pRefLogEntry)
+ {
+ pRefLogEntry->mnFirstXclTab = 0;
+ pRefLogEntry->mnLastXclTab = 0;
+ if (xSupbook)
+ xSupbook->FillRefLogEntry(*pRefLogEntry, aXti.mnFirstSBTab, aXti.mnLastSBTab);
+ }
+
+ return aXti;
+}
+
+void XclExpSupbookBuffer::Save( XclExpStream& rStrm )
+{
+ maSupbookList.Save( rStrm );
+}
+
+void XclExpSupbookBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ // Unused external references are not saved, only kept in memory.
+ // Those that are saved must be indexed from 1, so indexes must be reordered
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ vector<sal_uInt16> aExternFileIds;
+ for (size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos)
+ {
+ XclExpSupbookRef xRef(maSupbookList.GetRecord(nPos));
+ // fileIDs are indexed from 1 in xlsx, and from 0 in ScExternalRefManager
+ // converting between them require a -1 or +1
+ if (xRef->GetType() == XclSupbookType::Extern)
+ aExternFileIds.push_back(xRef->GetFileId() - 1);
+ }
+ if (aExternFileIds.size() > 0)
+ pRefMgr->setSkipUnusedFileIds(aExternFileIds);
+
+ ::std::map< sal_uInt16, OUString > aMap;
+ for (size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos)
+ {
+ XclExpSupbookRef xRef( maSupbookList.GetRecord( nPos));
+ if (xRef->GetType() != XclSupbookType::Extern)
+ continue; // handle only external reference (for now?)
+
+ sal_uInt16 nId = xRef->GetFileId();
+ sal_uInt16 nUsedId = pRefMgr->convertFileIdToUsedFileId(nId - 1) + 1;
+ const OUString& rUrl = xRef->GetUrl();
+ ::std::pair< ::std::map< sal_uInt16, OUString >::iterator, bool > aInsert(
+ aMap.insert( ::std::make_pair( nId, rUrl)));
+ if (!aInsert.second)
+ {
+ SAL_WARN( "sc.filter", "XclExpSupbookBuffer::SaveXml: file ID already used: " << nId <<
+ " wanted for " << rUrl << " and is " << (*aInsert.first).second <<
+ (rUrl == (*aInsert.first).second ? " multiple Supbook not supported" : ""));
+ continue;
+ }
+ OUString sId;
+ sax_fastparser::FSHelperPtr pExternalLink = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "externalLinks/externalLink", nUsedId),
+ XclXmlUtils::GetStreamName( nullptr, "externalLinks/externalLink", nUsedId),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml",
+ CREATE_OFFICEDOC_RELATION_TYPE("externalLink"),
+ &sId );
+
+ // externalReference entry in workbook externalReferences
+ rStrm.GetCurrentStream()->singleElement( XML_externalReference,
+ FSNS(XML_r, XML_id), sId.toUtf8() );
+
+ // Each externalBook in a separate stream.
+ rStrm.PushStream( pExternalLink );
+ xRef->SaveXml( rStrm );
+ rStrm.PopStream();
+ }
+}
+
+bool XclExpSupbookBuffer::HasExternalReferences() const
+{
+ for (size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos)
+ {
+ if (maSupbookList.GetRecord( nPos)->GetType() == XclSupbookType::Extern)
+ return true;
+ }
+ return false;
+}
+
+bool XclExpSupbookBuffer::GetSupbookUrl(
+ XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex, std::u16string_view rUrl ) const
+{
+ for( size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos )
+ {
+ rxSupbook = maSupbookList.GetRecord( nPos );
+ if( rxSupbook->IsUrlLink( rUrl ) )
+ {
+ rnIndex = ulimit_cast< sal_uInt16 >( nPos );
+ return true;
+ }
+ }
+ return false;
+}
+
+bool XclExpSupbookBuffer::GetSupbookDde( XclExpSupbookRef& rxSupbook,
+ sal_uInt16& rnIndex, std::u16string_view rApplic, std::u16string_view rTopic ) const
+{
+ for( size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos )
+ {
+ rxSupbook = maSupbookList.GetRecord( nPos );
+ if( rxSupbook->IsDdeLink( rApplic, rTopic ) )
+ {
+ rnIndex = ulimit_cast< sal_uInt16 >( nPos );
+ return true;
+ }
+ }
+ return false;
+}
+
+sal_uInt16 XclExpSupbookBuffer::Append( XclExpSupbookRef const & xSupbook )
+{
+ maSupbookList.AppendRecord( xSupbook );
+ return ulimit_cast< sal_uInt16 >( maSupbookList.GetSize() - 1 );
+}
+
+// Export link manager ========================================================
+
+XclExpLinkManagerImpl::XclExpLinkManagerImpl( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpLinkManagerImpl5::XclExpLinkManagerImpl5( const XclExpRoot& rRoot ) :
+ XclExpLinkManagerImpl( rRoot )
+{
+}
+
+void XclExpLinkManagerImpl5::FindExtSheet(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry )
+{
+ FindInternal( rnExtSheet, rnFirstXclTab, nFirstScTab );
+ if( (rnFirstXclTab == EXC_TAB_DELETED) || (nFirstScTab == nLastScTab) )
+ {
+ rnLastXclTab = rnFirstXclTab;
+ }
+ else
+ {
+ sal_uInt16 nDummyExtSheet;
+ FindInternal( nDummyExtSheet, rnLastXclTab, nLastScTab );
+ }
+
+ OSL_ENSURE( !pRefLogEntry, "XclExpLinkManagerImpl5::FindExtSheet - fill reflog entry not implemented" );
+}
+
+sal_uInt16 XclExpLinkManagerImpl5::FindExtSheet( sal_Unicode cCode )
+{
+ sal_uInt16 nExtSheet;
+ FindInternal( nExtSheet, cCode );
+ return nExtSheet;
+}
+
+void XclExpLinkManagerImpl5::FindExtSheet(
+ sal_uInt16 /*nFileId*/, const OUString& /*rTabName*/, sal_uInt16 /*nXclTabSpan*/,
+ sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnFirstSBTab*/, sal_uInt16& /*rnLastSBTab*/,
+ XclExpRefLogEntry* /*pRefLogEntry*/ )
+{
+ // not implemented
+}
+
+void XclExpLinkManagerImpl5::StoreCellRange( const ScSingleRefData& /*rRef1*/, const ScSingleRefData& /*rRef2*/, const ScAddress& /*rPos*/ )
+{
+ // not implemented
+}
+
+void XclExpLinkManagerImpl5::StoreCell( sal_uInt16 /*nFileId*/, const OUString& /*rTabName*/, const ScAddress& /*rPos*/ )
+{
+ // not implemented
+}
+
+void XclExpLinkManagerImpl5::StoreCellRange( sal_uInt16 /*nFileId*/, const OUString& /*rTabName*/, const ScRange& /*rRange*/ )
+{
+ // not implemented
+}
+
+bool XclExpLinkManagerImpl5::InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ XclExpExtSheetRef xExtSheet = FindInternal( rnExtSheet, EXC_EXTSH_ADDIN );
+ if( xExtSheet )
+ {
+ rnExtName = xExtSheet->InsertAddIn( rName );
+ return rnExtName > 0;
+ }
+ return false;
+}
+
+bool XclExpLinkManagerImpl5::InsertEuroTool(
+ sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnExtName*/, const OUString& /*rName*/ )
+{
+ return false;
+}
+
+bool XclExpLinkManagerImpl5::InsertDde(
+ sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnExtName*/,
+ const OUString& /*rApplic*/, const OUString& /*rTopic*/, const OUString& /*rItem*/ )
+{
+ // not implemented
+ return false;
+}
+
+bool XclExpLinkManagerImpl5::InsertExtName(
+ sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnExtName*/, const OUString& /*rUrl*/,
+ const OUString& /*rName*/, const ScExternalRefCache::TokenArrayRef& /*rArray*/ )
+{
+ // not implemented
+ return false;
+}
+
+void XclExpLinkManagerImpl5::Save( XclExpStream& rStrm )
+{
+ if( sal_uInt16 nExtSheetCount = GetExtSheetCount() )
+ {
+ // EXTERNCOUNT record
+ XclExpUInt16Record( EXC_ID_EXTERNCOUNT, nExtSheetCount ).Save( rStrm );
+ // list of EXTERNSHEET records with EXTERNNAME, XCT, CRN records
+ maExtSheetList.Save( rStrm );
+ }
+}
+
+void XclExpLinkManagerImpl5::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+ // not applicable
+}
+
+sal_uInt16 XclExpLinkManagerImpl5::GetExtSheetCount() const
+{
+ return static_cast< sal_uInt16 >( maExtSheetList.GetSize() );
+}
+
+sal_uInt16 XclExpLinkManagerImpl5::AppendInternal( XclExpExtSheetRef const & xExtSheet )
+{
+ if( GetExtSheetCount() < 0x7FFF )
+ {
+ maExtSheetList.AppendRecord( xExtSheet );
+ // return negated one-based EXTERNSHEET index (i.e. 0xFFFD for 3rd record)
+ return static_cast< sal_uInt16 >( -GetExtSheetCount() );
+ }
+ return 0;
+}
+
+void XclExpLinkManagerImpl5::CreateInternal()
+{
+ if( !maIntTabMap.empty() )
+ return;
+
+ // create EXTERNSHEET records for all internal exported sheets
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+ for( SCTAB nScTab = 0, nScCnt = rTabInfo.GetScTabCount(); nScTab < nScCnt; ++nScTab )
+ {
+ if( rTabInfo.IsExportTab( nScTab ) )
+ {
+ XclExpExtSheetRef xRec;
+ if( nScTab == GetCurrScTab() )
+ xRec = new XclExpExternSheet( GetRoot(), EXC_EXTSH_OWNTAB );
+ else
+ xRec = new XclExpExternSheet( GetRoot(), rTabInfo.GetScTabName( nScTab ) );
+ maIntTabMap[ nScTab ] = AppendInternal( xRec );
+ }
+ }
+}
+
+XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::GetInternal( sal_uInt16 nExtSheet )
+{
+ return maExtSheetList.GetRecord( static_cast< sal_uInt16 >( -nExtSheet - 1 ) );
+}
+
+XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::FindInternal(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab, SCTAB nScTab )
+{
+ // create internal EXTERNSHEET records on demand
+ CreateInternal();
+
+ // try to find an EXTERNSHEET record - if not, return a "deleted sheet" reference
+ XclExpExtSheetRef xExtSheet;
+ XclExpIntTabMap::const_iterator aIt = maIntTabMap.find( nScTab );
+ if( aIt == maIntTabMap.end() )
+ {
+ xExtSheet = FindInternal( rnExtSheet, EXC_EXTSH_OWNDOC );
+ rnXclTab = EXC_TAB_DELETED;
+ }
+ else
+ {
+ rnExtSheet = aIt->second;
+ xExtSheet = GetInternal( rnExtSheet );
+ rnXclTab = GetTabInfo().GetXclTab( nScTab );
+ }
+ return xExtSheet;
+}
+
+XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::FindInternal(
+ sal_uInt16& rnExtSheet, sal_Unicode cCode )
+{
+ XclExpExtSheetRef xExtSheet;
+ XclExpCodeMap::const_iterator aIt = maCodeMap.find( cCode );
+ if( aIt == maCodeMap.end() )
+ {
+ xExtSheet = new XclExpExternSheet( GetRoot(), cCode );
+ rnExtSheet = maCodeMap[ cCode ] = AppendInternal( xExtSheet );
+ }
+ else
+ {
+ rnExtSheet = aIt->second;
+ xExtSheet = GetInternal( rnExtSheet );
+ }
+ return xExtSheet;
+}
+
+XclExpLinkManagerImpl8::XclExpLinkManagerImpl8( const XclExpRoot& rRoot ) :
+ XclExpLinkManagerImpl( rRoot ),
+ maSBBuffer( rRoot )
+{
+}
+
+void XclExpLinkManagerImpl8::FindExtSheet(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry )
+{
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+ rnFirstXclTab = rTabInfo.GetXclTab( nFirstScTab );
+ rnLastXclTab = rTabInfo.GetXclTab( nLastScTab );
+ rnExtSheet = InsertXti( maSBBuffer.GetXti( rnFirstXclTab, rnLastXclTab, pRefLogEntry ) );
+}
+
+sal_uInt16 XclExpLinkManagerImpl8::FindExtSheet( sal_Unicode cCode )
+{
+ OSL_ENSURE( (cCode == EXC_EXTSH_OWNDOC) || (cCode == EXC_EXTSH_ADDIN),
+ "XclExpLinkManagerImpl8::FindExtSheet - unknown externsheet code" );
+ return InsertXti( maSBBuffer.GetXti( EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+}
+
+void XclExpLinkManagerImpl8::FindExtSheet(
+ sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry )
+{
+ XclExpXti aXti = maSBBuffer.GetXti(nFileId, rTabName, nXclTabSpan, pRefLogEntry);
+ rnExtSheet = InsertXti(aXti);
+ rnFirstSBTab = aXti.mnFirstSBTab;
+ rnLastSBTab = aXti.mnLastSBTab;
+}
+
+void XclExpLinkManagerImpl8::StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos )
+{
+ ScAddress aAbs1 = rRef1.toAbs(GetRoot().GetDoc(), rPos);
+ ScAddress aAbs2 = rRef2.toAbs(GetRoot().GetDoc(), rPos);
+ if (!(!rRef1.IsDeleted() && !rRef2.IsDeleted() && (aAbs1.Tab() >= 0) && (aAbs2.Tab() >= 0)))
+ return;
+
+ const XclExpTabInfo& rTabInfo = GetTabInfo();
+ SCTAB nFirstScTab = aAbs1.Tab();
+ SCTAB nLastScTab = aAbs2.Tab();
+ ScRange aRange(aAbs1.Col(), aAbs1.Row(), 0, aAbs2.Col(), aAbs2.Row(), 0);
+ for (SCTAB nScTab = nFirstScTab; nScTab <= nLastScTab; ++nScTab)
+ {
+ if( rTabInfo.IsExternalTab( nScTab ) )
+ {
+ aRange.aStart.SetTab( nScTab );
+ aRange.aEnd.SetTab( nScTab );
+ maSBBuffer.StoreCellRange( aRange );
+ }
+ }
+}
+
+void XclExpLinkManagerImpl8::StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos )
+{
+ maSBBuffer.StoreCell(nFileId, rTabName, rPos);
+}
+
+void XclExpLinkManagerImpl8::StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange )
+{
+ maSBBuffer.StoreCellRange(nFileId, rTabName, rRange);
+}
+
+bool XclExpLinkManagerImpl8::InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ sal_uInt16 nSupbook;
+ if( maSBBuffer.InsertAddIn( nSupbook, rnExtName, rName ) )
+ {
+ rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+ return true;
+ }
+ return false;
+}
+
+bool XclExpLinkManagerImpl8::InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ sal_uInt16 nSupbook;
+ if( maSBBuffer.InsertEuroTool( nSupbook, rnExtName, rName ) )
+ {
+ rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+ return true;
+ }
+ return false;
+}
+
+bool XclExpLinkManagerImpl8::InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem )
+{
+ sal_uInt16 nSupbook;
+ if( maSBBuffer.InsertDde( nSupbook, rnExtName, rApplic, rTopic, rItem ) )
+ {
+ rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+ return true;
+ }
+ return false;
+}
+
+bool XclExpLinkManagerImpl8::InsertExtName( sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rUrl, const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ sal_uInt16 nSupbook;
+ if( maSBBuffer.InsertExtName( nSupbook, rnExtName, rUrl, rName, rArray ) )
+ {
+ rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+ return true;
+ }
+ return false;
+}
+
+void XclExpLinkManagerImpl8::Save( XclExpStream& rStrm )
+{
+ if( maXtiVec.empty() )
+ return;
+
+ // SUPBOOKs, XCTs, CRNs, EXTERNNAMEs
+ maSBBuffer.Save( rStrm );
+
+ // EXTERNSHEET
+ sal_uInt16 nCount = ulimit_cast< sal_uInt16 >( maXtiVec.size() );
+ rStrm.StartRecord( EXC_ID_EXTERNSHEET, 2 + 6 * nCount );
+ rStrm << nCount;
+ rStrm.SetSliceSize( 6 );
+ for( const auto& rXti : maXtiVec )
+ rXti.Save( rStrm );
+ rStrm.EndRecord();
+}
+
+void XclExpLinkManagerImpl8::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (maSBBuffer.HasExternalReferences())
+ {
+ sax_fastparser::FSHelperPtr pWorkbook = rStrm.GetCurrentStream();
+ pWorkbook->startElement(XML_externalReferences);
+
+ // externalLink, externalBook, sheetNames, sheetDataSet, externalName
+ maSBBuffer.SaveXml( rStrm );
+
+ pWorkbook->endElement( XML_externalReferences);
+ }
+
+ // TODO: equivalent for EXTERNSHEET in OOXML?
+#if 0
+ if( !maXtiVec.empty() )
+ {
+ for( const auto& rXti : maXtiVec )
+ rXti.SaveXml( rStrm );
+ }
+#endif
+}
+
+sal_uInt16 XclExpLinkManagerImpl8::InsertXti( const XclExpXti& rXti )
+{
+ auto aIt = std::find(maXtiVec.begin(), maXtiVec.end(), rXti);
+ if (aIt != maXtiVec.end())
+ return ulimit_cast< sal_uInt16 >( std::distance(maXtiVec.begin(), aIt) );
+ maXtiVec.push_back( rXti );
+ return ulimit_cast< sal_uInt16 >( maXtiVec.size() - 1 );
+}
+
+XclExpLinkManager::XclExpLinkManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5:
+ mxImpl = std::make_shared<XclExpLinkManagerImpl5>( rRoot );
+ break;
+ case EXC_BIFF8:
+ mxImpl = std::make_shared<XclExpLinkManagerImpl8>( rRoot );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+XclExpLinkManager::~XclExpLinkManager()
+{
+}
+
+void XclExpLinkManager::FindExtSheet(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab,
+ SCTAB nScTab, XclExpRefLogEntry* pRefLogEntry )
+{
+ mxImpl->FindExtSheet( rnExtSheet, rnXclTab, rnXclTab, nScTab, nScTab, pRefLogEntry );
+}
+
+void XclExpLinkManager::FindExtSheet(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry )
+{
+ mxImpl->FindExtSheet( rnExtSheet, rnFirstXclTab, rnLastXclTab, nFirstScTab, nLastScTab, pRefLogEntry );
+}
+
+sal_uInt16 XclExpLinkManager::FindExtSheet( sal_Unicode cCode )
+{
+ return mxImpl->FindExtSheet( cCode );
+}
+
+void XclExpLinkManager::FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry )
+{
+ mxImpl->FindExtSheet( nFileId, rTabName, nXclTabSpan, rnExtSheet, rnFirstSBTab, rnLastSBTab, pRefLogEntry );
+}
+
+void XclExpLinkManager::StoreCell( const ScSingleRefData& rRef, const ScAddress& rPos )
+{
+ mxImpl->StoreCellRange(rRef, rRef, rPos);
+}
+
+void XclExpLinkManager::StoreCellRange( const ScComplexRefData& rRef, const ScAddress& rPos )
+{
+ mxImpl->StoreCellRange(rRef.Ref1, rRef.Ref2, rPos);
+}
+
+void XclExpLinkManager::StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos )
+{
+ mxImpl->StoreCell(nFileId, rTabName, rPos);
+}
+
+void XclExpLinkManager::StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange )
+{
+ mxImpl->StoreCellRange(nFileId, rTabName, rRange);
+}
+
+bool XclExpLinkManager::InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ return mxImpl->InsertAddIn( rnExtSheet, rnExtName, rName );
+}
+
+bool XclExpLinkManager::InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ return mxImpl->InsertEuroTool( rnExtSheet, rnExtName, rName );
+}
+
+bool XclExpLinkManager::InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem )
+{
+ return mxImpl->InsertDde( rnExtSheet, rnExtName, rApplic, rTopic, rItem );
+}
+
+bool XclExpLinkManager::InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl, const OUString& rName,
+ const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ return mxImpl->InsertExtName(rnExtSheet, rnExtName, rUrl, rName, rArray);
+}
+
+void XclExpLinkManager::Save( XclExpStream& rStrm )
+{
+ mxImpl->Save( rStrm );
+}
+
+void XclExpLinkManager::SaveXml( XclExpXmlStream& rStrm )
+{
+ mxImpl->SaveXml( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xename.cxx b/sc/source/filter/excel/xename.cxx
new file mode 100644
index 000000000..c8fd0ed37
--- /dev/null
+++ b/sc/source/filter/excel/xename.cxx
@@ -0,0 +1,870 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xename.hxx>
+
+#include <map>
+
+#include <document.hxx>
+#include <rangenam.hxx>
+#include <tokenarray.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <excrecds.hxx>
+#include <xlname.hxx>
+#include <xeformula.hxx>
+#include <xestring.hxx>
+#include <xltools.hxx>
+
+#include <formula/grammar.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+
+using namespace ::oox;
+
+// *** Helper classes ***
+
+namespace {
+
+/** Represents an internal defined name, supports writing it to a NAME record. */
+class XclExpName : public XclExpRecord, protected XclExpRoot
+{
+public:
+ /** Creates a standard defined name. */
+ explicit XclExpName( const XclExpRoot& rRoot, const OUString& rName );
+ /** Creates a built-in defined name. */
+ explicit XclExpName( const XclExpRoot& rRoot, sal_Unicode cBuiltIn );
+
+ /** Sets a token array containing the definition of this name. */
+ void SetTokenArray( const XclTokenArrayRef& xTokArr );
+ /** Changes this defined name to be local on the specified Calc sheet. */
+ void SetLocalTab( SCTAB nScTab );
+ /** Hides or unhides the defined name. */
+ void SetHidden( bool bHidden = true );
+ /** Changes this name to be the call to a VB macro function or procedure.
+ @param bVBasic true = Visual Basic macro, false = Sheet macro.
+ @param bFunc true = Macro function; false = Macro procedure. */
+ void SetMacroCall( bool bVBasic, bool bFunc );
+
+ /** Sets the name's symbol value
+ @param sValue the name's symbolic value */
+ void SetSymbol( const OUString& rValue );
+
+ /** Returns the original name (title) of this defined name. */
+ const OUString& GetOrigName() const { return maOrigName; }
+ /** Returns the Excel built-in name index of this defined name.
+ @return The built-in name index or EXC_BUILTIN_UNKNOWN for user-defined names. */
+ sal_Unicode GetBuiltInName() const { return mcBuiltIn; }
+
+ /** Returns the symbol value for this defined name. */
+ const OUString& GetSymbol() const { return msSymbol; }
+
+ /** Returns true, if this is a document-global defined name. */
+ bool IsGlobal() const { return mnXclTab == EXC_NAME_GLOBAL; }
+ /** Returns the Calc sheet of a local defined name. */
+ SCTAB GetScTab() const { return mnScTab; }
+
+ /** Returns true, if this defined name is volatile. */
+ bool IsVolatile() const;
+ /** Returns true, if this defined name describes a macro call.
+ @param bFunc true = Macro function; false = Macro procedure. */
+ bool IsMacroCall( bool bVBasic, bool bFunc ) const;
+
+ /** Writes the entire NAME record to the passed stream. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the NAME record to the passed stream. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+ /** Convert localized range separators */
+ OUString GetWithDefaultRangeSeparator( const OUString& rSymbol ) const;
+
+private:
+ OUString maOrigName; /// The original user-defined name.
+ OUString msSymbol; /// The value of the symbol
+ XclExpStringRef mxName; /// The name as Excel string object.
+ XclTokenArrayRef mxTokArr; /// The definition of the defined name.
+ sal_Unicode mcBuiltIn; /// The built-in index for built-in names.
+ SCTAB mnScTab; /// The Calc sheet index for local names.
+ sal_uInt16 mnFlags; /// Additional flags for this defined name.
+ sal_uInt16 mnExtSheet; /// The 1-based index to a global EXTERNSHEET record.
+ sal_uInt16 mnXclTab; /// The 1-based Excel sheet index for local names.
+};
+
+}
+
+/** Implementation class of the name manager. */
+class XclExpNameManagerImpl : protected XclExpRoot
+{
+public:
+ explicit XclExpNameManagerImpl( const XclExpRoot& rRoot );
+
+ /** Creates NAME records for built-in and user defined names. */
+ void Initialize();
+
+ /** Inserts the Calc name with the passed index and returns the Excel NAME index. */
+ sal_uInt16 InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab );
+
+ /** Inserts a new built-in defined name. */
+ sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, SCTAB nScTab, const ScRangeList& aRangeList );
+ sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, const ScRange& aRange );
+ /** Inserts a new defined name. Sets another unused name, if rName already exists. */
+ sal_uInt16 InsertUniqueName( const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab );
+ /** Returns index of an existing name, or creates a name without definition. */
+ sal_uInt16 InsertRawName( const OUString& rName );
+ /** Searches or inserts a defined name describing a macro name.
+ @param bVBasic true = Visual Basic macro; false = Sheet macro.
+ @param bFunc true = Macro function; false = Macro procedure. */
+ sal_uInt16 InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden );
+
+ /** Returns the NAME record at the specified position or 0 on error. */
+ const XclExpName* GetName( sal_uInt16 nNameIdx ) const;
+
+ /** Writes the entire list of NAME records.
+ @descr In BIFF7 and lower, writes the entire global link table, which
+ consists of an EXTERNCOUNT record, several EXTERNSHEET records, and
+ the list of NAME records. */
+ void Save( XclExpStream& rStrm );
+
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ typedef XclExpRecordList< XclExpName > XclExpNameList;
+ typedef XclExpNameList::RecordRefType XclExpNameRef;
+
+ typedef ::std::map< ::std::pair<SCTAB, OUString>, sal_uInt16> NamedExpMap;
+
+private:
+ /**
+ * @param nTab 0-based table index, or SCTAB_GLOBAL for global names.
+ * @param nScIdx calc's name index.
+ *
+ * @return excel's name index.
+ */
+ sal_uInt16 FindNamedExp( SCTAB nTab, OUString sName );
+
+ /** Returns the index of an existing built-in NAME record with the passed definition, otherwise 0. */
+ sal_uInt16 FindBuiltInNameIdx( const OUString& rName,
+ const OUString& sSymbol ) const;
+ /** Returns an unused name for the passed name. */
+ OUString GetUnusedName( const OUString& rName ) const;
+
+ /** Appends a new NAME record to the record list.
+ @return The 1-based NAME record index used elsewhere in the Excel file. */
+ sal_uInt16 Append( XclExpName* pName );
+ sal_uInt16 Append( XclExpNameRef const & rxName ) { return Append(rxName.get()); }
+ /** Creates a new NAME record for the passed user-defined name.
+ @return The 1-based NAME record index used elsewhere in the Excel file. */
+ sal_uInt16 CreateName( SCTAB nTab, const ScRangeData& rRangeData );
+
+ /** Creates NAME records for all built-in names in the document. */
+ void CreateBuiltInNames();
+ /** Creates NAME records for all user-defined names in the document. */
+ void CreateUserNames();
+
+private:
+ /**
+ * Maps Calc's named range to Excel's NAME records. Global names use
+ * -1 as their table index, whereas sheet-local names have 0-based table
+ * index.
+ */
+ NamedExpMap maNamedExpMap;
+ XclExpNameList maNameList; /// List of NAME records.
+ size_t mnFirstUserIdx; /// List index of first user-defined NAME record.
+};
+
+// *** Implementation ***
+
+XclExpName::XclExpName( const XclExpRoot& rRoot, const OUString& rName ) :
+ XclExpRecord( EXC_ID_NAME ),
+ XclExpRoot( rRoot ),
+ maOrigName( rName ),
+ mxName( XclExpStringHelper::CreateString( rRoot, rName, XclStrFlags::EightBitLength ) ),
+ mcBuiltIn( EXC_BUILTIN_UNKNOWN ),
+ mnScTab( SCTAB_GLOBAL ),
+ mnFlags( EXC_NAME_DEFAULT ),
+ mnExtSheet( EXC_NAME_GLOBAL ),
+ mnXclTab( EXC_NAME_GLOBAL )
+{
+}
+
+XclExpName::XclExpName( const XclExpRoot& rRoot, sal_Unicode cBuiltIn ) :
+ XclExpRecord( EXC_ID_NAME ),
+ XclExpRoot( rRoot ),
+ mcBuiltIn( cBuiltIn ),
+ mnScTab( SCTAB_GLOBAL ),
+ mnFlags( EXC_NAME_DEFAULT ),
+ mnExtSheet( EXC_NAME_GLOBAL ),
+ mnXclTab( EXC_NAME_GLOBAL )
+{
+ // filter source range is hidden in Excel
+ if( cBuiltIn == EXC_BUILTIN_FILTERDATABASE )
+ SetHidden();
+
+ // special case for BIFF5/7 filter source range - name appears as plain text without built-in flag
+ if( (GetBiff() <= EXC_BIFF5) && (cBuiltIn == EXC_BUILTIN_FILTERDATABASE) )
+ {
+ OUString aName( XclTools::GetXclBuiltInDefName( EXC_BUILTIN_FILTERDATABASE ) );
+ mxName = XclExpStringHelper::CreateString( rRoot, aName, XclStrFlags::EightBitLength );
+ maOrigName = XclTools::GetXclBuiltInDefName( cBuiltIn );
+ }
+ else
+ {
+ maOrigName = XclTools::GetBuiltInDefNameXml( cBuiltIn ) ;
+ mxName = XclExpStringHelper::CreateString( rRoot, cBuiltIn, XclStrFlags::EightBitLength );
+ ::set_flag( mnFlags, EXC_NAME_BUILTIN );
+ }
+}
+
+void XclExpName::SetTokenArray( const XclTokenArrayRef& xTokArr )
+{
+ mxTokArr = xTokArr;
+}
+
+void XclExpName::SetLocalTab( SCTAB nScTab )
+{
+ OSL_ENSURE( GetTabInfo().IsExportTab( nScTab ), "XclExpName::SetLocalTab - invalid sheet index" );
+ if( !GetTabInfo().IsExportTab( nScTab ) )
+ return;
+
+ mnScTab = nScTab;
+ GetGlobalLinkManager().FindExtSheet( mnExtSheet, mnXclTab, nScTab );
+
+ // special handling for NAME record
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5: // EXTERNSHEET index is positive in NAME record
+ mnExtSheet = ~mnExtSheet + 1;
+ break;
+ case EXC_BIFF8: // EXTERNSHEET index not used, but must be created in link table
+ mnExtSheet = 0;
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+
+ // Excel sheet index is 1-based
+ ++mnXclTab;
+}
+
+void XclExpName::SetHidden( bool bHidden )
+{
+ ::set_flag( mnFlags, EXC_NAME_HIDDEN, bHidden );
+}
+
+void XclExpName::SetMacroCall( bool bVBasic, bool bFunc )
+{
+ ::set_flag( mnFlags, EXC_NAME_PROC );
+ ::set_flag( mnFlags, EXC_NAME_VB, bVBasic );
+ ::set_flag( mnFlags, EXC_NAME_FUNC, bFunc );
+}
+
+void XclExpName::SetSymbol( const OUString& rSymbol )
+{
+ msSymbol = rSymbol;
+}
+
+bool XclExpName::IsVolatile() const
+{
+ return mxTokArr && mxTokArr->IsVolatile();
+}
+
+bool XclExpName::IsMacroCall( bool bVBasic, bool bFunc ) const
+{
+ return
+ (::get_flag( mnFlags, EXC_NAME_VB ) == bVBasic) &&
+ (::get_flag( mnFlags, EXC_NAME_FUNC ) == bFunc);
+}
+
+void XclExpName::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mxName && (mxName->Len() > 0), "XclExpName::Save - missing name" );
+ OSL_ENSURE( !(IsGlobal() && ::get_flag( mnFlags, EXC_NAME_BUILTIN )), "XclExpName::Save - global built-in name" );
+ SetRecSize( 11 + mxName->GetSize() + (mxTokArr ? mxTokArr->GetSize() : 2) );
+ XclExpRecord::Save( rStrm );
+}
+
+OUString XclExpName::GetWithDefaultRangeSeparator( const OUString& rSymbol ) const
+{
+ sal_Int32 nPos = rSymbol.indexOf(';');
+ if ( nPos > -1 )
+ {
+ // convert with validation
+ ScRange aRange;
+ ScAddress::Details detailsXL( ::formula::FormulaGrammar::CONV_XL_A1 );
+ ScRefFlags nRes = aRange.Parse( rSymbol.copy(0, nPos), GetDoc(), detailsXL );
+ if ( nRes & ScRefFlags::VALID )
+ {
+ nRes = aRange.Parse( rSymbol.copy(nPos+1), GetDoc(), detailsXL );
+ if ( nRes & ScRefFlags::VALID )
+ {
+ return rSymbol.replaceFirst(";", ",");
+ }
+ }
+ }
+ return rSymbol;
+}
+
+void XclExpName::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement( XML_definedName,
+ // OOXTODO: XML_comment, "",
+ // OOXTODO: XML_customMenu, "",
+ // OOXTODO: XML_description, "",
+ XML_function, ToPsz( ::get_flag( mnFlags, EXC_NAME_VB ) ),
+ // OOXTODO: XML_functionGroupId, "",
+ // OOXTODO: XML_help, "",
+ XML_hidden, ToPsz( ::get_flag( mnFlags, EXC_NAME_HIDDEN ) ),
+ XML_localSheetId, mnScTab == SCTAB_GLOBAL ? nullptr : OString::number( mnScTab ).getStr(),
+ XML_name, maOrigName.toUtf8(),
+ // OOXTODO: XML_publishToServer, "",
+ // OOXTODO: XML_shortcutKey, "",
+ // OOXTODO: XML_statusBar, "",
+ XML_vbProcedure, ToPsz( ::get_flag( mnFlags, EXC_NAME_VB ) )
+ // OOXTODO: XML_workbookParameter, "",
+ // OOXTODO: XML_xlm, ""
+ );
+ rWorkbook->writeEscaped( GetWithDefaultRangeSeparator( msSymbol ) );
+ rWorkbook->endElement( XML_definedName );
+}
+
+void XclExpName::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nFmlaSize = mxTokArr ? mxTokArr->GetSize() : 0;
+
+ rStrm << mnFlags // flags
+ << sal_uInt8( 0 ); // keyboard shortcut
+ mxName->WriteLenField( rStrm ); // length of name
+ rStrm << nFmlaSize // size of token array
+ << mnExtSheet // BIFF5/7: EXTSHEET index, BIFF8: not used
+ << mnXclTab // 1-based sheet index for local names
+ << sal_uInt32( 0 ); // length of menu/descr/help/status text
+ mxName->WriteFlagField( rStrm ); // BIFF8 flag field (no-op in <=BIFF7)
+ mxName->WriteBuffer( rStrm ); // character array of the name
+ if( mxTokArr )
+ mxTokArr->WriteArray( rStrm ); // token array without size
+}
+
+/** Returns true (needed fixing) if FormulaToken was not absolute and 3D.
+ So, regardless of whether the fix was successful or not, true is still returned since a fix was required.*/
+static bool lcl_EnsureAbs3DToken( const SCTAB nTab, formula::FormulaToken* pTok, const bool bFix = true )
+{
+ bool bFixRequired = false;
+ if ( !pTok || ( pTok->GetType() != formula::svSingleRef && pTok->GetType() != formula::svDoubleRef ) )
+ return bFixRequired;
+
+ ScSingleRefData* pRef1 = pTok->GetSingleRef();
+ if ( !pRef1 )
+ return bFixRequired;
+
+ ScSingleRefData* pRef2 = nullptr;
+ if ( pTok->GetType() == formula::svDoubleRef )
+ pRef2 = pTok->GetSingleRef2();
+
+ if ( pRef1->IsTabRel() || !pRef1->IsFlag3D() )
+ {
+ bFixRequired = true;
+ if ( bFix )
+ {
+ if ( pRef1->IsTabRel() && nTab != SCTAB_GLOBAL )
+ pRef1->SetAbsTab( nTab + pRef1->Tab() ); //XLS requirement
+ if ( !pRef1->IsTabRel() )
+ {
+ pRef1->SetFlag3D( true ); //XLSX requirement
+ if ( pRef2 && !pRef2->IsTabRel() )
+ pRef2->SetFlag3D( pRef2->Tab() != pRef1->Tab() );
+ }
+ }
+ }
+
+ if ( pRef2 && pRef2->IsTabRel() && !pRef1->IsTabRel() )
+ {
+ bFixRequired = true;
+ if ( bFix && nTab != SCTAB_GLOBAL )
+ {
+ pRef2->SetAbsTab( nTab + pRef2->Tab() );
+ pRef2->SetFlag3D( pRef2->Tab() != pRef1->Tab() );
+ }
+ }
+ return bFixRequired;
+}
+
+XclExpNameManagerImpl::XclExpNameManagerImpl( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnFirstUserIdx( 0 )
+{
+}
+
+void XclExpNameManagerImpl::Initialize()
+{
+ CreateBuiltInNames();
+ mnFirstUserIdx = maNameList.GetSize();
+ CreateUserNames();
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab )
+{
+ sal_uInt16 nNameIdx = 0;
+ const ScRangeData* pData = nullptr;
+ ScRangeName* pRN = (nTab == SCTAB_GLOBAL) ? GetDoc().GetRangeName() : GetDoc().GetRangeName(nTab);
+ if (pRN)
+ pData = pRN->findByIndex(nScNameIdx);
+
+ if (pData)
+ {
+ bool bEmulateGlobalRelativeTable = false;
+ const ScTokenArray* pCode = pData->GetCode();
+ if ( pCode
+ && nTab == SCTAB_GLOBAL
+ && (pData->HasType( ScRangeData::Type::AbsPos ) || pData->HasType( ScRangeData::Type::AbsArea )) )
+ {
+ bEmulateGlobalRelativeTable = lcl_EnsureAbs3DToken( nTab, pCode->FirstToken(), /*bFix=*/false );
+ }
+ nNameIdx = FindNamedExp( bEmulateGlobalRelativeTable ? nCurrTab : nTab, pData->GetName() );
+ if (!nNameIdx)
+ nNameIdx = CreateName(nTab, *pData);
+ }
+
+ return nNameIdx;
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, const ScRange& aRange )
+{
+ XclExpNameRef xName = new XclExpName( GetRoot(), cBuiltIn );
+ xName->SetTokenArray( xTokArr );
+ xName->SetLocalTab( aRange.aStart.Tab() );
+ OUString sSymbol(aRange.Format(GetDoc(), ScRefFlags::RANGE_ABS_3D, ScAddress::Details( ::formula::FormulaGrammar::CONV_XL_A1)));
+ xName->SetSymbol( sSymbol );
+ return Append( xName );
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, SCTAB nScTab, const ScRangeList& rRangeList )
+{
+ XclExpNameRef xName = new XclExpName( GetRoot(), cBuiltIn );
+ xName->SetTokenArray( xTokArr );
+ xName->SetLocalTab( nScTab );
+ OUString sSymbol;
+ rRangeList.Format( sSymbol, ScRefFlags::RANGE_ABS_3D, GetDoc(), ::formula::FormulaGrammar::CONV_XL_A1 );
+ xName->SetSymbol( sSymbol );
+ return Append( xName );
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertUniqueName(
+ const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab )
+{
+ OSL_ENSURE( !rName.isEmpty(), "XclExpNameManagerImpl::InsertUniqueName - empty name" );
+ XclExpNameRef xName = new XclExpName( GetRoot(), GetUnusedName( rName ) );
+ xName->SetTokenArray( xTokArr );
+ xName->SetLocalTab( nScTab );
+ return Append( xName );
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertRawName( const OUString& rName )
+{
+ // empty name? may occur in broken external Calc tokens
+ if( rName.isEmpty() )
+ return 0;
+
+ // try to find an existing NAME record, regardless of its type
+ for( size_t nListIdx = mnFirstUserIdx, nListSize = maNameList.GetSize(); nListIdx < nListSize; ++nListIdx )
+ {
+ XclExpNameRef xName = maNameList.GetRecord( nListIdx );
+ if( xName->IsGlobal() && (xName->GetOrigName() == rName) )
+ return static_cast< sal_uInt16 >( nListIdx + 1 );
+ }
+
+ // create a new NAME record
+ XclExpNameRef xName = new XclExpName( GetRoot(), rName );
+ return Append( xName );
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden )
+{
+ // empty name? may occur in broken external Calc tokens
+ if( rMacroName.isEmpty() )
+ return 0;
+
+ // try to find an existing NAME record
+ for( size_t nListIdx = mnFirstUserIdx, nListSize = maNameList.GetSize(); nListIdx < nListSize; ++nListIdx )
+ {
+ XclExpNameRef xName = maNameList.GetRecord( nListIdx );
+ if( xName->IsMacroCall( bVBasic, bFunc ) && (xName->GetOrigName() == rMacroName) )
+ return static_cast< sal_uInt16 >( nListIdx + 1 );
+ }
+
+ // create a new NAME record
+ XclExpNameRef xName = new XclExpName( GetRoot(), rMacroName );
+ xName->SetMacroCall( bVBasic, bFunc );
+ xName->SetHidden( bHidden );
+
+ // for sheet macros, add a #NAME! error
+ if( !bVBasic )
+ xName->SetTokenArray( GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NAME ) );
+
+ return Append( xName );
+}
+
+const XclExpName* XclExpNameManagerImpl::GetName( sal_uInt16 nNameIdx ) const
+{
+ OSL_ENSURE( maNameList.HasRecord( nNameIdx - 1 ), "XclExpNameManagerImpl::GetName - wrong record index" );
+ return maNameList.GetRecord( nNameIdx - 1 );
+}
+
+void XclExpNameManagerImpl::Save( XclExpStream& rStrm )
+{
+ maNameList.Save( rStrm );
+}
+
+void XclExpNameManagerImpl::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maNameList.IsEmpty() )
+ return;
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement(XML_definedNames);
+ maNameList.SaveXml( rStrm );
+ rWorkbook->endElement( XML_definedNames );
+}
+
+// private --------------------------------------------------------------------
+
+sal_uInt16 XclExpNameManagerImpl::FindNamedExp( SCTAB nTab, OUString sName )
+{
+ NamedExpMap::key_type key(nTab, sName);
+ NamedExpMap::const_iterator itr = maNamedExpMap.find(key);
+ return (itr == maNamedExpMap.end()) ? 0 : itr->second;
+}
+
+sal_uInt16 XclExpNameManagerImpl::FindBuiltInNameIdx(
+ const OUString& rName, const OUString& sSymbol ) const
+{
+ /* Get built-in index from the name. Special case: the database range
+ 'unnamed' will be mapped to Excel's built-in '_FilterDatabase' name. */
+ sal_Unicode cBuiltIn = XclTools::GetBuiltInDefNameIndex( rName );
+
+ if( cBuiltIn < EXC_BUILTIN_UNKNOWN )
+ {
+ // try to find the record in existing built-in NAME record list
+ for( size_t nPos = 0; nPos < mnFirstUserIdx; ++nPos )
+ {
+ XclExpNameRef xName = maNameList.GetRecord( nPos );
+ if( xName->GetBuiltInName() == cBuiltIn && xName->GetSymbol().replace(';', ',') == sSymbol.replace(';', ',') )
+ {
+ // tdf#112567 restore the original built-in names with non-localized separators
+ // TODO: support more localizations, if needed
+ if ( xName->GetSymbol() != sSymbol )
+ {
+ xName->SetSymbol(xName->GetSymbol().replace(';', ','));
+ }
+ return static_cast< sal_uInt16 >( nPos + 1 );
+ }
+ }
+ }
+ return 0;
+}
+
+OUString XclExpNameManagerImpl::GetUnusedName( const OUString& rName ) const
+{
+ OUString aNewName( rName );
+ sal_Int32 nAppIdx = 0;
+ bool bExist = true;
+ while( bExist )
+ {
+ // search the list of user-defined names
+ bExist = false;
+ for( size_t nPos = mnFirstUserIdx, nSize = maNameList.GetSize(); !bExist && (nPos < nSize); ++nPos )
+ {
+ XclExpNameRef xName = maNameList.GetRecord( nPos );
+ bExist = xName->GetOrigName() == aNewName;
+ // name exists -> create a new name "<originalname>_<counter>"
+ if( bExist )
+ aNewName = rName + "_" + OUString::number( ++nAppIdx );
+ }
+ }
+ return aNewName;
+}
+
+sal_uInt16 XclExpNameManagerImpl::Append( XclExpName* pName )
+{
+ if( maNameList.GetSize() == 0xFFFF )
+ return 0;
+ maNameList.AppendRecord( pName );
+ return static_cast< sal_uInt16 >( maNameList.GetSize() ); // 1-based
+}
+
+sal_uInt16 XclExpNameManagerImpl::CreateName( SCTAB nTab, const ScRangeData& rRangeData )
+{
+ const OUString& rName = rRangeData.GetName();
+
+ /* #i38821# recursive names: first insert the (empty) name object,
+ otherwise a recursive call of this function from the formula compiler
+ with the same defined name will not find it and will create it again. */
+ size_t nOldListSize = maNameList.GetSize();
+ XclExpNameRef xName = new XclExpName( GetRoot(), rName );
+ if (nTab != SCTAB_GLOBAL)
+ xName->SetLocalTab(nTab);
+ sal_uInt16 nNameIdx = Append( xName );
+ // store the index of the NAME record in the lookup map
+ NamedExpMap::key_type key(nTab, rRangeData.GetName());
+ maNamedExpMap[key] = nNameIdx;
+
+ /* Create the definition formula.
+ This may cause recursive creation of other defined names. */
+ if( const ScTokenArray* pScTokArr = const_cast< ScRangeData& >( rRangeData ).GetCode() )
+ {
+ XclTokenArrayRef xTokArr;
+ OUString sSymbol;
+ // MSO requires named ranges to have absolute sheet references
+ if ( rRangeData.HasType( ScRangeData::Type::AbsPos ) || rRangeData.HasType( ScRangeData::Type::AbsArea ) )
+ {
+ // Don't modify the actual document; use a temporary copy to create the export formulas.
+ ScTokenArray aTokenCopy( pScTokArr->CloneValue() );
+ lcl_EnsureAbs3DToken(nTab, aTokenCopy.FirstToken());
+
+ xTokArr = GetFormulaCompiler().CreateFormula(EXC_FMLATYPE_NAME, aTokenCopy);
+ if ( GetOutput() != EXC_OUTPUT_BINARY )
+ {
+ ScCompiler aComp(GetDoc(), rRangeData.GetPos(), aTokenCopy,
+ formula::FormulaGrammar::GRAM_OOXML);
+ aComp.CreateStringFromTokenArray( sSymbol );
+ }
+ }
+ else
+ {
+ bool bOOXML = GetOutput() == EXC_OUTPUT_XML_2007;
+ xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, *pScTokArr, bOOXML ?
+ &rRangeData.GetPos() : nullptr );
+ sSymbol = rRangeData.GetSymbol( (GetOutput() == EXC_OUTPUT_BINARY) ?
+ formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 : formula::FormulaGrammar::GRAM_OOXML);
+ }
+ xName->SetTokenArray( xTokArr );
+ xName->SetSymbol( sSymbol );
+
+ /* Try to replace by existing built-in name - complete token array is
+ needed for comparison, and due to the recursion problem above this
+ cannot be done earlier. If a built-in name is found, the created NAME
+ record for this name and all following records in the list must be
+ deleted, otherwise they may contain wrong name list indexes. */
+ sal_uInt16 nBuiltInIdx = FindBuiltInNameIdx( rName, sSymbol );
+ if( nBuiltInIdx != 0 )
+ {
+ // delete the new NAME records
+ while( maNameList.GetSize() > nOldListSize )
+ maNameList.RemoveRecord( maNameList.GetSize() - 1 );
+ // use index of the found built-in NAME record
+ key = NamedExpMap::key_type(nTab, rRangeData.GetName());
+ maNamedExpMap[key] = nNameIdx = nBuiltInIdx;
+ }
+ }
+
+ return nNameIdx;
+}
+
+void XclExpNameManagerImpl::CreateBuiltInNames()
+{
+ ScDocument& rDoc = GetDoc();
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+
+ /* #i2394# built-in defined names must be sorted by the name of the
+ containing sheet. Example: SheetA!Print_Range must be stored *before*
+ SheetB!Print_Range, regardless of the position of SheetA in the document! */
+ for( SCTAB nScTabIdx = 0, nScTabCount = rTabInfo.GetScTabCount(); nScTabIdx < nScTabCount; ++nScTabIdx )
+ {
+ // find real sheet index from the nScTabIdx counter
+ SCTAB nScTab = rTabInfo.GetRealScTab( nScTabIdx );
+ // create NAME records for all built-in names of this sheet
+ if( rTabInfo.IsExportTab( nScTab ) )
+ {
+ // *** 1) print ranges *** ----------------------------------------
+
+ if( rDoc.HasPrintRange() )
+ {
+ ScRangeList aRangeList;
+ for( sal_uInt16 nIdx = 0, nCount = rDoc.GetPrintRangeCount( nScTab ); nIdx < nCount; ++nIdx )
+ {
+ const ScRange* pPrintRange = rDoc.GetPrintRange( nScTab, nIdx );
+ if (!pPrintRange)
+ continue;
+ ScRange aRange( *pPrintRange );
+ // Calc document does not care about sheet index in print ranges
+ aRange.aStart.SetTab( nScTab );
+ aRange.aEnd.SetTab( nScTab );
+ aRange.PutInOrder();
+ aRangeList.push_back( aRange );
+ }
+ // create the NAME record (do not warn if ranges are shrunken)
+ GetAddressConverter().ValidateRangeList( aRangeList, false );
+ if( !aRangeList.empty() )
+ GetNameManager().InsertBuiltInName( EXC_BUILTIN_PRINTAREA, aRangeList );
+ }
+
+ // *** 2) print titles *** ----------------------------------------
+
+ ScRangeList aTitleList;
+ // repeated columns
+ if( std::optional<ScRange> oColRange = rDoc.GetRepeatColRange( nScTab ) )
+ aTitleList.push_back( ScRange(
+ oColRange->aStart.Col(), 0, nScTab,
+ oColRange->aEnd.Col(), GetXclMaxPos().Row(), nScTab ) );
+ // repeated rows
+ if( std::optional<ScRange> oRowRange = rDoc.GetRepeatRowRange( nScTab ) )
+ aTitleList.push_back( ScRange(
+ 0, oRowRange->aStart.Row(), nScTab,
+ GetXclMaxPos().Col(), oRowRange->aEnd.Row(), nScTab ) );
+ // create the NAME record
+ GetAddressConverter().ValidateRangeList( aTitleList, false );
+ if( !aTitleList.empty() )
+ GetNameManager().InsertBuiltInName( EXC_BUILTIN_PRINTTITLES, aTitleList );
+
+ // *** 3) filter ranges *** ---------------------------------------
+
+ if( GetBiff() == EXC_BIFF8 )
+ GetFilterManager().InitTabFilter( nScTab );
+ }
+ }
+}
+
+void XclExpNameManagerImpl::CreateUserNames()
+{
+ std::vector<ScRangeData*> vEmulateAsLocalRange;
+ const ScRangeName& rNamedRanges = GetNamedRanges();
+ for (const auto& rEntry : rNamedRanges)
+ {
+ // skip definitions of shared formulas
+ if (!FindNamedExp(SCTAB_GLOBAL, rEntry.second->GetName()))
+ {
+ const ScTokenArray* pCode = rEntry.second->GetCode();
+ if ( pCode
+ && (rEntry.second->HasType( ScRangeData::Type::AbsPos ) || rEntry.second->HasType( ScRangeData::Type::AbsArea ))
+ && lcl_EnsureAbs3DToken( SCTAB_GLOBAL, pCode->FirstToken(), /*bFix=*/false ) )
+ {
+ vEmulateAsLocalRange.emplace_back(rEntry.second.get());
+ }
+ else
+ CreateName(SCTAB_GLOBAL, *rEntry.second);
+ }
+ }
+ //look at sheets containing local range names
+ ScRangeName::TabNameCopyMap rLocalNames;
+ GetDoc().GetAllTabRangeNames(rLocalNames);
+ for (const auto& [rTab, pRangeName] : rLocalNames)
+ {
+ for (const auto& rEntry : *pRangeName)
+ {
+ // skip definitions of shared formulas
+ if (!FindNamedExp(rTab, rEntry.second->GetName()))
+ CreateName(rTab, *rEntry.second);
+ }
+ }
+
+ // Emulate relative global variables by creating a copy in each local range.
+ // Creating AFTER true local range names so that conflicting global names will be ignored.
+ for ( SCTAB nTab = 0; nTab < GetDoc().GetTableCount(); ++nTab )
+ {
+ for ( auto rangeDataItr : vEmulateAsLocalRange )
+ {
+ if ( !FindNamedExp(nTab, rangeDataItr->GetName()) )
+ CreateName(nTab, *rangeDataItr );
+ }
+ }
+}
+
+XclExpNameManager::XclExpNameManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mxImpl( std::make_shared<XclExpNameManagerImpl>( rRoot ) )
+{
+}
+
+XclExpNameManager::~XclExpNameManager()
+{
+}
+
+void XclExpNameManager::Initialize()
+{
+ mxImpl->Initialize();
+}
+
+sal_uInt16 XclExpNameManager::InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab )
+{
+ return mxImpl->InsertName( nTab, nScNameIdx, nCurrTab );
+}
+
+sal_uInt16 XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn, const ScRange& rRange )
+{
+ XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, rRange );
+ return mxImpl->InsertBuiltInName( cBuiltIn, xTokArr, rRange );
+}
+
+sal_uInt16 XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn, const ScRangeList& rRangeList )
+{
+ sal_uInt16 nNameIdx = 0;
+ if( !rRangeList.empty() )
+ {
+ XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, rRangeList );
+ nNameIdx = mxImpl->InsertBuiltInName( cBuiltIn, xTokArr, rRangeList.front().aStart.Tab(), rRangeList );
+ }
+ return nNameIdx;
+}
+
+sal_uInt16 XclExpNameManager::InsertUniqueName(
+ const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab )
+{
+ return mxImpl->InsertUniqueName( rName, xTokArr, nScTab );
+}
+
+sal_uInt16 XclExpNameManager::InsertRawName( const OUString& rName )
+{
+ return mxImpl->InsertRawName( rName );
+}
+
+sal_uInt16 XclExpNameManager::InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden )
+{
+ return mxImpl->InsertMacroCall( rMacroName, bVBasic, bFunc, bHidden );
+}
+
+OUString XclExpNameManager::GetOrigName( sal_uInt16 nNameIdx ) const
+{
+ const XclExpName* pName = mxImpl->GetName( nNameIdx );
+ return pName ? pName->GetOrigName() : OUString();
+}
+
+SCTAB XclExpNameManager::GetScTab( sal_uInt16 nNameIdx ) const
+{
+ const XclExpName* pName = mxImpl->GetName( nNameIdx );
+ return pName ? pName->GetScTab() : SCTAB_GLOBAL;
+}
+
+bool XclExpNameManager::IsVolatile( sal_uInt16 nNameIdx ) const
+{
+ const XclExpName* pName = mxImpl->GetName( nNameIdx );
+ return pName && pName->IsVolatile();
+}
+
+void XclExpNameManager::Save( XclExpStream& rStrm )
+{
+ mxImpl->Save( rStrm );
+}
+
+void XclExpNameManager::SaveXml( XclExpXmlStream& rStrm )
+{
+ mxImpl->SaveXml( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xepage.cxx b/sc/source/filter/excel/xepage.cxx
new file mode 100644
index 000000000..56ecd2d6b
--- /dev/null
+++ b/sc/source/filter/excel/xepage.cxx
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xepage.hxx>
+#include <svl/itemset.hxx>
+#include <scitems.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svx/pageitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+#include <sax/fastattribs.hxx>
+#include <document.hxx>
+#include <stlpool.hxx>
+#include <attrib.hxx>
+#include <xehelper.hxx>
+#include <xeescher.hxx>
+#include <xltools.hxx>
+
+#include <set>
+#include <limits>
+
+using namespace ::oox;
+
+using ::std::set;
+using ::std::numeric_limits;
+
+// Page settings records ======================================================
+
+// Header/footer --------------------------------------------------------------
+
+XclExpHeaderFooter::XclExpHeaderFooter( sal_uInt16 nRecId, const OUString& rHdrString ) :
+ XclExpRecord( nRecId ),
+ maHdrString( rHdrString )
+{
+}
+
+void XclExpHeaderFooter::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ sal_Int32 nElement;
+ switch(GetRecId()) {
+ case EXC_ID_HEADER_FIRST: nElement = XML_firstHeader; break;
+ case EXC_ID_FOOTER_FIRST: nElement = XML_firstFooter; break;
+ case EXC_ID_HEADER_EVEN: nElement = XML_evenHeader; break;
+ case EXC_ID_FOOTER_EVEN: nElement = XML_evenFooter; break;
+ case EXC_ID_HEADER: nElement = XML_oddHeader; break;
+ case EXC_ID_FOOTER:
+ default: nElement = XML_oddFooter;
+ }
+ rWorksheet->startElement(nElement);
+ rWorksheet->writeEscaped( maHdrString );
+ rWorksheet->endElement( nElement );
+}
+
+void XclExpHeaderFooter::WriteBody( XclExpStream& rStrm )
+{
+ if( !maHdrString.isEmpty() )
+ {
+ XclExpString aExString;
+ if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
+ aExString.AssignByte( maHdrString, rStrm.GetRoot().GetTextEncoding(), XclStrFlags::EightBitLength );
+ else
+ aExString.Assign( maHdrString, XclStrFlags::NONE, 255 ); // 16-bit length, but max 255 chars
+ rStrm << aExString;
+ }
+}
+
+// General page settings ------------------------------------------------------
+
+XclExpSetup::XclExpSetup( const XclPageData& rPageData ) :
+ XclExpRecord( EXC_ID_SETUP, 34 ),
+ mrData( rPageData )
+{
+}
+
+void XclExpSetup::SaveXml( XclExpXmlStream& rStrm )
+{
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+ if( rStrm.getVersion() != oox::core::ISOIEC_29500_2008 ||
+ mrData.mnStrictPaperSize != EXC_PAPERSIZE_USER )
+ {
+ pAttrList->add( XML_paperSize, OString::number( mrData.mnPaperSize ).getStr() );
+ }
+ else
+ {
+ pAttrList->add( XML_paperWidth, OString::number( mrData.mnPaperWidth ) + "mm" );
+ pAttrList->add( XML_paperHeight, OString::number( mrData.mnPaperHeight ) + "mm" );
+ // pAttrList->add( XML_paperUnits, "mm" );
+ }
+ pAttrList->add( XML_scale, OString::number( mrData.mnScaling ).getStr() );
+ pAttrList->add( XML_fitToWidth, OString::number( mrData.mnFitToWidth ).getStr() );
+ pAttrList->add( XML_fitToHeight, OString::number( mrData.mnFitToHeight ).getStr() );
+ pAttrList->add( XML_pageOrder, mrData.mbPrintInRows ? "overThenDown" : "downThenOver" );
+ pAttrList->add( XML_orientation, mrData.mbPortrait ? "portrait" : "landscape" ); // OOXTODO: "default"?
+ // tdf#48767 if XML_usePrinterDefaults field is exist, then XML_orientation is always "portrait" in MS Excel
+ // To resolve that import issue, if XML_usePrinterDefaults has default value (false) then XML_usePrinterDefaults is not added.
+ if ( !mrData.mbValid )
+ pAttrList->add( XML_usePrinterDefaults, ToPsz( !mrData.mbValid ) );
+ pAttrList->add( XML_blackAndWhite, ToPsz( mrData.mbBlackWhite ) );
+ pAttrList->add( XML_draft, ToPsz( mrData.mbDraftQuality ) );
+ pAttrList->add( XML_cellComments, mrData.mbPrintNotes ? "atEnd" : "none" ); // OOXTODO: "asDisplayed"?
+
+ if ( mrData.mbManualStart )
+ {
+ pAttrList->add( XML_firstPageNumber, OString::number( mrData.mnStartPage ).getStr() );
+ pAttrList->add( XML_useFirstPageNumber, ToPsz( mrData.mbManualStart ) );
+ }
+ // OOXTODO: XML_errors, // == displayed|blank|dash|NA
+ pAttrList->add( XML_horizontalDpi, OString::number( mrData.mnHorPrintRes ).getStr() );
+ pAttrList->add( XML_verticalDpi, OString::number( mrData.mnVerPrintRes ).getStr() );
+ pAttrList->add( XML_copies, OString::number( mrData.mnCopies ).getStr() );
+ // OOXTODO: devMode settings part RelationshipId: FSNS( XML_r, XML_id ),
+
+ rStrm.GetCurrentStream()->singleElement( XML_pageSetup, pAttrList );
+}
+
+void XclExpSetup::WriteBody( XclExpStream& rStrm )
+{
+ XclBiff eBiff = rStrm.GetRoot().GetBiff();
+
+ sal_uInt16 nFlags = 0;
+ ::set_flag( nFlags, EXC_SETUP_INROWS, mrData.mbPrintInRows );
+ ::set_flag( nFlags, EXC_SETUP_PORTRAIT, mrData.mbPortrait );
+ ::set_flag( nFlags, EXC_SETUP_INVALID, !mrData.mbValid );
+ ::set_flag( nFlags, EXC_SETUP_BLACKWHITE, mrData.mbBlackWhite );
+ if( eBiff >= EXC_BIFF5 )
+ {
+ ::set_flag( nFlags, EXC_SETUP_DRAFT, mrData.mbDraftQuality );
+ /* Set the Comments/Notes to "At end of sheet" if Print Notes is true.
+ We don't currently support "as displayed on sheet". Thus this value
+ will be re-interpreted to "At end of sheet". */
+ const sal_uInt16 nNotes = EXC_SETUP_PRINTNOTES | EXC_SETUP_NOTES_END;
+ ::set_flag( nFlags, nNotes, mrData.mbPrintNotes );
+ ::set_flag( nFlags, EXC_SETUP_STARTPAGE, mrData.mbManualStart );
+ }
+
+ rStrm << mrData.mnPaperSize << mrData.mnScaling << mrData.mnStartPage
+ << mrData.mnFitToWidth << mrData.mnFitToHeight << nFlags;
+ if( eBiff >= EXC_BIFF5 )
+ {
+ rStrm << mrData.mnHorPrintRes << mrData.mnVerPrintRes
+ << mrData.mfHeaderMargin << mrData.mfFooterMargin << mrData.mnCopies;
+ }
+}
+
+// Manual page breaks ---------------------------------------------------------
+
+XclExpPageBreaks::XclExpPageBreaks( sal_uInt16 nRecId, const ScfUInt16Vec& rPageBreaks, sal_uInt16 nMaxPos ) :
+ XclExpRecord( nRecId ),
+ mrPageBreaks( rPageBreaks ),
+ mnMaxPos( nMaxPos )
+{
+}
+
+void XclExpPageBreaks::Save( XclExpStream& rStrm )
+{
+ if( !mrPageBreaks.empty() )
+ {
+ SetRecSize( 2 + ((rStrm.GetRoot().GetBiff() <= EXC_BIFF5) ? 2 : 6) * mrPageBreaks.size() );
+ XclExpRecord::Save( rStrm );
+ }
+}
+
+void XclExpPageBreaks::WriteBody( XclExpStream& rStrm )
+{
+ bool bWriteRange = (rStrm.GetRoot().GetBiff() == EXC_BIFF8);
+
+ rStrm << static_cast< sal_uInt16 >( mrPageBreaks.size() );
+ for( const auto& rPageBreak : mrPageBreaks )
+ {
+ rStrm << rPageBreak;
+ if( bWriteRange )
+ rStrm << sal_uInt16( 0 ) << mnMaxPos;
+ }
+}
+
+void XclExpPageBreaks::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mrPageBreaks.empty() )
+ return;
+
+ sal_Int32 nElement = GetRecId() == EXC_ID_HORPAGEBREAKS ? XML_rowBreaks : XML_colBreaks;
+ sax_fastparser::FSHelperPtr& pWorksheet = rStrm.GetCurrentStream();
+ OString sNumPageBreaks = OString::number( mrPageBreaks.size() );
+ pWorksheet->startElement( nElement,
+ XML_count, sNumPageBreaks,
+ XML_manualBreakCount, sNumPageBreaks );
+ for( const auto& rPageBreak : mrPageBreaks )
+ {
+ pWorksheet->singleElement( XML_brk,
+ XML_id, OString::number(rPageBreak),
+ XML_man, "true",
+ XML_max, OString::number(mnMaxPos),
+ XML_min, "0"
+ // OOXTODO: XML_pt, ""
+ );
+ }
+ pWorksheet->endElement( nElement );
+}
+
+// Page settings ==============================================================
+
+XclExpPageSettings::XclExpPageSettings( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ ScDocument& rDoc = GetDoc();
+ SCTAB nScTab = GetCurrScTab();
+
+ if( SfxStyleSheetBase* pStyleSheet = GetStyleSheetPool().Find( rDoc.GetPageStyle( nScTab ), SfxStyleFamily::Page ) )
+ {
+ const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
+ maData.mbValid = true;
+
+ // *** page settings ***
+
+ maData.mbPrintInRows = ! rItemSet.Get( ATTR_PAGE_TOPDOWN ).GetValue();
+ maData.mbHorCenter = rItemSet.Get( ATTR_PAGE_HORCENTER ).GetValue();
+ maData.mbVerCenter = rItemSet.Get( ATTR_PAGE_VERCENTER ).GetValue();
+ maData.mbPrintHeadings = rItemSet.Get( ATTR_PAGE_HEADERS ).GetValue();
+ maData.mbPrintGrid = rItemSet.Get( ATTR_PAGE_GRID ).GetValue();
+ maData.mbPrintNotes = rItemSet.Get( ATTR_PAGE_NOTES ).GetValue();
+
+ maData.mnStartPage = rItemSet.Get( ATTR_PAGE_FIRSTPAGENO ).GetValue();
+ maData.mbManualStart = maData.mnStartPage && (!nScTab || rDoc.NeedPageResetAfterTab( nScTab - 1 ));
+
+ const SvxLRSpaceItem& rLRItem = rItemSet.Get( ATTR_LRSPACE );
+ maData.mfLeftMargin = XclTools::GetInchFromTwips( rLRItem.GetLeft() );
+ maData.mfRightMargin = XclTools::GetInchFromTwips( rLRItem.GetRight() );
+ const SvxULSpaceItem& rULItem = rItemSet.Get( ATTR_ULSPACE );
+ maData.mfTopMargin = XclTools::GetInchFromTwips( rULItem.GetUpper() );
+ maData.mfBottomMargin = XclTools::GetInchFromTwips( rULItem.GetLower() );
+
+ const SvxPageItem& rPageItem = rItemSet.Get( ATTR_PAGE );
+ const SvxSizeItem& rSizeItem = rItemSet.Get( ATTR_PAGE_SIZE );
+ maData.SetScPaperSize( rSizeItem.GetSize(), !rPageItem.IsLandscape() );
+
+ const ScPageScaleToItem& rScaleToItem = rItemSet.Get( ATTR_PAGE_SCALETO );
+ sal_uInt16 nPages = rItemSet.Get( ATTR_PAGE_SCALETOPAGES ).GetValue();
+ sal_uInt16 nScale = rItemSet.Get( ATTR_PAGE_SCALE ).GetValue();
+
+ if( ScfTools::CheckItem( rItemSet, ATTR_PAGE_SCALETO, false ) && rScaleToItem.IsValid() )
+ {
+ maData.mnFitToWidth = rScaleToItem.GetWidth();
+ maData.mnFitToHeight = rScaleToItem.GetHeight();
+ maData.mbFitToPages = true;
+ }
+ else if( ScfTools::CheckItem( rItemSet, ATTR_PAGE_SCALETOPAGES, false ) && nPages )
+ {
+ maData.mnFitToWidth = 1;
+ maData.mnFitToHeight = nPages;
+ maData.mbFitToPages = true;
+ }
+ else if( nScale )
+ {
+ maData.mnScaling = nScale;
+ maData.mbFitToPages = false;
+ }
+
+ maData.mxBrushItem.reset( new SvxBrushItem( rItemSet.Get( ATTR_BACKGROUND ) ) );
+ maData.mbUseEvenHF = false;
+ maData.mbUseFirstHF = false;
+
+ // *** header and footer ***
+
+ XclExpHFConverter aHFConv( GetRoot() );
+
+ // header
+ const SfxItemSet& rHdrItemSet = rItemSet.Get( ATTR_PAGE_HEADERSET ).GetItemSet();
+ if( rHdrItemSet.Get( ATTR_PAGE_ON ).GetValue() )
+ {
+ const ScPageHFItem& rHFItem = rItemSet.Get( ATTR_PAGE_HEADERRIGHT );
+ aHFConv.GenerateString( rHFItem.GetLeftArea(), rHFItem.GetCenterArea(), rHFItem.GetRightArea() );
+ maData.maHeader = aHFConv.GetHFString();
+ if ( rHdrItemSet.HasItem(ATTR_PAGE_SHARED) && !rHdrItemSet.Get(ATTR_PAGE_SHARED).GetValue())
+ {
+ const ScPageHFItem& rHFItemLeft = rItemSet.Get( ATTR_PAGE_HEADERLEFT );
+ aHFConv.GenerateString( rHFItemLeft.GetLeftArea(), rHFItemLeft.GetCenterArea(), rHFItemLeft.GetRightArea() );
+ maData.maHeaderEven = aHFConv.GetHFString();
+ maData.mbUseEvenHF = true;
+ }
+ else
+ {
+ // If maData.mbUseEvenHF become true, then we will need a copy of maHeader in maHeaderEven.
+ maData.maHeaderEven = maData.maHeader;
+ }
+ if (rHdrItemSet.HasItem(ATTR_PAGE_SHARED_FIRST) && !rHdrItemSet.Get(ATTR_PAGE_SHARED_FIRST).GetValue())
+ {
+ const ScPageHFItem& rHFItemFirst = rItemSet.Get( ATTR_PAGE_HEADERFIRST );
+ aHFConv.GenerateString( rHFItemFirst.GetLeftArea(), rHFItemFirst.GetCenterArea(), rHFItemFirst.GetRightArea() );
+ maData.maHeaderFirst = aHFConv.GetHFString();
+ maData.mbUseFirstHF = true;
+ }
+ else
+ {
+ maData.maHeaderFirst = maData.maHeader;
+ }
+ // header height (Excel excludes header from top margin)
+ sal_Int32 nHdrHeight = rHdrItemSet.Get( ATTR_PAGE_DYNAMIC ).GetValue() ?
+ // dynamic height: calculate header height, add header <-> sheet area distance
+ (aHFConv.GetTotalHeight() + rHdrItemSet.Get( ATTR_ULSPACE ).GetLower()) :
+ // static height: ATTR_PAGE_SIZE already includes header <-> sheet area distance
+ static_cast< sal_Int32 >( rHdrItemSet.Get( ATTR_PAGE_SIZE ).GetSize().Height() );
+ maData.mfHeaderMargin = maData.mfTopMargin;
+ maData.mfTopMargin += XclTools::GetInchFromTwips( nHdrHeight );
+ }
+
+ // footer
+ const SfxItemSet& rFtrItemSet = rItemSet.Get( ATTR_PAGE_FOOTERSET ).GetItemSet();
+ if( rFtrItemSet.Get( ATTR_PAGE_ON ).GetValue() )
+ {
+ const ScPageHFItem& rHFItem = rItemSet.Get( ATTR_PAGE_FOOTERRIGHT );
+ aHFConv.GenerateString( rHFItem.GetLeftArea(), rHFItem.GetCenterArea(), rHFItem.GetRightArea() );
+ maData.maFooter = aHFConv.GetHFString();
+ if (rFtrItemSet.HasItem(ATTR_PAGE_SHARED) && !rFtrItemSet.Get(ATTR_PAGE_SHARED).GetValue())
+ {
+ const ScPageHFItem& rHFItemLeft = rItemSet.Get( ATTR_PAGE_FOOTERLEFT );
+ aHFConv.GenerateString( rHFItemLeft.GetLeftArea(), rHFItemLeft.GetCenterArea(), rHFItemLeft.GetRightArea() );
+ maData.maFooterEven = aHFConv.GetHFString();
+ maData.mbUseEvenHF = true;
+ }
+ else
+ {
+ maData.maFooterEven = maData.maFooter;
+ }
+ if (rFtrItemSet.HasItem(ATTR_PAGE_SHARED_FIRST) && !rFtrItemSet.Get(ATTR_PAGE_SHARED_FIRST).GetValue())
+ {
+ const ScPageHFItem& rHFItemFirst = rItemSet.Get( ATTR_PAGE_FOOTERFIRST );
+ aHFConv.GenerateString( rHFItemFirst.GetLeftArea(), rHFItemFirst.GetCenterArea(), rHFItemFirst.GetRightArea() );
+ maData.maFooterFirst = aHFConv.GetHFString();
+ maData.mbUseFirstHF = true;
+ }
+ else
+ {
+ maData.maFooterFirst = maData.maFooter;
+ }
+ // footer height (Excel excludes footer from bottom margin)
+ sal_Int32 nFtrHeight = rFtrItemSet.Get( ATTR_PAGE_DYNAMIC ).GetValue() ?
+ // dynamic height: calculate footer height, add sheet area <-> footer distance
+ (aHFConv.GetTotalHeight() + rFtrItemSet.Get( ATTR_ULSPACE ).GetUpper()) :
+ // static height: ATTR_PAGE_SIZE already includes sheet area <-> footer distance
+ static_cast< sal_Int32 >( rFtrItemSet.Get( ATTR_PAGE_SIZE ).GetSize().Height() );
+ maData.mfFooterMargin = maData.mfBottomMargin;
+ maData.mfBottomMargin += XclTools::GetInchFromTwips( nFtrHeight );
+ }
+ }
+
+ // *** page breaks ***
+
+ set<SCROW> aRowBreaks;
+ rDoc.GetAllRowBreaks(aRowBreaks, nScTab, false, true);
+
+ SCROW const nMaxRow = numeric_limits<sal_uInt16>::max();
+ for (const SCROW nRow : aRowBreaks)
+ {
+ if (nRow > nMaxRow)
+ break;
+
+ maData.maHorPageBreaks.push_back(nRow);
+ }
+
+ if (maData.maHorPageBreaks.size() > 1026)
+ {
+ // Excel allows only up to 1026 page breaks. Trim any excess page breaks.
+ ScfUInt16Vec::iterator itr = maData.maHorPageBreaks.begin();
+ ::std::advance(itr, 1026);
+ maData.maHorPageBreaks.erase(itr, maData.maHorPageBreaks.end());
+ }
+
+ set<SCCOL> aColBreaks;
+ rDoc.GetAllColBreaks(aColBreaks, nScTab, false, true);
+ for (const auto& rColBreak : aColBreaks)
+ maData.maVerPageBreaks.push_back(rColBreak);
+}
+
+namespace {
+
+class XclExpXmlStartHeaderFooterElementRecord : public XclExpXmlElementRecord
+{
+public:
+ explicit XclExpXmlStartHeaderFooterElementRecord(sal_Int32 const nElement, bool const bDifferentOddEven = false, bool const bDifferentFirst = false)
+ : XclExpXmlElementRecord(nElement), mbDifferentOddEven(bDifferentOddEven), mbDifferentFirst(bDifferentFirst) {}
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ bool mbDifferentOddEven;
+ bool mbDifferentFirst;
+};
+
+}
+
+void XclExpXmlStartHeaderFooterElementRecord::SaveXml(XclExpXmlStream& rStrm)
+{
+ // OOXTODO: we currently only emit oddHeader/oddFooter elements, and
+ // do not support the first/even/odd page distinction.
+ sax_fastparser::FSHelperPtr& rStream = rStrm.GetCurrentStream();
+ rStream->startElement( mnElement,
+ // OOXTODO: XML_alignWithMargins,
+ XML_differentFirst, mbDifferentFirst ? "true" : "false",
+ XML_differentOddEven, mbDifferentOddEven ? "true" : "false"
+ // OOXTODO: XML_scaleWithDoc
+ );
+}
+
+void XclExpPageSettings::Save( XclExpStream& rStrm )
+{
+ XclExpBoolRecord( EXC_ID_PRINTHEADERS, maData.mbPrintHeadings ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_PRINTGRIDLINES, maData.mbPrintGrid ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_GRIDSET, true ).Save( rStrm );
+ XclExpPageBreaks( EXC_ID_HORPAGEBREAKS, maData.maHorPageBreaks, static_cast< sal_uInt16 >( GetXclMaxPos().Col() ) ).Save( rStrm );
+ XclExpPageBreaks( EXC_ID_VERPAGEBREAKS, maData.maVerPageBreaks, static_cast< sal_uInt16 >( GetXclMaxPos().Row() ) ).Save( rStrm );
+ XclExpHeaderFooter( EXC_ID_HEADER, maData.maHeader ).Save( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER, maData.maFooter ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_HCENTER, maData.mbHorCenter ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_VCENTER, maData.mbVerCenter ).Save( rStrm );
+ XclExpDoubleRecord( EXC_ID_LEFTMARGIN, maData.mfLeftMargin ).Save( rStrm );
+ XclExpDoubleRecord( EXC_ID_RIGHTMARGIN, maData.mfRightMargin ).Save( rStrm );
+ XclExpDoubleRecord( EXC_ID_TOPMARGIN, maData.mfTopMargin ).Save( rStrm );
+ XclExpDoubleRecord( EXC_ID_BOTTOMMARGIN, maData.mfBottomMargin ).Save( rStrm );
+ XclExpSetup( maData ).Save( rStrm );
+
+ if( (GetBiff() == EXC_BIFF8) && maData.mxBrushItem )
+ if( const Graphic* pGraphic = maData.mxBrushItem->GetGraphic() )
+ XclExpImgData( *pGraphic, EXC_ID8_IMGDATA ).Save( rStrm );
+}
+
+void XclExpPageSettings::SaveXml( XclExpXmlStream& rStrm )
+{
+ XclExpXmlStartSingleElementRecord( XML_printOptions ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_PRINTHEADERS, maData.mbPrintHeadings, XML_headings ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_PRINTGRIDLINES, maData.mbPrintGrid, XML_gridLines ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_GRIDSET, true, XML_gridLinesSet ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_HCENTER, maData.mbHorCenter, XML_horizontalCentered ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_VCENTER, maData.mbVerCenter, XML_verticalCentered ).SaveXml( rStrm );
+ XclExpXmlEndSingleElementRecord().SaveXml( rStrm ); // XML_printOptions
+
+ XclExpXmlStartSingleElementRecord( XML_pageMargins ).SaveXml( rStrm );
+ XclExpDoubleRecord( EXC_ID_LEFTMARGIN, maData.mfLeftMargin ).SetAttribute( XML_left )->SaveXml( rStrm );
+ XclExpDoubleRecord( EXC_ID_RIGHTMARGIN, maData.mfRightMargin ).SetAttribute( XML_right )->SaveXml( rStrm );
+ XclExpDoubleRecord( EXC_ID_TOPMARGIN, maData.mfTopMargin ).SetAttribute( XML_top )->SaveXml( rStrm );
+ XclExpDoubleRecord( EXC_ID_BOTTOMMARGIN, maData.mfBottomMargin ).SetAttribute( XML_bottom )->SaveXml( rStrm );
+ XclExpDoubleRecord( 0, maData.mfHeaderMargin).SetAttribute( XML_header )->SaveXml( rStrm );
+ XclExpDoubleRecord( 0, maData.mfFooterMargin).SetAttribute( XML_footer )->SaveXml( rStrm );
+ XclExpXmlEndSingleElementRecord().SaveXml( rStrm ); // XML_pageMargins
+
+ XclExpSetup( maData ).SaveXml( rStrm );
+
+ XclExpXmlStartHeaderFooterElementRecord(XML_headerFooter, maData.mbUseEvenHF, maData.mbUseFirstHF).SaveXml(rStrm);
+ XclExpHeaderFooter( EXC_ID_HEADER, maData.maHeader ).SaveXml( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER, maData.maFooter ).SaveXml( rStrm );
+ if (maData.mbUseEvenHF)
+ {
+ XclExpHeaderFooter( EXC_ID_HEADER_EVEN, maData.maHeaderEven ).SaveXml( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER_EVEN, maData.maFooterEven ).SaveXml( rStrm );
+ }
+ if (maData.mbUseFirstHF)
+ {
+ XclExpHeaderFooter( EXC_ID_HEADER_FIRST, maData.maHeaderFirst ).SaveXml( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER_FIRST, maData.maFooterFirst ).SaveXml( rStrm );
+ }
+ XclExpXmlEndElementRecord( XML_headerFooter ).SaveXml( rStrm );
+
+ XclExpPageBreaks( EXC_ID_HORPAGEBREAKS, maData.maHorPageBreaks,
+ static_cast< sal_uInt16 >( GetXclMaxPos().Col() ) ).SaveXml( rStrm );
+ XclExpPageBreaks( EXC_ID_VERPAGEBREAKS, maData.maVerPageBreaks,
+ static_cast< sal_uInt16 >( GetXclMaxPos().Row() ) ).SaveXml( rStrm );
+}
+
+XclExpImgData* XclExpPageSettings::getGraphicExport()
+{
+ if( const Graphic* pGraphic = maData.mxBrushItem->GetGraphic() )
+ return new XclExpImgData( *pGraphic, EXC_ID8_IMGDATA );
+
+ return nullptr;
+}
+
+XclExpChartPageSettings::XclExpChartPageSettings( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpChartPageSettings::Save( XclExpStream& rStrm )
+{
+ XclExpHeaderFooter( EXC_ID_HEADER, maData.maHeader ).Save( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER, maData.maFooter ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_HCENTER, maData.mbHorCenter ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_VCENTER, maData.mbVerCenter ).Save( rStrm );
+ XclExpSetup( maData ).Save( rStrm );
+ XclExpUInt16Record( EXC_ID_PRINTSIZE, EXC_PRINTSIZE_FULL ).Save( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xepivot.cxx b/sc/source/filter/excel/xepivot.cxx
new file mode 100644
index 000000000..34564e30e
--- /dev/null
+++ b/sc/source/filter/excel/xepivot.cxx
@@ -0,0 +1,1702 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xepivot.hxx>
+#include <xehelper.hxx>
+#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+
+#include <algorithm>
+#include <math.h>
+#include <string_view>
+
+#include <osl/diagnose.h>
+#include <sot/storage.hxx>
+#include <document.hxx>
+#include <dpcache.hxx>
+#include <dpgroup.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <dpshttab.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <xestring.hxx>
+#include <xelink.hxx>
+#include <dputil.hxx>
+#include <generalfunction.hxx>
+#include <svl/numformat.hxx>
+
+using namespace ::oox;
+
+using ::com::sun::star::sheet::DataPilotFieldOrientation;
+using ::com::sun::star::sheet::DataPilotFieldOrientation_ROW;
+using ::com::sun::star::sheet::DataPilotFieldOrientation_COLUMN;
+using ::com::sun::star::sheet::DataPilotFieldOrientation_PAGE;
+using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
+using ::com::sun::star::sheet::DataPilotFieldSortInfo;
+using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
+using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
+using ::com::sun::star::sheet::DataPilotFieldReference;
+
+// Pivot cache
+
+namespace {
+
+// constants to track occurrence of specific data types
+const sal_uInt16 EXC_PCITEM_DATA_STRING = 0x0001; /// String, empty, boolean, error.
+const sal_uInt16 EXC_PCITEM_DATA_DOUBLE = 0x0002; /// Double with fraction.
+const sal_uInt16 EXC_PCITEM_DATA_INTEGER = 0x0004; /// Integer, double without fraction.
+const sal_uInt16 EXC_PCITEM_DATA_DATE = 0x0008; /// Date, time, date/time.
+
+/** Maps a bitfield consisting of EXC_PCITEM_DATA_* flags above to SXFIELD data type bitfield. */
+const sal_uInt16 spnPCItemFlags[] =
+{ // STR DBL INT DAT
+ EXC_SXFIELD_DATA_NONE,
+ EXC_SXFIELD_DATA_STR, // x
+ EXC_SXFIELD_DATA_INT, // x
+ EXC_SXFIELD_DATA_STR_INT, // x x
+ EXC_SXFIELD_DATA_DBL, // x
+ EXC_SXFIELD_DATA_STR_DBL, // x x
+ EXC_SXFIELD_DATA_INT, // x x
+ EXC_SXFIELD_DATA_STR_INT, // x x x
+ EXC_SXFIELD_DATA_DATE, // x
+ EXC_SXFIELD_DATA_DATE_STR, // x x
+ EXC_SXFIELD_DATA_DATE_NUM, // x x
+ EXC_SXFIELD_DATA_DATE_STR, // x x x
+ EXC_SXFIELD_DATA_DATE_NUM, // x x
+ EXC_SXFIELD_DATA_DATE_STR, // x x x
+ EXC_SXFIELD_DATA_DATE_NUM, // x x x
+ EXC_SXFIELD_DATA_DATE_STR // x x x x
+};
+
+} // namespace
+
+XclExpPCItem::XclExpPCItem( const OUString& rText ) :
+ XclExpRecord( (!rText.isEmpty()) ? EXC_ID_SXSTRING : EXC_ID_SXEMPTY, 0 ),
+ mnTypeFlag( EXC_PCITEM_DATA_STRING )
+{
+ if( !rText.isEmpty() )
+ SetText( rText );
+ else
+ SetEmpty();
+}
+
+XclExpPCItem::XclExpPCItem( double fValue, const OUString& rText ) :
+ XclExpRecord( EXC_ID_SXDOUBLE, 8 )
+{
+ SetDouble( fValue, rText );
+ mnTypeFlag = (fValue - floor( fValue ) == 0.0) ?
+ EXC_PCITEM_DATA_INTEGER : EXC_PCITEM_DATA_DOUBLE;
+}
+
+XclExpPCItem::XclExpPCItem( const DateTime& rDateTime, const OUString& rText ) :
+ XclExpRecord( EXC_ID_SXDATETIME, 8 )
+{
+ SetDateTime( rDateTime, rText );
+ mnTypeFlag = EXC_PCITEM_DATA_DATE;
+}
+
+XclExpPCItem::XclExpPCItem( sal_Int16 nValue ) :
+ XclExpRecord( EXC_ID_SXINTEGER, 2 ),
+ mnTypeFlag( EXC_PCITEM_DATA_INTEGER )
+{
+ SetInteger( nValue );
+}
+
+XclExpPCItem::XclExpPCItem( bool bValue, const OUString& rText ) :
+ XclExpRecord( EXC_ID_SXBOOLEAN, 2 ),
+ mnTypeFlag( EXC_PCITEM_DATA_STRING )
+{
+ SetBool( bValue, rText );
+}
+
+bool XclExpPCItem::EqualsText( std::u16string_view rText ) const
+{
+ return rText.empty() ? IsEmpty() : (GetText() && (*GetText() == rText));
+}
+
+bool XclExpPCItem::EqualsDouble( double fValue ) const
+{
+ return GetDouble() && (*GetDouble() == fValue);
+}
+
+bool XclExpPCItem::EqualsDateTime( const DateTime& rDateTime ) const
+{
+ return GetDateTime() && (*GetDateTime() == rDateTime);
+}
+
+bool XclExpPCItem::EqualsBool( bool bValue ) const
+{
+ return GetBool() && (*GetBool() == bValue);
+}
+
+void XclExpPCItem::WriteBody( XclExpStream& rStrm )
+{
+ if( const OUString* pText = GetText() )
+ {
+ rStrm << XclExpString( *pText );
+ }
+ else if( const double* pfValue = GetDouble() )
+ {
+ rStrm << *pfValue;
+ }
+ else if( const sal_Int16* pnValue = GetInteger() )
+ {
+ rStrm << *pnValue;
+ }
+ else if( const DateTime* pDateTime = GetDateTime() )
+ {
+ sal_uInt16 nYear = static_cast< sal_uInt16 >( pDateTime->GetYear() );
+ sal_uInt16 nMonth = pDateTime->GetMonth();
+ sal_uInt8 nDay = static_cast< sal_uInt8 >( pDateTime->GetDay() );
+ sal_uInt8 nHour = static_cast< sal_uInt8 >( pDateTime->GetHour() );
+ sal_uInt8 nMin = static_cast< sal_uInt8 >( pDateTime->GetMin() );
+ sal_uInt8 nSec = static_cast< sal_uInt8 >( pDateTime->GetSec() );
+ if( nYear < 1900 ) { nYear = 1900; nMonth = 1; nDay = 0; }
+ rStrm << nYear << nMonth << nDay << nHour << nMin << nSec;
+ }
+ else if( const bool* pbValue = GetBool() )
+ {
+ rStrm << static_cast< sal_uInt16 >( *pbValue ? 1 : 0 );
+ }
+ else
+ {
+ // nothing to do for SXEMPTY
+ OSL_ENSURE( IsEmpty(), "XclExpPCItem::WriteBody - no data found" );
+ }
+}
+
+XclExpPCField::XclExpPCField(
+ const XclExpRoot& rRoot, sal_uInt16 nFieldIdx,
+ const ScDPObject& rDPObj, const ScRange& rRange ) :
+ XclExpRecord( EXC_ID_SXFIELD ),
+ XclPCField( EXC_PCFIELD_STANDARD, nFieldIdx ),
+ XclExpRoot( rRoot ),
+ mnTypeFlags( 0 )
+{
+ // general settings for the standard field, insert all items from source range
+ InitStandardField( rRange );
+
+ // add special settings for inplace numeric grouping
+ if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
+ {
+ if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
+ {
+ if( const ScDPSaveNumGroupDimension* pNumGroupDim = pSaveDimData->GetNumGroupDim( GetFieldName() ) )
+ {
+ const ScDPNumGroupInfo& rNumInfo = pNumGroupDim->GetInfo();
+ const ScDPNumGroupInfo& rDateInfo = pNumGroupDim->GetDateInfo();
+ OSL_ENSURE( !rNumInfo.mbEnable || !rDateInfo.mbEnable,
+ "XclExpPCField::XclExpPCField - numeric and date grouping enabled" );
+
+ if( rNumInfo.mbEnable )
+ InitNumGroupField( rDPObj, rNumInfo );
+ else if( rDateInfo.mbEnable )
+ InitDateGroupField( rDPObj, rDateInfo, pNumGroupDim->GetDatePart() );
+ }
+ }
+ }
+
+ // final settings (flags, item numbers)
+ Finalize();
+}
+
+XclExpPCField::XclExpPCField(
+ const XclExpRoot& rRoot, sal_uInt16 nFieldIdx,
+ const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim, const XclExpPCField& rBaseField ) :
+ XclExpRecord( EXC_ID_SXFIELD ),
+ XclPCField( EXC_PCFIELD_STDGROUP, nFieldIdx ),
+ XclExpRoot( rRoot ),
+ mnTypeFlags( 0 )
+{
+ // add base field info (always using first base field, not predecessor of this field) ***
+ OSL_ENSURE( rBaseField.GetFieldName() == rGroupDim.GetSourceDimName(),
+ "XclExpPCField::FillFromGroup - wrong base cache field" );
+ maFieldInfo.maName = rGroupDim.GetGroupDimName();
+ maFieldInfo.mnGroupBase = rBaseField.GetFieldIndex();
+
+ // add standard group info or date group info
+ const ScDPNumGroupInfo& rDateInfo = rGroupDim.GetDateInfo();
+ if( rDateInfo.mbEnable && (rGroupDim.GetDatePart() != 0) )
+ InitDateGroupField( rDPObj, rDateInfo, rGroupDim.GetDatePart() );
+ else
+ InitStdGroupField( rBaseField, rGroupDim );
+
+ // final settings (flags, item numbers)
+ Finalize();
+}
+
+XclExpPCField::~XclExpPCField()
+{
+}
+
+void XclExpPCField::SetGroupChildField( const XclExpPCField& rChildField )
+{
+ OSL_ENSURE( !::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ),
+ "XclExpPCField::SetGroupChildIndex - field already has a grouping child field" );
+ ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
+ maFieldInfo.mnGroupChild = rChildField.GetFieldIndex();
+}
+
+sal_uInt16 XclExpPCField::GetItemCount() const
+{
+ return static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
+}
+
+const XclExpPCItem* XclExpPCField::GetItem( sal_uInt16 nItemIdx ) const
+{
+ return GetVisItemList().GetRecord( nItemIdx );
+}
+
+sal_uInt16 XclExpPCField::GetItemIndex( std::u16string_view rItemName ) const
+{
+ const XclExpPCItemList& rItemList = GetVisItemList();
+ for( size_t nPos = 0, nSize = rItemList.GetSize(); nPos < nSize; ++nPos )
+ if( rItemList.GetRecord( nPos )->ConvertToText() == rItemName )
+ return static_cast< sal_uInt16 >( nPos );
+ return EXC_PC_NOITEM;
+}
+
+std::size_t XclExpPCField::GetIndexSize() const
+{
+ return Has16BitIndexes() ? 2 : 1;
+}
+
+void XclExpPCField::WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const
+{
+ // only standard fields write item indexes
+ if( nSrcRow < maIndexVec.size() )
+ {
+ sal_uInt16 nIndex = maIndexVec[ nSrcRow ];
+ if( Has16BitIndexes() )
+ rStrm << nIndex;
+ else
+ rStrm << static_cast< sal_uInt8 >( nIndex );
+ }
+}
+
+void XclExpPCField::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( IsSupportedField(), "XclExpPCField::Save - unknown field type" );
+ // SXFIELD
+ XclExpRecord::Save( rStrm );
+ // SXFDBTYPE
+ XclExpUInt16Record( EXC_ID_SXFDBTYPE, EXC_SXFDBTYPE_DEFAULT ).Save( rStrm );
+ // list of grouping items
+ maGroupItemList.Save( rStrm );
+ // SXGROUPINFO
+ WriteSxgroupinfo( rStrm );
+ // SXNUMGROUP and additional grouping items (grouping limit settings)
+ WriteSxnumgroup( rStrm );
+ // list of original items
+ maOrigItemList.Save( rStrm );
+}
+
+// private --------------------------------------------------------------------
+
+const XclExpPCField::XclExpPCItemList& XclExpPCField::GetVisItemList() const
+{
+ OSL_ENSURE( IsStandardField() == maGroupItemList.IsEmpty(),
+ "XclExpPCField::GetVisItemList - unexpected additional items in standard field" );
+ return IsStandardField() ? maOrigItemList : maGroupItemList;
+}
+
+void XclExpPCField::InitStandardField( const ScRange& rRange )
+{
+ OSL_ENSURE( IsStandardField(), "XclExpPCField::InitStandardField - only for standard fields" );
+ OSL_ENSURE( rRange.aStart.Col() == rRange.aEnd.Col(), "XclExpPCField::InitStandardField - cell range with multiple columns" );
+
+ ScDocument& rDoc = GetDoc();
+ SvNumberFormatter& rFormatter = GetFormatter();
+
+ // field name is in top cell of the range
+ ScAddress aPos( rRange.aStart );
+ maFieldInfo.maName = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
+ // #i76047# maximum field name length in pivot cache is 255
+ if (maFieldInfo.maName.getLength() > EXC_PC_MAXSTRLEN)
+ maFieldInfo.maName = maFieldInfo.maName.copy(0, EXC_PC_MAXSTRLEN);
+
+ // loop over all cells, create pivot cache items
+ for( aPos.IncRow(); (aPos.Row() <= rRange.aEnd.Row()) && (maOrigItemList.GetSize() < EXC_PC_MAXITEMCOUNT); aPos.IncRow() )
+ {
+ OUString aText = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
+ if( rDoc.HasValueData( aPos.Col(), aPos.Row(), aPos.Tab() ) )
+ {
+ double fValue = rDoc.GetValue( aPos );
+ SvNumFormatType nFmtType = rFormatter.GetType( rDoc.GetNumberFormat( rDoc.GetNonThreadedContext(), aPos ) );
+ if( nFmtType == SvNumFormatType::LOGICAL )
+ InsertOrigBoolItem( fValue != 0, aText );
+ else if( nFmtType & SvNumFormatType::DATETIME )
+ InsertOrigDateTimeItem( GetDateTimeFromDouble( ::std::max( fValue, 0.0 ) ), aText );
+ else
+ InsertOrigDoubleItem( fValue, aText );
+ }
+ else
+ {
+ InsertOrigTextItem( aText );
+ }
+ }
+}
+
+void XclExpPCField::InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim )
+{
+ OSL_ENSURE( IsGroupField(), "XclExpPCField::InitStdGroupField - only for standard grouping fields" );
+
+ maFieldInfo.mnBaseItems = rBaseField.GetItemCount();
+ maGroupOrder.resize( maFieldInfo.mnBaseItems, EXC_PC_NOITEM );
+
+ // loop over all groups of this field
+ for( tools::Long nGroupIdx = 0, nGroupCount = rGroupDim.GetGroupCount(); nGroupIdx < nGroupCount; ++nGroupIdx )
+ {
+ const ScDPSaveGroupItem& rGroupItem = rGroupDim.GetGroupByIndex( nGroupIdx );
+ // the index of the new item containing the grouping name
+ sal_uInt16 nGroupItemIdx = EXC_PC_NOITEM;
+ // loop over all elements of one group
+ for( size_t nElemIdx = 0, nElemCount = rGroupItem.GetElementCount(); nElemIdx < nElemCount; ++nElemIdx )
+ {
+ if (const OUString* pElemName = rGroupItem.GetElementByIndex(nElemIdx))
+ {
+ // try to find the item that is part of the group in the base field
+ sal_uInt16 nBaseItemIdx = rBaseField.GetItemIndex( *pElemName );
+ if( nBaseItemIdx < maFieldInfo.mnBaseItems )
+ {
+ // add group name item only if there are any valid base items
+ if( nGroupItemIdx == EXC_PC_NOITEM )
+ nGroupItemIdx = InsertGroupItem( new XclExpPCItem( rGroupItem.GetGroupName() ) );
+ maGroupOrder[ nBaseItemIdx ] = nGroupItemIdx;
+ }
+ }
+ }
+ }
+
+ // add items and base item indexes of all ungrouped elements
+ for( sal_uInt16 nBaseItemIdx = 0; nBaseItemIdx < maFieldInfo.mnBaseItems; ++nBaseItemIdx )
+ // items that are not part of a group still have the EXC_PC_NOITEM entry
+ if( maGroupOrder[ nBaseItemIdx ] == EXC_PC_NOITEM )
+ // try to find the base item
+ if( const XclExpPCItem* pBaseItem = rBaseField.GetItem( nBaseItemIdx ) )
+ // create a clone of the base item, insert its index into item order list
+ maGroupOrder[ nBaseItemIdx ] = InsertGroupItem( new XclExpPCItem( *pBaseItem ) );
+}
+
+void XclExpPCField::InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo )
+{
+ OSL_ENSURE( IsStandardField(), "XclExpPCField::InitNumGroupField - only for standard fields" );
+ OSL_ENSURE( rNumInfo.mbEnable, "XclExpPCField::InitNumGroupField - numeric grouping not enabled" );
+
+ // new field type, date type, limit settings (min/max/step/auto)
+ if( rNumInfo.mbDateValues )
+ {
+ // special case: group by days with step count
+ meFieldType = EXC_PCFIELD_DATEGROUP;
+ maNumGroupInfo.SetScDateType( css::sheet::DataPilotFieldGroupBy::DAYS );
+ SetDateGroupLimit( rNumInfo, true );
+ }
+ else
+ {
+ meFieldType = EXC_PCFIELD_NUMGROUP;
+ maNumGroupInfo.SetNumType();
+ SetNumGroupLimit( rNumInfo );
+ }
+
+ // generate visible items
+ InsertNumDateGroupItems( rDPObj, rNumInfo );
+}
+
+void XclExpPCField::InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart )
+{
+ OSL_ENSURE( IsStandardField() || IsStdGroupField(), "XclExpPCField::InitDateGroupField - only for standard fields" );
+ OSL_ENSURE( rDateInfo.mbEnable, "XclExpPCField::InitDateGroupField - date grouping not enabled" );
+
+ // new field type
+ meFieldType = IsStandardField() ? EXC_PCFIELD_DATEGROUP : EXC_PCFIELD_DATECHILD;
+
+ // date type, limit settings (min/max/step/auto)
+ maNumGroupInfo.SetScDateType( nDatePart );
+ SetDateGroupLimit( rDateInfo, false );
+
+ // generate visible items
+ InsertNumDateGroupItems( rDPObj, rDateInfo, nDatePart );
+}
+
+void XclExpPCField::InsertItemArrayIndex( size_t nListPos )
+{
+ OSL_ENSURE( IsStandardField(), "XclExpPCField::InsertItemArrayIndex - only for standard fields" );
+ maIndexVec.push_back( static_cast< sal_uInt16 >( nListPos ) );
+}
+
+void XclExpPCField::InsertOrigItem( XclExpPCItem* pNewItem )
+{
+ size_t nItemIdx = maOrigItemList.GetSize();
+ maOrigItemList.AppendNewRecord( pNewItem );
+ InsertItemArrayIndex( nItemIdx );
+ mnTypeFlags |= pNewItem->GetTypeFlag();
+}
+
+void XclExpPCField::InsertOrigTextItem( const OUString& rText )
+{
+ size_t nPos = 0;
+ bool bFound = false;
+ // #i76047# maximum item text length in pivot cache is 255
+ OUString aShortText = rText.copy( 0, ::std::min(rText.getLength(), EXC_PC_MAXSTRLEN ) );
+ for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
+ if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsText( aShortText )) )
+ InsertItemArrayIndex( nPos );
+ if( !bFound )
+ InsertOrigItem( new XclExpPCItem( aShortText ) );
+}
+
+void XclExpPCField::InsertOrigDoubleItem( double fValue, const OUString& rText )
+{
+ size_t nPos = 0;
+ bool bFound = false;
+ for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
+ if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDouble( fValue )) )
+ InsertItemArrayIndex( nPos );
+ if( !bFound )
+ InsertOrigItem( new XclExpPCItem( fValue, rText ) );
+}
+
+void XclExpPCField::InsertOrigDateTimeItem( const DateTime& rDateTime, const OUString& rText )
+{
+ size_t nPos = 0;
+ bool bFound = false;
+ for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
+ if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDateTime( rDateTime )) )
+ InsertItemArrayIndex( nPos );
+ if( !bFound )
+ InsertOrigItem( new XclExpPCItem( rDateTime, rText ) );
+}
+
+void XclExpPCField::InsertOrigBoolItem( bool bValue, const OUString& rText )
+{
+ size_t nPos = 0;
+ bool bFound = false;
+ for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
+ if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsBool( bValue )) )
+ InsertItemArrayIndex( nPos );
+ if( !bFound )
+ InsertOrigItem( new XclExpPCItem( bValue, rText ) );
+}
+
+sal_uInt16 XclExpPCField::InsertGroupItem( XclExpPCItem* pNewItem )
+{
+ maGroupItemList.AppendNewRecord( pNewItem );
+ return static_cast< sal_uInt16 >( maGroupItemList.GetSize() - 1 );
+}
+
+void XclExpPCField::InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart )
+{
+ OSL_ENSURE( rDPObj.GetSheetDesc(), "XclExpPCField::InsertNumDateGroupItems - cannot generate element list" );
+ const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc();
+ if(!pSrcDesc)
+ return;
+
+ // get the string collection with original source elements
+ const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ const ScDPDimensionSaveData* pDimData = nullptr;
+ if (pSaveData)
+ pDimData = pSaveData->GetExistingDimensionData();
+
+ const ScDPCache* pCache = pSrcDesc->CreateCache(pDimData);
+ if (!pCache)
+ return;
+
+ ScSheetDPData aDPData(&GetDoc(), *pSrcDesc, *pCache);
+ tools::Long nDim = GetFieldIndex();
+ // get the string collection with generated grouping elements
+ ScDPNumGroupDimension aTmpDim( rNumInfo );
+ if( nDatePart != 0 )
+ aTmpDim.SetDateDimension();
+ const std::vector<SCROW>& aMemberIds = aTmpDim.GetNumEntries(
+ static_cast<SCCOL>(nDim), pCache);
+ for (SCROW nMemberId : aMemberIds)
+ {
+ const ScDPItemData* pData = aDPData.GetMemberById(nDim, nMemberId);
+ if ( pData )
+ {
+ OUString aStr = pCache->GetFormattedString(nDim, *pData, false);
+ InsertGroupItem(new XclExpPCItem(aStr));
+ }
+ }
+}
+
+void XclExpPCField::SetNumGroupLimit( const ScDPNumGroupInfo& rNumInfo )
+{
+ ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rNumInfo.mbAutoStart );
+ ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rNumInfo.mbAutoEnd );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfStart ) );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfEnd ) );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfStep ) );
+}
+
+void XclExpPCField::SetDateGroupLimit( const ScDPNumGroupInfo& rDateInfo, bool bUseStep )
+{
+ ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rDateInfo.mbAutoStart );
+ ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rDateInfo.mbAutoEnd );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.mfStart ) ) );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.mfEnd ) ) );
+ sal_Int16 nStep = bUseStep ? limit_cast< sal_Int16 >( rDateInfo.mfStep, 1, SAL_MAX_INT16 ) : 1;
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( nStep ) );
+}
+
+void XclExpPCField::Finalize()
+{
+ // flags
+ ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS, !GetVisItemList().IsEmpty() );
+ // Excel writes long indexes even for 0x0100 items (indexes from 0x00 to 0xFF)
+ ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT, maOrigItemList.GetSize() >= 0x0100 );
+ ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP, IsNumGroupField() || IsDateGroupField() );
+ /* mnTypeFlags is updated in all Insert***Item() functions. Now the flags
+ for the current combination of item types is added to the flags. */
+ ::set_flag( maFieldInfo.mnFlags, spnPCItemFlags[ mnTypeFlags ] );
+
+ // item count fields
+ maFieldInfo.mnVisItems = static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
+ maFieldInfo.mnGroupItems = static_cast< sal_uInt16 >( maGroupItemList.GetSize() );
+ // maFieldInfo.mnBaseItems set in InitStdGroupField()
+ maFieldInfo.mnOrigItems = static_cast< sal_uInt16 >( maOrigItemList.GetSize() );
+}
+
+void XclExpPCField::WriteSxnumgroup( XclExpStream& rStrm )
+{
+ if( IsNumGroupField() || IsDateGroupField() )
+ {
+ // SXNUMGROUP record
+ rStrm.StartRecord( EXC_ID_SXNUMGROUP, 2 );
+ rStrm << maNumGroupInfo;
+ rStrm.EndRecord();
+
+ // limits (min/max/step) for numeric grouping
+ OSL_ENSURE( maNumGroupLimits.GetSize() == 3,
+ "XclExpPCField::WriteSxnumgroup - missing numeric grouping limits" );
+ maNumGroupLimits.Save( rStrm );
+ }
+}
+
+void XclExpPCField::WriteSxgroupinfo( XclExpStream& rStrm )
+{
+ OSL_ENSURE( IsStdGroupField() != maGroupOrder.empty(),
+ "XclExpPCField::WriteSxgroupinfo - missing grouping info" );
+ if( IsStdGroupField() && !maGroupOrder.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_SXGROUPINFO, 2 * maGroupOrder.size() );
+ for( const auto& rItem : maGroupOrder )
+ rStrm << rItem;
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpPCField::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maFieldInfo;
+}
+
+XclExpPivotCache::XclExpPivotCache( const XclExpRoot& rRoot, const ScDPObject& rDPObj, sal_uInt16 nListIdx ) :
+ XclExpRoot( rRoot ),
+ mnListIdx( nListIdx ),
+ mbValid( false )
+{
+ // source from sheet only
+ const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc();
+ if(!pSrcDesc)
+ return;
+
+ /* maOrigSrcRange: Range received from the DataPilot object.
+ maExpSrcRange: Range written to the DCONREF record.
+ maDocSrcRange: Range used to get source data from Calc document.
+ This range may be shorter than maExpSrcRange to improve export
+ performance (#i22541#). */
+ maOrigSrcRange = maExpSrcRange = maDocSrcRange = pSrcDesc->GetSourceRange();
+ maSrcRangeName = pSrcDesc->GetRangeName();
+
+ // internal sheet data only
+ SCTAB nScTab = maExpSrcRange.aStart.Tab();
+ if( !((nScTab == maExpSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( nScTab )) )
+ return;
+
+ // ValidateRange() restricts source range to valid Excel limits
+ if( !GetAddressConverter().ValidateRange( maExpSrcRange, true ) )
+ return;
+
+ // #i22541# skip empty cell areas (performance)
+ SCCOL nDocCol1, nDocCol2;
+ SCROW nDocRow1, nDocRow2;
+ GetDoc().GetDataStart( nScTab, nDocCol1, nDocRow1 );
+ GetDoc().GetPrintArea( nScTab, nDocCol2, nDocRow2, false );
+ SCCOL nSrcCol1 = maExpSrcRange.aStart.Col();
+ SCROW nSrcRow1 = maExpSrcRange.aStart.Row();
+ SCCOL nSrcCol2 = maExpSrcRange.aEnd.Col();
+ SCROW nSrcRow2 = maExpSrcRange.aEnd.Row();
+
+ // #i22541# do not store index list for too big ranges
+ if( 2 * (nDocRow2 - nDocRow1) < (nSrcRow2 - nSrcRow1) )
+ ::set_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA, false );
+
+ // adjust row indexes, keep one row of empty area to surely have the empty cache item
+ if( nSrcRow1 < nDocRow1 )
+ nSrcRow1 = nDocRow1 - 1;
+ if( nSrcRow2 > nDocRow2 )
+ nSrcRow2 = nDocRow2 + 1;
+
+ maDocSrcRange.aStart.SetCol( ::std::max( nDocCol1, nSrcCol1 ) );
+ maDocSrcRange.aStart.SetRow( nSrcRow1 );
+ maDocSrcRange.aEnd.SetCol( ::std::min( nDocCol2, nSrcCol2 ) );
+ maDocSrcRange.aEnd.SetRow( nSrcRow2 );
+
+ GetDoc().GetName( nScTab, maTabName );
+ maPCInfo.mnSrcRecs = static_cast< sal_uInt32 >( maExpSrcRange.aEnd.Row() - maExpSrcRange.aStart.Row() );
+ maPCInfo.mnStrmId = nListIdx + 1;
+ maPCInfo.mnSrcType = EXC_SXDB_SRC_SHEET;
+
+ AddFields( rDPObj );
+
+ mbValid = true;
+}
+
+bool XclExpPivotCache::HasItemIndexList() const
+{
+ return ::get_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA );
+}
+
+sal_uInt16 XclExpPivotCache::GetFieldCount() const
+{
+ return static_cast< sal_uInt16 >( maFieldList.GetSize() );
+}
+
+const XclExpPCField* XclExpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
+{
+ return maFieldList.GetRecord( nFieldIdx );
+}
+
+bool XclExpPivotCache::HasAddFields() const
+{
+ // pivot cache can be shared, if there are no additional cache fields
+ return maPCInfo.mnStdFields < maPCInfo.mnTotalFields;
+}
+
+bool XclExpPivotCache::HasEqualDataSource( const ScDPObject& rDPObj ) const
+{
+ /* For now, only sheet sources are supported, therefore it is enough to
+ compare the ScSheetSourceDesc. Later, there should be done more complicated
+ comparisons regarding the source type of rDPObj and this cache. */
+ if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
+ return pSrcDesc->GetSourceRange() == maOrigSrcRange;
+ return false;
+}
+
+void XclExpPivotCache::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
+ // SXIDSTM
+ XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).Save( rStrm );
+ // SXVS
+ XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).Save( rStrm );
+
+ if (!maSrcRangeName.isEmpty())
+ // DCONNAME
+ WriteDConName(rStrm);
+ else
+ // DCONREF
+ WriteDconref(rStrm);
+
+ // create the pivot cache storage stream
+ WriteCacheStream();
+}
+
+void XclExpPivotCache::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+}
+
+void XclExpPivotCache::AddFields( const ScDPObject& rDPObj )
+{
+ AddStdFields( rDPObj );
+ maPCInfo.mnStdFields = GetFieldCount();
+ AddGroupFields( rDPObj );
+ maPCInfo.mnTotalFields = GetFieldCount();
+};
+
+void XclExpPivotCache::AddStdFields( const ScDPObject& rDPObj )
+{
+ // if item index list is not written, used shortened source range (maDocSrcRange) for performance
+ const ScRange& rRange = HasItemIndexList() ? maExpSrcRange : maDocSrcRange;
+ // create a standard pivot cache field for each source column
+ for( SCCOL nScCol = rRange.aStart.Col(), nEndScCol = rRange.aEnd.Col(); nScCol <= nEndScCol; ++nScCol )
+ {
+ ScRange aColRange( rRange );
+ aColRange.aStart.SetCol( nScCol );
+ aColRange.aEnd.SetCol( nScCol );
+ maFieldList.AppendNewRecord( new XclExpPCField(
+ GetRoot(), GetFieldCount(), rDPObj, aColRange ) );
+ }
+}
+
+void XclExpPivotCache::AddGroupFields( const ScDPObject& rDPObj )
+{
+ const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ if(!pSaveData)
+ return;
+ const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData();
+ if( !pSaveDimData )
+ return;
+
+ // loop over all existing standard fields to find their group fields
+ for( sal_uInt16 nFieldIdx = 0; nFieldIdx < maPCInfo.mnStdFields; ++nFieldIdx )
+ {
+ if( XclExpPCField* pCurrStdField = maFieldList.GetRecord( nFieldIdx ) )
+ {
+ const ScDPSaveGroupDimension* pGroupDim = pSaveDimData->GetGroupDimForBase( pCurrStdField->GetFieldName() );
+ XclExpPCField* pLastGroupField = pCurrStdField;
+ while( pGroupDim )
+ {
+ // insert the new grouping field
+ XclExpPCFieldRef xNewGroupField = new XclExpPCField(
+ GetRoot(), GetFieldCount(), rDPObj, *pGroupDim, *pCurrStdField );
+ maFieldList.AppendRecord( xNewGroupField );
+
+ // register new grouping field at current grouping field, building a chain
+ pLastGroupField->SetGroupChildField( *xNewGroupField );
+
+ // next grouping dimension
+ pGroupDim = pSaveDimData->GetGroupDimForBase( pGroupDim->GetGroupDimName() );
+ pLastGroupField = xNewGroupField.get();
+ }
+ }
+ }
+}
+
+void XclExpPivotCache::WriteDconref( XclExpStream& rStrm ) const
+{
+ XclExpString aRef( XclExpUrlHelper::EncodeUrl( GetRoot(), u"", &maTabName ) );
+ rStrm.StartRecord( EXC_ID_DCONREF, 7 + aRef.GetSize() );
+ rStrm << static_cast< sal_uInt16 >( maExpSrcRange.aStart.Row() )
+ << static_cast< sal_uInt16 >( maExpSrcRange.aEnd.Row() )
+ << static_cast< sal_uInt8 >( maExpSrcRange.aStart.Col() )
+ << static_cast< sal_uInt8 >( maExpSrcRange.aEnd.Col() )
+ << aRef
+ << sal_uInt8( 0 );
+ rStrm.EndRecord();
+}
+
+void XclExpPivotCache::WriteDConName( XclExpStream& rStrm ) const
+{
+ XclExpString aName(maSrcRangeName);
+ rStrm.StartRecord(EXC_ID_DCONNAME, aName.GetSize() + 2);
+ rStrm << aName << sal_uInt16(0);
+ rStrm.EndRecord();
+}
+
+void XclExpPivotCache::WriteCacheStream()
+{
+ tools::SvRef<SotStorage> xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
+ tools::SvRef<SotStorageStream> xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( maPCInfo.mnStrmId ) );
+ if( !xSvStrm.is() )
+ return;
+
+ XclExpStream aStrm( *xSvStrm, GetRoot() );
+ // SXDB
+ WriteSxdb( aStrm );
+ // SXDBEX
+ WriteSxdbex( aStrm );
+ // field list (SXFIELD and items)
+ maFieldList.Save( aStrm );
+ // index table (list of SXINDEXLIST)
+ WriteSxindexlistList( aStrm );
+ // EOF
+ XclExpEmptyRecord( EXC_ID_EOF ).Save( aStrm );
+}
+
+void XclExpPivotCache::WriteSxdb( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXDB, 21 );
+ rStrm << maPCInfo;
+ rStrm.EndRecord();
+}
+
+void XclExpPivotCache::WriteSxdbex( XclExpStream& rStrm )
+{
+ rStrm.StartRecord( EXC_ID_SXDBEX, 12 );
+ rStrm << EXC_SXDBEX_CREATION_DATE
+ << sal_uInt32( 0 ); // number of SXFORMULA records
+ rStrm.EndRecord();
+}
+
+void XclExpPivotCache::WriteSxindexlistList( XclExpStream& rStrm ) const
+{
+ if( !HasItemIndexList() )
+ return;
+
+ std::size_t nRecSize = 0;
+ size_t nPos, nSize = maFieldList.GetSize();
+ for( nPos = 0; nPos < nSize; ++nPos )
+ nRecSize += maFieldList.GetRecord( nPos )->GetIndexSize();
+
+ for( sal_uInt32 nSrcRow = 0; nSrcRow < maPCInfo.mnSrcRecs; ++nSrcRow )
+ {
+ rStrm.StartRecord( EXC_ID_SXINDEXLIST, nRecSize );
+ for( nPos = 0; nPos < nSize; ++nPos )
+ maFieldList.GetRecord( nPos )->WriteIndex( rStrm, nSrcRow );
+ rStrm.EndRecord();
+ }
+}
+
+// Pivot table
+
+namespace {
+
+/** Returns a display string for a data field containing the field name and aggregation function. */
+OUString lclGetDataFieldCaption( std::u16string_view rFieldName, ScGeneralFunction eFunc )
+{
+ OUString aCaption;
+
+ TranslateId pResIdx;
+ switch( eFunc )
+ {
+ case ScGeneralFunction::SUM: pResIdx = STR_FUN_TEXT_SUM; break;
+ case ScGeneralFunction::COUNT: pResIdx = STR_FUN_TEXT_COUNT; break;
+ case ScGeneralFunction::AVERAGE: pResIdx = STR_FUN_TEXT_AVG; break;
+ case ScGeneralFunction::MAX: pResIdx = STR_FUN_TEXT_MAX; break;
+ case ScGeneralFunction::MIN: pResIdx = STR_FUN_TEXT_MIN; break;
+ case ScGeneralFunction::PRODUCT: pResIdx = STR_FUN_TEXT_PRODUCT; break;
+ case ScGeneralFunction::COUNTNUMS: pResIdx = STR_FUN_TEXT_COUNT; break;
+ case ScGeneralFunction::STDEV: pResIdx = STR_FUN_TEXT_STDDEV; break;
+ case ScGeneralFunction::STDEVP: pResIdx = STR_FUN_TEXT_STDDEV; break;
+ case ScGeneralFunction::VAR: pResIdx = STR_FUN_TEXT_VAR; break;
+ case ScGeneralFunction::VARP: pResIdx = STR_FUN_TEXT_VAR; break;
+ default:;
+ }
+ if (pResIdx)
+ aCaption = ScResId(pResIdx) + " - ";
+ aCaption += rFieldName;
+ return aCaption;
+}
+
+} // namespace
+
+XclExpPTItem::XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx ) :
+ XclExpRecord( EXC_ID_SXVI, 8 ),
+ mpCacheItem( rCacheField.GetItem( nCacheIdx ) )
+{
+ maItemInfo.mnType = EXC_SXVI_TYPE_DATA;
+ maItemInfo.mnCacheIdx = nCacheIdx;
+ maItemInfo.maVisName.mbUseCache = mpCacheItem != nullptr;
+}
+
+XclExpPTItem::XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx ) :
+ XclExpRecord( EXC_ID_SXVI, 8 ),
+ mpCacheItem( nullptr )
+{
+ maItemInfo.mnType = nItemType;
+ maItemInfo.mnCacheIdx = nCacheIdx;
+ maItemInfo.maVisName.mbUseCache = true;
+}
+
+OUString XclExpPTItem::GetItemName() const
+{
+ return mpCacheItem ? mpCacheItem->ConvertToText() : OUString();
+}
+
+void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem )
+{
+ // #i115659# GetIsVisible() is not valid if HasIsVisible() returns false, default is 'visible' then
+ ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, rSaveMem.HasIsVisible() && !rSaveMem.GetIsVisible() );
+ // #i115659# GetShowDetails() is not valid if HasShowDetails() returns false, default is 'show detail' then
+ ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, rSaveMem.HasShowDetails() && !rSaveMem.GetShowDetails() );
+
+ // visible name
+ const std::optional<OUString> & pVisName = rSaveMem.GetLayoutName();
+ if (pVisName && *pVisName != GetItemName())
+ maItemInfo.SetVisName(*pVisName);
+}
+
+void XclExpPTItem::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maItemInfo;
+}
+
+XclExpPTField::XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
+ mrPTable( rPTable ),
+ mpCacheField( rPTable.GetCacheField( nCacheIdx ) )
+{
+ maFieldInfo.mnCacheIdx = nCacheIdx;
+
+ // create field items
+ if( mpCacheField )
+ for( sal_uInt16 nItemIdx = 0, nItemCount = mpCacheField->GetItemCount(); nItemIdx < nItemCount; ++nItemIdx )
+ maItemList.AppendNewRecord( new XclExpPTItem( *mpCacheField, nItemIdx ) );
+ maFieldInfo.mnItemCount = static_cast< sal_uInt16 >( maItemList.GetSize() );
+}
+
+// data access ----------------------------------------------------------------
+
+OUString XclExpPTField::GetFieldName() const
+{
+ return mpCacheField ? mpCacheField->GetFieldName() : OUString();
+}
+
+sal_uInt16 XclExpPTField::GetLastDataInfoIndex() const
+{
+ OSL_ENSURE( !maDataInfoVec.empty(), "XclExpPTField::GetLastDataInfoIndex - no data info found" );
+ // will return 0xFFFF for empty vector -> ok
+ return static_cast< sal_uInt16 >( maDataInfoVec.size() - 1 );
+}
+
+sal_uInt16 XclExpPTField::GetItemIndex( std::u16string_view rName, sal_uInt16 nDefaultIdx ) const
+{
+ for( size_t nPos = 0, nSize = maItemList.GetSize(); nPos < nSize; ++nPos )
+ if( maItemList.GetRecord( nPos )->GetItemName() == rName )
+ return static_cast< sal_uInt16 >( nPos );
+ return nDefaultIdx;
+}
+
+// fill data --------------------------------------------------------------
+
+/**
+ * Calc's subtotal names are escaped with backslashes ('\'), while Excel's
+ * are not escaped at all.
+ */
+static OUString lcl_convertCalcSubtotalName(const OUString& rName)
+{
+ OUStringBuffer aBuf;
+ const sal_Unicode* p = rName.getStr();
+ sal_Int32 n = rName.getLength();
+ bool bEscaped = false;
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ const sal_Unicode c = p[i];
+ if (!bEscaped && c == '\\')
+ {
+ bEscaped = true;
+ continue;
+ }
+
+ aBuf.append(c);
+ bEscaped = false;
+ }
+ return aBuf.makeStringAndClear();
+}
+
+void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+{
+ // orientation
+ DataPilotFieldOrientation eOrient = rSaveDim.GetOrientation();
+ OSL_ENSURE( eOrient != DataPilotFieldOrientation_DATA, "XclExpPTField::SetPropertiesFromDim - called for data field" );
+ maFieldInfo.AddApiOrient( eOrient );
+
+ // show empty items (#i115659# GetShowEmpty() is not valid if HasShowEmpty() returns false, default is false then)
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.HasShowEmpty() && rSaveDim.GetShowEmpty() );
+
+ // visible name
+ const std::optional<OUString> & pLayoutName = rSaveDim.GetLayoutName();
+ if (pLayoutName && *pLayoutName != GetFieldName())
+ maFieldInfo.SetVisName(*pLayoutName);
+
+ const std::optional<OUString> & pSubtotalName = rSaveDim.GetSubtotalName();
+ if (pSubtotalName)
+ {
+ OUString aSubName = lcl_convertCalcSubtotalName(*pSubtotalName);
+ maFieldExtInfo.mpFieldTotalName = aSubName;
+ }
+
+ // subtotals
+ XclPTSubtotalVec aSubtotals;
+ aSubtotals.reserve( static_cast< size_t >( rSaveDim.GetSubTotalsCount() ) );
+ for( tools::Long nSubtIdx = 0, nSubtCount = rSaveDim.GetSubTotalsCount(); nSubtIdx < nSubtCount; ++nSubtIdx )
+ aSubtotals.push_back( rSaveDim.GetSubTotalFunc( nSubtIdx ) );
+ maFieldInfo.SetSubtotals( aSubtotals );
+
+ // sorting
+ if( const DataPilotFieldSortInfo* pSortInfo = rSaveDim.GetSortInfo() )
+ {
+ maFieldExtInfo.SetApiSortMode( pSortInfo->Mode );
+ if( pSortInfo->Mode == css::sheet::DataPilotFieldSortMode::DATA )
+ maFieldExtInfo.mnSortField = mrPTable.GetDataFieldIndex( pSortInfo->Field, EXC_SXVDEX_SORT_OWN );
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC, pSortInfo->IsAscending );
+ }
+
+ // auto show
+ if( const DataPilotFieldAutoShowInfo* pShowInfo = rSaveDim.GetAutoShowInfo() )
+ {
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW, pShowInfo->IsEnabled );
+ maFieldExtInfo.SetApiAutoShowMode( pShowInfo->ShowItemsMode );
+ maFieldExtInfo.SetApiAutoShowCount( pShowInfo->ItemCount );
+ maFieldExtInfo.mnShowField = mrPTable.GetDataFieldIndex( pShowInfo->DataField, EXC_SXVDEX_SHOW_NONE );
+ }
+
+ // layout
+ if( const DataPilotFieldLayoutInfo* pLayoutInfo = rSaveDim.GetLayoutInfo() )
+ {
+ maFieldExtInfo.SetApiLayoutMode( pLayoutInfo->LayoutMode );
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK, pLayoutInfo->AddEmptyLines );
+ }
+
+ // special page field properties
+ if( eOrient == DataPilotFieldOrientation_PAGE )
+ {
+ maPageInfo.mnField = GetFieldIndex();
+ maPageInfo.mnSelItem = EXC_SXPI_ALLITEMS;
+ }
+
+ // item properties
+ const ScDPSaveDimension::MemberList &rMembers = rSaveDim.GetMembers();
+ for (const auto& pMember : rMembers)
+ if( XclExpPTItem* pItem = GetItemAcc( pMember->GetName() ) )
+ pItem->SetPropertiesFromMember( *pMember );
+}
+
+void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+{
+ maDataInfoVec.emplace_back( );
+ XclPTDataFieldInfo& rDataInfo = maDataInfoVec.back();
+ rDataInfo.mnField = GetFieldIndex();
+
+ // orientation
+ maFieldInfo.AddApiOrient( DataPilotFieldOrientation_DATA );
+
+ // aggregation function
+ ScGeneralFunction eFunc = rSaveDim.GetFunction();
+ rDataInfo.SetApiAggFunc( eFunc );
+
+ // visible name
+ const std::optional<OUString> & pVisName = rSaveDim.GetLayoutName();
+ if (pVisName)
+ rDataInfo.SetVisName(*pVisName);
+ else
+ rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
+
+ // result field reference
+ if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() )
+ {
+ rDataInfo.SetApiRefType( pFieldRef->ReferenceType );
+ rDataInfo.SetApiRefItemType( pFieldRef->ReferenceItemType );
+ if( const XclExpPTField* pRefField = mrPTable.GetField( pFieldRef->ReferenceField ) )
+ {
+ rDataInfo.mnRefField = pRefField->GetFieldIndex();
+ if( pFieldRef->ReferenceItemType == css::sheet::DataPilotFieldReferenceItemType::NAMED )
+ rDataInfo.mnRefItem = pRefField->GetItemIndex( pFieldRef->ReferenceItemName, 0 );
+ }
+ }
+}
+
+void XclExpPTField::AppendSubtotalItems()
+{
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) AppendSubtotalItem( EXC_SXVI_TYPE_DEFAULT );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_SUM ) AppendSubtotalItem( EXC_SXVI_TYPE_SUM );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNT ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNT );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) AppendSubtotalItem( EXC_SXVI_TYPE_AVERAGE );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MAX ) AppendSubtotalItem( EXC_SXVI_TYPE_MAX );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MIN ) AppendSubtotalItem( EXC_SXVI_TYPE_MIN );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_PROD ) AppendSubtotalItem( EXC_SXVI_TYPE_PROD );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNTNUM );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEV ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEV );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEVP );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VAR ) AppendSubtotalItem( EXC_SXVI_TYPE_VAR );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VARP ) AppendSubtotalItem( EXC_SXVI_TYPE_VARP );
+}
+
+// records --------------------------------------------------------------------
+
+void XclExpPTField::WriteSxpiEntry( XclExpStream& rStrm ) const
+{
+ rStrm << maPageInfo;
+}
+
+void XclExpPTField::WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const
+{
+ OSL_ENSURE( nDataInfoIdx < maDataInfoVec.size(), "XclExpPTField::WriteSxdi - data field not found" );
+ if( nDataInfoIdx < maDataInfoVec.size() )
+ {
+ rStrm.StartRecord( EXC_ID_SXDI, 12 );
+ rStrm << maDataInfoVec[ nDataInfoIdx ];
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpPTField::Save( XclExpStream& rStrm )
+{
+ // SXVD
+ WriteSxvd( rStrm );
+ // list of SXVI records
+ maItemList.Save( rStrm );
+ // SXVDEX
+ WriteSxvdex( rStrm );
+}
+
+// private --------------------------------------------------------------------
+
+XclExpPTItem* XclExpPTField::GetItemAcc( std::u16string_view rName )
+{
+ XclExpPTItem* pItem = nullptr;
+ for( size_t nPos = 0, nSize = maItemList.GetSize(); !pItem && (nPos < nSize); ++nPos )
+ if( maItemList.GetRecord( nPos )->GetItemName() == rName )
+ pItem = maItemList.GetRecord( nPos );
+ return pItem;
+}
+
+void XclExpPTField::AppendSubtotalItem( sal_uInt16 nItemType )
+{
+ maItemList.AppendNewRecord( new XclExpPTItem( nItemType, EXC_SXVI_DEFAULT_CACHE ) );
+ ++maFieldInfo.mnItemCount;
+}
+
+void XclExpPTField::WriteSxvd( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXVD, 10 );
+ rStrm << maFieldInfo;
+ rStrm.EndRecord();
+}
+
+void XclExpPTField::WriteSxvdex( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXVDEX, 20 );
+ rStrm << maFieldExtInfo;
+ rStrm.EndRecord();
+}
+
+XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& rDPObj, const XclExpPivotCache& rPCache ) :
+ XclExpRoot( rRoot ),
+ mrPCache( rPCache ),
+ maDataOrientField( *this, EXC_SXIVD_DATA ),
+ mnOutScTab( 0 ),
+ mbValid( false ),
+ mbFilterBtn( false )
+{
+ const ScRange& rOutScRange = rDPObj.GetOutRange();
+ if( !GetAddressConverter().ConvertRange( maPTInfo.maOutXclRange, rOutScRange, true ) )
+ return;
+
+ // DataPilot properties -----------------------------------------------
+
+ // pivot table properties from DP object
+ mnOutScTab = rOutScRange.aStart.Tab();
+ maPTInfo.maTableName = rDPObj.GetName();
+ maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex();
+
+ maPTViewEx9Info.Init( rDPObj );
+
+ const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ if( !pSaveData )
+ return;
+
+ // additional properties from ScDPSaveData
+ SetPropertiesFromDP( *pSaveData );
+
+ // loop over all dimensions ---------------------------------------
+
+ /* 1) Default-construct all pivot table fields for all pivot cache fields. */
+ for( sal_uInt16 nFieldIdx = 0, nFieldCount = mrPCache.GetFieldCount(); nFieldIdx < nFieldCount; ++nFieldIdx )
+ maFieldList.AppendNewRecord( new XclExpPTField( *this, nFieldIdx ) );
+
+ const ScDPSaveData::DimsType& rDimList = pSaveData->GetDimensions();
+
+ /* 2) First process all data dimensions, they are needed for extended
+ settings of row/column/page fields (sorting/auto show). */
+ for (auto const& iter : rDimList)
+ {
+ if (iter->GetOrientation() == DataPilotFieldOrientation_DATA)
+ SetDataFieldPropertiesFromDim(*iter);
+ }
+
+ /* 3) Row/column/page/hidden fields. */
+ for (auto const& iter : rDimList)
+ {
+ if (iter->GetOrientation() != DataPilotFieldOrientation_DATA)
+ SetFieldPropertiesFromDim(*iter);
+ }
+
+ // Finalize -------------------------------------------------------
+
+ Finalize();
+ mbValid = true;
+}
+
+const XclExpPCField* XclExpPivotTable::GetCacheField( sal_uInt16 nCacheIdx ) const
+{
+ return mrPCache.GetField( nCacheIdx );
+}
+
+const XclExpPTField* XclExpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
+{
+ return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : maFieldList.GetRecord( nFieldIdx );
+}
+
+const XclExpPTField* XclExpPivotTable::GetField( std::u16string_view rName ) const
+{
+ return const_cast< XclExpPivotTable* >( this )->GetFieldAcc( rName );
+}
+
+sal_uInt16 XclExpPivotTable::GetDataFieldIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const
+{
+ auto aIt = std::find_if(maDataFields.begin(), maDataFields.end(),
+ [this, &rName](const XclPTDataFieldPos& rDataField) {
+ const XclExpPTField* pField = GetField( rDataField.first );
+ return pField && pField->GetFieldName() == rName;
+ });
+ if (aIt != maDataFields.end())
+ return static_cast< sal_uInt16 >( std::distance(maDataFields.begin(), aIt) );
+ return nDefaultIdx;
+}
+
+void XclExpPivotTable::Save( XclExpStream& rStrm )
+{
+ if( !mbValid )
+ return;
+
+ // SXVIEW
+ WriteSxview( rStrm );
+ // pivot table fields (SXVD, SXVDEX, and item records)
+ maFieldList.Save( rStrm );
+ // SXIVD records for row and column fields
+ WriteSxivd( rStrm, maRowFields );
+ WriteSxivd( rStrm, maColFields );
+ // SXPI
+ WriteSxpi( rStrm );
+ // list of SXDI records containing data field info
+ WriteSxdiList( rStrm );
+ // SXLI records
+ WriteSxli( rStrm, maPTInfo.mnDataRows, maPTInfo.mnRowFields );
+ WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields );
+ // SXEX
+ WriteSxex( rStrm );
+ // QSISXTAG
+ WriteQsiSxTag( rStrm );
+ // SXVIEWEX9
+ WriteSxViewEx9( rStrm );
+}
+
+XclExpPTField* XclExpPivotTable::GetFieldAcc( std::u16string_view rName )
+{
+ XclExpPTField* pField = nullptr;
+ for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
+ if( maFieldList.GetRecord( nPos )->GetFieldName() == rName )
+ pField = maFieldList.GetRecord( nPos );
+ return pField;
+}
+
+XclExpPTField* XclExpPivotTable::GetFieldAcc( const ScDPSaveDimension& rSaveDim )
+{
+ // data field orientation field?
+ if( rSaveDim.IsDataLayout() )
+ return &maDataOrientField;
+
+ // a real dimension
+ OUString aFieldName = ScDPUtil::getSourceDimensionName(rSaveDim.GetName());
+ return aFieldName.isEmpty() ? nullptr : GetFieldAcc(aFieldName);
+}
+
+// fill data --------------------------------------------------------------
+
+void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData )
+{
+ ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND, rSaveData.GetRowGrand() );
+ ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() );
+ ::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() );
+ mbFilterBtn = rSaveData.GetFilterButton();
+ const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension();
+
+ if (pDim && pDim->GetLayoutName())
+ maPTInfo.maDataName = *pDim->GetLayoutName();
+ else
+ maPTInfo.maDataName = ScResId(STR_PIVOT_DATA);
+}
+
+void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+{
+ XclExpPTField* pField = GetFieldAcc( rSaveDim );
+ if(!pField)
+ return;
+
+ // field properties
+ pField->SetPropertiesFromDim( rSaveDim );
+
+ // update the corresponding field position list
+ DataPilotFieldOrientation eOrient = rSaveDim.GetOrientation();
+ sal_uInt16 nFieldIdx = pField->GetFieldIndex();
+ bool bDataLayout = nFieldIdx == EXC_SXIVD_DATA;
+ bool bMultiData = maDataFields.size() > 1;
+
+ if( bDataLayout && !bMultiData )
+ return;
+
+ switch( eOrient )
+ {
+ case DataPilotFieldOrientation_ROW:
+ maRowFields.push_back( nFieldIdx );
+ if( bDataLayout )
+ maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
+ break;
+ case DataPilotFieldOrientation_COLUMN:
+ maColFields.push_back( nFieldIdx );
+ if( bDataLayout )
+ maPTInfo.mnDataAxis = EXC_SXVD_AXIS_COL;
+ break;
+ case DataPilotFieldOrientation_PAGE:
+ maPageFields.push_back( nFieldIdx );
+ OSL_ENSURE( !bDataLayout, "XclExpPivotTable::SetFieldPropertiesFromDim - wrong orientation for data fields" );
+ break;
+ case DataPilotFieldOrientation_DATA:
+ OSL_FAIL( "XclExpPivotTable::SetFieldPropertiesFromDim - called for data field" );
+ break;
+ default:;
+ }
+}
+
+void XclExpPivotTable::SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+{
+ if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
+ {
+ // field properties
+ pField->SetDataPropertiesFromDim( rSaveDim );
+ // update the data field position list
+ maDataFields.emplace_back( pField->GetFieldIndex(), pField->GetLastDataInfoIndex() );
+ }
+}
+
+void XclExpPivotTable::Finalize()
+{
+ // field numbers
+ maPTInfo.mnFields = static_cast< sal_uInt16 >( maFieldList.GetSize() );
+ maPTInfo.mnRowFields = static_cast< sal_uInt16 >( maRowFields.size() );
+ maPTInfo.mnColFields = static_cast< sal_uInt16 >( maColFields.size() );
+ maPTInfo.mnPageFields = static_cast< sal_uInt16 >( maPageFields.size() );
+ maPTInfo.mnDataFields = static_cast< sal_uInt16 >( maDataFields.size() );
+
+ maPTExtInfo.mnPagePerRow = maPTInfo.mnPageFields;
+ maPTExtInfo.mnPagePerCol = (maPTInfo.mnPageFields > 0) ? 1 : 0;
+
+ // subtotal items
+ for( size_t nPos = 0, nSize = maFieldList.GetSize(); nPos < nSize; ++nPos )
+ maFieldList.GetRecord( nPos )->AppendSubtotalItems();
+
+ // find data field orientation field
+ maPTInfo.mnDataPos = EXC_SXVIEW_DATALAST;
+ const ScfUInt16Vec* pFieldVec = nullptr;
+ switch( maPTInfo.mnDataAxis )
+ {
+ case EXC_SXVD_AXIS_ROW: pFieldVec = &maRowFields; break;
+ case EXC_SXVD_AXIS_COL: pFieldVec = &maColFields; break;
+ }
+
+ if( pFieldVec && !pFieldVec->empty() && (pFieldVec->back() != EXC_SXIVD_DATA) )
+ {
+ ScfUInt16Vec::const_iterator aIt = ::std::find( pFieldVec->begin(), pFieldVec->end(), EXC_SXIVD_DATA );
+ if( aIt != pFieldVec->end() )
+ maPTInfo.mnDataPos = static_cast< sal_uInt16 >( std::distance(pFieldVec->begin(), aIt) );
+ }
+
+ // single data field is always row oriented
+ if( maPTInfo.mnDataAxis == EXC_SXVD_AXIS_NONE )
+ maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
+
+ // update output range (initialized in ctor)
+ sal_uInt16& rnXclCol1 = maPTInfo.maOutXclRange.maFirst.mnCol;
+ sal_uInt32& rnXclRow1 = maPTInfo.maOutXclRange.maFirst.mnRow;
+ sal_uInt16& rnXclCol2 = maPTInfo.maOutXclRange.maLast.mnCol;
+ sal_uInt32& rnXclRow2 = maPTInfo.maOutXclRange.maLast.mnRow;
+ // exclude page fields from output range
+ rnXclRow1 = rnXclRow1 + maPTInfo.mnPageFields;
+ // exclude filter button from output range
+ if( mbFilterBtn )
+ ++rnXclRow1;
+ // exclude empty row between (filter button and/or page fields) and table
+ if( mbFilterBtn || maPTInfo.mnPageFields )
+ ++rnXclRow1;
+
+ // data area
+ sal_uInt16& rnDataXclCol = maPTInfo.maDataXclPos.mnCol;
+ sal_uInt32& rnDataXclRow = maPTInfo.maDataXclPos.mnRow;
+ rnDataXclCol = rnXclCol1 + maPTInfo.mnRowFields;
+ rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1;
+ if( maDataFields.empty() )
+ ++rnDataXclRow;
+
+ bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0);
+ if (bExtraHeaderRow)
+ // Insert an extra row only when there is no column field.
+ ++rnDataXclRow;
+
+ rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol );
+ rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow );
+ maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1;
+ maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1;
+
+ // first heading
+ maPTInfo.mnFirstHeadRow = rnXclRow1 + 1;
+ if (bExtraHeaderRow)
+ maPTInfo.mnFirstHeadRow += 1;
+}
+
+// records ----------------------------------------------------------------
+
+void XclExpPivotTable::WriteSxview( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXVIEW, 46 + maPTInfo.maTableName.getLength() + maPTInfo.maDataName.getLength() );
+ rStrm << maPTInfo;
+ rStrm.EndRecord();
+}
+
+void XclExpPivotTable::WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields )
+{
+ if( !rFields.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_SXIVD, rFields.size() * 2 );
+ for( const auto& rField : rFields )
+ rStrm << rField;
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpPivotTable::WriteSxpi( XclExpStream& rStrm ) const
+{
+ if( !maPageFields.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_SXPI, maPageFields.size() * 6 );
+ rStrm.SetSliceSize( 6 );
+ for( const auto& rPageField : maPageFields )
+ {
+ XclExpPTFieldRef xField = maFieldList.GetRecord( rPageField );
+ if( xField )
+ xField->WriteSxpiEntry( rStrm );
+ }
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpPivotTable::WriteSxdiList( XclExpStream& rStrm ) const
+{
+ for( const auto& [rFieldIdx, rDataInfoIdx] : maDataFields )
+ {
+ XclExpPTFieldRef xField = maFieldList.GetRecord( rFieldIdx );
+ if( xField )
+ xField->WriteSxdi( rStrm, rDataInfoIdx );
+ }
+}
+
+void XclExpPivotTable::WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount )
+{
+ if( nLineCount <= 0 )
+ return;
+
+ std::size_t nLineSize = 8 + 2 * nIndexCount;
+ rStrm.StartRecord( EXC_ID_SXLI, nLineSize * nLineCount );
+
+ /* Excel expects the records to be filled completely, do not
+ set a segment size... */
+// rStrm.SetSliceSize( nLineSize );
+
+ for( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ // Excel XP needs a partly initialized SXLI record
+ rStrm << sal_uInt16( 0 ) // number of equal index entries
+ << EXC_SXVI_TYPE_DATA
+ << nIndexCount
+ << EXC_SXLI_DEFAULTFLAGS;
+ rStrm.WriteZeroBytes( 2 * nIndexCount );
+ }
+ rStrm.EndRecord();
+}
+
+void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXEX, 24 );
+ rStrm << maPTExtInfo;
+ rStrm.EndRecord();
+}
+
+void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( 0x0802, 32 );
+
+ sal_uInt16 const nRecordType = 0x0802;
+ sal_uInt16 const nDummyFlags = 0x0000;
+ sal_uInt16 const nTableType = 1; // 0 = query table : 1 = pivot table
+
+ rStrm << nRecordType << nDummyFlags << nTableType;
+
+ // General flags
+ sal_uInt16 const nFlags = 0x0001;
+#if 0
+ // for doc purpose
+ sal_uInt16 nFlags = 0x0000;
+ bool bEnableRefresh = true;
+ bool bPCacheInvalid = false;
+ bool bOlapPTReport = false;
+
+ if (bEnableRefresh) nFlags |= 0x0001;
+ if (bPCacheInvalid) nFlags |= 0x0002;
+ if (bOlapPTReport) nFlags |= 0x0004;
+#endif
+ rStrm << nFlags;
+
+ // Feature-specific options. The value differs depending on the table
+ // type, but we assume the table type is always pivot table.
+ sal_uInt32 const nOptions = 0x00000000;
+#if 0
+ // documentation for which bit is for what
+ bool bNoStencil = false;
+ bool bHideTotal = false;
+ bool bEmptyRows = false;
+ bool bEmptyCols = false;
+ if (bNoStencil) nOptions |= 0x00000001;
+ if (bHideTotal) nOptions |= 0x00000002;
+ if (bEmptyRows) nOptions |= 0x00000008;
+ if (bEmptyCols) nOptions |= 0x00000010;
+#endif
+ rStrm << nOptions;
+
+ sal_uInt8 eXclVer = 0; // Excel2000
+ sal_uInt8 const nOffsetBytes = 16;
+ rStrm << eXclVer // version table last refreshed
+ << eXclVer // minimum version to refresh
+ << nOffsetBytes
+ << eXclVer; // first version created
+
+ rStrm << XclExpString(maPTInfo.maTableName);
+ rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for.
+
+ rStrm.EndRecord();
+}
+
+void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const
+{
+ // Until we sync the autoformat ids only export if using grid header layout
+ // That could only have been set via xls import so far.
+ if ( 0 == maPTViewEx9Info.mnGridLayout )
+ {
+ rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 );
+ rStrm << maPTViewEx9Info;
+ rStrm.EndRecord();
+ }
+}
+
+namespace {
+
+const SCTAB EXC_PTMGR_PIVOTCACHES = SCTAB_MAX;
+
+/** Record wrapper class to write the pivot caches or pivot tables. */
+class XclExpPivotRecWrapper : public XclExpRecordBase
+{
+public:
+ explicit XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab );
+ virtual void Save( XclExpStream& rStrm ) override;
+private:
+ XclExpPivotTableManager& mrPTMgr;
+ SCTAB mnScTab;
+};
+
+XclExpPivotRecWrapper::XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ) :
+ mrPTMgr( rPTMgr ),
+ mnScTab( nScTab )
+{
+}
+
+void XclExpPivotRecWrapper::Save( XclExpStream& rStrm )
+{
+ if( mnScTab == EXC_PTMGR_PIVOTCACHES )
+ mrPTMgr.WritePivotCaches( rStrm );
+ else
+ mrPTMgr.WritePivotTables( rStrm, mnScTab );
+}
+
+} // namespace
+
+XclExpPivotTableManager::XclExpPivotTableManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpPivotTableManager::CreatePivotTables()
+{
+ if( ScDPCollection* pDPColl = GetDoc().GetDPCollection() )
+ for( size_t nDPObj = 0, nCount = pDPColl->GetCount(); nDPObj < nCount; ++nDPObj )
+ {
+ ScDPObject& rDPObj = (*pDPColl)[ nDPObj ];
+ if( const XclExpPivotCache* pPCache = CreatePivotCache( rDPObj ) )
+ maPTableList.AppendNewRecord( new XclExpPivotTable( GetRoot(), rDPObj, *pPCache ) );
+ }
+}
+
+XclExpRecordRef XclExpPivotTableManager::CreatePivotCachesRecord()
+{
+ return new XclExpPivotRecWrapper( *this, EXC_PTMGR_PIVOTCACHES );
+}
+
+XclExpRecordRef XclExpPivotTableManager::CreatePivotTablesRecord( SCTAB nScTab )
+{
+ return new XclExpPivotRecWrapper( *this, nScTab );
+}
+
+void XclExpPivotTableManager::WritePivotCaches( XclExpStream& rStrm )
+{
+ maPCacheList.Save( rStrm );
+}
+
+void XclExpPivotTableManager::WritePivotTables( XclExpStream& rStrm, SCTAB nScTab )
+{
+ for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
+ {
+ XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
+ if( xPTable->GetScTab() == nScTab )
+ xPTable->Save( rStrm );
+ }
+}
+
+const XclExpPivotCache* XclExpPivotTableManager::CreatePivotCache( const ScDPObject& rDPObj )
+{
+ // try to find a pivot cache with the same data source
+ /* #i25110# In Excel, the pivot cache contains additional fields
+ (i.e. grouping info, calculated fields). If the passed DataPilot object
+ or the found cache contains this data, do not share the cache with
+ multiple pivot tables. */
+ if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
+ {
+ const ScDPDimensionSaveData* pDimSaveData = pSaveData->GetExistingDimensionData();
+ // no dimension save data at all or save data does not contain grouping info
+ if( !pDimSaveData || !pDimSaveData->HasGroupDimensions() )
+ {
+ // check all existing pivot caches
+ for( size_t nPos = 0, nSize = maPCacheList.GetSize(); nPos < nSize; ++nPos )
+ {
+ XclExpPivotCache* pPCache = maPCacheList.GetRecord( nPos );
+ // pivot cache does not have grouping info and source data is equal
+ if( !pPCache->HasAddFields() && pPCache->HasEqualDataSource( rDPObj ) )
+ return pPCache;
+ }
+ }
+ }
+
+ // create a new pivot cache
+ sal_uInt16 nNewCacheIdx = static_cast< sal_uInt16 >( maPCacheList.GetSize() );
+ XclExpPivotCacheRef xNewPCache = new XclExpPivotCache( GetRoot(), rDPObj, nNewCacheIdx );
+ if( xNewPCache->IsValid() )
+ {
+ maPCacheList.AppendRecord( xNewPCache.get() );
+ return xNewPCache.get();
+ }
+
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xepivotxml.cxx b/sc/source/filter/excel/xepivotxml.cxx
new file mode 100644
index 000000000..ecc39caae
--- /dev/null
+++ b/sc/source/filter/excel/xepivotxml.cxx
@@ -0,0 +1,1187 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <xepivotxml.hxx>
+#include <dpcache.hxx>
+#include <dpdimsave.hxx>
+#include <dpitemdata.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dputil.hxx>
+#include <document.hxx>
+#include <generalfunction.hxx>
+#include <unonames.hxx>
+#include <xestyle.hxx>
+#include <xeroot.hxx>
+
+#include <o3tl/temporary.hxx>
+#include <o3tl/safeint.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <sal/log.hxx>
+#include <sax/tools/converter.hxx>
+#include <sax/fastattribs.hxx>
+#include <svl/numformat.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotOutputRangeType.hpp>
+#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
+
+#include <vector>
+
+using namespace oox;
+using namespace com::sun::star;
+
+namespace {
+
+void savePivotCacheRecordsXml( XclExpXmlStream& rStrm, const ScDPCache& rCache )
+{
+ SCROW nCount = rCache.GetDataSize();
+ size_t nFieldCount = rCache.GetFieldCount();
+
+ sax_fastparser::FSHelperPtr& pRecStrm = rStrm.GetCurrentStream();
+ pRecStrm->startElement(XML_pivotCacheRecords,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ XML_count, OString::number(static_cast<tools::Long>(nCount)));
+
+ for (SCROW i = 0; i < nCount; ++i)
+ {
+ pRecStrm->startElement(XML_r);
+ for (size_t nField = 0; nField < nFieldCount; ++nField)
+ {
+ const ScDPCache::IndexArrayType* pArray = rCache.GetFieldIndexArray(nField);
+ assert(pArray);
+ assert(o3tl::make_unsigned(i) < pArray->size());
+
+ // We are using XML_x reference (like: <x v="0"/>), instead of values here (eg: <s v="No Discount"/>).
+ // That's why in SavePivotCacheXml method, we need to list all items.
+ pRecStrm->singleElement(XML_x, XML_v, OString::number((*pArray)[i]));
+ }
+ pRecStrm->endElement(XML_r);
+ }
+
+ pRecStrm->endElement(XML_pivotCacheRecords);
+}
+
+const char* toOOXMLAxisType( sheet::DataPilotFieldOrientation eOrient )
+{
+ switch (eOrient)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ return "axisCol";
+ case sheet::DataPilotFieldOrientation_ROW:
+ return "axisRow";
+ case sheet::DataPilotFieldOrientation_PAGE:
+ return "axisPage";
+ case sheet::DataPilotFieldOrientation_DATA:
+ return "axisValues";
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ default:
+ ;
+ }
+
+ return "";
+}
+
+const char* toOOXMLSubtotalType(ScGeneralFunction eFunc)
+{
+ switch (eFunc)
+ {
+ case ScGeneralFunction::SUM:
+ return "sum";
+ case ScGeneralFunction::COUNT:
+ return "count";
+ case ScGeneralFunction::AVERAGE:
+ return "average";
+ case ScGeneralFunction::MAX:
+ return "max";
+ case ScGeneralFunction::MIN:
+ return "min";
+ case ScGeneralFunction::PRODUCT:
+ return "product";
+ case ScGeneralFunction::COUNTNUMS:
+ return "countNums";
+ case ScGeneralFunction::STDEV:
+ return "stdDev";
+ case ScGeneralFunction::STDEVP:
+ return "stdDevp";
+ case ScGeneralFunction::VAR:
+ return "var";
+ case ScGeneralFunction::VARP:
+ return "varp";
+ default:
+ ;
+ }
+ return nullptr;
+}
+
+}
+
+XclExpXmlPivotCaches::XclExpXmlPivotCaches( const XclExpRoot& rRoot ) :
+ XclExpRoot(rRoot) {}
+
+void XclExpXmlPivotCaches::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& pWorkbookStrm = rStrm.GetCurrentStream();
+ pWorkbookStrm->startElement(XML_pivotCaches);
+
+ for (size_t i = 0, n = maCaches.size(); i < n; ++i)
+ {
+ const Entry& rEntry = maCaches[i];
+
+ sal_Int32 nCacheId = i + 1;
+ OUString aRelId;
+ sax_fastparser::FSHelperPtr pPCStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/pivotCache/", "pivotCacheDefinition", nCacheId),
+ XclXmlUtils::GetStreamName(nullptr, "pivotCache/pivotCacheDefinition", nCacheId),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ CREATE_XL_CONTENT_TYPE("pivotCacheDefinition"),
+ CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"),
+ &aRelId);
+
+ pWorkbookStrm->singleElement(XML_pivotCache,
+ XML_cacheId, OString::number(nCacheId),
+ FSNS(XML_r, XML_id), aRelId.toUtf8());
+
+ rStrm.PushStream(pPCStrm);
+ SavePivotCacheXml(rStrm, rEntry, nCacheId);
+ rStrm.PopStream();
+ }
+
+ pWorkbookStrm->endElement(XML_pivotCaches);
+}
+
+void XclExpXmlPivotCaches::SetCaches( std::vector<Entry>&& rCaches )
+{
+ maCaches = std::move(rCaches);
+}
+
+bool XclExpXmlPivotCaches::HasCaches() const
+{
+ return !maCaches.empty();
+}
+
+const XclExpXmlPivotCaches::Entry* XclExpXmlPivotCaches::GetCache( sal_Int32 nCacheId ) const
+{
+ if (nCacheId <= 0)
+ // cache ID is 1-based.
+ return nullptr;
+
+ size_t nPos = nCacheId - 1;
+ if (nPos >= maCaches.size())
+ return nullptr;
+
+ return &maCaches[nPos];
+}
+
+namespace {
+/**
+ * Create combined date and time string according the requirements of Excel.
+ * A single point in time can be represented by concatenating a complete date expression,
+ * the letter T as a delimiter, and a valid time expression. For example, "2007-04-05T14:30".
+ *
+ * fSerialDateTime - a number representing the number of days since 1900-Jan-0 (integer portion of the number),
+ * plus a fractional portion of a 24 hour day (fractional portion of the number).
+ */
+OUString GetExcelFormattedDate( double fSerialDateTime, const SvNumberFormatter& rFormatter )
+{
+ // tdf#125055: properly round the value to seconds when truncating nanoseconds below
+ constexpr double fHalfSecond = 1 / 86400.0 * 0.5;
+ css::util::DateTime aUDateTime
+ = (DateTime(rFormatter.GetNullDate()) + fSerialDateTime + fHalfSecond).GetUNODateTime();
+ // We need to reset nanoseconds, to avoid string like: "1982-02-18T16:04:47.999999849"
+ aUDateTime.NanoSeconds = 0;
+ OUStringBuffer sBuf;
+ ::sax::Converter::convertDateTime(sBuf, aUDateTime, nullptr, true);
+ return sBuf.makeStringAndClear();
+}
+
+// Excel seems to expect different order of group item values; we need to rearrange elements
+// to output "<date1" first, then elements, then ">date2" last.
+// Since ScDPItemData::DateFirst is -1, ScDPItemData::DateLast is 10000, and other date group
+// items would fit between those in order (like 0 = Jan, 1 = Feb, etc.), we can simply sort
+// the items by value.
+std::vector<OUString> SortGroupItems(const ScDPCache& rCache, tools::Long nDim)
+{
+ struct ItemData
+ {
+ sal_Int32 nVal;
+ const ScDPItemData* pData;
+ };
+ std::vector<ItemData> aDataToSort;
+ ScfInt32Vec aGIIds;
+ rCache.GetGroupDimMemberIds(nDim, aGIIds);
+ for (sal_Int32 id : aGIIds)
+ {
+ const ScDPItemData* pGIData = rCache.GetItemDataById(nDim, id);
+ if (pGIData->GetType() == ScDPItemData::GroupValue)
+ {
+ auto aGroupVal = pGIData->GetGroupValue();
+ aDataToSort.push_back({ aGroupVal.mnValue, pGIData });
+ }
+ }
+ std::sort(aDataToSort.begin(), aDataToSort.end(),
+ [](const ItemData& a, const ItemData& b) { return a.nVal < b.nVal; });
+
+ std::vector<OUString> aSortedResult;
+ for (const auto& el : aDataToSort)
+ {
+ aSortedResult.push_back(rCache.GetFormattedString(nDim, *el.pData, false));
+ }
+ return aSortedResult;
+}
+} // namespace
+
+void XclExpXmlPivotCaches::SavePivotCacheXml( XclExpXmlStream& rStrm, const Entry& rEntry, sal_Int32 nCounter )
+{
+ assert(rEntry.mpCache);
+ const ScDPCache& rCache = *rEntry.mpCache;
+
+ sax_fastparser::FSHelperPtr& pDefStrm = rStrm.GetCurrentStream();
+
+ OUString aRelId;
+ sax_fastparser::FSHelperPtr pRecStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/pivotCache/", "pivotCacheRecords", nCounter),
+ XclXmlUtils::GetStreamName(nullptr, "pivotCacheRecords", nCounter),
+ pDefStrm->getOutputStream(),
+ CREATE_XL_CONTENT_TYPE("pivotCacheRecords"),
+ CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheRecords"),
+ &aRelId);
+
+ rStrm.PushStream(pRecStrm);
+ savePivotCacheRecordsXml(rStrm, rCache);
+ rStrm.PopStream();
+
+ pDefStrm->startElement(XML_pivotCacheDefinition,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ FSNS(XML_r, XML_id), aRelId.toUtf8(),
+ XML_recordCount, OString::number(rEntry.mpCache->GetDataSize()),
+ XML_createdVersion, "3"); // MS Excel 2007, tdf#112936: setting version number makes MSO to handle the pivot table differently
+
+ pDefStrm->startElement(XML_cacheSource, XML_type, "worksheet");
+
+ OUString aSheetName;
+ GetDoc().GetName(rEntry.maSrcRange.aStart.Tab(), aSheetName);
+ pDefStrm->singleElement(XML_worksheetSource,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rEntry.maSrcRange),
+ XML_sheet, aSheetName.toUtf8());
+
+ pDefStrm->endElement(XML_cacheSource);
+
+ size_t nCount = rCache.GetFieldCount();
+ const size_t nGroupFieldCount = rCache.GetGroupFieldCount();
+ pDefStrm->startElement(XML_cacheFields,
+ XML_count, OString::number(static_cast<tools::Long>(nCount + nGroupFieldCount)));
+
+ auto WriteFieldGroup = [this, &rCache, pDefStrm](size_t i, size_t base) {
+ const sal_Int32 nDatePart = rCache.GetGroupType(i);
+ if (!nDatePart)
+ return;
+ OString sGroupBy;
+ switch (nDatePart)
+ {
+ case sheet::DataPilotFieldGroupBy::SECONDS:
+ sGroupBy = "seconds";
+ break;
+ case sheet::DataPilotFieldGroupBy::MINUTES:
+ sGroupBy = "minutes";
+ break;
+ case sheet::DataPilotFieldGroupBy::HOURS:
+ sGroupBy = "hours";
+ break;
+ case sheet::DataPilotFieldGroupBy::DAYS:
+ sGroupBy = "days";
+ break;
+ case sheet::DataPilotFieldGroupBy::MONTHS:
+ sGroupBy = "months";
+ break;
+ case sheet::DataPilotFieldGroupBy::QUARTERS:
+ sGroupBy = "quarters";
+ break;
+ case sheet::DataPilotFieldGroupBy::YEARS:
+ sGroupBy = "years";
+ break;
+ }
+
+ // fieldGroup element
+ pDefStrm->startElement(XML_fieldGroup, XML_base, OString::number(base));
+
+ SvNumberFormatter& rFormatter = GetFormatter();
+
+ // rangePr element
+ const ScDPNumGroupInfo* pGI = rCache.GetNumGroupInfo(i);
+ auto pGroupAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ pGroupAttList->add(XML_groupBy, sGroupBy);
+ // Possible TODO: find out when to write autoStart attribute for years grouping
+ pGroupAttList->add(XML_startDate, GetExcelFormattedDate(pGI->mfStart, rFormatter).toUtf8());
+ pGroupAttList->add(XML_endDate, GetExcelFormattedDate(pGI->mfEnd, rFormatter).toUtf8());
+ if (pGI->mfStep)
+ pGroupAttList->add(XML_groupInterval, OString::number(pGI->mfStep));
+ pDefStrm->singleElement(XML_rangePr, pGroupAttList);
+
+ // groupItems element
+ auto aElemVec = SortGroupItems(rCache, i);
+ pDefStrm->startElement(XML_groupItems, XML_count, OString::number(aElemVec.size()));
+ for (const auto& sElem : aElemVec)
+ {
+ pDefStrm->singleElement(XML_s, XML_v, sElem.toUtf8());
+ }
+ pDefStrm->endElement(XML_groupItems);
+ pDefStrm->endElement(XML_fieldGroup);
+ };
+
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ OUString aName = rCache.GetDimensionName(i);
+
+ pDefStrm->startElement(XML_cacheField,
+ XML_name, aName.toUtf8(),
+ XML_numFmtId, OString::number(0));
+
+ const ScDPCache::ScDPItemDataVec& rFieldItems = rCache.GetDimMemberValues(i);
+
+ std::set<ScDPItemData::Type> aDPTypes;
+ double fMin = std::numeric_limits<double>::infinity(), fMax = -std::numeric_limits<double>::infinity();
+ bool isValueInteger = true;
+ bool isContainsDate = rCache.IsDateDimension(i);
+ bool isLongText = false;
+ for (const auto& rFieldItem : rFieldItems)
+ {
+ ScDPItemData::Type eType = rFieldItem.GetType();
+ // tdf#123939 : error and string are same for cache; if both are present, keep only one
+ if (eType == ScDPItemData::Error)
+ eType = ScDPItemData::String;
+ aDPTypes.insert(eType);
+ if (eType == ScDPItemData::Value)
+ {
+ double fVal = rFieldItem.GetValue();
+ fMin = std::min(fMin, fVal);
+ fMax = std::max(fMax, fVal);
+
+ // Check if all values are integers
+ if (isValueInteger && (modf(fVal, &o3tl::temporary(double())) != 0.0))
+ {
+ isValueInteger = false;
+ }
+ }
+ else if (eType == ScDPItemData::String && !isLongText)
+ {
+ isLongText = rFieldItem.GetString().getLength() > 255;
+ }
+ }
+
+ auto pAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ // TODO In same cases, disable listing of items, as it is done in MS Excel.
+ // Exporting savePivotCacheRecordsXml method needs to be updated accordingly
+ //bool bListItems = true;
+
+ std::set<ScDPItemData::Type> aDPTypesWithoutBlank = aDPTypes;
+ aDPTypesWithoutBlank.erase(ScDPItemData::Empty);
+
+ const bool isContainsString = aDPTypesWithoutBlank.count(ScDPItemData::String) > 0;
+ const bool isContainsBlank = aDPTypes.count(ScDPItemData::Empty) > 0;
+ const bool isContainsNumber
+ = !isContainsDate && aDPTypesWithoutBlank.count(ScDPItemData::Value) > 0;
+ bool isContainsNonDate = !(isContainsDate && aDPTypesWithoutBlank.size() <= 1);
+
+ // XML_containsSemiMixedTypes possible values:
+ // 1 - (Default) at least one text value, or can also contain a mix of other data types and blank values,
+ // or blank values only
+ // 0 - the field does not have a mix of text and other values
+ if (!(isContainsString || (aDPTypes.size() > 1) || (isContainsBlank && aDPTypesWithoutBlank.empty())))
+ pAttList->add(XML_containsSemiMixedTypes, ToPsz10(false));
+
+ if (!isContainsNonDate)
+ pAttList->add(XML_containsNonDate, ToPsz10(false));
+
+ if (isContainsDate)
+ pAttList->add(XML_containsDate, ToPsz10(true));
+
+ // default for containsString field is true, so we are writing only when is false
+ if (!isContainsString)
+ pAttList->add(XML_containsString, ToPsz10(false));
+
+ if (isContainsBlank)
+ pAttList->add(XML_containsBlank, ToPsz10(true));
+
+ // XML_containsMixedType possible values:
+ // 1 - field contains more than one data type
+ // 0 - (Default) only one data type. The field can still contain blank values (that's why we are using aDPTypesWithoutBlank)
+ if (aDPTypesWithoutBlank.size() > 1)
+ pAttList->add(XML_containsMixedTypes, ToPsz10(true));
+
+ // If field contain mixed types (Date and Numbers), MS Excel is saving only "minDate" and "maxDate" and not "minValue" and "maxValue"
+ // Example how Excel is saving mixed Date and Numbers:
+ // <sharedItems containsSemiMixedTypes="0" containsDate="1" containsString="0" containsMixedTypes="1" minDate="1900-01-03T22:26:04" maxDate="1900-01-07T14:02:04" />
+ // Example how Excel is saving Dates only:
+ // <sharedItems containsSemiMixedTypes="0" containsNonDate="0" containsDate="1" containsString="0" minDate="1903-08-24T07:40:48" maxDate="2024-05-23T07:12:00"/>
+ if (isContainsNumber)
+ pAttList->add(XML_containsNumber, ToPsz10(true));
+
+ if (isValueInteger && isContainsNumber)
+ pAttList->add(XML_containsInteger, ToPsz10(true));
+
+
+ // Number type fields could be mixed with blank types, and it shouldn't be treated as listed items.
+ // Example:
+ // <cacheField name="employeeID" numFmtId="0">
+ // <sharedItems containsString="0" containsBlank="1" containsNumber="1" containsInteger="1" minValue="35" maxValue="89"/>
+ // </cacheField>
+ if (isContainsNumber)
+ {
+ pAttList->add(XML_minValue, OString::number(fMin));
+ pAttList->add(XML_maxValue, OString::number(fMax));
+ }
+
+ if (isContainsDate)
+ {
+ pAttList->add(XML_minDate, GetExcelFormattedDate(fMin, GetFormatter()).toUtf8());
+ pAttList->add(XML_maxDate, GetExcelFormattedDate(fMax, GetFormatter()).toUtf8());
+ }
+
+ //if (bListItems) // see TODO above
+ {
+ pAttList->add(XML_count, OString::number(static_cast<tools::Long>(rFieldItems.size())));
+ }
+
+ if (isLongText)
+ {
+ pAttList->add(XML_longText, ToPsz10(true));
+ }
+
+ pDefStrm->startElement(XML_sharedItems, pAttList);
+
+ //if (bListItems) // see TODO above
+ {
+ for (const ScDPItemData& rItem : rFieldItems)
+ {
+ switch (rItem.GetType())
+ {
+ case ScDPItemData::String:
+ pDefStrm->singleElement(XML_s, XML_v, rItem.GetString().toUtf8());
+ break;
+ case ScDPItemData::Value:
+ if (isContainsDate)
+ {
+ pDefStrm->singleElement(XML_d,
+ XML_v, GetExcelFormattedDate(rItem.GetValue(), GetFormatter()).toUtf8());
+ }
+ else
+ pDefStrm->singleElement(XML_n,
+ XML_v, OString::number(rItem.GetValue()));
+ break;
+ case ScDPItemData::Empty:
+ pDefStrm->singleElement(XML_m);
+ break;
+ case ScDPItemData::Error:
+ pDefStrm->singleElement(XML_e,
+ XML_v, rItem.GetString().toUtf8());
+ break;
+ case ScDPItemData::GroupValue: // Should not happen here!
+ case ScDPItemData::RangeStart:
+ // TODO : What do we do with these types?
+ pDefStrm->singleElement(XML_m);
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ pDefStrm->endElement(XML_sharedItems);
+
+ WriteFieldGroup(i, i);
+
+ pDefStrm->endElement(XML_cacheField);
+ }
+
+ ScDPObject* pDPObject
+ = rCache.GetAllReferences().empty() ? nullptr : *rCache.GetAllReferences().begin();
+
+ for (size_t i = nCount; pDPObject && i < nCount + nGroupFieldCount; ++i)
+ {
+ const OUString aName = pDPObject->GetDimName(i, o3tl::temporary(bool()));
+ // tdf#126748: DPObject might not reference all group fields, when there are several
+ // DPObjects referencing this cache. Trying to get a dimension data for a field not used
+ // in a given DPObject will give nullptr, and dereferencing it then will crash. To avoid
+ // the crash, until there's a correct method to find the names of group fields in cache,
+ // just skip the fields, creating bad cache data, which is of course a temporary hack.
+ // TODO: reimplement the whole block to get the names from another source, not from first
+ // cache reference.
+ if (aName.isEmpty())
+ break;
+
+ ScDPSaveData* pSaveData = pDPObject->GetSaveData();
+ assert(pSaveData);
+
+ const ScDPSaveGroupDimension* pDim = pSaveData->GetDimensionData()->GetNamedGroupDim(aName);
+ assert(pDim);
+
+ const SCCOL nBase = rCache.GetDimensionIndex(pDim->GetSourceDimName());
+ assert(nBase >= 0);
+
+ pDefStrm->startElement(XML_cacheField, XML_name, aName.toUtf8(), XML_numFmtId,
+ OString::number(0), XML_databaseField, ToPsz10(false));
+ WriteFieldGroup(i, nBase);
+ pDefStrm->endElement(XML_cacheField);
+ }
+
+ pDefStrm->endElement(XML_cacheFields);
+
+ pDefStrm->endElement(XML_pivotCacheDefinition);
+}
+
+XclExpXmlPivotTableManager::XclExpXmlPivotTableManager( const XclExpRoot& rRoot ) :
+ XclExpRoot(rRoot), maCaches(rRoot) {}
+
+void XclExpXmlPivotTableManager::Initialize()
+{
+ ScDocument& rDoc = GetDoc();
+ if (!rDoc.HasPivotTable())
+ // No pivot table to export.
+ return;
+
+ ScDPCollection* pDPColl = rDoc.GetDPCollection();
+ if (!pDPColl)
+ return;
+
+ // Update caches from DPObject
+ for (size_t i = 0; i < pDPColl->GetCount(); ++i)
+ {
+ ScDPObject& rDPObj = (*pDPColl)[i];
+ rDPObj.SyncAllDimensionMembers();
+ (void)rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::TABLE);
+ }
+
+ // Go through the caches first.
+
+ std::vector<XclExpXmlPivotCaches::Entry> aCaches;
+ const ScDPCollection::SheetCaches& rSheetCaches = pDPColl->GetSheetCaches();
+ const std::vector<ScRange>& rRanges = rSheetCaches.getAllRanges();
+ for (const auto & rRange : rRanges)
+ {
+ const ScDPCache* pCache = rSheetCaches.getExistingCache(rRange);
+ if (!pCache)
+ continue;
+
+ // Get all pivot objects that reference this cache, and set up an
+ // object to cache ID mapping.
+ const ScDPCache::ScDPObjectSet& rRefs = pCache->GetAllReferences();
+ for (const auto& rRef : rRefs)
+ maCacheIdMap.emplace(rRef, aCaches.size()+1);
+
+ XclExpXmlPivotCaches::Entry aEntry;
+ aEntry.mpCache = pCache;
+ aEntry.maSrcRange = rRange;
+ aCaches.push_back(aEntry); // Cache ID equals position + 1.
+ }
+
+ // TODO : Handle name and database caches as well.
+
+ for (size_t i = 0, n = pDPColl->GetCount(); i < n; ++i)
+ {
+ const ScDPObject& rDPObj = (*pDPColl)[i];
+
+ // Get the cache ID for this pivot table.
+ CacheIdMapType::iterator itCache = maCacheIdMap.find(&rDPObj);
+ if (itCache == maCacheIdMap.end())
+ // No cache ID found. Something is wrong here...
+ continue;
+
+ sal_Int32 nCacheId = itCache->second;
+ SCTAB nTab = rDPObj.GetOutRange().aStart.Tab();
+
+ TablesType::iterator it = m_Tables.find(nTab);
+ if (it == m_Tables.end())
+ {
+ // Insert a new instance for this sheet index.
+ std::pair<TablesType::iterator, bool> r =
+ m_Tables.insert(std::make_pair(nTab, std::make_unique<XclExpXmlPivotTables>(GetRoot(), maCaches)));
+ it = r.first;
+ }
+
+ XclExpXmlPivotTables *const p = it->second.get();
+ p->AppendTable(&rDPObj, nCacheId, i+1);
+ }
+
+ maCaches.SetCaches(std::move(aCaches));
+}
+
+XclExpXmlPivotCaches& XclExpXmlPivotTableManager::GetCaches()
+{
+ return maCaches;
+}
+
+XclExpXmlPivotTables* XclExpXmlPivotTableManager::GetTablesBySheet( SCTAB nTab )
+{
+ TablesType::iterator const it = m_Tables.find(nTab);
+ return it == m_Tables.end() ? nullptr : it->second.get();
+}
+
+XclExpXmlPivotTables::Entry::Entry( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId ) :
+ mpTable(pTable), mnCacheId(nCacheId), mnPivotId(nPivotId) {}
+
+XclExpXmlPivotTables::XclExpXmlPivotTables( const XclExpRoot& rRoot, const XclExpXmlPivotCaches& rCaches ) :
+ XclExpRoot(rRoot), mrCaches(rCaches) {}
+
+void XclExpXmlPivotTables::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& pWSStrm = rStrm.GetCurrentStream(); // worksheet stream
+
+ for (const auto& rTable : maTables)
+ {
+ const ScDPObject& rObj = *rTable.mpTable;
+ sal_Int32 nCacheId = rTable.mnCacheId;
+ sal_Int32 nPivotId = rTable.mnPivotId;
+
+ sax_fastparser::FSHelperPtr pPivotStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/pivotTables/", "pivotTable", nPivotId),
+ XclXmlUtils::GetStreamName(nullptr, "../pivotTables/pivotTable", nPivotId),
+ pWSStrm->getOutputStream(),
+ CREATE_XL_CONTENT_TYPE("pivotTable"),
+ CREATE_OFFICEDOC_RELATION_TYPE("pivotTable"));
+
+ rStrm.PushStream(pPivotStrm);
+ SavePivotTableXml(rStrm, rObj, nCacheId);
+ rStrm.PopStream();
+ }
+}
+
+namespace {
+
+struct DataField
+{
+ tools::Long mnPos; // field index in pivot cache.
+ const ScDPSaveDimension* mpDim;
+
+ DataField( tools::Long nPos, const ScDPSaveDimension* pDim ) : mnPos(nPos), mpDim(pDim) {}
+};
+
+/** Returns an OOXML subtotal function name string. See ECMA-376-1:2016 18.18.43 */
+OString GetSubtotalFuncName(ScGeneralFunction eFunc)
+{
+ switch (eFunc)
+ {
+ case ScGeneralFunction::SUM: return "sum";
+ case ScGeneralFunction::COUNT: return "count";
+ case ScGeneralFunction::AVERAGE: return "avg";
+ case ScGeneralFunction::MAX: return "max";
+ case ScGeneralFunction::MIN: return "min";
+ case ScGeneralFunction::PRODUCT: return "product";
+ case ScGeneralFunction::COUNTNUMS: return "countA";
+ case ScGeneralFunction::STDEV: return "stdDev";
+ case ScGeneralFunction::STDEVP: return "stdDevP";
+ case ScGeneralFunction::VAR: return "var";
+ case ScGeneralFunction::VARP: return "varP";
+ default:;
+ }
+ return "default";
+}
+
+sal_Int32 GetSubtotalAttrToken(ScGeneralFunction eFunc)
+{
+ switch (eFunc)
+ {
+ case ScGeneralFunction::SUM: return XML_sumSubtotal;
+ case ScGeneralFunction::COUNT: return XML_countSubtotal;
+ case ScGeneralFunction::AVERAGE: return XML_avgSubtotal;
+ case ScGeneralFunction::MAX: return XML_maxSubtotal;
+ case ScGeneralFunction::MIN: return XML_minSubtotal;
+ case ScGeneralFunction::PRODUCT: return XML_productSubtotal;
+ case ScGeneralFunction::COUNTNUMS: return XML_countASubtotal;
+ case ScGeneralFunction::STDEV: return XML_stdDevSubtotal;
+ case ScGeneralFunction::STDEVP: return XML_stdDevPSubtotal;
+ case ScGeneralFunction::VAR: return XML_varSubtotal;
+ case ScGeneralFunction::VARP: return XML_varPSubtotal;
+ default:;
+ }
+ return XML_defaultSubtotal;
+}
+
+// An item is expected to contain sequences of css::xml::FastAttribute and css::xml::Attribute
+void WriteGrabBagItemToStream(XclExpXmlStream& rStrm, sal_Int32 tokenId, const css::uno::Any& rItem)
+{
+ css::uno::Sequence<css::uno::Any> aSeqs;
+ if(!(rItem >>= aSeqs))
+ return;
+
+ auto& pStrm = rStrm.GetCurrentStream();
+ pStrm->write("<")->writeId(tokenId);
+
+ css::uno::Sequence<css::xml::FastAttribute> aFastSeq;
+ css::uno::Sequence<css::xml::Attribute> aUnkSeq;
+ for (const auto& a : std::as_const(aSeqs))
+ {
+ if (a >>= aFastSeq)
+ {
+ for (const auto& rAttr : std::as_const(aFastSeq))
+ rStrm.WriteAttributes(rAttr.Token, rAttr.Value);
+ }
+ else if (a >>= aUnkSeq)
+ {
+ for (const auto& rAttr : std::as_const(aUnkSeq))
+ pStrm->write(" ")
+ ->write(rAttr.Name)
+ ->write("=\"")
+ ->writeEscaped(rAttr.Value)
+ ->write("\"");
+ }
+ }
+
+ pStrm->write("/>");
+}
+}
+
+void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDPObject& rDPObj, sal_Int32 nCacheId )
+{
+ typedef std::unordered_map<OUString, long> NameToIdMapType;
+
+ const XclExpXmlPivotCaches::Entry* pCacheEntry = mrCaches.GetCache(nCacheId);
+ if (!pCacheEntry)
+ // Something is horribly wrong. Check your logic.
+ return;
+
+ const ScDPCache& rCache = *pCacheEntry->mpCache;
+
+ const ScDPSaveData& rSaveData = *rDPObj.GetSaveData();
+
+ size_t nFieldCount = rCache.GetFieldCount() + rCache.GetGroupFieldCount();
+ std::vector<const ScDPSaveDimension*> aCachedDims;
+ NameToIdMapType aNameToIdMap;
+
+ aCachedDims.reserve(nFieldCount);
+ for (size_t i = 0; i < nFieldCount; ++i)
+ {
+ OUString aName = const_cast<ScDPObject&>(rDPObj).GetDimName(i, o3tl::temporary(bool()));
+ aNameToIdMap.emplace(aName, aCachedDims.size());
+ const ScDPSaveDimension* pDim = rSaveData.GetExistingDimensionByName(aName);
+ aCachedDims.push_back(pDim);
+ }
+
+ std::vector<tools::Long> aRowFields;
+ std::vector<tools::Long> aColFields;
+ std::vector<tools::Long> aPageFields;
+ std::vector<DataField> aDataFields;
+
+ tools::Long nDataDimCount = rSaveData.GetDataDimensionCount();
+ // Use dimensions in the save data to get their correct ordering.
+ // Dimension order here is significant as they specify the order of
+ // appearance in each axis.
+ const ScDPSaveData::DimsType& rDims = rSaveData.GetDimensions();
+ bool bTabularMode = false;
+ for (const auto & i : rDims)
+ {
+ const ScDPSaveDimension& rDim = *i;
+
+ tools::Long nPos = -1; // position in cache
+ if (rDim.IsDataLayout())
+ nPos = -2; // Excel uses an index of -2 to indicate a data layout field.
+ else
+ {
+ OUString aSrcName = ScDPUtil::getSourceDimensionName(rDim.GetName());
+ NameToIdMapType::iterator it = aNameToIdMap.find(aSrcName);
+ if (it != aNameToIdMap.end())
+ nPos = it->second;
+
+ if (nPos == -1)
+ continue;
+
+ if (!aCachedDims[nPos])
+ continue;
+ }
+
+ sheet::DataPilotFieldOrientation eOrient = rDim.GetOrientation();
+
+ switch (eOrient)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ if (nPos == -2 && nDataDimCount <= 1)
+ break;
+ aColFields.push_back(nPos);
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ aRowFields.push_back(nPos);
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ aPageFields.push_back(nPos);
+ break;
+ case sheet::DataPilotFieldOrientation_DATA:
+ aDataFields.emplace_back(nPos, &rDim);
+ break;
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ default:
+ ;
+ }
+ if(rDim.GetLayoutInfo())
+ bTabularMode |= (rDim.GetLayoutInfo()->LayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
+ }
+
+ sax_fastparser::FSHelperPtr& pPivotStrm = rStrm.GetCurrentStream();
+ pPivotStrm->startElement(XML_pivotTableDefinition,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ XML_name, rDPObj.GetName().toUtf8(),
+ XML_cacheId, OString::number(nCacheId),
+ XML_applyNumberFormats, ToPsz10(false),
+ XML_applyBorderFormats, ToPsz10(false),
+ XML_applyFontFormats, ToPsz10(false),
+ XML_applyPatternFormats, ToPsz10(false),
+ XML_applyAlignmentFormats, ToPsz10(false),
+ XML_applyWidthHeightFormats, ToPsz10(false),
+ XML_dataCaption, "Values",
+ XML_useAutoFormatting, ToPsz10(false),
+ XML_itemPrintTitles, ToPsz10(true),
+ XML_indent, ToPsz10(false),
+ XML_outline, ToPsz10(!bTabularMode),
+ XML_outlineData, ToPsz10(!bTabularMode),
+ XML_compact, ToPsz10(false),
+ XML_compactData, ToPsz10(false));
+
+ // NB: Excel's range does not include page field area (if any).
+ ScRange aOutRange = rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::TABLE);
+
+ sal_Int32 nFirstHeaderRow = rDPObj.GetHeaderLayout() ? 2 : 1;
+ sal_Int32 nFirstDataRow = 2;
+ sal_Int32 nFirstDataCol = 1;
+ ScRange aResRange = rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::RESULT);
+
+ if (!aOutRange.IsValid())
+ aOutRange = rDPObj.GetOutRange();
+
+ if (aOutRange.IsValid() && aResRange.IsValid())
+ {
+ nFirstDataRow = aResRange.aStart.Row() - aOutRange.aStart.Row();
+ nFirstDataCol = aResRange.aStart.Col() - aOutRange.aStart.Col();
+ }
+
+ pPivotStrm->write("<")->writeId(XML_location);
+ rStrm.WriteAttributes(XML_ref,
+ XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aOutRange),
+ XML_firstHeaderRow, OUString::number(nFirstHeaderRow),
+ XML_firstDataRow, OUString::number(nFirstDataRow),
+ XML_firstDataCol, OUString::number(nFirstDataCol));
+
+ if (!aPageFields.empty())
+ {
+ rStrm.WriteAttributes(XML_rowPageCount, OUString::number(static_cast<tools::Long>(aPageFields.size())));
+ rStrm.WriteAttributes(XML_colPageCount, OUString::number(1));
+ }
+
+ pPivotStrm->write("/>");
+
+ // <pivotFields> - It must contain all fields in the pivot cache even if
+ // only some of them are used in the pivot table. The order must be as
+ // they appear in the cache.
+
+ pPivotStrm->startElement(XML_pivotFields,
+ XML_count, OString::number(static_cast<tools::Long>(aCachedDims.size())));
+
+ for (size_t i = 0; i < nFieldCount; ++i)
+ {
+ const ScDPSaveDimension* pDim = aCachedDims[i];
+ if (!pDim)
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false));
+ continue;
+ }
+
+ bool bDimInTabularMode = false;
+ if(pDim->GetLayoutInfo())
+ bDimInTabularMode = (pDim->GetLayoutInfo()->LayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
+
+ sheet::DataPilotFieldOrientation eOrient = pDim->GetOrientation();
+
+ if (eOrient == sheet::DataPilotFieldOrientation_HIDDEN)
+ {
+ if(bDimInTabularMode)
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false),
+ XML_outline, ToPsz10(false));
+ }
+ else
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false));
+ }
+ continue;
+ }
+
+ if (eOrient == sheet::DataPilotFieldOrientation_DATA)
+ {
+ if(bDimInTabularMode)
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_dataField, ToPsz10(true),
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false),
+ XML_outline, ToPsz10(false));
+ }
+ else
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_dataField, ToPsz10(true),
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false));
+ }
+ continue;
+ }
+
+ // Dump field items.
+ std::vector<ScDPLabelData::Member> aMembers;
+ {
+ // We need to get the members in actual order, getting which requires non-const reference here
+ auto& dpo = const_cast<ScDPObject&>(rDPObj);
+ dpo.GetMembers(i, dpo.GetUsedHierarchy(i), aMembers);
+ }
+
+ std::vector<OUString> aCacheFieldItems;
+ if (i < rCache.GetFieldCount() && !rCache.GetGroupType(i))
+ {
+ for (const auto& it : rCache.GetDimMemberValues(i))
+ {
+ OUString sFormattedName;
+ if (it.HasStringData() || it.IsEmpty())
+ sFormattedName = it.GetString();
+ else
+ sFormattedName = const_cast<ScDPObject&>(rDPObj).GetFormattedString(
+ pDim->GetName(), it.GetValue());
+ aCacheFieldItems.push_back(sFormattedName);
+ }
+ }
+ else
+ {
+ aCacheFieldItems = SortGroupItems(rCache, i);
+ }
+ // The pair contains the member index in cache and if it is hidden
+ std::vector< std::pair<size_t, bool> > aMemberSequence;
+ std::set<size_t> aUsedCachePositions;
+ for (const auto & rMember : aMembers)
+ {
+ auto it = std::find(aCacheFieldItems.begin(), aCacheFieldItems.end(), rMember.maName);
+ if (it != aCacheFieldItems.end())
+ {
+ size_t nCachePos = static_cast<size_t>(std::distance(aCacheFieldItems.begin(), it));
+ auto aInserted = aUsedCachePositions.insert(nCachePos);
+ if (aInserted.second)
+ aMemberSequence.emplace_back(std::make_pair(nCachePos, !rMember.mbVisible));
+ }
+ }
+ // Now add all remaining cache items as hidden
+ for (size_t nItem = 0; nItem < aCacheFieldItems.size(); ++nItem)
+ {
+ if (aUsedCachePositions.find(nItem) == aUsedCachePositions.end())
+ aMemberSequence.emplace_back(nItem, true);
+ }
+
+ // tdf#125086: check if this field *also* appears in Data region
+ bool bAppearsInData = false;
+ {
+ OUString aSrcName = ScDPUtil::getSourceDimensionName(pDim->GetName());
+ const auto it = std::find_if(
+ aDataFields.begin(), aDataFields.end(), [&aSrcName](const DataField& rDataField) {
+ OUString aThisName
+ = ScDPUtil::getSourceDimensionName(rDataField.mpDim->GetName());
+ return aThisName == aSrcName;
+ });
+ if (it != aDataFields.end())
+ bAppearsInData = true;
+ }
+
+ auto pAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ pAttList->add(XML_axis, toOOXMLAxisType(eOrient));
+ if (bAppearsInData)
+ pAttList->add(XML_dataField, ToPsz10(true));
+ pAttList->add(XML_compact, ToPsz10(false));
+ pAttList->add(XML_showAll, ToPsz10(false));
+
+ tools::Long nSubTotalCount = pDim->GetSubTotalsCount();
+ std::vector<OString> aSubtotalSequence;
+ bool bHasDefaultSubtotal = false;
+ for (tools::Long nSubTotal = 0; nSubTotal < nSubTotalCount; ++nSubTotal)
+ {
+ ScGeneralFunction eFunc = pDim->GetSubTotalFunc(nSubTotal);
+ aSubtotalSequence.push_back(GetSubtotalFuncName(eFunc));
+ sal_Int32 nAttToken = GetSubtotalAttrToken(eFunc);
+ if (nAttToken == XML_defaultSubtotal)
+ bHasDefaultSubtotal = true;
+ else if (!pAttList->hasAttribute(nAttToken))
+ pAttList->add(nAttToken, ToPsz10(true));
+ }
+ // XML_defaultSubtotal is true by default; only write it if it's false
+ if (!bHasDefaultSubtotal)
+ pAttList->add(XML_defaultSubtotal, ToPsz10(false));
+
+ if(bDimInTabularMode)
+ pAttList->add( XML_outline, ToPsz10(false));
+ pPivotStrm->startElement(XML_pivotField, pAttList);
+
+ pPivotStrm->startElement(XML_items,
+ XML_count, OString::number(static_cast<tools::Long>(aMemberSequence.size() + aSubtotalSequence.size())));
+
+ for (const auto & nMember : aMemberSequence)
+ {
+ auto pItemAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ if (nMember.second)
+ pItemAttList->add(XML_h, ToPsz10(true));
+ pItemAttList->add(XML_x, OString::number(static_cast<tools::Long>(nMember.first)));
+ pPivotStrm->singleElement(XML_item, pItemAttList);
+ }
+
+ for (const OString& sSubtotal : aSubtotalSequence)
+ {
+ pPivotStrm->singleElement(XML_item, XML_t, sSubtotal);
+ }
+
+ pPivotStrm->endElement(XML_items);
+ pPivotStrm->endElement(XML_pivotField);
+ }
+
+ pPivotStrm->endElement(XML_pivotFields);
+
+ // <rowFields>
+
+ if (!aRowFields.empty())
+ {
+ pPivotStrm->startElement(XML_rowFields,
+ XML_count, OString::number(static_cast<tools::Long>(aRowFields.size())));
+
+ for (const auto& rRowField : aRowFields)
+ {
+ pPivotStrm->singleElement(XML_field, XML_x, OString::number(rRowField));
+ }
+
+ pPivotStrm->endElement(XML_rowFields);
+ }
+
+ // <rowItems>
+
+ // <colFields>
+
+ if (!aColFields.empty())
+ {
+ pPivotStrm->startElement(XML_colFields,
+ XML_count, OString::number(static_cast<tools::Long>(aColFields.size())));
+
+ for (const auto& rColField : aColFields)
+ {
+ pPivotStrm->singleElement(XML_field, XML_x, OString::number(rColField));
+ }
+
+ pPivotStrm->endElement(XML_colFields);
+ }
+
+ // <colItems>
+
+ // <pageFields>
+
+ if (!aPageFields.empty())
+ {
+ pPivotStrm->startElement(XML_pageFields,
+ XML_count, OString::number(static_cast<tools::Long>(aPageFields.size())));
+
+ for (const auto& rPageField : aPageFields)
+ {
+ pPivotStrm->singleElement(XML_pageField,
+ XML_fld, OString::number(rPageField),
+ XML_hier, OString::number(-1)); // TODO : handle this correctly.
+ }
+
+ pPivotStrm->endElement(XML_pageFields);
+ }
+
+ // <dataFields>
+
+ if (!aDataFields.empty())
+ {
+ css::uno::Reference<css::container::XNameAccess> xDimsByName;
+ if (auto xDimSupplier = const_cast<ScDPObject&>(rDPObj).GetSource())
+ xDimsByName = xDimSupplier->getDimensions();
+
+ pPivotStrm->startElement(XML_dataFields,
+ XML_count, OString::number(static_cast<tools::Long>(aDataFields.size())));
+
+ for (const auto& rDataField : aDataFields)
+ {
+ tools::Long nDimIdx = rDataField.mnPos;
+ assert(nDimIdx == -2 || aCachedDims[nDimIdx]); // the loop above should have screened for NULL's, skip check for -2 "data field"
+ const ScDPSaveDimension& rDim = *rDataField.mpDim;
+ std::optional<OUString> pName = rDim.GetLayoutName();
+ // tdf#124651: despite being optional in CT_DataField according to ECMA-376 Part 1,
+ // Excel (at least 2016) seems to insist on the presence of "name" attribute in
+ // dataField element.
+ // tdf#124881: try to create a meaningful name; don't use empty string.
+ if (!pName)
+ pName = ScDPUtil::getDisplayedMeasureName(
+ rDim.GetName(), ScDPUtil::toSubTotalFunc(rDim.GetFunction()));
+ auto pItemAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ pItemAttList->add(XML_name, pName->toUtf8());
+ pItemAttList->add(XML_fld, OString::number(nDimIdx));
+ const char* pSubtotal = toOOXMLSubtotalType(rDim.GetFunction());
+ if (pSubtotal)
+ pItemAttList->add(XML_subtotal, pSubtotal);
+ if (xDimsByName)
+ {
+ try
+ {
+ css::uno::Reference<css::beans::XPropertySet> xDimProps(
+ xDimsByName->getByName(rDim.GetName()), uno::UNO_QUERY_THROW);
+ css::uno::Any aVal = xDimProps->getPropertyValue(SC_UNONAME_NUMFMT);
+ sal_uInt32 nScNumFmt = aVal.get<sal_uInt32>();
+ sal_uInt16 nXclNumFmt = GetRoot().GetNumFmtBuffer().Insert(nScNumFmt);
+ pItemAttList->add(XML_numFmtId, OString::number(nXclNumFmt));
+ }
+ catch (uno::Exception&)
+ {
+ SAL_WARN("sc.filter",
+ "Couldn't get number format for data field " << rDim.GetName());
+ // Just skip exporting number format
+ }
+ }
+ pPivotStrm->singleElement(XML_dataField, pItemAttList);
+ }
+
+ pPivotStrm->endElement(XML_dataFields);
+ }
+
+ // Now add style info (use grab bag, or just a set which is default on Excel 2007 through 2016)
+ if (const auto [bHas, aVal] = rDPObj.GetInteropGrabBagValue("pivotTableStyleInfo"); bHas)
+ WriteGrabBagItemToStream(rStrm, XML_pivotTableStyleInfo, aVal);
+ else
+ pPivotStrm->singleElement(XML_pivotTableStyleInfo, XML_name, "PivotStyleLight16",
+ XML_showRowHeaders, "1", XML_showColHeaders, "1",
+ XML_showRowStripes, "0", XML_showColStripes, "0",
+ XML_showLastColumn, "1");
+
+ OUString aBuf = "../pivotCache/pivotCacheDefinition" +
+ OUString::number(nCacheId) +
+ ".xml";
+
+ rStrm.addRelation(
+ pPivotStrm->getOutputStream(),
+ CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"),
+ aBuf);
+
+ pPivotStrm->endElement(XML_pivotTableDefinition);
+}
+
+void XclExpXmlPivotTables::AppendTable( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId )
+{
+ maTables.emplace_back(pTable, nCacheId, nPivotId);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xerecord.cxx b/sc/source/filter/excel/xerecord.cxx
new file mode 100644
index 000000000..8778d75b5
--- /dev/null
+++ b/sc/source/filter/excel/xerecord.cxx
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xerecord.hxx>
+#include <xeroot.hxx>
+#include <xltools.hxx>
+
+#include <oox/token/tokens.hxx>
+#include <oox/export/utils.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::oox;
+
+// Base classes to export Excel records =======================================
+
+XclExpRecordBase::~XclExpRecordBase()
+{
+}
+
+void XclExpRecordBase::Save( XclExpStream& /*rStrm*/ )
+{
+}
+
+void XclExpRecordBase::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+}
+
+XclExpDelegatingRecord::XclExpDelegatingRecord( XclExpRecordBase* pRecord ) :
+ mpRecord( pRecord )
+{
+}
+
+XclExpDelegatingRecord::~XclExpDelegatingRecord()
+{
+ // Do Nothing; we use Delegating Record for other objects we "know" will
+ // survive...
+}
+
+void XclExpDelegatingRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mpRecord )
+ mpRecord->SaveXml( rStrm );
+}
+
+XclExpXmlElementRecord::XclExpXmlElementRecord(sal_Int32 const nElement)
+ : mnElement( nElement )
+{
+}
+
+XclExpXmlElementRecord::~XclExpXmlElementRecord()
+{
+}
+
+XclExpXmlStartElementRecord::XclExpXmlStartElementRecord(sal_Int32 const nElement)
+ : XclExpXmlElementRecord(nElement)
+{
+}
+
+XclExpXmlStartElementRecord::~XclExpXmlStartElementRecord()
+{
+}
+
+void XclExpXmlStartElementRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStream = rStrm.GetCurrentStream();
+ // TODO: no generic way to add attributes here, but it appears to
+ // not be needed yet
+ rStream->startElement(mnElement);
+}
+
+XclExpXmlEndElementRecord::XclExpXmlEndElementRecord( sal_Int32 nElement )
+ : XclExpXmlElementRecord( nElement )
+{
+}
+
+XclExpXmlEndElementRecord::~XclExpXmlEndElementRecord()
+{
+}
+
+void XclExpXmlEndElementRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->endElement( mnElement );
+}
+
+XclExpXmlStartSingleElementRecord::XclExpXmlStartSingleElementRecord(
+ sal_Int32 const nElement)
+ : XclExpXmlElementRecord( nElement )
+{
+}
+
+XclExpXmlStartSingleElementRecord::~XclExpXmlStartSingleElementRecord()
+{
+}
+
+void XclExpXmlStartSingleElementRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStream = rStrm.GetCurrentStream();
+ rStream->write( "<" )->writeId( mnElement );
+}
+
+XclExpXmlEndSingleElementRecord::XclExpXmlEndSingleElementRecord()
+{
+}
+
+XclExpXmlEndSingleElementRecord::~XclExpXmlEndSingleElementRecord()
+{
+}
+
+void XclExpXmlEndSingleElementRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->write( "/>" );
+}
+
+XclExpRecord::XclExpRecord( sal_uInt16 nRecId, std::size_t nRecSize ) :
+ mnRecSize( nRecSize ),
+ mnRecId( nRecId )
+{
+}
+
+XclExpRecord::~XclExpRecord()
+{
+}
+
+void XclExpRecord::SetRecHeader( sal_uInt16 nRecId, std::size_t nRecSize )
+{
+ SetRecId( nRecId );
+ SetRecSize( nRecSize );
+}
+
+void XclExpRecord::WriteBody( XclExpStream& /*rStrm*/ )
+{
+}
+
+void XclExpRecord::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mnRecId != EXC_ID_UNKNOWN, "XclExpRecord::Save - record ID uninitialized" );
+ rStrm.StartRecord( mnRecId, mnRecSize );
+ WriteBody( rStrm );
+ rStrm.EndRecord();
+}
+
+template<>
+void XclExpValueRecord<double>::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mnAttribute == -1 )
+ return;
+ rStrm.WriteAttributes(mnAttribute, OUString::number(maValue));
+}
+
+void XclExpBoolRecord::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast< sal_uInt16 >( mbValue ? 1 : 0 );
+}
+
+void XclExpBoolRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mnAttribute == -1 )
+ return;
+
+ rStrm.WriteAttributes(
+ // HACK: HIDEOBJ (excdoc.cxx) should be its own object to handle XML_showObjects
+ mnAttribute, mnAttribute == XML_showObjects ? "all" : ToPsz( mbValue ));
+}
+
+XclExpDummyRecord::XclExpDummyRecord( sal_uInt16 nRecId, const void* pRecData, std::size_t nRecSize ) :
+ XclExpRecord( nRecId )
+{
+ SetData( pRecData, nRecSize );
+}
+
+void XclExpDummyRecord::SetData( const void* pRecData, std::size_t nRecSize )
+{
+ mpData = pRecData;
+ SetRecSize( pRecData ? nRecSize : 0 );
+}
+
+void XclExpDummyRecord::WriteBody( XclExpStream& rStrm )
+{
+ rStrm.Write( mpData, GetRecSize() );
+}
+
+// Future records =============================================================
+
+XclExpFutureRecord::XclExpFutureRecord( XclFutureRecType eRecType, sal_uInt16 nRecId, std::size_t nRecSize ) :
+ XclExpRecord( nRecId, nRecSize ),
+ meRecType( eRecType )
+{
+}
+
+void XclExpFutureRecord::Save( XclExpStream& rStrm )
+{
+ rStrm.StartRecord( GetRecId(), GetRecSize() + ((meRecType == EXC_FUTUREREC_UNUSEDREF) ? 12 : 4) );
+ rStrm << GetRecId() << sal_uInt16( 0 );
+ if( meRecType == EXC_FUTUREREC_UNUSEDREF )
+ rStrm.WriteZeroBytes( 8 );
+ WriteBody( rStrm );
+ rStrm.EndRecord();
+}
+
+XclExpSubStream::XclExpSubStream( sal_uInt16 nSubStrmType ) :
+ mnSubStrmType( nSubStrmType )
+{
+}
+
+void XclExpSubStream::Save( XclExpStream& rStrm )
+{
+ // BOF record
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF2:
+ rStrm.StartRecord( EXC_ID2_BOF, 4 );
+ rStrm << sal_uInt16( 7 ) << mnSubStrmType;
+ rStrm.EndRecord();
+ break;
+ case EXC_BIFF3:
+ rStrm.StartRecord( EXC_ID3_BOF, 6 );
+ rStrm << sal_uInt16( 0 ) << mnSubStrmType << sal_uInt16( 2104 );
+ rStrm.EndRecord();
+ break;
+ case EXC_BIFF4:
+ rStrm.StartRecord( EXC_ID4_BOF, 6 );
+ rStrm << sal_uInt16( 0 ) << mnSubStrmType << sal_uInt16( 1705 );
+ rStrm.EndRecord();
+ break;
+ case EXC_BIFF5:
+ rStrm.StartRecord( EXC_ID5_BOF, 8 );
+ rStrm << EXC_BOF_BIFF5 << mnSubStrmType << sal_uInt16( 4915 ) << sal_uInt16( 1994 );
+ rStrm.EndRecord();
+ break;
+ case EXC_BIFF8:
+ rStrm.StartRecord( EXC_ID5_BOF, 16 );
+ rStrm << EXC_BOF_BIFF8 << mnSubStrmType << sal_uInt16( 3612 ) << sal_uInt16( 1996 );
+ rStrm << sal_uInt32( 1 ) << sal_uInt32( 6 );
+ rStrm.EndRecord();
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+
+ // substream records
+ XclExpRecordList<>::Save( rStrm );
+
+ // EOF record
+ rStrm.StartRecord( EXC_ID_EOF, 0 );
+ rStrm.EndRecord();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeroot.cxx b/sc/source/filter/excel/xeroot.cxx
new file mode 100644
index 000000000..3bb35d301
--- /dev/null
+++ b/sc/source/filter/excel/xeroot.cxx
@@ -0,0 +1,360 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <officecfg/Office/Common.hxx>
+#include <rtl/random.h>
+#include <sal/log.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <xecontent.hxx>
+#include <xeescher.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xepivot.hxx>
+#include <xestyle.hxx>
+#include <xeroot.hxx>
+#include <xepivotxml.hxx>
+#include <xedbdata.hxx>
+#include <xlcontent.hxx>
+#include <xlname.hxx>
+#include <xllink.hxx>
+
+#include <excrecds.hxx>
+#include <tabprotection.hxx>
+#include <document.hxx>
+
+#include <formulabase.hxx>
+#include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+using namespace ::com::sun::star;
+
+// Global data ================================================================
+
+XclExpRootData::XclExpRootData( XclBiff eBiff, SfxMedium& rMedium,
+ const tools::SvRef<SotStorage>& xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc ) :
+ XclRootData( eBiff, rMedium, xRootStrg, rDoc, eTextEnc, true )
+{
+ mbRelUrl = mrMedium.IsRemote()
+ ? officecfg::Office::Common::Save::URL::Internet::get()
+ : officecfg::Office::Common::Save::URL::FileSystem::get();
+ maStringBuf.setLength(0);
+}
+
+XclExpRootData::~XclExpRootData()
+{
+}
+
+XclExpRoot::XclExpRoot( XclExpRootData& rExpRootData ) :
+ XclRoot( rExpRootData ),
+ mrExpData( rExpRootData )
+{
+}
+
+XclExpTabInfo& XclExpRoot::GetTabInfo() const
+{
+ OSL_ENSURE( mrExpData.mxTabInfo, "XclExpRoot::GetTabInfo - missing object (wrong BIFF?)" );
+ return *mrExpData.mxTabInfo;
+}
+
+XclExpAddressConverter& XclExpRoot::GetAddressConverter() const
+{
+ OSL_ENSURE( mrExpData.mxAddrConv, "XclExpRoot::GetAddressConverter - missing object (wrong BIFF?)" );
+ return *mrExpData.mxAddrConv;
+}
+
+XclExpFormulaCompiler& XclExpRoot::GetFormulaCompiler() const
+{
+ OSL_ENSURE( mrExpData.mxFmlaComp, "XclExpRoot::GetFormulaCompiler - missing object (wrong BIFF?)" );
+ return *mrExpData.mxFmlaComp;
+}
+
+XclExpProgressBar& XclExpRoot::GetProgressBar() const
+{
+ OSL_ENSURE( mrExpData.mxProgress, "XclExpRoot::GetProgressBar - missing object (wrong BIFF?)" );
+ return *mrExpData.mxProgress;
+}
+
+XclExpSst& XclExpRoot::GetSst() const
+{
+ OSL_ENSURE( mrExpData.mxSst, "XclExpRoot::GetSst - missing object (wrong BIFF?)" );
+ return *mrExpData.mxSst;
+}
+
+XclExpPalette& XclExpRoot::GetPalette() const
+{
+ OSL_ENSURE( mrExpData.mxPalette, "XclExpRoot::GetPalette - missing object (wrong BIFF?)" );
+ return *mrExpData.mxPalette;
+}
+
+XclExpFontBuffer& XclExpRoot::GetFontBuffer() const
+{
+ OSL_ENSURE( mrExpData.mxFontBfr, "XclExpRoot::GetFontBuffer - missing object (wrong BIFF?)" );
+ return *mrExpData.mxFontBfr;
+}
+
+XclExpNumFmtBuffer& XclExpRoot::GetNumFmtBuffer() const
+{
+ OSL_ENSURE( mrExpData.mxNumFmtBfr, "XclExpRoot::GetNumFmtBuffer - missing object (wrong BIFF?)" );
+ return *mrExpData.mxNumFmtBfr;
+}
+
+XclExpXFBuffer& XclExpRoot::GetXFBuffer() const
+{
+ OSL_ENSURE( mrExpData.mxXFBfr, "XclExpRoot::GetXFBuffer - missing object (wrong BIFF?)" );
+ return *mrExpData.mxXFBfr;
+}
+
+XclExpLinkManager& XclExpRoot::GetGlobalLinkManager() const
+{
+ OSL_ENSURE( mrExpData.mxGlobLinkMgr, "XclExpRoot::GetGlobalLinkManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxGlobLinkMgr;
+}
+
+XclExpLinkManager& XclExpRoot::GetLocalLinkManager() const
+{
+ OSL_ENSURE( GetLocalLinkMgrRef(), "XclExpRoot::GetLocalLinkManager - missing object (wrong BIFF?)" );
+ return *GetLocalLinkMgrRef();
+}
+
+XclExpNameManager& XclExpRoot::GetNameManager() const
+{
+ OSL_ENSURE( mrExpData.mxNameMgr, "XclExpRoot::GetNameManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxNameMgr;
+}
+
+XclExpObjectManager& XclExpRoot::GetObjectManager() const
+{
+ OSL_ENSURE( mrExpData.mxObjMgr, "XclExpRoot::GetObjectManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxObjMgr;
+}
+
+XclExpFilterManager& XclExpRoot::GetFilterManager() const
+{
+ OSL_ENSURE( mrExpData.mxFilterMgr, "XclExpRoot::GetFilterManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxFilterMgr;
+}
+
+XclExpDxfs& XclExpRoot::GetDxfs() const
+{
+ OSL_ENSURE( mrExpData.mxDxfs, "XclExpRoot::GetDxfs - missing object ( wrong BIFF?)" );
+ return *mrExpData.mxDxfs;
+}
+
+XclExpPivotTableManager& XclExpRoot::GetPivotTableManager() const
+{
+ OSL_ENSURE( mrExpData.mxPTableMgr, "XclExpRoot::GetPivotTableManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxPTableMgr;
+}
+
+XclExpXmlPivotTableManager& XclExpRoot::GetXmlPivotTableManager()
+{
+ assert(mrExpData.mxXmlPTableMgr);
+ return *mrExpData.mxXmlPTableMgr;
+}
+
+XclExpTablesManager& XclExpRoot::GetTablesManager()
+{
+ assert(mrExpData.mxTablesMgr);
+ return *mrExpData.mxTablesMgr;
+}
+
+void XclExpRoot::InitializeConvert()
+{
+ mrExpData.mxTabInfo = std::make_shared<XclExpTabInfo>( GetRoot() );
+ mrExpData.mxAddrConv = std::make_shared<XclExpAddressConverter>( GetRoot() );
+ mrExpData.mxFmlaComp = std::make_shared<XclExpFormulaCompiler>( GetRoot() );
+ mrExpData.mxProgress = std::make_shared<XclExpProgressBar>( GetRoot() );
+
+ GetProgressBar().Initialize();
+}
+
+void XclExpRoot::InitializeGlobals()
+{
+ SetCurrScTab( SCTAB_GLOBAL );
+
+ if( GetBiff() >= EXC_BIFF5 )
+ {
+ mrExpData.mxPalette = new XclExpPalette( GetRoot() );
+ mrExpData.mxFontBfr = new XclExpFontBuffer( GetRoot() );
+ mrExpData.mxNumFmtBfr = new XclExpNumFmtBuffer( GetRoot() );
+ mrExpData.mxXFBfr = new XclExpXFBuffer( GetRoot() );
+ mrExpData.mxGlobLinkMgr = new XclExpLinkManager( GetRoot() );
+ mrExpData.mxNameMgr = new XclExpNameManager( GetRoot() );
+ }
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ mrExpData.mxSst = new XclExpSst();
+ mrExpData.mxObjMgr = std::make_shared<XclExpObjectManager>( GetRoot() );
+ mrExpData.mxFilterMgr = std::make_shared<XclExpFilterManager>( GetRoot() );
+ mrExpData.mxPTableMgr = std::make_shared<XclExpPivotTableManager>( GetRoot() );
+ // BIFF8: only one link manager for all sheets
+ mrExpData.mxLocLinkMgr = mrExpData.mxGlobLinkMgr;
+ mrExpData.mxDxfs = new XclExpDxfs( GetRoot() );
+ }
+
+ if( GetOutput() == EXC_OUTPUT_XML_2007 )
+ {
+ mrExpData.mxXmlPTableMgr = std::make_shared<XclExpXmlPivotTableManager>(GetRoot());
+ mrExpData.mxTablesMgr = std::make_shared<XclExpTablesManager>(GetRoot());
+
+ do
+ {
+ ScDocument& rDoc = GetDoc();
+ // Pass the model factory to OpCodeProvider, not the process
+ // service factory, otherwise a FormulaOpCodeMapperObj would be
+ // instantiated instead of a ScFormulaOpCodeMapperObj and the
+ // ScCompiler virtuals not be called! Which would be the case with
+ // the current (2013-01-24) rDoc.GetServiceManager()
+ const SfxObjectShell* pShell = rDoc.GetDocumentShell();
+ if (!pShell)
+ {
+ SAL_WARN( "sc", "XclExpRoot::InitializeGlobals - no object shell");
+ break;
+ }
+ uno::Reference< lang::XComponent > xComponent = pShell->GetModel();
+ if (!xComponent.is())
+ {
+ SAL_WARN( "sc", "XclExpRoot::InitializeGlobals - no component");
+ break;
+ }
+ uno::Reference< lang::XMultiServiceFactory > xModelFactory( xComponent, uno::UNO_QUERY);
+ oox::xls::OpCodeProvider aOpCodeProvider(xModelFactory, false);
+ // Compiler mocks about non-matching ctor or conversion from
+ // Sequence<...> to Sequence<const ...> if directly created or passed,
+ // conversion through Any works around.
+ uno::Any aAny( aOpCodeProvider.getOoxParserMap());
+ uno::Sequence< const sheet::FormulaOpCodeMapEntry > aOpCodeMapping;
+ if (!(aAny >>= aOpCodeMapping))
+ {
+ SAL_WARN( "sc", "XclExpRoot::InitializeGlobals - no OpCodeMap");
+ break;
+ }
+ ScCompiler aCompiler( rDoc, ScAddress(), rDoc.GetGrammar());
+ mrExpData.mxOpCodeMap = formula::FormulaCompiler::CreateOpCodeMap( aOpCodeMapping, true);
+ } while(false);
+ }
+
+ GetXFBuffer().Initialize();
+ GetNameManager().Initialize();
+}
+
+void XclExpRoot::InitializeTable( SCTAB nScTab )
+{
+ SetCurrScTab( nScTab );
+ if( GetBiff() == EXC_BIFF5 )
+ {
+ // local link manager per sheet
+ mrExpData.mxLocLinkMgr = new XclExpLinkManager( GetRoot() );
+ }
+}
+
+void XclExpRoot::InitializeSave()
+{
+ GetPalette().Finalize();
+ GetXFBuffer().Finalize();
+}
+
+XclExpRecordRef XclExpRoot::CreateRecord( sal_uInt16 nRecId ) const
+{
+ XclExpRecordRef xRec;
+ switch( nRecId )
+ {
+ case EXC_ID_PALETTE: xRec = mrExpData.mxPalette; break;
+ case EXC_ID_FONTLIST: xRec = mrExpData.mxFontBfr; break;
+ case EXC_ID_FORMATLIST: xRec = mrExpData.mxNumFmtBfr; break;
+ case EXC_ID_XFLIST: xRec = mrExpData.mxXFBfr; break;
+ case EXC_ID_SST: xRec = mrExpData.mxSst; break;
+ case EXC_ID_EXTERNSHEET: xRec = GetLocalLinkMgrRef(); break;
+ case EXC_ID_NAME: xRec = mrExpData.mxNameMgr; break;
+ case EXC_ID_DXFS: xRec = mrExpData.mxDxfs; break;
+ }
+ OSL_ENSURE( xRec, "XclExpRoot::CreateRecord - unknown record ID or missing object" );
+ return xRec;
+}
+
+bool XclExpRoot::IsDocumentEncrypted() const
+{
+ // We need to encrypt the content when the document structure is protected.
+ const ScDocProtection* pDocProt = GetDoc().GetDocProtection();
+ if (pDocProt && pDocProt->isProtected() && pDocProt->isOptionEnabled(ScDocProtection::STRUCTURE))
+ return true;
+
+ // Whether password is entered directly into the save dialog.
+ return GetEncryptionData().hasElements();
+}
+
+uno::Sequence< beans::NamedValue > XclExpRoot::GenerateEncryptionData( const OUString& aPass )
+{
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+
+ if ( !aPass.isEmpty() && aPass.getLength() < 16 )
+ {
+ rtlRandomPool aRandomPool = rtl_random_createPool ();
+ sal_uInt8 pnDocId[16];
+ rtl_random_getBytes( aRandomPool, pnDocId, 16 );
+
+ rtl_random_destroyPool( aRandomPool );
+
+ sal_uInt16 pnPasswd[16] = {};
+ for( sal_Int32 nChar = 0; nChar < aPass.getLength(); ++nChar )
+ pnPasswd[nChar] = aPass[nChar];
+
+ ::msfilter::MSCodec_Std97 aCodec;
+ aCodec.InitKey( pnPasswd, pnDocId );
+ aEncryptionData = aCodec.GetEncryptionData();
+ }
+
+ return aEncryptionData;
+}
+
+uno::Sequence< beans::NamedValue > XclExpRoot::GetEncryptionData() const
+{
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(GetMedium().GetItemSet(), SID_ENCRYPTIONDATA, false);
+ if ( pEncryptionDataItem )
+ pEncryptionDataItem->GetValue() >>= aEncryptionData;
+ else
+ {
+ // try to get the encryption data from the password
+ const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium().GetItemSet(), SID_PASSWORD, false);
+ if ( pPasswordItem && !pPasswordItem->GetValue().isEmpty() )
+ aEncryptionData = GenerateEncryptionData( pPasswordItem->GetValue() );
+ }
+
+ return aEncryptionData;
+}
+
+uno::Sequence< beans::NamedValue > XclExpRoot::GenerateDefaultEncryptionData()
+{
+ return GenerateEncryptionData( GetDefaultPassword() );
+}
+
+XclExpRootData::XclExpLinkMgrRef const & XclExpRoot::GetLocalLinkMgrRef() const
+{
+ return IsInGlobals() ? mrExpData.mxGlobLinkMgr : mrExpData.mxLocLinkMgr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xestream.cxx b/sc/source/filter/excel/xestream.cxx
new file mode 100644
index 000000000..f0486cc37
--- /dev/null
+++ b/sc/source/filter/excel/xestream.cxx
@@ -0,0 +1,1259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <utility>
+
+#include <filter/msfilter/util.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/random.h>
+#include <sax/fshelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <sot/storage.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <officecfg/Office/Calc.hxx>
+
+#include <docuno.hxx>
+#include <xestream.hxx>
+#include <xladdress.hxx>
+#include <xlstring.hxx>
+#include <xltools.hxx>
+#include <xeroot.hxx>
+#include <xestring.hxx>
+#include <xlstyle.hxx>
+#include <rangelst.hxx>
+#include <compiler.hxx>
+#include <formulacell.hxx>
+#include <tokenarray.hxx>
+#include <tokenstringcontext.hxx>
+#include <refreshtimerprotector.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <root.hxx>
+#include <sfx2/app.hxx>
+
+#include <docsh.hxx>
+#include <viewdata.hxx>
+#include <excdoc.hxx>
+
+#include <oox/token/tokens.hxx>
+#include <oox/token/relationship.hxx>
+#include <oox/export/drawingml.hxx>
+#include <oox/export/utils.hxx>
+#include <formula/grammar.hxx>
+#include <oox/ole/vbaexport.hxx>
+#include <excelvbaproject.hxx>
+
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <memory>
+#include <comphelper/storagehelper.hxx>
+
+#include <externalrefmgr.hxx>
+
+#define DEBUG_XL_ENCRYPTION 0
+
+using ::com::sun::star::uno::XInterface;
+using ::std::vector;
+
+using namespace com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+using namespace ::formula;
+using namespace ::oox;
+
+XclExpStream::XclExpStream( SvStream& rOutStrm, const XclExpRoot& rRoot, sal_uInt16 nMaxRecSize ) :
+ mrStrm( rOutStrm ),
+ mrRoot( rRoot ),
+ mbUseEncrypter( false ),
+ mnMaxRecSize( nMaxRecSize ),
+ mnCurrMaxSize( 0 ),
+ mnMaxSliceSize( 0 ),
+ mnHeaderSize( 0 ),
+ mnCurrSize( 0 ),
+ mnSliceSize( 0 ),
+ mnPredictSize( 0 ),
+ mnLastSizePos( 0 ),
+ mbInRec( false )
+{
+ if( mnMaxRecSize == 0 )
+ mnMaxRecSize = (mrRoot.GetBiff() <= EXC_BIFF5) ? EXC_MAXRECSIZE_BIFF5 : EXC_MAXRECSIZE_BIFF8;
+ mnMaxContSize = mnMaxRecSize;
+}
+
+XclExpStream::~XclExpStream()
+{
+ mrStrm.FlushBuffer();
+}
+
+void XclExpStream::StartRecord( sal_uInt16 nRecId, std::size_t nRecSize )
+{
+ OSL_ENSURE( !mbInRec, "XclExpStream::StartRecord - another record still open" );
+ DisableEncryption();
+ mnMaxContSize = mnCurrMaxSize = mnMaxRecSize;
+ mnPredictSize = nRecSize;
+ mbInRec = true;
+ InitRecord( nRecId );
+ SetSliceSize( 0 );
+ EnableEncryption();
+}
+
+void XclExpStream::EndRecord()
+{
+ OSL_ENSURE( mbInRec, "XclExpStream::EndRecord - no record open" );
+ DisableEncryption();
+ UpdateRecSize();
+ mrStrm.Seek( STREAM_SEEK_TO_END );
+ mbInRec = false;
+}
+
+void XclExpStream::SetSliceSize( sal_uInt16 nSize )
+{
+ mnMaxSliceSize = nSize;
+ mnSliceSize = 0;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_Int8 nValue )
+{
+ PrepareWrite( 1 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteSChar( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_uInt8 nValue )
+{
+ PrepareWrite( 1 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteUChar( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_Int16 nValue )
+{
+ PrepareWrite( 2 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteInt16( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_uInt16 nValue )
+{
+ PrepareWrite( 2 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteUInt16( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_Int32 nValue )
+{
+ PrepareWrite( 4 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteInt32( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_uInt32 nValue )
+{
+ PrepareWrite( 4 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteUInt32( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( float fValue )
+{
+ PrepareWrite( 4 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, fValue);
+ else
+ mrStrm.WriteFloat( fValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( double fValue )
+{
+ PrepareWrite( 8 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, fValue);
+ else
+ mrStrm.WriteDouble( fValue );
+ return *this;
+}
+
+std::size_t XclExpStream::Write( const void* pData, std::size_t nBytes )
+{
+ std::size_t nRet = 0;
+ if( pData && (nBytes > 0) )
+ {
+ if( mbInRec )
+ {
+ const sal_uInt8* pBuffer = static_cast< const sal_uInt8* >( pData );
+ std::size_t nBytesLeft = nBytes;
+ bool bValid = true;
+
+ while( bValid && (nBytesLeft > 0) )
+ {
+ std::size_t nWriteLen = ::std::min< std::size_t >( PrepareWrite(), nBytesLeft );
+ std::size_t nWriteRet = nWriteLen;
+ if (mbUseEncrypter && HasValidEncrypter())
+ {
+ OSL_ENSURE(nWriteLen > 0, "XclExpStream::Write: write length is 0!");
+ vector<sal_uInt8> aBytes(nWriteLen);
+ memcpy(aBytes.data(), pBuffer, nWriteLen);
+ mxEncrypter->EncryptBytes(mrStrm, aBytes);
+ // TODO: How do I check if all the bytes have been successfully written ?
+ }
+ else
+ {
+ nWriteRet = mrStrm.WriteBytes(pBuffer, nWriteLen);
+ bValid = (nWriteLen == nWriteRet);
+ OSL_ENSURE( bValid, "XclExpStream::Write - stream write error" );
+ }
+ pBuffer += nWriteRet;
+ nRet += nWriteRet;
+ nBytesLeft -= nWriteRet;
+ UpdateSizeVars( nWriteRet );
+ }
+ }
+ else
+ nRet = mrStrm.WriteBytes(pData, nBytes);
+ }
+ return nRet;
+}
+
+void XclExpStream::WriteZeroBytes( std::size_t nBytes )
+{
+ if( mbInRec )
+ {
+ std::size_t nBytesLeft = nBytes;
+ while( nBytesLeft > 0 )
+ {
+ std::size_t nWriteLen = ::std::min< std::size_t >( PrepareWrite(), nBytesLeft );
+ WriteRawZeroBytes( nWriteLen );
+ nBytesLeft -= nWriteLen;
+ UpdateSizeVars( nWriteLen );
+ }
+ }
+ else
+ WriteRawZeroBytes( nBytes );
+}
+
+void XclExpStream::WriteZeroBytesToRecord( std::size_t nBytes )
+{
+ if (!mbInRec)
+ // not in record.
+ return;
+
+ for (std::size_t i = 0; i < nBytes; ++i)
+ *this << sal_uInt8(0)/*nZero*/;
+}
+
+void XclExpStream::CopyFromStream(SvStream& rInStrm, sal_uInt64 const nBytes)
+{
+ sal_uInt64 const nRemaining(rInStrm.remainingSize());
+ sal_uInt64 nBytesLeft = ::std::min(nBytes, nRemaining);
+ if( nBytesLeft <= 0 )
+ return;
+
+ const std::size_t nMaxBuffer = 4096;
+ std::unique_ptr<sal_uInt8[]> pBuffer(
+ new sal_uInt8[ ::std::min<std::size_t>(nBytesLeft, nMaxBuffer) ]);
+ bool bValid = true;
+
+ while( bValid && (nBytesLeft > 0) )
+ {
+ std::size_t nWriteLen = ::std::min<std::size_t>(nBytesLeft, nMaxBuffer);
+ rInStrm.ReadBytes(pBuffer.get(), nWriteLen);
+ std::size_t nWriteRet = Write( pBuffer.get(), nWriteLen );
+ bValid = (nWriteLen == nWriteRet);
+ nBytesLeft -= nWriteRet;
+ }
+}
+
+void XclExpStream::WriteUnicodeBuffer( const ScfUInt16Vec& rBuffer, sal_uInt8 nFlags )
+{
+ SetSliceSize( 0 );
+ nFlags &= EXC_STRF_16BIT; // repeat only 16bit flag
+ sal_uInt16 nCharLen = nFlags ? 2 : 1;
+
+ for( const auto& rItem : rBuffer )
+ {
+ if( mbInRec && (mnCurrSize + nCharLen > mnCurrMaxSize) )
+ {
+ StartContinue();
+ operator<<( nFlags );
+ }
+ if( nCharLen == 2 )
+ operator<<( rItem );
+ else
+ operator<<( static_cast< sal_uInt8 >( rItem ) );
+ }
+}
+
+// Xcl has an obscure sense of whether starting a new record or not,
+// and crashes if it encounters the string header at the very end of a record.
+// Thus we add 1 to give some room, seems like they do it that way but with another count (10?)
+void XclExpStream::WriteByteString( const OString& rString )
+{
+ SetSliceSize( 0 );
+ std::size_t nLen = ::std::min< std::size_t >( rString.getLength(), 0x00FF );
+ nLen = ::std::min< std::size_t >( nLen, 0xFF );
+
+ sal_uInt16 nLeft = PrepareWrite();
+ if( mbInRec && (nLeft <= 1) )
+ StartContinue();
+
+ operator<<( static_cast< sal_uInt8 >( nLen ) );
+ Write( rString.getStr(), nLen );
+}
+
+void XclExpStream::WriteCharBuffer( const ScfUInt8Vec& rBuffer )
+{
+ SetSliceSize( 0 );
+ Write( rBuffer.data(), rBuffer.size() );
+}
+
+void XclExpStream::SetEncrypter( XclExpEncrypterRef const & xEncrypter )
+{
+ mxEncrypter = xEncrypter;
+}
+
+bool XclExpStream::HasValidEncrypter() const
+{
+ return mxEncrypter && mxEncrypter->IsValid();
+}
+
+void XclExpStream::EnableEncryption( bool bEnable )
+{
+ mbUseEncrypter = bEnable && HasValidEncrypter();
+}
+
+void XclExpStream::DisableEncryption()
+{
+ EnableEncryption(false);
+}
+
+void XclExpStream::SetSvStreamPos(sal_uInt64 const nPos)
+{
+ OSL_ENSURE( !mbInRec, "XclExpStream::SetSvStreamPos - not allowed inside of a record" );
+ mbInRec ? 0 : mrStrm.Seek( nPos );
+}
+
+// private --------------------------------------------------------------------
+
+void XclExpStream::InitRecord( sal_uInt16 nRecId )
+{
+ mrStrm.Seek( STREAM_SEEK_TO_END );
+ mrStrm.WriteUInt16( nRecId );
+
+ mnLastSizePos = mrStrm.Tell();
+ mnHeaderSize = static_cast< sal_uInt16 >( ::std::min< std::size_t >( mnPredictSize, mnCurrMaxSize ) );
+ mrStrm.WriteUInt16( mnHeaderSize );
+ mnCurrSize = mnSliceSize = 0;
+}
+
+void XclExpStream::UpdateRecSize()
+{
+ if( mnCurrSize != mnHeaderSize )
+ {
+ mrStrm.Seek( mnLastSizePos );
+ mrStrm.WriteUInt16( mnCurrSize );
+ }
+}
+
+void XclExpStream::UpdateSizeVars( std::size_t nSize )
+{
+ OSL_ENSURE( mnCurrSize + nSize <= mnCurrMaxSize, "XclExpStream::UpdateSizeVars - record overwritten" );
+ mnCurrSize = mnCurrSize + static_cast< sal_uInt16 >( nSize );
+
+ if( mnMaxSliceSize > 0 )
+ {
+ OSL_ENSURE( mnSliceSize + nSize <= mnMaxSliceSize, "XclExpStream::UpdateSizeVars - slice overwritten" );
+ mnSliceSize = mnSliceSize + static_cast< sal_uInt16 >( nSize );
+ if( mnSliceSize >= mnMaxSliceSize )
+ mnSliceSize = 0;
+ }
+}
+
+void XclExpStream::StartContinue()
+{
+ UpdateRecSize();
+ mnCurrMaxSize = mnMaxContSize;
+ mnPredictSize -= mnCurrSize;
+ InitRecord( EXC_ID_CONT );
+}
+
+void XclExpStream::PrepareWrite( sal_uInt16 nSize )
+{
+ if( mbInRec )
+ {
+ if( (mnCurrSize + nSize > mnCurrMaxSize) ||
+ ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
+ StartContinue();
+ UpdateSizeVars( nSize );
+ }
+}
+
+sal_uInt16 XclExpStream::PrepareWrite()
+{
+ sal_uInt16 nRet = 0;
+ if( mbInRec )
+ {
+ if( (mnCurrSize >= mnCurrMaxSize) ||
+ ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
+ StartContinue();
+ UpdateSizeVars( 0 );
+
+ nRet = (mnMaxSliceSize > 0) ? (mnMaxSliceSize - mnSliceSize) : (mnCurrMaxSize - mnCurrSize);
+ }
+ return nRet;
+}
+
+void XclExpStream::WriteRawZeroBytes( std::size_t nBytes )
+{
+ const sal_uInt32 nData = 0;
+ std::size_t nBytesLeft = nBytes;
+ while( nBytesLeft >= sizeof( nData ) )
+ {
+ mrStrm.WriteUInt32( nData );
+ nBytesLeft -= sizeof( nData );
+ }
+ if( nBytesLeft )
+ mrStrm.WriteBytes(&nData, nBytesLeft);
+}
+
+XclExpBiff8Encrypter::XclExpBiff8Encrypter( const XclExpRoot& rRoot ) :
+ mnOldPos(STREAM_SEEK_TO_END),
+ mbValid(false)
+{
+ Sequence< NamedValue > aEncryptionData = rRoot.GetEncryptionData();
+ if( !aEncryptionData.hasElements() )
+ // Empty password. Get the default biff8 password.
+ aEncryptionData = XclExpRoot::GenerateDefaultEncryptionData();
+ Init( aEncryptionData );
+}
+
+XclExpBiff8Encrypter::~XclExpBiff8Encrypter()
+{
+}
+
+void XclExpBiff8Encrypter::GetSaltDigest( sal_uInt8 pnSaltDigest[16] ) const
+{
+ if ( sizeof( mpnSaltDigest ) == 16 )
+ memcpy( pnSaltDigest, mpnSaltDigest, 16 );
+}
+
+void XclExpBiff8Encrypter::GetSalt( sal_uInt8 pnSalt[16] ) const
+{
+ if ( sizeof( mpnSalt ) == 16 )
+ memcpy( pnSalt, mpnSalt, 16 );
+}
+
+void XclExpBiff8Encrypter::GetDocId( sal_uInt8 pnDocId[16] ) const
+{
+ if ( sizeof( mpnDocId ) == 16 )
+ memcpy( pnDocId, mpnDocId, 16 );
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt8 nData )
+{
+ vector<sal_uInt8> aByte { nData };
+ EncryptBytes(rStrm, aByte);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt16 nData )
+{
+ ::std::vector<sal_uInt8> pnBytes
+ {
+ o3tl::narrowing<sal_uInt8>(nData & 0xFF),
+ o3tl::narrowing<sal_uInt8>((nData >> 8) & 0xFF)
+ };
+ EncryptBytes(rStrm, pnBytes);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt32 nData )
+{
+ ::std::vector<sal_uInt8> pnBytes
+ {
+ o3tl::narrowing<sal_uInt8>(nData & 0xFF),
+ o3tl::narrowing<sal_uInt8>((nData >> 8) & 0xFF),
+ o3tl::narrowing<sal_uInt8>((nData >> 16) & 0xFF),
+ o3tl::narrowing<sal_uInt8>((nData >> 24) & 0xFF)
+ };
+ EncryptBytes(rStrm, pnBytes);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, float fValue )
+{
+ ::std::vector<sal_uInt8> pnBytes(4);
+ memcpy(pnBytes.data(), &fValue, 4);
+ EncryptBytes(rStrm, pnBytes);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, double fValue )
+{
+ ::std::vector<sal_uInt8> pnBytes(8);
+ memcpy(pnBytes.data(), &fValue, 8);
+ EncryptBytes(rStrm, pnBytes);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int8 nData )
+{
+ Encrypt(rStrm, static_cast<sal_uInt8>(nData));
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int16 nData )
+{
+ Encrypt(rStrm, static_cast<sal_uInt16>(nData));
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int32 nData )
+{
+ Encrypt(rStrm, static_cast<sal_uInt32>(nData));
+}
+
+void XclExpBiff8Encrypter::Init( const Sequence< NamedValue >& rEncryptionData )
+{
+ mbValid = false;
+
+ if( !maCodec.InitCodec( rEncryptionData ) )
+ return;
+
+ maCodec.GetDocId( mpnDocId );
+
+ // generate the salt here
+ rtlRandomPool aRandomPool = rtl_random_createPool ();
+ rtl_random_getBytes( aRandomPool, mpnSalt, 16 );
+ rtl_random_destroyPool( aRandomPool );
+
+ memset( mpnSaltDigest, 0, sizeof( mpnSaltDigest ) );
+
+ // generate salt hash.
+ ::msfilter::MSCodec_Std97 aCodec;
+ aCodec.InitCodec( rEncryptionData );
+ aCodec.CreateSaltDigest( mpnSalt, mpnSaltDigest );
+
+ // verify to make sure it's in good shape.
+ mbValid = maCodec.VerifyKey( mpnSalt, mpnSaltDigest );
+}
+
+sal_uInt32 XclExpBiff8Encrypter::GetBlockPos( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt32 >( nStrmPos / EXC_ENCR_BLOCKSIZE );
+}
+
+sal_uInt16 XclExpBiff8Encrypter::GetOffsetInBlock( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt16 >( nStrmPos % EXC_ENCR_BLOCKSIZE );
+}
+
+void XclExpBiff8Encrypter::EncryptBytes( SvStream& rStrm, vector<sal_uInt8>& aBytes )
+{
+ sal_uInt64 nStrmPos = rStrm.Tell();
+ sal_uInt16 nBlockOffset = GetOffsetInBlock(nStrmPos);
+ sal_uInt32 nBlockPos = GetBlockPos(nStrmPos);
+
+#if DEBUG_XL_ENCRYPTION
+ fprintf(stdout, "XclExpBiff8Encrypter::EncryptBytes: stream pos = %ld offset in block = %d block pos = %ld\n",
+ nStrmPos, nBlockOffset, nBlockPos);
+#endif
+
+ sal_uInt16 nSize = static_cast< sal_uInt16 >( aBytes.size() );
+ if (nSize == 0)
+ return;
+
+#if DEBUG_XL_ENCRYPTION
+ fprintf(stdout, "RAW: ");
+ for (sal_uInt16 i = 0; i < nSize; ++i)
+ fprintf(stdout, "%2.2X ", aBytes[i]);
+ fprintf(stdout, "\n");
+#endif
+
+ if (mnOldPos != nStrmPos)
+ {
+ sal_uInt16 nOldOffset = GetOffsetInBlock(mnOldPos);
+ sal_uInt32 nOldBlockPos = GetBlockPos(mnOldPos);
+
+ if ( (nBlockPos != nOldBlockPos) || (nBlockOffset < nOldOffset) )
+ {
+ maCodec.InitCipher(nBlockPos);
+ nOldOffset = 0;
+ }
+
+ if (nBlockOffset > nOldOffset)
+ maCodec.Skip(nBlockOffset - nOldOffset);
+ }
+
+ sal_uInt16 nBytesLeft = nSize;
+ sal_uInt16 nPos = 0;
+ while (nBytesLeft > 0)
+ {
+ sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - nBlockOffset;
+ sal_uInt16 nEncBytes = ::std::min(nBlockLeft, nBytesLeft);
+
+ bool bRet = maCodec.Encode(&aBytes[nPos], nEncBytes, &aBytes[nPos], nEncBytes);
+ OSL_ENSURE(bRet, "XclExpBiff8Encrypter::EncryptBytes: encryption failed!!");
+
+ std::size_t nRet = rStrm.WriteBytes(&aBytes[nPos], nEncBytes);
+ OSL_ENSURE(nRet == nEncBytes, "XclExpBiff8Encrypter::EncryptBytes: fail to write to stream!!");
+
+ nStrmPos = rStrm.Tell();
+ nBlockOffset = GetOffsetInBlock(nStrmPos);
+ nBlockPos = GetBlockPos(nStrmPos);
+ if (nBlockOffset == 0)
+ maCodec.InitCipher(nBlockPos);
+
+ nBytesLeft -= nEncBytes;
+ nPos += nEncBytes;
+ }
+ mnOldPos = nStrmPos;
+}
+
+static const char* lcl_GetErrorString( FormulaError nScErrCode )
+{
+ sal_uInt8 nXclErrCode = XclTools::GetXclErrorCode( nScErrCode );
+ switch( nXclErrCode )
+ {
+ case EXC_ERR_NULL: return "#NULL!";
+ case EXC_ERR_DIV0: return "#DIV/0!";
+ case EXC_ERR_VALUE: return "#VALUE!";
+ case EXC_ERR_REF: return "#REF!";
+ case EXC_ERR_NAME: return "#NAME?";
+ case EXC_ERR_NUM: return "#NUM!";
+ case EXC_ERR_NA:
+ default: return "#N/A";
+ }
+}
+
+void XclXmlUtils::GetFormulaTypeAndValue( ScFormulaCell& rCell, const char*& rsType, OUString& rsValue )
+{
+ sc::FormulaResultValue aResValue = rCell.GetResult();
+
+ switch (aResValue.meType)
+ {
+ case sc::FormulaResultValue::Error:
+ rsType = "e";
+ rsValue = ToOUString(lcl_GetErrorString(aResValue.mnError));
+ break;
+ case sc::FormulaResultValue::Value:
+ rsType = rCell.GetFormatType() == SvNumFormatType::LOGICAL
+ && (aResValue.mfValue == 0.0 || aResValue.mfValue == 1.0)
+ ? "b"
+ : "n";
+ rsValue = OUString::number(aResValue.mfValue);
+ break;
+ case sc::FormulaResultValue::String:
+ rsType = "str";
+ rsValue = rCell.GetString().getString();
+ break;
+ case sc::FormulaResultValue::Invalid:
+ default:
+ // TODO : double-check this to see if this is correct.
+ rsType = "inlineStr";
+ rsValue = rCell.GetString().getString();
+ }
+}
+
+OUString XclXmlUtils::GetStreamName( const char* sStreamDir, const char* sStream, sal_Int32 nId )
+{
+ OUStringBuffer sBuf;
+ if( sStreamDir )
+ sBuf.appendAscii( sStreamDir );
+ sBuf.appendAscii( sStream );
+ if( nId )
+ sBuf.append( nId );
+ if( strstr(sStream, "vml") )
+ sBuf.append( ".vml" );
+ else
+ sBuf.append( ".xml" );
+ return sBuf.makeStringAndClear();
+}
+
+OString XclXmlUtils::ToOString( const Color& rColor )
+{
+ char buf[9];
+ sprintf( buf, "%.2X%.2X%.2X%.2X", rColor.GetAlpha(), rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
+ buf[8] = '\0';
+ return OString(buf);
+}
+
+OStringBuffer& XclXmlUtils::ToOString( OStringBuffer& s, const ScAddress& rAddress )
+{
+ rAddress.Format(s, ScRefFlags::VALID, nullptr, ScAddress::Details( FormulaGrammar::CONV_XL_A1));
+ return s;
+}
+
+OString XclXmlUtils::ToOString( const ScfUInt16Vec& rBuffer )
+{
+ if(rBuffer.empty())
+ return OString();
+
+ const sal_uInt16* pBuffer = rBuffer.data();
+ return OString(
+ reinterpret_cast<sal_Unicode const *>(pBuffer), rBuffer.size(),
+ RTL_TEXTENCODING_UTF8);
+}
+
+OString XclXmlUtils::ToOString( const ScDocument& rDoc, const ScRange& rRange, bool bFullAddressNotation )
+{
+ OUString sRange(rRange.Format( rDoc, ScRefFlags::VALID,
+ ScAddress::Details( FormulaGrammar::CONV_XL_A1 ),
+ bFullAddressNotation ) );
+ return sRange.toUtf8();
+}
+
+OString XclXmlUtils::ToOString( const ScDocument& rDoc, const ScRangeList& rRangeList )
+{
+ OUString s;
+ rRangeList.Format(s, ScRefFlags::VALID, rDoc, FormulaGrammar::CONV_XL_OOX, ' ');
+ return s.toUtf8();
+}
+
+static ScAddress lcl_ToAddress( const XclAddress& rAddress )
+{
+ return ScAddress( rAddress.mnCol, rAddress.mnRow, 0 );
+}
+
+OStringBuffer& XclXmlUtils::ToOString( OStringBuffer& s, const XclAddress& rAddress )
+{
+ return ToOString( s, lcl_ToAddress( rAddress ));
+}
+
+OString XclXmlUtils::ToOString( const XclExpString& s )
+{
+ OSL_ENSURE( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
+ return ToOString( s.GetUnicodeBuffer() );
+}
+
+static ScRange lcl_ToRange( const XclRange& rRange )
+{
+ ScRange aRange;
+
+ aRange.aStart = lcl_ToAddress( rRange.maFirst );
+ aRange.aEnd = lcl_ToAddress( rRange.maLast );
+
+ return aRange;
+}
+
+OString XclXmlUtils::ToOString( const ScDocument& rDoc, const XclRangeList& rRanges )
+{
+ ScRangeList aRanges;
+ for( const auto& rRange : rRanges )
+ {
+ aRanges.push_back( lcl_ToRange( rRange ) );
+ }
+ return ToOString( rDoc, aRanges );
+}
+
+OUString XclXmlUtils::ToOUString( const char* s )
+{
+ return OUString( s, static_cast<sal_Int32>(strlen( s )), RTL_TEXTENCODING_ASCII_US );
+}
+
+OUString XclXmlUtils::ToOUString( const ScfUInt16Vec& rBuf, sal_Int32 nStart, sal_Int32 nLength )
+{
+ if( nLength == -1 || ( nLength > (static_cast<sal_Int32>(rBuf.size()) - nStart) ) )
+ nLength = (rBuf.size() - nStart);
+
+ return nLength > 0
+ ? OUString(
+ reinterpret_cast<sal_Unicode const *>(&rBuf[nStart]), nLength)
+ : OUString();
+}
+
+OUString XclXmlUtils::ToOUString(
+ sc::CompileFormulaContext& rCtx, const ScAddress& rAddress, const ScTokenArray* pTokenArray,
+ FormulaError nErrCode )
+{
+ ScCompiler aCompiler( rCtx, rAddress, const_cast<ScTokenArray&>(*pTokenArray));
+
+ /* TODO: isn't this the same as passed in rCtx and thus superfluous? */
+ aCompiler.SetGrammar(FormulaGrammar::GRAM_OOXML);
+
+ sal_Int32 nLen = pTokenArray->GetLen();
+ OUStringBuffer aBuffer( nLen ? (nLen * 5) : 8 );
+ if (nLen)
+ aCompiler.CreateStringFromTokenArray( aBuffer );
+ else
+ {
+ if (nErrCode != FormulaError::NONE)
+ aCompiler.AppendErrorConstant( aBuffer, nErrCode);
+ else
+ {
+ // No code SHOULD be an "error cell", assert caller thought of that
+ // and it really is.
+ assert(!"No code and no error.");
+ }
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+OUString XclXmlUtils::ToOUString( const XclExpString& s )
+{
+ OSL_ENSURE( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
+ return ToOUString( s.GetUnicodeBuffer() );
+}
+
+static void lcl_WriteValue( const sax_fastparser::FSHelperPtr& rStream, sal_Int32 nElement, const char* pValue )
+{
+ if( !pValue )
+ return;
+ rStream->singleElement(nElement, XML_val, pValue);
+}
+
+static const char* lcl_GetUnderlineStyle( FontLineStyle eUnderline, bool& bHaveUnderline )
+{
+ bHaveUnderline = true;
+ switch( eUnderline )
+ {
+ // OOXTODO: doubleAccounting, singleAccounting
+ // OOXTODO: what should be done with the other FontLineStyle values?
+ case LINESTYLE_SINGLE: return "single";
+ case LINESTYLE_DOUBLE: return "double";
+ case LINESTYLE_NONE:
+ default: bHaveUnderline = false; return "none";
+ }
+}
+
+static const char* lcl_ToVerticalAlignmentRun( SvxEscapement eEscapement, bool& bHaveAlignment )
+{
+ bHaveAlignment = true;
+ switch( eEscapement )
+ {
+ case SvxEscapement::Superscript: return "superscript";
+ case SvxEscapement::Subscript: return "subscript";
+ case SvxEscapement::Off:
+ default: bHaveAlignment = false; return "baseline";
+ }
+}
+
+sax_fastparser::FSHelperPtr XclXmlUtils::WriteFontData( sax_fastparser::FSHelperPtr pStream, const XclFontData& rFontData, sal_Int32 nFontId )
+{
+ bool bHaveUnderline, bHaveVertAlign;
+ const char* pUnderline = lcl_GetUnderlineStyle( rFontData.GetScUnderline(), bHaveUnderline );
+ const char* pVertAlign = lcl_ToVerticalAlignmentRun( rFontData.GetScEscapement(), bHaveVertAlign );
+
+ lcl_WriteValue( pStream, XML_b, rFontData.mnWeight > 400 ? ToPsz( true ) : nullptr );
+ lcl_WriteValue( pStream, XML_i, rFontData.mbItalic ? ToPsz( true ) : nullptr );
+ lcl_WriteValue( pStream, XML_strike, rFontData.mbStrikeout ? ToPsz( true ) : nullptr );
+ // OOXTODO: lcl_WriteValue( rStream, XML_condense, ); // mac compatibility setting
+ // OOXTODO: lcl_WriteValue( rStream, XML_extend, ); // compatibility setting
+ lcl_WriteValue( pStream, XML_outline, rFontData.mbOutline ? ToPsz( true ) : nullptr );
+ lcl_WriteValue( pStream, XML_shadow, rFontData.mbShadow ? ToPsz( true ) : nullptr );
+ lcl_WriteValue( pStream, XML_u, bHaveUnderline ? pUnderline : nullptr );
+ lcl_WriteValue( pStream, XML_vertAlign, bHaveVertAlign ? pVertAlign : nullptr );
+ lcl_WriteValue( pStream, XML_sz, OString::number( rFontData.mnHeight / 20.0 ).getStr() ); // Twips->Pt
+ if( rFontData.maColor != Color( ColorAlpha, 0, 0xFF, 0xFF, 0xFF ) )
+ pStream->singleElement( XML_color,
+ // OOXTODO: XML_auto, bool
+ // OOXTODO: XML_indexed, uint
+ XML_rgb, XclXmlUtils::ToOString(rFontData.maColor)
+ // OOXTODO: XML_theme, index into <clrScheme/>
+ // OOXTODO: XML_tint, double
+ );
+ lcl_WriteValue( pStream, nFontId, rFontData.maName.toUtf8().getStr() );
+ lcl_WriteValue( pStream, XML_family, OString::number( rFontData.mnFamily ).getStr() );
+ lcl_WriteValue( pStream, XML_charset, rFontData.mnCharSet != 0 ? OString::number( rFontData.mnCharSet ).getStr() : nullptr );
+
+ return pStream;
+}
+
+XclExpXmlStream::XclExpXmlStream( const uno::Reference< XComponentContext >& rCC, bool bExportVBA, bool bExportTemplate )
+ : XmlFilterBase( rCC ),
+ mpRoot( nullptr ),
+ mbExportVBA(bExportVBA),
+ mbExportTemplate(bExportTemplate)
+{
+}
+
+XclExpXmlStream::~XclExpXmlStream()
+{
+ assert(maStreams.empty() && "Forgotten PopStream()?");
+}
+
+sax_fastparser::FSHelperPtr& XclExpXmlStream::GetCurrentStream()
+{
+ OSL_ENSURE( !maStreams.empty(), "XclExpXmlStream::GetCurrentStream - no current stream" );
+ return maStreams.top();
+}
+
+void XclExpXmlStream::PushStream( sax_fastparser::FSHelperPtr const & aStream )
+{
+ maStreams.push( aStream );
+}
+
+void XclExpXmlStream::PopStream()
+{
+ OSL_ENSURE( !maStreams.empty(), "XclExpXmlStream::PopStream - stack is empty!" );
+ maStreams.pop();
+}
+
+sax_fastparser::FSHelperPtr XclExpXmlStream::GetStreamForPath( const OUString& sPath )
+{
+ if( maOpenedStreamMap.find( sPath ) == maOpenedStreamMap.end() )
+ return sax_fastparser::FSHelperPtr();
+ return maOpenedStreamMap[ sPath ].second;
+}
+
+void XclExpXmlStream::WriteAttribute(sal_Int32 nAttr, std::u16string_view sVal)
+{
+ GetCurrentStream()->write(" ")->writeId(nAttr)->write("=\"")->writeEscaped(sVal)->write("\"");
+}
+
+sax_fastparser::FSHelperPtr XclExpXmlStream::CreateOutputStream (
+ const OUString& sFullStream,
+ std::u16string_view sRelativeStream,
+ const uno::Reference< XOutputStream >& xParentRelation,
+ const char* sContentType,
+ std::u16string_view sRelationshipType,
+ OUString* pRelationshipId )
+{
+ OUString sRelationshipId;
+ if (xParentRelation.is())
+ sRelationshipId = addRelation( xParentRelation, OUString(sRelationshipType), sRelativeStream );
+ else
+ sRelationshipId = addRelation( OUString(sRelationshipType), sRelativeStream );
+
+ if( pRelationshipId )
+ *pRelationshipId = sRelationshipId;
+
+ sax_fastparser::FSHelperPtr p = openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) );
+
+ maOpenedStreamMap[ sFullStream ] = std::make_pair( sRelationshipId, p );
+
+ return p;
+}
+
+bool XclExpXmlStream::importDocument() noexcept
+{
+ return false;
+}
+
+oox::vml::Drawing* XclExpXmlStream::getVmlDrawing()
+{
+ return nullptr;
+}
+
+const oox::drawingml::Theme* XclExpXmlStream::getCurrentTheme() const
+{
+ return nullptr;
+}
+
+oox::drawingml::table::TableStyleListPtr XclExpXmlStream::getTableStyles()
+{
+ return oox::drawingml::table::TableStyleListPtr();
+}
+
+oox::drawingml::chart::ChartConverter* XclExpXmlStream::getChartConverter()
+{
+ // DO NOT CALL
+ return nullptr;
+}
+
+ScDocShell* XclExpXmlStream::getDocShell()
+{
+ uno::Reference< XInterface > xModel( getModel(), UNO_QUERY );
+
+ ScModelObj *pObj = dynamic_cast < ScModelObj* >( xModel.get() );
+
+ if ( pObj )
+ return static_cast < ScDocShell* >( pObj->GetEmbeddedObject() );
+
+ return nullptr;
+}
+
+bool XclExpXmlStream::exportDocument()
+{
+ ScDocShell* pShell = getDocShell();
+ ScDocument& rDoc = pShell->GetDocument();
+ ScRefreshTimerProtector aProt(rDoc.GetRefreshTimerControlAddress());
+
+ const bool bValidateTabNames = officecfg::Office::Calc::Filter::Export::MS_Excel::TruncateLongSheetNames::get();
+ std::vector<OUString> aOriginalTabNames;
+ if (bValidateTabNames)
+ {
+ validateTabNames(aOriginalTabNames);
+ }
+
+ uno::Reference<task::XStatusIndicator> xStatusIndicator = getStatusIndicator();
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->start(ScResId(STR_SAVE_DOC), 100);
+
+ // NOTE: Don't use SotStorage or SvStream any more, and never call
+ // SfxMedium::GetOutStream() anywhere in the xlsx export filter code!
+ // Instead, write via XOutputStream instance.
+ tools::SvRef<SotStorage> rStorage = static_cast<SotStorage*>(nullptr);
+ drawingml::DrawingML::ResetMlCounters();
+ drawingml::DrawingML::PushExportGraphics();
+
+ XclExpRootData aData(
+ EXC_BIFF8, *pShell->GetMedium (), rStorage, rDoc,
+ msfilter::util::getBestTextEncodingFromLocale(
+ Application::GetSettings().GetLanguageTag().getLocale()));
+ aData.meOutput = EXC_OUTPUT_XML_2007;
+ aData.maXclMaxPos.Set( EXC_MAXCOL_XML_2007, EXC_MAXROW_XML_2007, EXC_MAXTAB_XML_2007 );
+ aData.maMaxPos.SetCol( ::std::min( aData.maScMaxPos.Col(), aData.maXclMaxPos.Col() ) );
+ aData.maMaxPos.SetRow( ::std::min( aData.maScMaxPos.Row(), aData.maXclMaxPos.Row() ) );
+ aData.maMaxPos.SetTab( ::std::min( aData.maScMaxPos.Tab(), aData.maXclMaxPos.Tab() ) );
+ aData.mpCompileFormulaCxt = std::make_shared<sc::CompileFormulaContext>(rDoc);
+ // set target path to get correct relative links to target document, not source
+ INetURLObject aPath(getFileUrl());
+ aData.maBasePath = OUString("file:///" + aPath.GetPath() + "\\").replace('\\', '/')
+ // fix for Linux
+ .replaceFirst("file:////", "file:///");
+
+ XclExpRoot aRoot( aData );
+
+ mpRoot = &aRoot;
+ aRoot.GetOldRoot().pER = &aRoot;
+ aRoot.GetOldRoot().eDateiTyp = Biff8;
+ // Get the viewsettings before processing
+ if (ScViewData* pViewData = ScDocShell::GetViewData())
+ pViewData->WriteExtOptions( mpRoot->GetExtDocOptions() );
+
+ OUString const workbook = "xl/workbook.xml";
+ const char* pWorkbookContentType = nullptr;
+ if (mbExportVBA)
+ {
+ if (mbExportTemplate)
+ {
+ pWorkbookContentType = "application/vnd.ms-excel.template.macroEnabled.main+xml";
+ }
+ else
+ {
+ pWorkbookContentType = "application/vnd.ms-excel.sheet.macroEnabled.main+xml";
+ }
+ }
+ else
+ {
+ if (mbExportTemplate)
+ {
+ pWorkbookContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml";
+ }
+ else
+ {
+ pWorkbookContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
+ }
+ }
+
+ PushStream( CreateOutputStream( workbook, workbook,
+ uno::Reference <XOutputStream>(),
+ pWorkbookContentType,
+ oox::getRelationship(Relationship::OFFICEDOCUMENT) ) );
+
+ if (mbExportVBA)
+ {
+ VbaExport aExport(getModel());
+ if (aExport.containsVBAProject())
+ {
+ SvMemoryStream aVbaStream(4096, 4096);
+ tools::SvRef<SotStorage> pVBAStorage(new SotStorage(aVbaStream));
+ aExport.exportVBA( pVBAStorage.get() );
+ aVbaStream.Seek(0);
+ css::uno::Reference<css::io::XInputStream> xVBAStream(
+ new utl::OInputStreamWrapper(aVbaStream));
+ css::uno::Reference<css::io::XOutputStream> xVBAOutput =
+ openFragmentStream("xl/vbaProject.bin", "application/vnd.ms-office.vbaProject");
+ comphelper::OStorageHelper::CopyInputToOutput(xVBAStream, xVBAOutput);
+
+ addRelation(GetCurrentStream()->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT), u"vbaProject.bin");
+ }
+ }
+
+ // destruct at the end of the block
+ {
+ ExcDocument aDocRoot( aRoot );
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(10);
+ aDocRoot.ReadDoc();
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(40);
+ aDocRoot.WriteXml( *this );
+ rDoc.GetExternalRefManager()->disableSkipUnusedFileIds();
+ }
+
+ PopStream();
+ // Free all FSHelperPtr, to flush data before committing storage
+ maOpenedStreamMap.clear();
+
+ commitStorage();
+
+ if (bValidateTabNames)
+ {
+ restoreTabNames(aOriginalTabNames);
+ }
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ mpRoot = nullptr;
+
+ drawingml::DrawingML::PopExportGraphics();
+
+ return true;
+}
+
+::oox::ole::VbaProject* XclExpXmlStream::implCreateVbaProject() const
+{
+ return new ::oox::xls::ExcelVbaProject( getComponentContext(), uno::Reference< XSpreadsheetDocument >( getModel(), UNO_QUERY ) );
+}
+
+OUString XclExpXmlStream::getImplementationName()
+{
+ return "TODO";
+}
+
+void XclExpXmlStream::validateTabNames(std::vector<OUString>& aOriginalTabNames)
+{
+ const int MAX_TAB_NAME_LENGTH = 31;
+
+ ScDocShell* pShell = getDocShell();
+ ScDocument& rDoc = pShell->GetDocument();
+
+ // get original names
+ aOriginalTabNames.resize(rDoc.GetTableCount());
+ for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
+ {
+ rDoc.GetName(nTab, aOriginalTabNames[nTab]);
+ }
+
+ // new tab names
+ std::vector<OUString> aNewTabNames;
+ aNewTabNames.reserve(rDoc.GetTableCount());
+
+ // check and rename
+ for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
+ {
+ const OUString& rOriginalName = aOriginalTabNames[nTab];
+ if (rOriginalName.getLength() > MAX_TAB_NAME_LENGTH)
+ {
+ OUString aNewName;
+
+ // let's try just truncate "<first 31 chars>"
+ if (aNewName.isEmpty())
+ {
+ aNewName = rOriginalName.copy(0, MAX_TAB_NAME_LENGTH);
+ if (aNewTabNames.end() != std::find(aNewTabNames.begin(), aNewTabNames.end(), aNewName) ||
+ aOriginalTabNames.end() != std::find(aOriginalTabNames.begin(), aOriginalTabNames.end(), aNewName))
+ {
+ // was found => let's use another tab name
+ aNewName.clear();
+ }
+ }
+
+ // let's try "<first N chars>-XXX" template
+ for (int digits=1; digits<10 && aNewName.isEmpty(); digits++)
+ {
+ const int rangeStart = pow(10, digits - 1);
+ const int rangeEnd = pow(10, digits);
+
+ for (int i=rangeStart; i<rangeEnd && aNewName.isEmpty(); i++)
+ {
+ aNewName = OUString::Concat(rOriginalName.subView(0, MAX_TAB_NAME_LENGTH - 1 - digits)) + "-" + OUString::number(i);
+ if (aNewTabNames.end() != std::find(aNewTabNames.begin(), aNewTabNames.end(), aNewName) ||
+ aOriginalTabNames.end() != std::find(aOriginalTabNames.begin(), aOriginalTabNames.end(), aNewName))
+ {
+ // was found => let's use another tab name
+ aNewName.clear();
+ }
+ }
+ }
+
+ if (!aNewName.isEmpty())
+ {
+ // new name was created => rename
+ renameTab(nTab, aNewName);
+ aNewTabNames.push_back(aNewName);
+ }
+ else
+ {
+ // default: do not rename
+ aNewTabNames.push_back(rOriginalName);
+ }
+ }
+ else
+ {
+ // default: do not rename
+ aNewTabNames.push_back(rOriginalName);
+ }
+ }
+}
+
+void XclExpXmlStream::restoreTabNames(const std::vector<OUString>& aOriginalTabNames)
+{
+ ScDocShell* pShell = getDocShell();
+ ScDocument& rDoc = pShell->GetDocument();
+
+ for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
+ {
+ const OUString& rOriginalName = aOriginalTabNames[nTab];
+
+ OUString rModifiedName;
+ rDoc.GetName(nTab, rModifiedName);
+
+ if (rOriginalName != rModifiedName)
+ {
+ renameTab(nTab, rOriginalName);
+ }
+ }
+}
+
+void XclExpXmlStream::renameTab(SCTAB aTab, OUString aNewName)
+{
+ ScDocShell* pShell = getDocShell();
+ ScDocument& rDoc = pShell->GetDocument();
+
+ bool bAutoCalcShellDisabled = rDoc.IsAutoCalcShellDisabled();
+ bool bIdleEnabled = rDoc.IsIdleEnabled();
+
+ rDoc.SetAutoCalcShellDisabled( true );
+ rDoc.EnableIdle(false);
+
+ if (rDoc.RenameTab(aTab, aNewName))
+ {
+ SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScTablesChanged));
+ }
+
+ rDoc.SetAutoCalcShellDisabled( bAutoCalcShellDisabled );
+ rDoc.EnableIdle(bIdleEnabled);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xestring.cxx b/sc/source/filter/excel/xestring.cxx
new file mode 100644
index 000000000..295f37709
--- /dev/null
+++ b/sc/source/filter/excel/xestring.cxx
@@ -0,0 +1,565 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <algorithm>
+#include <cassert>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <tools/solar.h>
+#include <xlstyle.hxx>
+#include <xestyle.hxx>
+#include <xestream.hxx>
+#include <xestring.hxx>
+#include <oox/token/tokens.hxx>
+
+using namespace ::oox;
+
+namespace {
+
+// compare vectors
+
+/** Compares two vectors.
+ @return A negative value, if rLeft<rRight; or a positive value, if rLeft>rRight;
+ or 0, if rLeft==rRight. */
+template< typename Type >
+int lclCompareVectors( const ::std::vector< Type >& rLeft, const ::std::vector< Type >& rRight )
+{
+ int nResult = 0;
+
+ // 1st: compare all elements of the vectors
+ auto [aItL, aItR] = std::mismatch(rLeft.begin(), rLeft.end(), rRight.begin(), rRight.end());
+ if ((aItL != rLeft.end()) && (aItR != rRight.end()))
+ nResult = static_cast< int >( *aItL ) - static_cast< int >( *aItR );
+ else
+ // 2nd: compare the vector sizes. Shorter vector is less
+ nResult = static_cast< int >( rLeft.size() ) - static_cast< int >( rRight.size() );
+
+ return nResult;
+}
+
+// hashing helpers
+
+/** Base class for value hashers.
+ @descr These function objects are used to hash any value to a sal_uInt32 value. */
+template< typename Type >
+struct XclHasher {};
+
+template< typename Type >
+struct XclDirectHasher : public XclHasher< Type >
+{
+ sal_uInt32 operator()( Type nVal ) const { return nVal; }
+};
+
+struct XclFormatRunHasher : public XclHasher< const XclFormatRun& >
+{
+ sal_uInt32 operator()( const XclFormatRun& rRun ) const
+ { return (rRun.mnChar << 8) ^ rRun.mnFontIdx; }
+};
+
+/** Calculates a hash value from a vector.
+ @descr Uses the passed hasher function object to calculate hash values from
+ all vector elements. */
+template< typename Type, typename ValueHasher >
+sal_uInt16 lclHashVector( const ::std::vector< Type >& rVec, const ValueHasher& rHasher )
+{
+ sal_uInt32 nHash = rVec.size();
+ for( const auto& rItem : rVec )
+ nHash = (nHash * 31) + rHasher( rItem );
+ return static_cast< sal_uInt16 >( nHash ^ (nHash >> 16) );
+}
+
+/** Calculates a hash value from a vector. Uses XclDirectHasher to hash the vector elements. */
+template< typename Type >
+sal_uInt16 lclHashVector( const ::std::vector< Type >& rVec )
+{
+ return lclHashVector( rVec, XclDirectHasher< Type >() );
+}
+
+} // namespace
+
+// constructors ---------------------------------------------------------------
+
+XclExpString::XclExpString( XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Init( 0, nFlags, nMaxLen, true );
+}
+
+XclExpString::XclExpString( const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Assign( rString, nFlags, nMaxLen );
+}
+
+// assign ---------------------------------------------------------------------
+
+void XclExpString::Assign( const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Build( rString.getStr(), rString.getLength(), nFlags, nMaxLen );
+}
+
+void XclExpString::Assign( sal_Unicode cChar )
+{
+ Build( &cChar, 1, XclStrFlags::NONE, EXC_STR_MAXLEN );
+}
+
+void XclExpString::AssignByte(
+ std::u16string_view rString, rtl_TextEncoding eTextEnc, XclStrFlags nFlags,
+ sal_uInt16 nMaxLen )
+{
+ // length may differ from length of rString
+ OString aByteStr(OUStringToOString(rString, eTextEnc));
+ Build(aByteStr.getStr(), aByteStr.getLength(), nFlags, nMaxLen);
+}
+
+// append ---------------------------------------------------------------------
+
+void XclExpString::Append( std::u16string_view rString )
+{
+ BuildAppend( rString );
+}
+
+void XclExpString::AppendByte( std::u16string_view rString, rtl_TextEncoding eTextEnc )
+{
+ if (!rString.empty())
+ {
+ // length may differ from length of rString
+ OString aByteStr(OUStringToOString(rString, eTextEnc));
+ BuildAppend(aByteStr);
+ }
+}
+
+void XclExpString::AppendByte( sal_Unicode cChar, rtl_TextEncoding eTextEnc )
+{
+ if( !cChar )
+ {
+ char cByteChar = 0;
+ BuildAppend( std::string_view(&cByteChar, 1) );
+ }
+ else
+ {
+ OString aByteStr( &cChar, 1, eTextEnc ); // length may be >1
+ BuildAppend( aByteStr );
+ }
+}
+
+// formatting runs ------------------------------------------------------------
+
+void XclExpString::AppendFormat( sal_uInt16 nChar, sal_uInt16 nFontIdx, bool bDropDuplicate )
+{
+ OSL_ENSURE( maFormats.empty() || (maFormats.back().mnChar < nChar), "XclExpString::AppendFormat - invalid char index" );
+ size_t nMaxSize = static_cast< size_t >( mbIsBiff8 ? EXC_STR_MAXLEN : EXC_STR_MAXLEN_8BIT );
+ if( maFormats.empty() || ((maFormats.size() < nMaxSize) && (!bDropDuplicate || (maFormats.back().mnFontIdx != nFontIdx))) )
+ maFormats.emplace_back( nChar, nFontIdx );
+}
+
+void XclExpString::AppendTrailingFormat( sal_uInt16 nFontIdx )
+{
+ AppendFormat( mnLen, nFontIdx, false );
+}
+
+void XclExpString::LimitFormatCount( sal_uInt16 nMaxCount )
+{
+ if( maFormats.size() > nMaxCount )
+ maFormats.erase( maFormats.begin() + nMaxCount, maFormats.end() );
+}
+
+sal_uInt16 XclExpString::GetLeadingFont()
+{
+ sal_uInt16 nFontIdx = EXC_FONT_NOTFOUND;
+ if( !maFormats.empty() && (maFormats.front().mnChar == 0) )
+ {
+ nFontIdx = maFormats.front().mnFontIdx;
+ }
+ return nFontIdx;
+}
+
+sal_uInt16 XclExpString::RemoveLeadingFont()
+{
+ sal_uInt16 nFontIdx = GetLeadingFont();
+ if( nFontIdx != EXC_FONT_NOTFOUND )
+ {
+ maFormats.erase( maFormats.begin() );
+ }
+ return nFontIdx;
+}
+
+bool XclExpString::IsEqual( const XclExpString& rCmp ) const
+{
+ return
+ (mnLen == rCmp.mnLen) &&
+ (mbIsBiff8 == rCmp.mbIsBiff8) &&
+ (mbIsUnicode == rCmp.mbIsUnicode) &&
+ (mbWrapped == rCmp.mbWrapped) &&
+ (
+ ( mbIsBiff8 && (maUniBuffer == rCmp.maUniBuffer)) ||
+ (!mbIsBiff8 && (maCharBuffer == rCmp.maCharBuffer))
+ ) &&
+ (maFormats == rCmp.maFormats);
+}
+
+bool XclExpString::IsLessThan( const XclExpString& rCmp ) const
+{
+ int nResult = mbIsBiff8 ?
+ lclCompareVectors( maUniBuffer, rCmp.maUniBuffer ) :
+ lclCompareVectors( maCharBuffer, rCmp.maCharBuffer );
+ return (nResult != 0) ? (nResult < 0) : (maFormats < rCmp.maFormats);
+}
+
+// get data -------------------------------------------------------------------
+
+sal_uInt16 XclExpString::GetFormatsCount() const
+{
+ return static_cast< sal_uInt16 >( maFormats.size() );
+}
+
+sal_uInt8 XclExpString::GetFlagField() const
+{
+ return (mbIsUnicode ? EXC_STRF_16BIT : 0) | (IsWriteFormats() ? EXC_STRF_RICH : 0);
+}
+
+sal_uInt16 XclExpString::GetHeaderSize() const
+{
+ return
+ (mb8BitLen ? 1 : 2) + // length field
+ (IsWriteFlags() ? 1 : 0) + // flag field
+ (IsWriteFormats() ? 2 : 0); // richtext formatting count
+}
+
+std::size_t XclExpString::GetBufferSize() const
+{
+ return static_cast<std::size_t>(mnLen) * (mbIsUnicode ? 2 : 1);
+}
+
+std::size_t XclExpString::GetSize() const
+{
+ return
+ GetHeaderSize() + // header
+ GetBufferSize() + // character buffer
+ (IsWriteFormats() ? (4 * GetFormatsCount()) : 0); // richtext formatting
+}
+
+sal_uInt16 XclExpString::GetChar( sal_uInt16 nCharIdx ) const
+{
+ OSL_ENSURE( nCharIdx < Len(), "XclExpString::GetChar - invalid character index" );
+ return static_cast< sal_uInt16 >( mbIsBiff8 ? maUniBuffer[ nCharIdx ] : maCharBuffer[ nCharIdx ] );
+}
+
+sal_uInt16 XclExpString::GetHash() const
+{
+ return
+ (mbIsBiff8 ? lclHashVector( maUniBuffer ) : lclHashVector( maCharBuffer )) ^
+ lclHashVector( maFormats, XclFormatRunHasher() );
+}
+
+// streaming ------------------------------------------------------------------
+
+void XclExpString::WriteLenField( XclExpStream& rStrm ) const
+{
+ if( mb8BitLen )
+ rStrm << static_cast< sal_uInt8 >( mnLen );
+ else
+ rStrm << mnLen;
+}
+
+void XclExpString::WriteFlagField( XclExpStream& rStrm ) const
+{
+ if( mbIsBiff8 )
+ {
+ PrepareWrite( rStrm, 1 );
+ rStrm << GetFlagField();
+ rStrm.SetSliceSize( 0 );
+ }
+}
+
+void XclExpString::WriteHeader( XclExpStream& rStrm ) const
+{
+ OSL_ENSURE( !mb8BitLen || (mnLen < 256), "XclExpString::WriteHeader - string too long" );
+ PrepareWrite( rStrm, GetHeaderSize() );
+ // length
+ WriteLenField( rStrm );
+ // flag field
+ if( IsWriteFlags() )
+ rStrm << GetFlagField();
+ // format run count
+ if( IsWriteFormats() )
+ rStrm << GetFormatsCount();
+ rStrm.SetSliceSize( 0 );
+}
+
+void XclExpString::WriteBuffer( XclExpStream& rStrm ) const
+{
+ if( mbIsBiff8 )
+ rStrm.WriteUnicodeBuffer( maUniBuffer, GetFlagField() );
+ else
+ rStrm.WriteCharBuffer( maCharBuffer );
+}
+
+void XclExpString::WriteFormats( XclExpStream& rStrm, bool bWriteSize ) const
+{
+ if( !IsRich() )
+ return;
+
+ if( mbIsBiff8 )
+ {
+ if( bWriteSize )
+ rStrm << GetFormatsCount();
+ rStrm.SetSliceSize( 4 );
+ for( const auto& rFormat : maFormats )
+ rStrm << rFormat.mnChar << rFormat.mnFontIdx;
+ }
+ else
+ {
+ if( bWriteSize )
+ rStrm << static_cast< sal_uInt8 >( GetFormatsCount() );
+ rStrm.SetSliceSize( 2 );
+ for( const auto& rFormat : maFormats )
+ rStrm << static_cast< sal_uInt8 >( rFormat.mnChar ) << static_cast< sal_uInt8 >( rFormat.mnFontIdx );
+ }
+ rStrm.SetSliceSize( 0 );
+}
+
+void XclExpString::Write( XclExpStream& rStrm ) const
+{
+ if (!mbSkipHeader)
+ WriteHeader( rStrm );
+ WriteBuffer( rStrm );
+ if( IsWriteFormats() ) // only in BIFF8 included in string
+ WriteFormats( rStrm );
+}
+
+void XclExpString::WriteHeaderToMem( sal_uInt8* pnMem ) const
+{
+ assert(pnMem);
+ OSL_ENSURE( !mb8BitLen || (mnLen < 256), "XclExpString::WriteHeaderToMem - string too long" );
+ OSL_ENSURE( !IsWriteFormats(), "XclExpString::WriteHeaderToMem - formatted strings not supported" );
+ // length
+ if( mb8BitLen )
+ {
+ *pnMem = static_cast< sal_uInt8 >( mnLen );
+ ++pnMem;
+ }
+ else
+ {
+ ShortToSVBT16( mnLen, pnMem );
+ pnMem += 2;
+ }
+ // flag field
+ if( IsWriteFlags() )
+ *pnMem = GetFlagField();
+}
+
+void XclExpString::WriteBufferToMem( sal_uInt8* pnMem ) const
+{
+ assert(pnMem);
+ if( IsEmpty() )
+ return;
+
+ if( mbIsBiff8 )
+ {
+ for( const sal_uInt16 nChar : maUniBuffer )
+ {
+ *pnMem = static_cast< sal_uInt8 >( nChar );
+ ++pnMem;
+ if( mbIsUnicode )
+ {
+ *pnMem = static_cast< sal_uInt8 >( nChar >> 8 );
+ ++pnMem;
+ }
+ }
+ }
+ else
+ memcpy( pnMem, maCharBuffer.data(), mnLen );
+}
+
+void XclExpString::WriteToMem( sal_uInt8* pnMem ) const
+{
+ WriteHeaderToMem( pnMem );
+ WriteBufferToMem( pnMem + GetHeaderSize() );
+}
+
+static sal_uInt16 lcl_WriteRun( XclExpXmlStream& rStrm, const ScfUInt16Vec& rBuffer, sal_uInt16 nStart, sal_Int32 nLength, const XclExpFont* pFont )
+{
+ if( nLength == 0 )
+ return nStart;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(XML_r);
+ if( pFont )
+ {
+ const XclFontData& rFontData = pFont->GetFontData();
+ rWorksheet->startElement(XML_rPr);
+ XclXmlUtils::WriteFontData( rWorksheet, rFontData, XML_rFont );
+ rWorksheet->endElement( XML_rPr );
+ }
+ rWorksheet->startElement(XML_t, FSNS(XML_xml, XML_space), "preserve");
+ rWorksheet->writeEscaped( XclXmlUtils::ToOUString( rBuffer, nStart, nLength ) );
+ rWorksheet->endElement( XML_t );
+ rWorksheet->endElement( XML_r );
+ return nStart + nLength;
+}
+
+void XclExpString::WriteXml( XclExpXmlStream& rStrm ) const
+{
+ sax_fastparser::FSHelperPtr rWorksheet = rStrm.GetCurrentStream();
+
+ if( !IsWriteFormats() )
+ {
+ rWorksheet->startElement(XML_t, FSNS(XML_xml, XML_space), "preserve");
+ rWorksheet->writeEscaped( XclXmlUtils::ToOUString( *this ) );
+ rWorksheet->endElement( XML_t );
+ }
+ else
+ {
+ XclExpFontBuffer& rFonts = rStrm.GetRoot().GetFontBuffer();
+
+ sal_uInt16 nStart = 0;
+ const XclExpFont* pFont = nullptr;
+ for ( const auto& rFormat : maFormats )
+ {
+ nStart = lcl_WriteRun( rStrm, GetUnicodeBuffer(),
+ nStart, rFormat.mnChar-nStart, pFont );
+ pFont = rFonts.GetFont( rFormat.mnFontIdx );
+ }
+ lcl_WriteRun( rStrm, GetUnicodeBuffer(),
+ nStart, GetUnicodeBuffer().size() - nStart, pFont );
+ }
+}
+
+bool XclExpString::IsWriteFlags() const
+{
+ return mbIsBiff8 && (!IsEmpty() || !mbSmartFlags);
+}
+
+bool XclExpString::IsWriteFormats() const
+{
+ return mbIsBiff8 && !mbSkipFormats && IsRich();
+}
+
+void XclExpString::SetStrLen( sal_Int32 nNewLen )
+{
+ sal_uInt16 nAllowedLen = (mb8BitLen && (mnMaxLen > 255)) ? 255 : mnMaxLen;
+ mnLen = limit_cast< sal_uInt16 >( nNewLen, 0, nAllowedLen );
+}
+
+void XclExpString::CharsToBuffer( const sal_Unicode* pcSource, sal_Int32 nBegin, sal_Int32 nLen )
+{
+ OSL_ENSURE( maUniBuffer.size() >= o3tl::make_unsigned( nBegin + nLen ),
+ "XclExpString::CharsToBuffer - char buffer invalid" );
+ ScfUInt16Vec::iterator aBeg = maUniBuffer.begin() + nBegin;
+ ScfUInt16Vec::iterator aEnd = aBeg + nLen;
+ const sal_Unicode* pcSrcChar = pcSource;
+ for( ScfUInt16Vec::iterator aIt = aBeg; aIt != aEnd; ++aIt, ++pcSrcChar )
+ {
+ *aIt = static_cast< sal_uInt16 >( *pcSrcChar );
+ if( *aIt & 0xFF00 )
+ mbIsUnicode = true;
+ }
+ if( !mbWrapped )
+ mbWrapped = ::std::find( aBeg, aEnd, EXC_LF ) != aEnd;
+}
+
+void XclExpString::CharsToBuffer( const char* pcSource, sal_Int32 nBegin, sal_Int32 nLen )
+{
+ OSL_ENSURE( maCharBuffer.size() >= o3tl::make_unsigned( nBegin + nLen ),
+ "XclExpString::CharsToBuffer - char buffer invalid" );
+ ScfUInt8Vec::iterator aBeg = maCharBuffer.begin() + nBegin;
+ ScfUInt8Vec::iterator aEnd = aBeg + nLen;
+ const char* pcSrcChar = pcSource;
+ for( ScfUInt8Vec::iterator aIt = aBeg; aIt != aEnd; ++aIt, ++pcSrcChar )
+ *aIt = static_cast< sal_uInt8 >( *pcSrcChar );
+ mbIsUnicode = false;
+ if( !mbWrapped )
+ mbWrapped = ::std::find( aBeg, aEnd, EXC_LF_C ) != aEnd;
+}
+
+void XclExpString::Init( sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen, bool bBiff8 )
+{
+ mbIsBiff8 = bBiff8;
+ mbIsUnicode = bBiff8 && ( nFlags & XclStrFlags::ForceUnicode );
+ mb8BitLen = bool( nFlags & XclStrFlags::EightBitLength );
+ mbSmartFlags = bBiff8 && ( nFlags & XclStrFlags::SmartFlags );
+ mbSkipFormats = bool( nFlags & XclStrFlags::SeparateFormats );
+ mbWrapped = false;
+ mbSkipHeader = bool( nFlags & XclStrFlags::NoHeader );
+ mnMaxLen = nMaxLen;
+ SetStrLen( nCurrLen );
+
+ maFormats.clear();
+ if( mbIsBiff8 )
+ {
+ maCharBuffer.clear();
+ maUniBuffer.resize( mnLen );
+ }
+ else
+ {
+ maUniBuffer.clear();
+ maCharBuffer.resize( mnLen );
+ }
+}
+
+void XclExpString::Build( const sal_Unicode* pcSource, sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Init( nCurrLen, nFlags, nMaxLen, true );
+ CharsToBuffer( pcSource, 0, mnLen );
+}
+
+void XclExpString::Build( const char* pcSource, sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Init( nCurrLen, nFlags, nMaxLen, false );
+ CharsToBuffer( pcSource, 0, mnLen );
+}
+
+void XclExpString::InitAppend( sal_Int32 nAddLen )
+{
+ SetStrLen( static_cast< sal_Int32 >( mnLen ) + nAddLen );
+ if( mbIsBiff8 )
+ maUniBuffer.resize( mnLen );
+ else
+ maCharBuffer.resize( mnLen );
+}
+
+void XclExpString::BuildAppend( std::u16string_view rSource )
+{
+ OSL_ENSURE( mbIsBiff8, "XclExpString::BuildAppend - must not be called at byte strings" );
+ if( mbIsBiff8 )
+ {
+ sal_uInt16 nOldLen = mnLen;
+ InitAppend( rSource.size() );
+ CharsToBuffer( rSource.data(), nOldLen, mnLen - nOldLen );
+ }
+}
+
+void XclExpString::BuildAppend( std::string_view rSource )
+{
+ OSL_ENSURE( !mbIsBiff8, "XclExpString::BuildAppend - must not be called at unicode strings" );
+ if( !mbIsBiff8 )
+ {
+ sal_uInt16 nOldLen = mnLen;
+ InitAppend( rSource.size() );
+ CharsToBuffer( rSource.data(), nOldLen, mnLen - nOldLen );
+ }
+}
+
+void XclExpString::PrepareWrite( XclExpStream& rStrm, sal_uInt16 nBytes ) const
+{
+ rStrm.SetSliceSize( nBytes + (mbIsUnicode ? 2 : 1) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xestyle.cxx b/sc/source/filter/excel/xestyle.cxx
new file mode 100644
index 000000000..1e9c426a3
--- /dev/null
+++ b/sc/source/filter/excel/xestyle.cxx
@@ -0,0 +1,3306 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <xestyle.hxx>
+
+#include <algorithm>
+#include <iterator>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <comphelper/processfactory.hxx>
+#include <rtl/tencinfo.h>
+#include <vcl/font.hxx>
+#include <svl/languageoptions.hxx>
+#include <scitems.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/langitem.hxx>
+#include <document.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <patattr.hxx>
+#include <attrib.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <xestring.hxx>
+#include <xltools.hxx>
+#include <conditio.hxx>
+#include <dbdata.hxx>
+#include <filterentries.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <svl/numformat.hxx>
+
+using namespace ::com::sun::star;
+using namespace oox;
+
+// PALETTE record - color information =========================================
+
+namespace {
+
+sal_uInt32 lclGetWeighting( XclExpColorType eType )
+{
+ switch( eType )
+ {
+ case EXC_COLOR_CHARTLINE: return 1;
+ case EXC_COLOR_CELLBORDER:
+ case EXC_COLOR_CHARTAREA: return 2;
+ case EXC_COLOR_CELLTEXT:
+ case EXC_COLOR_CHARTTEXT:
+ case EXC_COLOR_CTRLTEXT: return 10;
+ case EXC_COLOR_TABBG:
+ case EXC_COLOR_CELLAREA: return 20;
+ case EXC_COLOR_GRID: return 50;
+ default: OSL_FAIL( "lclGetWeighting - unknown color type" );
+ }
+ return 1;
+}
+
+sal_Int32 lclGetColorDistance( const Color& rColor1, const Color& rColor2 )
+{
+ sal_Int32 nDist = rColor1.GetRed() - rColor2.GetRed();
+ nDist *= nDist * 77;
+ sal_Int32 nDummy = rColor1.GetGreen() - rColor2.GetGreen();
+ nDist += nDummy * nDummy * 151;
+ nDummy = rColor1.GetBlue() - rColor2.GetBlue();
+ nDist += nDummy * nDummy * 28;
+ return nDist;
+}
+
+sal_uInt8 lclGetMergedColorComp( sal_uInt8 nComp1, sal_uInt32 nWeight1, sal_uInt8 nComp2, sal_uInt32 nWeight2 )
+{
+ sal_uInt8 nComp1Dist = ::std::min< sal_uInt8 >( nComp1, 0xFF - nComp1 );
+ sal_uInt8 nComp2Dist = ::std::min< sal_uInt8 >( nComp2, 0xFF - nComp2 );
+ if( nComp1Dist != nComp2Dist )
+ {
+ /* #i36945# One of the passed RGB components is nearer at the limits (0x00 or 0xFF).
+ Increase its weighting to prevent fading of the colors during reduction. */
+ const sal_uInt8& rnCompNearer = (nComp1Dist < nComp2Dist) ? nComp1 : nComp2;
+ sal_uInt32& rnWeight = (nComp1Dist < nComp2Dist) ? nWeight1 : nWeight2;
+ rnWeight *= ((rnCompNearer - 0x80L) * (rnCompNearer - 0x7FL) / 0x1000L + 1);
+ }
+ sal_uInt32 nWSum = nWeight1 + nWeight2;
+ return static_cast< sal_uInt8 >( (nComp1 * nWeight1 + nComp2 * nWeight2 + nWSum / 2) / nWSum );
+}
+
+void lclSetMixedColor( Color& rDest, const Color& rSrc1, const Color& rSrc2 )
+{
+ rDest.SetRed( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetRed() ) + rSrc2.GetRed()) / 2 ) );
+ rDest.SetGreen( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetGreen() ) + rSrc2.GetGreen()) / 2 ) );
+ rDest.SetBlue( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetBlue() ) + rSrc2.GetBlue()) / 2 ) );
+}
+
+} // namespace
+
+// additional classes for color reduction -------------------------------------
+
+namespace {
+
+/** Represents an entry in a color list.
+
+ The color stores a weighting value, which increases the more the color is
+ used in the document. Heavy-weighted colors will change less than others on
+ color reduction.
+ */
+class XclListColor
+{
+private:
+ Color maColor; /// The color value of this palette entry.
+ sal_uInt32 mnColorId; /// Unique color ID for color reduction.
+ sal_uInt32 mnWeight; /// Weighting for color reduction.
+ bool mbBaseColor; /// true = Handle as base color, (don't remove/merge).
+
+public:
+ explicit XclListColor( const Color& rColor, sal_uInt32 nColorId );
+
+ /** Returns the RGB color value of the color. */
+ const Color& GetColor() const { return maColor; }
+ /** Returns the unique ID of the color. */
+ sal_uInt32 GetColorId() const { return mnColorId; }
+ /** Returns the current weighting of the color. */
+ sal_uInt32 GetWeighting() const { return mnWeight; }
+ /** Returns true, if this color is a base color, i.e. it will not be removed or merged. */
+ bool IsBaseColor() const { return mbBaseColor; }
+
+ /** Adds the passed weighting to this color. */
+ void AddWeighting( sal_uInt32 nWeight ) { mnWeight += nWeight; }
+ /** Merges this color with rColor, regarding weighting settings. */
+ void Merge( const XclListColor& rColor );
+};
+
+XclListColor::XclListColor( const Color& rColor, sal_uInt32 nColorId ) :
+ maColor( rColor ),
+ mnColorId( nColorId ),
+ mnWeight( 0 )
+{
+ mbBaseColor =
+ ((rColor.GetRed() == 0x00) || (rColor.GetRed() == 0xFF)) &&
+ ((rColor.GetGreen() == 0x00) || (rColor.GetGreen() == 0xFF)) &&
+ ((rColor.GetBlue() == 0x00) || (rColor.GetBlue() == 0xFF));
+}
+
+void XclListColor::Merge( const XclListColor& rColor )
+{
+ sal_uInt32 nWeight2 = rColor.GetWeighting();
+ // do not change RGB value of base colors
+ if( !mbBaseColor )
+ {
+ maColor.SetRed( lclGetMergedColorComp( maColor.GetRed(), mnWeight, rColor.maColor.GetRed(), nWeight2 ) );
+ maColor.SetGreen( lclGetMergedColorComp( maColor.GetGreen(), mnWeight, rColor.maColor.GetGreen(), nWeight2 ) );
+ maColor.SetBlue( lclGetMergedColorComp( maColor.GetBlue(), mnWeight, rColor.maColor.GetBlue(), nWeight2 ) );
+ }
+ AddWeighting( nWeight2 );
+}
+
+/** Data for each inserted original color, represented by a color ID. */
+struct XclColorIdData
+{
+ Color maColor; /// The original inserted color.
+ sal_uInt32 mnIndex; /// Maps current color ID to color list or export color vector.
+ /** Sets the contents of this struct. */
+ void Set( const Color& rColor, sal_uInt32 nIndex ) { maColor = rColor; mnIndex = nIndex; }
+};
+
+/** A color that will be written to the Excel file. */
+struct XclPaletteColor
+{
+ Color maColor; /// Resulting color to export.
+ bool mbUsed; /// true = Entry is used in the document.
+
+ explicit XclPaletteColor( const Color& rColor ) : maColor( rColor ), mbUsed( false ) {}
+ void SetColor( const Color& rColor ) { maColor = rColor; mbUsed = true; }
+};
+
+/** Maps a color list index to a palette index.
+ @descr Used to remap the color ID data vector from list indexes to palette indexes. */
+struct XclRemap
+{
+ sal_uInt32 mnPalIndex; /// Index to palette.
+ bool mbProcessed; /// true = List color already processed.
+
+ explicit XclRemap() : mnPalIndex( 0 ), mbProcessed( false ) {}
+ void SetIndex( sal_uInt32 nPalIndex )
+ { mnPalIndex = nPalIndex; mbProcessed = true; }
+};
+
+/** Stores the nearest palette color index of a list color. */
+struct XclNearest
+{
+ sal_uInt32 mnPalIndex; /// Index to nearest palette color.
+ sal_Int32 mnDist; /// Distance to palette color.
+
+ explicit XclNearest() : mnPalIndex( 0 ), mnDist( 0 ) {}
+};
+
+} // namespace
+
+class XclExpPaletteImpl
+{
+public:
+ explicit XclExpPaletteImpl( const XclDefaultPalette& rDefPal );
+
+ /** Inserts the color into the list and updates weighting.
+ @param nAutoDefault The Excel palette index for automatic color.
+ @return A unique ID for this color. */
+ sal_uInt32 InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault = 0 );
+ /** Returns the color ID representing a fixed Excel palette index (i.e. for auto colors). */
+ static sal_uInt32 GetColorIdFromIndex( sal_uInt16 nIndex );
+
+ /** Reduces the color list to the maximum count of the current BIFF version. */
+ void Finalize();
+
+ /** Returns the Excel palette index of the color with passed color ID. */
+ sal_uInt16 GetColorIndex( sal_uInt32 nColorId ) const;
+
+ /** Returns a foreground and background color for the two passed color IDs.
+ @descr If rnXclPattern contains a solid pattern, this function tries to find
+ the two best fitting colors and a mix pattern (25%, 50% or 75%) for nForeColorId.
+ This will result in a better approximation to the passed foreground color. */
+ void GetMixedColors(
+ sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
+ sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const;
+
+ /** Returns the RGB color for a (non-zero-based) Excel palette entry.
+ @return The color from current or default palette or COL_AUTO, if nothing else found. */
+ Color GetColor( sal_uInt16 nXclIndex ) const;
+
+ /** Returns true, if all colors of the palette are equal to default palette colors. */
+ bool IsDefaultPalette() const;
+ /** Writes the color list (contents of the palette record) to the passed stream. */
+ void WriteBody( XclExpStream& rStrm );
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ /** Returns the Excel index of a 0-based color index. */
+ static sal_uInt16 GetXclIndex( sal_uInt32 nIndex )
+ { return static_cast< sal_uInt16 >( nIndex + EXC_COLOR_USEROFFSET ); }
+
+ /** Returns the original inserted color represented by the color ID nColorId. */
+ const Color& GetOriginalColor( sal_uInt32 nColorId ) const;
+
+ /** Searches for rColor, returns the ordered insertion index for rColor in rnIndex. */
+ XclListColor* SearchListEntry( const Color& rColor, sal_uInt32& rnIndex );
+ /** Creates and inserts a new color list entry at the specified list position. */
+ XclListColor* CreateListEntry( const Color& rColor, sal_uInt32 nIndex );
+
+ /** Raw and fast reduction of the palette. */
+ void RawReducePalette( sal_uInt32 nPass );
+ /** Reduction of one color using advanced color merging based on color weighting. */
+ void ReduceLeastUsedColor();
+
+ /** Finds the least used color and returns its current list index. */
+ sal_uInt32 GetLeastUsedListColor() const;
+ /** Returns the list index of the color nearest to rColor.
+ @param nIgnore List index of a color which will be ignored.
+ @return The list index of the found color. */
+ sal_uInt32 GetNearestListColor( const Color& rColor, sal_uInt32 nIgnore ) const;
+ /** Returns the list index of the color nearest to the color with list index nIndex. */
+ sal_uInt32 GetNearestListColor( sal_uInt32 nIndex ) const;
+
+ /** Returns in rnIndex the palette index of the color nearest to rColor.
+ Searches for default colors only (colors never replaced).
+ @return The distance from passed color to found color. */
+ sal_Int32 GetNearestPaletteColor(
+ sal_uInt32& rnIndex,
+ const Color& rColor ) const;
+ /** Returns in rnFirst and rnSecond the palette indexes of the two colors nearest to rColor.
+ @return The minimum distance from passed color to found colors. */
+ sal_Int32 GetNearPaletteColors(
+ sal_uInt32& rnFirst, sal_uInt32& rnSecond,
+ const Color& rColor ) const;
+
+private:
+ typedef std::vector< std::unique_ptr<XclListColor> > XclListColorList;
+ typedef std::shared_ptr< XclListColorList > XclListColorListRef;
+
+ const XclDefaultPalette& mrDefPal; /// The default palette for the current BIFF version.
+ XclListColorListRef mxColorList; /// Working color list.
+ std::vector< XclColorIdData >
+ maColorIdDataVec; /// Data of all CIDs.
+ std::vector< XclPaletteColor >
+ maPalette; /// Contains resulting colors to export.
+ sal_uInt32 mnLastIdx; /// Last insertion index for search opt.
+};
+
+const sal_uInt32 EXC_PAL_INDEXBASE = 0xFFFF0000;
+const sal_uInt32 EXC_PAL_MAXRAWSIZE = 1024;
+
+XclExpPaletteImpl::XclExpPaletteImpl( const XclDefaultPalette& rDefPal ) :
+ mrDefPal( rDefPal ),
+ mxColorList( std::make_shared<XclListColorList>() ),
+ mnLastIdx( 0 )
+{
+ // initialize maPalette with default colors
+ sal_uInt16 nCount = static_cast< sal_uInt16 >( mrDefPal.GetColorCount() );
+ maPalette.reserve( nCount );
+ for( sal_uInt16 nIdx = 0; nIdx < nCount; ++nIdx )
+ maPalette.emplace_back( mrDefPal.GetDefColor( GetXclIndex( nIdx ) ) );
+
+ InsertColor( COL_BLACK, EXC_COLOR_CELLTEXT );
+}
+
+sal_uInt32 XclExpPaletteImpl::InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault )
+{
+ if( rColor == COL_AUTO )
+ return GetColorIdFromIndex( nAutoDefault );
+
+ sal_uInt32 nFoundIdx = 0;
+ XclListColor* pEntry = SearchListEntry( rColor, nFoundIdx );
+ if( !pEntry || (pEntry->GetColor() != rColor) )
+ pEntry = CreateListEntry( rColor, nFoundIdx );
+ pEntry->AddWeighting( lclGetWeighting( eType ) );
+
+ return pEntry->GetColorId();
+}
+
+sal_uInt32 XclExpPaletteImpl::GetColorIdFromIndex( sal_uInt16 nIndex )
+{
+ return EXC_PAL_INDEXBASE | nIndex;
+}
+
+void XclExpPaletteImpl::Finalize()
+{
+// --- build initial color ID data vector (maColorIdDataVec) ---
+
+ sal_uInt32 nCount = mxColorList->size();
+ maColorIdDataVec.resize( nCount );
+ for( sal_uInt32 nIdx = 0; nIdx < nCount; ++nIdx )
+ {
+ const XclListColor& listColor = *mxColorList->at( nIdx );
+ maColorIdDataVec[ listColor.GetColorId() ].Set( listColor.GetColor(), nIdx );
+ }
+
+// --- loop as long as current color count does not fit into palette of current BIFF ---
+
+ // phase 1: raw reduction (performance reasons, #i36945#)
+ sal_uInt32 nPass = 0;
+ while( mxColorList->size() > EXC_PAL_MAXRAWSIZE )
+ RawReducePalette( nPass++ );
+
+ // phase 2: precise reduction using advanced color merging based on color weighting
+ while( mxColorList->size() > mrDefPal.GetColorCount() )
+ ReduceLeastUsedColor();
+
+// --- use default palette and replace colors with nearest used colors ---
+
+ nCount = mxColorList->size();
+ std::vector< XclRemap > aRemapVec( nCount );
+ std::vector< XclNearest > aNearestVec( nCount );
+
+ // in each run: search the best fitting color and replace a default color with it
+ for( sal_uInt32 nRun = 0; nRun < nCount; ++nRun )
+ {
+ sal_uInt32 nIndex;
+ // find nearest unused default color for each unprocessed list color
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ aNearestVec[ nIndex ].mnDist = aRemapVec[ nIndex ].mbProcessed ? SAL_MAX_INT32 :
+ GetNearestPaletteColor( aNearestVec[ nIndex ].mnPalIndex, mxColorList->at( nIndex )->GetColor() );
+ // find the list color which is nearest to a default color
+ sal_uInt32 nFound = 0;
+ for( nIndex = 1; nIndex < nCount; ++nIndex )
+ if( aNearestVec[ nIndex ].mnDist < aNearestVec[ nFound ].mnDist )
+ nFound = nIndex;
+ // replace default color with list color
+ sal_uInt32 nNearest = aNearestVec[ nFound ].mnPalIndex;
+ OSL_ENSURE( nNearest < maPalette.size(), "XclExpPaletteImpl::Finalize - algorithm error" );
+ maPalette[ nNearest ].SetColor( mxColorList->at( nFound )->GetColor() );
+ aRemapVec[ nFound ].SetIndex( nNearest );
+ }
+
+ // remap color ID data map (maColorIdDataVec) from list indexes to palette indexes
+ for( auto& rColorIdData : maColorIdDataVec )
+ rColorIdData.mnIndex = aRemapVec[ rColorIdData.mnIndex ].mnPalIndex;
+}
+
+sal_uInt16 XclExpPaletteImpl::GetColorIndex( sal_uInt32 nColorId ) const
+{
+ sal_uInt16 nRet = 0;
+ if( nColorId >= EXC_PAL_INDEXBASE )
+ nRet = static_cast< sal_uInt16 >( nColorId & ~EXC_PAL_INDEXBASE );
+ else if( nColorId < maColorIdDataVec.size() )
+ nRet = GetXclIndex( maColorIdDataVec[ nColorId ].mnIndex );
+ return nRet;
+}
+
+void XclExpPaletteImpl::GetMixedColors(
+ sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
+ sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const
+{
+ rnXclForeIx = GetColorIndex( nForeColorId );
+ rnXclBackIx = GetColorIndex( nBackColorId );
+ if( (rnXclPattern != EXC_PATT_SOLID) || (nForeColorId >= maColorIdDataVec.size()) )
+ return;
+
+ // now we have solid pattern, and a defined foreground (background doesn't care for solid pattern)
+
+ sal_uInt32 nIndex1, nIndex2;
+ Color aForeColor( GetOriginalColor( nForeColorId ) );
+ sal_Int32 nFirstDist = GetNearPaletteColors( nIndex1, nIndex2, aForeColor );
+ if( (nIndex1 >= maPalette.size()) || (nIndex2 >= maPalette.size()) )
+ return;
+
+ Color aColorArr[ 5 ];
+ aColorArr[ 0 ] = maPalette[ nIndex1 ].maColor;
+ aColorArr[ 4 ] = maPalette[ nIndex2 ].maColor;
+ lclSetMixedColor( aColorArr[ 2 ], aColorArr[ 0 ], aColorArr[ 4 ] );
+ lclSetMixedColor( aColorArr[ 1 ], aColorArr[ 0 ], aColorArr[ 2 ] );
+ lclSetMixedColor( aColorArr[ 3 ], aColorArr[ 2 ], aColorArr[ 4 ] );
+
+ sal_Int32 nMinDist = nFirstDist;
+ sal_uInt32 nMinIndex = 0;
+ for( sal_uInt32 nCnt = 1; nCnt < 4; ++nCnt )
+ {
+ sal_Int32 nDist = lclGetColorDistance( aForeColor, aColorArr[ nCnt ] );
+ if( nDist < nMinDist )
+ {
+ nMinDist = nDist;
+ nMinIndex = nCnt;
+ }
+ }
+ rnXclForeIx = GetXclIndex( nIndex1 );
+ rnXclBackIx = GetXclIndex( nIndex2 );
+ if( nMinDist < nFirstDist )
+ {
+ switch( nMinIndex )
+ {
+ case 1: rnXclPattern = EXC_PATT_75_PERC; break;
+ case 2: rnXclPattern = EXC_PATT_50_PERC; break;
+ case 3: rnXclPattern = EXC_PATT_25_PERC; break;
+ }
+ }
+}
+
+Color XclExpPaletteImpl::GetColor( sal_uInt16 nXclIndex ) const
+{
+ if( nXclIndex >= EXC_COLOR_USEROFFSET )
+ {
+ sal_uInt32 nIdx = nXclIndex - EXC_COLOR_USEROFFSET;
+ if( nIdx < maPalette.size() )
+ return maPalette[ nIdx ].maColor;
+ }
+ return mrDefPal.GetDefColor( nXclIndex );
+}
+
+bool XclExpPaletteImpl::IsDefaultPalette() const
+{
+ bool bDefault = true;
+ for( sal_uInt32 nIdx = 0, nSize = static_cast< sal_uInt32 >( maPalette.size() ); bDefault && (nIdx < nSize); ++nIdx )
+ bDefault = maPalette[ nIdx ].maColor == mrDefPal.GetDefColor( GetXclIndex( nIdx ) );
+ return bDefault;
+}
+
+void XclExpPaletteImpl::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast< sal_uInt16 >( maPalette.size() );
+ for( const auto& rColor : maPalette )
+ rStrm << rColor.maColor;
+}
+
+void XclExpPaletteImpl::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maPalette.empty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_colors);
+ rStyleSheet->startElement(XML_indexedColors);
+ for( const auto& rColor : maPalette )
+ rStyleSheet->singleElement(XML_rgbColor, XML_rgb, XclXmlUtils::ToOString(rColor.maColor));
+ rStyleSheet->endElement( XML_indexedColors );
+ rStyleSheet->endElement( XML_colors );
+}
+
+const Color& XclExpPaletteImpl::GetOriginalColor( sal_uInt32 nColorId ) const
+{
+ if( nColorId < maColorIdDataVec.size() )
+ return maColorIdDataVec[ nColorId ].maColor;
+ return maPalette[ 0 ].maColor;
+}
+
+XclListColor* XclExpPaletteImpl::SearchListEntry( const Color& rColor, sal_uInt32& rnIndex )
+{
+ rnIndex = 0;
+
+ if (mxColorList->empty())
+ return nullptr;
+
+ XclListColor* pEntry = nullptr;
+
+ // search optimization for equal-colored objects occurring repeatedly
+ if (mnLastIdx < mxColorList->size())
+ {
+ pEntry = (*mxColorList)[mnLastIdx].get();
+ if( pEntry->GetColor() == rColor )
+ {
+ rnIndex = mnLastIdx;
+ return pEntry;
+ }
+ }
+
+ // binary search for color
+ sal_uInt32 nBegIdx = 0;
+ sal_uInt32 nEndIdx = mxColorList->size();
+ bool bFound = false;
+ while( !bFound && (nBegIdx < nEndIdx) )
+ {
+ rnIndex = (nBegIdx + nEndIdx) / 2;
+ pEntry = (*mxColorList)[rnIndex].get();
+ bFound = pEntry->GetColor() == rColor;
+ if( !bFound )
+ {
+ if( pEntry->GetColor() < rColor )
+ nBegIdx = rnIndex + 1;
+ else
+ nEndIdx = rnIndex;
+ }
+ }
+
+ // not found - use end of range as new insertion position
+ if( !bFound )
+ rnIndex = nEndIdx;
+
+ mnLastIdx = rnIndex;
+ return pEntry;
+}
+
+XclListColor* XclExpPaletteImpl::CreateListEntry( const Color& rColor, sal_uInt32 nIndex )
+{
+ XclListColor* pEntry = new XclListColor( rColor, mxColorList->size() );
+ mxColorList->insert(mxColorList->begin() + nIndex, std::unique_ptr<XclListColor>(pEntry));
+ return pEntry;
+}
+
+void XclExpPaletteImpl::RawReducePalette( sal_uInt32 nPass )
+{
+ /* Fast palette reduction - in each call of this function one RGB component
+ of each color is reduced to a lower number of distinct values.
+ Pass 0: Blue is reduced to 128 distinct values.
+ Pass 1: Red is reduced to 128 distinct values.
+ Pass 2: Green is reduced to 128 distinct values.
+ Pass 3: Blue is reduced to 64 distinct values.
+ Pass 4: Red is reduced to 64 distinct values.
+ Pass 5: Green is reduced to 64 distinct values.
+ And so on...
+ */
+
+ XclListColorListRef xOldList = mxColorList;
+ mxColorList = std::make_shared<XclListColorList>();
+
+ // maps old list indexes to new list indexes, used to update maColorIdDataVec
+ ScfUInt32Vec aListIndexMap;
+ aListIndexMap.reserve( xOldList->size() );
+
+ // preparations
+ sal_uInt8 nR, nG, nB;
+ sal_uInt8& rnComp = ((nPass % 3 == 0) ? nB : ((nPass % 3 == 1) ? nR : nG));
+ nPass /= 3;
+ OSL_ENSURE( nPass < 7, "XclExpPaletteImpl::RawReducePalette - reduction not terminated" );
+
+ static const sal_uInt8 spnFactor2[] = { 0x81, 0x82, 0x84, 0x88, 0x92, 0xAA, 0xFF };
+ sal_uInt8 nFactor1 = static_cast< sal_uInt8 >( 0x02 << nPass );
+ sal_uInt8 nFactor2 = spnFactor2[ nPass ];
+ sal_uInt8 nFactor3 = static_cast< sal_uInt8 >( 0x40 >> nPass );
+
+ // process each color in the old color list
+ for(const std::unique_ptr<XclListColor> & pOldColor : *xOldList)
+ {
+ // get the old list entry
+ const XclListColor* pOldEntry = pOldColor.get();
+ nR = pOldEntry->GetColor().GetRed();
+ nG = pOldEntry->GetColor().GetGreen();
+ nB = pOldEntry->GetColor().GetBlue();
+
+ /* Calculate the new RGB component (rnComp points to one of nR, nG, nB).
+ Using integer arithmetic with its rounding errors, the results of
+ this calculation are always exactly in the range 0x00 to 0xFF
+ (simply cutting the lower bits would darken the colors slightly). */
+ sal_uInt32 nNewComp = rnComp;
+ nNewComp /= nFactor1;
+ nNewComp *= nFactor2;
+ nNewComp /= nFactor3;
+ rnComp = static_cast< sal_uInt8 >( nNewComp );
+ Color aNewColor( nR, nG, nB );
+
+ // find or insert the new color
+ sal_uInt32 nFoundIdx = 0;
+ XclListColor* pNewEntry = SearchListEntry( aNewColor, nFoundIdx );
+ if( !pNewEntry || (pNewEntry->GetColor() != aNewColor) )
+ pNewEntry = CreateListEntry( aNewColor, nFoundIdx );
+ pNewEntry->AddWeighting( pOldEntry->GetWeighting() );
+ aListIndexMap.push_back( nFoundIdx );
+ }
+
+ // update color ID data map (maps color IDs to color list indexes), replace old by new list indexes
+ for( auto& rColorIdData : maColorIdDataVec )
+ rColorIdData.mnIndex = aListIndexMap[ rColorIdData.mnIndex ];
+}
+
+void XclExpPaletteImpl::ReduceLeastUsedColor()
+{
+ // find a list color to remove
+ sal_uInt32 nRemove = GetLeastUsedListColor();
+ // find its nearest neighbor
+ sal_uInt32 nKeep = GetNearestListColor( nRemove );
+
+ // merge both colors to one color, remove one color from list
+ XclListColor* pKeepEntry = mxColorList->at(nKeep).get();
+ XclListColor* pRemoveEntry = mxColorList->at(nRemove).get();
+ if( !(pKeepEntry && pRemoveEntry) )
+ return;
+
+ // merge both colors (if pKeepEntry is a base color, it will not change)
+ pKeepEntry->Merge( *pRemoveEntry );
+ // remove the less used color, adjust nKeep index if kept color follows removed color
+ XclListColorList::iterator itr = mxColorList->begin();
+ ::std::advance(itr, nRemove);
+ mxColorList->erase(itr);
+ if( nKeep > nRemove ) --nKeep;
+
+ // recalculate color ID data map (maps color IDs to color list indexes)
+ for( auto& rColorIdData : maColorIdDataVec )
+ {
+ if( rColorIdData.mnIndex > nRemove )
+ --rColorIdData.mnIndex;
+ else if( rColorIdData.mnIndex == nRemove )
+ rColorIdData.mnIndex = nKeep;
+ }
+}
+
+sal_uInt32 XclExpPaletteImpl::GetLeastUsedListColor() const
+{
+ sal_uInt32 nFound = 0;
+ sal_uInt32 nMinW = SAL_MAX_UINT32;
+
+ for( sal_uInt32 nIdx = 0, nCount = mxColorList->size(); nIdx < nCount; ++nIdx )
+ {
+ XclListColor& rEntry = *mxColorList->at( nIdx );
+ // ignore the base colors
+ if( !rEntry.IsBaseColor() && (rEntry.GetWeighting() < nMinW) )
+ {
+ nFound = nIdx;
+ nMinW = rEntry.GetWeighting();
+ }
+ }
+ return nFound;
+}
+
+sal_uInt32 XclExpPaletteImpl::GetNearestListColor( const Color& rColor, sal_uInt32 nIgnore ) const
+{
+ sal_uInt32 nFound = 0;
+ sal_Int32 nMinD = SAL_MAX_INT32;
+
+ for( sal_uInt32 nIdx = 0, nCount = mxColorList->size(); nIdx < nCount; ++nIdx )
+ {
+ if( nIdx != nIgnore )
+ {
+ if( XclListColor* pEntry = mxColorList->at(nIdx).get() )
+ {
+ sal_Int32 nDist = lclGetColorDistance( rColor, pEntry->GetColor() );
+ if( nDist < nMinD )
+ {
+ nFound = nIdx;
+ nMinD = nDist;
+ }
+ }
+ }
+ }
+ return nFound;
+}
+
+sal_uInt32 XclExpPaletteImpl::GetNearestListColor( sal_uInt32 nIndex ) const
+{
+ if (nIndex >= mxColorList->size())
+ return 0;
+ XclListColor* pEntry = mxColorList->at(nIndex).get();
+ return GetNearestListColor( pEntry->GetColor(), nIndex );
+}
+
+sal_Int32 XclExpPaletteImpl::GetNearestPaletteColor(
+ sal_uInt32& rnIndex, const Color& rColor ) const
+{
+ rnIndex = 0;
+ sal_Int32 nDist = SAL_MAX_INT32;
+
+ sal_uInt32 nPaletteIndex = 0;
+ for( const auto& rPaletteColor : maPalette )
+ {
+ if( !rPaletteColor.mbUsed )
+ {
+ sal_Int32 nCurrDist = lclGetColorDistance( rColor, rPaletteColor.maColor );
+ if( nCurrDist < nDist )
+ {
+ rnIndex = nPaletteIndex;
+ nDist = nCurrDist;
+ }
+ }
+ ++nPaletteIndex;
+ }
+ return nDist;
+}
+
+sal_Int32 XclExpPaletteImpl::GetNearPaletteColors(
+ sal_uInt32& rnFirst, sal_uInt32& rnSecond, const Color& rColor ) const
+{
+ rnFirst = rnSecond = 0;
+ sal_Int32 nDist1 = SAL_MAX_INT32;
+ sal_Int32 nDist2 = SAL_MAX_INT32;
+
+ sal_uInt32 nPaletteIndex = 0;
+ for( const auto& rPaletteColor : maPalette )
+ {
+ sal_Int32 nCurrDist = lclGetColorDistance( rColor, rPaletteColor.maColor );
+ if( nCurrDist < nDist1 )
+ {
+ rnSecond = rnFirst;
+ nDist2 = nDist1;
+ rnFirst = nPaletteIndex;
+ nDist1 = nCurrDist;
+ }
+ else if( nCurrDist < nDist2 )
+ {
+ rnSecond = nPaletteIndex;
+ nDist2 = nCurrDist;
+ }
+ ++nPaletteIndex;
+ }
+ return nDist1;
+}
+
+XclExpPalette::XclExpPalette( const XclExpRoot& rRoot ) :
+ XclDefaultPalette( rRoot ),
+ XclExpRecord( EXC_ID_PALETTE )
+{
+ mxImpl = std::make_shared<XclExpPaletteImpl>( *this );
+ SetRecSize( GetColorCount() * 4 + 2 );
+}
+
+XclExpPalette::~XclExpPalette()
+{
+}
+
+sal_uInt32 XclExpPalette::InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault )
+{
+ return mxImpl->InsertColor( rColor, eType, nAutoDefault );
+}
+
+sal_uInt32 XclExpPalette::GetColorIdFromIndex( sal_uInt16 nIndex )
+{
+ return XclExpPaletteImpl::GetColorIdFromIndex( nIndex );
+}
+
+void XclExpPalette::Finalize()
+{
+ mxImpl->Finalize();
+}
+
+sal_uInt16 XclExpPalette::GetColorIndex( sal_uInt32 nColorId ) const
+{
+ return mxImpl->GetColorIndex( nColorId );
+}
+
+void XclExpPalette::GetMixedColors(
+ sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
+ sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const
+{
+ return mxImpl->GetMixedColors( rnXclForeIx, rnXclBackIx, rnXclPattern, nForeColorId, nBackColorId );
+}
+
+Color XclExpPalette::GetColor( sal_uInt16 nXclIndex ) const
+{
+ return mxImpl->GetColor( nXclIndex );
+}
+
+void XclExpPalette::Save( XclExpStream& rStrm )
+{
+ if( !mxImpl->IsDefaultPalette() )
+ XclExpRecord::Save( rStrm );
+}
+
+void XclExpPalette::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( !mxImpl->IsDefaultPalette() )
+ mxImpl->SaveXml( rStrm );
+}
+
+void XclExpPalette::WriteBody( XclExpStream& rStrm )
+{
+ mxImpl->WriteBody( rStrm );
+}
+
+// FONT record - font information =============================================
+
+namespace {
+
+typedef ::std::pair< sal_uInt16, sal_Int16 > WhichAndScript;
+
+sal_Int16 lclCheckFontItems( const SfxItemSet& rItemSet,
+ const WhichAndScript& rWAS1, const WhichAndScript& rWAS2, const WhichAndScript& rWAS3 )
+{
+ if( ScfTools::CheckItem( rItemSet, rWAS1.first, false ) ) return rWAS1.second;
+ if( ScfTools::CheckItem( rItemSet, rWAS2.first, false ) ) return rWAS2.second;
+ if( ScfTools::CheckItem( rItemSet, rWAS3.first, false ) ) return rWAS3.second;
+ return 0;
+};
+
+} // namespace
+
+sal_Int16 XclExpFontHelper::GetFirstUsedScript( const XclExpRoot& rRoot, const SfxItemSet& rItemSet )
+{
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+
+ /* #i17050# #i107170# We need to determine which font items are set in the
+ item set, and which script type we should prefer according to the
+ current language settings. */
+
+ static const WhichAndScript WAS_LATIN( ATTR_FONT, css::i18n::ScriptType::LATIN );
+ static const WhichAndScript WAS_ASIAN( ATTR_CJK_FONT, css::i18n::ScriptType::ASIAN );
+ static const WhichAndScript WAS_CMPLX( ATTR_CTL_FONT, css::i18n::ScriptType::COMPLEX );
+
+ /* do not let a font from a parent style override an explicit
+ cell font. */
+
+ sal_Int16 nDefScript = rRoot.GetDefApiScript();
+ sal_Int16 nScript = 0;
+ const SfxItemSet* pCurrSet = &rItemSet;
+
+ while( (nScript == 0) && pCurrSet )
+ {
+ switch( nDefScript )
+ {
+ case ApiScriptType::LATIN:
+ nScript = lclCheckFontItems( *pCurrSet, WAS_LATIN, WAS_CMPLX, WAS_ASIAN );
+ break;
+ case ApiScriptType::ASIAN:
+ nScript = lclCheckFontItems( *pCurrSet, WAS_ASIAN, WAS_CMPLX, WAS_LATIN );
+ break;
+ case ApiScriptType::COMPLEX:
+ nScript = lclCheckFontItems( *pCurrSet, WAS_CMPLX, WAS_ASIAN, WAS_LATIN );
+ break;
+ default:
+ OSL_FAIL( "XclExpFontHelper::GetFirstUsedScript - unknown script type" );
+ nScript = ApiScriptType::LATIN;
+ };
+ pCurrSet = pCurrSet->GetParent();
+ }
+
+ if (nScript == 0)
+ nScript = nDefScript;
+
+ if (nScript == 0)
+ {
+ OSL_FAIL( "XclExpFontHelper::GetFirstUsedScript - unknown script type" );
+ nScript = ApiScriptType::LATIN;
+ }
+
+ return nScript;
+}
+
+vcl::Font XclExpFontHelper::GetFontFromItemSet( const XclExpRoot& rRoot, const SfxItemSet& rItemSet, sal_Int16 nScript )
+{
+ // if WEAK is passed, guess script type from existing items in the item set
+ if( nScript == css::i18n::ScriptType::WEAK )
+ nScript = GetFirstUsedScript( rRoot, rItemSet );
+
+ // convert to core script type constants
+ SvtScriptType nScScript = SvtLanguageOptions::FromI18NToSvtScriptType(nScript);
+
+ // fill the font object
+ vcl::Font aFont;
+ ScPatternAttr::GetFont( aFont, rItemSet, SC_AUTOCOL_RAW, nullptr, nullptr, nullptr, nScScript );
+ return aFont;
+}
+
+ScDxfFont XclExpFontHelper::GetDxfFontFromItemSet(const XclExpRoot& rRoot, const SfxItemSet& rItemSet)
+{
+ sal_Int16 nScript = GetFirstUsedScript(rRoot, rItemSet);
+
+ // convert to core script type constants
+ SvtScriptType nScScript = SvtLanguageOptions::FromI18NToSvtScriptType(nScript);
+ return ScPatternAttr::GetDxfFont(rItemSet, nScScript);
+}
+
+bool XclExpFontHelper::CheckItems( const XclExpRoot& rRoot, const SfxItemSet& rItemSet, sal_Int16 nScript, bool bDeep )
+{
+ static const sal_uInt16 pnCommonIds[] = {
+ ATTR_FONT_UNDERLINE, ATTR_FONT_CROSSEDOUT, ATTR_FONT_CONTOUR,
+ ATTR_FONT_SHADOWED, ATTR_FONT_COLOR, ATTR_FONT_LANGUAGE, 0 };
+ static const sal_uInt16 pnLatinIds[] = {
+ ATTR_FONT, ATTR_FONT_HEIGHT, ATTR_FONT_WEIGHT, ATTR_FONT_POSTURE, 0 };
+ static const sal_uInt16 pnAsianIds[] = {
+ ATTR_CJK_FONT, ATTR_CJK_FONT_HEIGHT, ATTR_CJK_FONT_WEIGHT, ATTR_CJK_FONT_POSTURE, 0 };
+ static const sal_uInt16 pnComplexIds[] = {
+ ATTR_CTL_FONT, ATTR_CTL_FONT_HEIGHT, ATTR_CTL_FONT_WEIGHT, ATTR_CTL_FONT_POSTURE, 0 };
+
+ bool bUsed = ScfTools::CheckItems( rItemSet, pnCommonIds, bDeep );
+ if( !bUsed )
+ {
+ namespace ApiScriptType = css::i18n::ScriptType;
+ // if WEAK is passed, guess script type from existing items in the item set
+ if( nScript == ApiScriptType::WEAK )
+ nScript = GetFirstUsedScript( rRoot, rItemSet );
+ // check the correct items
+ switch( nScript )
+ {
+ case ApiScriptType::LATIN: bUsed = ScfTools::CheckItems( rItemSet, pnLatinIds, bDeep ); break;
+ case ApiScriptType::ASIAN: bUsed = ScfTools::CheckItems( rItemSet, pnAsianIds, bDeep ); break;
+ case ApiScriptType::COMPLEX: bUsed = ScfTools::CheckItems( rItemSet, pnComplexIds, bDeep ); break;
+ default: OSL_FAIL( "XclExpFontHelper::CheckItems - unknown script type" );
+ }
+ }
+ return bUsed;
+}
+
+namespace {
+
+sal_uInt32 lclCalcHash( const XclFontData& rFontData )
+{
+ sal_uInt32 nHash = rFontData.maName.getLength();
+ nHash += sal_uInt32(rFontData.maColor) * 2;
+ nHash += rFontData.mnWeight * 3;
+ nHash += rFontData.mnCharSet * 5;
+ nHash += rFontData.mnFamily * 7;
+ nHash += rFontData.mnHeight * 11;
+ nHash += rFontData.mnUnderline * 13;
+ nHash += rFontData.mnEscapem * 17;
+ if( rFontData.mbItalic ) nHash += 19;
+ if( rFontData.mbStrikeout ) nHash += 23;
+ if( rFontData.mbOutline ) nHash += 29;
+ if( rFontData.mbShadow ) nHash += 31;
+ return nHash;
+}
+
+} // namespace
+
+XclExpFont::XclExpFont( const XclExpRoot& rRoot,
+ const XclFontData& rFontData, XclExpColorType eColorType ) :
+ XclExpRecord( EXC_ID2_FONT, 14 ),
+ XclExpRoot( rRoot ),
+ maData( rFontData )
+{
+ // insert font color into palette
+ mnColorId = rRoot.GetPalette().InsertColor( rFontData.maColor, eColorType, EXC_COLOR_FONTAUTO );
+ // hash value for faster comparison
+ mnHash = lclCalcHash( maData );
+ // record size
+ sal_Int32 nStrLen = maData.maName.getLength();
+ SetRecSize( ((GetBiff() == EXC_BIFF8) ? (nStrLen * 2 + 1) : nStrLen) + 15 );
+}
+
+bool XclExpFont::Equals( const XclFontData& rFontData, sal_uInt32 nHash ) const
+{
+ return (mnHash == nHash) && (maData == rFontData);
+}
+
+void XclExpFont::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_font);
+ XclXmlUtils::WriteFontData( rStyleSheet, maData, XML_name );
+ // OOXTODO: XML_scheme; //scheme/@val values: "major", "minor", "none"
+ rStyleSheet->endElement( XML_font );
+}
+
+// private --------------------------------------------------------------------
+
+void XclExpFont::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nAttr = EXC_FONTATTR_NONE;
+ ::set_flag( nAttr, EXC_FONTATTR_ITALIC, maData.mbItalic );
+ if( maData.mnUnderline > 0 )
+ ::set_flag( nAttr, EXC_FONTATTR_UNDERLINE, true );
+ ::set_flag( nAttr, EXC_FONTATTR_STRIKEOUT, maData.mbStrikeout );
+ ::set_flag( nAttr, EXC_FONTATTR_OUTLINE, maData.mbOutline );
+ ::set_flag( nAttr, EXC_FONTATTR_SHADOW, maData.mbShadow );
+
+ OSL_ENSURE( maData.maName.getLength() < 256, "XclExpFont::WriteBody - font name too long" );
+ XclExpString aFontName;
+ if( GetBiff() <= EXC_BIFF5 )
+ aFontName.AssignByte( maData.maName, GetTextEncoding(), XclStrFlags::EightBitLength );
+ else
+ aFontName.Assign( maData.maName, XclStrFlags::ForceUnicode | XclStrFlags::EightBitLength );
+
+ rStrm << maData.mnHeight
+ << nAttr
+ << GetPalette().GetColorIndex( mnColorId )
+ << maData.mnWeight
+ << maData.mnEscapem
+ << maData.mnUnderline
+ << maData.mnFamily
+ << maData.mnCharSet
+ << sal_uInt8( 0 )
+ << aFontName;
+}
+
+XclExpDxfFont::XclExpDxfFont(const XclExpRoot& rRoot,
+ const SfxItemSet& rItemSet):
+ XclExpRoot(rRoot)
+{
+ maDxfData = XclExpFontHelper::GetDxfFontFromItemSet(rRoot, rItemSet);
+}
+
+namespace {
+
+const char* getUnderlineOOXValue(FontLineStyle eUnderline)
+{
+ switch (eUnderline)
+ {
+ case LINESTYLE_NONE:
+ case LINESTYLE_DONTKNOW:
+ return "none";
+ case LINESTYLE_DOUBLE:
+ case LINESTYLE_DOUBLEWAVE:
+ return "double";
+ default:
+ return "single";
+ }
+}
+
+const char* getFontFamilyOOXValue(FontFamily eValue)
+{
+ switch (eValue)
+ {
+ case FAMILY_DONTKNOW:
+ return "0";
+ case FAMILY_SWISS:
+ case FAMILY_SYSTEM:
+ return "2";
+ case FAMILY_ROMAN:
+ return "1";
+ case FAMILY_SCRIPT:
+ return "4";
+ case FAMILY_MODERN:
+ return "3";
+ case FAMILY_DECORATIVE:
+ return "5";
+ default:
+ return "0";
+ }
+}
+
+}
+
+void XclExpDxfFont::SaveXml(XclExpXmlStream& rStrm)
+{
+ if (maDxfData.isEmpty())
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_font);
+
+ if (maDxfData.pFontAttr)
+ {
+ OUString aFontName = (*maDxfData.pFontAttr)->GetFamilyName();
+
+ aFontName = XclTools::GetXclFontName(aFontName);
+ if (!aFontName.isEmpty())
+ {
+ rStyleSheet->singleElement(XML_name, XML_val, aFontName);
+ }
+
+ rtl_TextEncoding eTextEnc = (*maDxfData.pFontAttr)->GetCharSet();
+ sal_uInt8 nExcelCharSet = rtl_getBestWindowsCharsetFromTextEncoding(eTextEnc);
+ if (nExcelCharSet)
+ {
+ rStyleSheet->singleElement(XML_charset, XML_val, OString::number(nExcelCharSet));
+ }
+
+ FontFamily eFamily = (*maDxfData.pFontAttr)->GetFamily();
+ const char* pVal = getFontFamilyOOXValue(eFamily);
+ if (pVal)
+ {
+ rStyleSheet->singleElement(XML_family, XML_val, pVal);
+ }
+ }
+
+ if (maDxfData.eWeight)
+ {
+ rStyleSheet->singleElement(XML_b,
+ XML_val, ToPsz10(*maDxfData.eWeight != WEIGHT_NORMAL));
+ }
+
+ if (maDxfData.eItalic)
+ {
+ bool bItalic = (*maDxfData.eItalic == ITALIC_OBLIQUE) || (*maDxfData.eItalic == ITALIC_NORMAL);
+ rStyleSheet->singleElement(XML_i, XML_val, ToPsz10(bItalic));
+ }
+
+ if (maDxfData.eStrike)
+ {
+ bool bStrikeout =
+ (*maDxfData.eStrike == STRIKEOUT_SINGLE) || (*maDxfData.eStrike == STRIKEOUT_DOUBLE) ||
+ (*maDxfData.eStrike == STRIKEOUT_BOLD) || (*maDxfData.eStrike == STRIKEOUT_SLASH) ||
+ (*maDxfData.eStrike == STRIKEOUT_X);
+
+ rStyleSheet->singleElement(XML_strike, XML_val, ToPsz10(bStrikeout));
+ }
+
+ if (maDxfData.bOutline)
+ {
+ rStyleSheet->singleElement(XML_outline, XML_val, ToPsz10(*maDxfData.bOutline));
+ }
+
+ if (maDxfData.bShadow)
+ {
+ rStyleSheet->singleElement(XML_shadow, XML_val, ToPsz10(*maDxfData.bShadow));
+ }
+
+ if (maDxfData.aColor)
+ {
+ rStyleSheet->singleElement(XML_color,
+ XML_rgb, XclXmlUtils::ToOString(*maDxfData.aColor));
+ }
+
+ if (maDxfData.nFontHeight)
+ {
+ rStyleSheet->singleElement(XML_sz,
+ XML_val, OString::number(*maDxfData.nFontHeight/20));
+ }
+
+ if (maDxfData.eUnder)
+ {
+ const char* pVal = getUnderlineOOXValue(*maDxfData.eUnder);
+ rStyleSheet->singleElement(XML_u, XML_val, pVal);
+ }
+
+ rStyleSheet->endElement(XML_font);
+}
+
+XclExpBlindFont::XclExpBlindFont( const XclExpRoot& rRoot ) :
+ XclExpFont( rRoot, XclFontData(), EXC_COLOR_CELLTEXT )
+{
+}
+
+bool XclExpBlindFont::Equals( const XclFontData& /*rFontData*/, sal_uInt32 /*nHash*/ ) const
+{
+ return false;
+}
+
+void XclExpBlindFont::Save( XclExpStream& /*rStrm*/ )
+{
+ // do nothing
+}
+
+XclExpFontBuffer::XclExpFontBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnXclMaxSize( 0 )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF4: mnXclMaxSize = EXC_FONT_MAXCOUNT4; break;
+ case EXC_BIFF5: mnXclMaxSize = EXC_FONT_MAXCOUNT5; break;
+ case EXC_BIFF8: mnXclMaxSize = EXC_FONT_MAXCOUNT8; break;
+ default: DBG_ERROR_BIFF();
+ }
+ InitDefaultFonts();
+}
+
+const XclExpFont* XclExpFontBuffer::GetFont( sal_uInt16 nXclFont ) const
+{
+ return maFontList.GetRecord( nXclFont );
+}
+
+const XclFontData& XclExpFontBuffer::GetAppFontData() const
+{
+ return maFontList.GetRecord( EXC_FONT_APP )->GetFontData(); // exists always
+}
+
+sal_uInt16 XclExpFontBuffer::Insert(
+ const XclFontData& rFontData, XclExpColorType eColorType, bool bAppFont )
+{
+ if( bAppFont )
+ {
+ XclExpFontRef xFont = new XclExpFont( GetRoot(), rFontData, eColorType );
+ maFontList.ReplaceRecord( xFont, EXC_FONT_APP );
+ // set width of '0' character for column width export
+ SetCharWidth( xFont->GetFontData() );
+ return EXC_FONT_APP;
+ }
+
+ size_t nPos = Find( rFontData );
+ if( nPos == EXC_FONTLIST_NOTFOUND )
+ {
+ // not found in buffer - create new font
+ size_t nSize = maFontList.GetSize();
+ if( nSize < mnXclMaxSize )
+ {
+ // possible to insert
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), rFontData, eColorType ) );
+ nPos = nSize; // old size is last position now
+ }
+ else
+ {
+ // buffer is full - ignore new font, use default font
+ nPos = EXC_FONT_APP;
+ }
+ }
+ return static_cast< sal_uInt16 >( nPos );
+}
+
+sal_uInt16 XclExpFontBuffer::Insert(
+ const SvxFont& rFont, XclExpColorType eColorType )
+{
+ return Insert( XclFontData( rFont ), eColorType );
+}
+
+sal_uInt16 XclExpFontBuffer::Insert( const SfxItemSet& rItemSet,
+ sal_Int16 nScript, XclExpColorType eColorType, bool bAppFont )
+{
+ // #i17050# script type now provided by caller
+ vcl::Font aFont = XclExpFontHelper::GetFontFromItemSet( GetRoot(), rItemSet, nScript );
+ return Insert( XclFontData( aFont ), eColorType, bAppFont );
+}
+
+void XclExpFontBuffer::Save( XclExpStream& rStrm )
+{
+ maFontList.Save( rStrm );
+}
+
+void XclExpFontBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maFontList.IsEmpty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_fonts, XML_count, OString::number(maFontList.GetSize()));
+
+ maFontList.SaveXml( rStrm );
+
+ rStyleSheet->endElement( XML_fonts );
+}
+
+// private --------------------------------------------------------------------
+
+void XclExpFontBuffer::InitDefaultFonts()
+{
+ XclFontData aFontData;
+ aFontData.maName = "Arial";
+ aFontData.SetScFamily( FAMILY_DONTKNOW );
+ aFontData.SetFontEncoding( ScfTools::GetSystemTextEncoding() );
+ aFontData.SetScHeight( 200 ); // 200 twips = 10 pt
+ aFontData.SetScWeight( WEIGHT_NORMAL );
+
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5:
+ {
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ aFontData.SetScWeight( WEIGHT_BOLD );
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ aFontData.SetScWeight( WEIGHT_NORMAL );
+ aFontData.SetScPosture( ITALIC_NORMAL );
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ aFontData.SetScWeight( WEIGHT_BOLD );
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ // the blind font with index 4
+ maFontList.AppendNewRecord( new XclExpBlindFont( GetRoot() ) );
+ // already add the first user defined font (Excel does it too)
+ aFontData.SetScWeight( WEIGHT_NORMAL );
+ aFontData.SetScPosture( ITALIC_NONE );
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ }
+ break;
+ case EXC_BIFF8:
+ {
+ XclExpFontRef xFont = new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT );
+ maFontList.AppendRecord( xFont );
+ maFontList.AppendRecord( xFont );
+ maFontList.AppendRecord( xFont );
+ maFontList.AppendRecord( xFont );
+ if( GetOutput() == EXC_OUTPUT_BINARY )
+ // the blind font with index 4
+ maFontList.AppendNewRecord( new XclExpBlindFont( GetRoot() ) );
+ }
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+size_t XclExpFontBuffer::Find( const XclFontData& rFontData )
+{
+ sal_uInt32 nHash = lclCalcHash( rFontData );
+ for( size_t nPos = 0, nSize = maFontList.GetSize(); nPos < nSize; ++nPos )
+ if( maFontList.GetRecord( nPos )->Equals( rFontData, nHash ) )
+ return nPos;
+ return EXC_FONTLIST_NOTFOUND;
+}
+
+// FORMAT record - number formats =============================================
+
+namespace {
+
+/** Predicate for search algorithm. */
+struct XclExpNumFmtPred
+{
+ sal_uInt32 mnScNumFmt;
+ explicit XclExpNumFmtPred( sal_uInt32 nScNumFmt ) : mnScNumFmt( nScNumFmt ) {}
+ bool operator()( const XclExpNumFmt& rFormat ) const
+ { return rFormat.mnScNumFmt == mnScNumFmt; }
+};
+
+}
+
+void XclExpNumFmt::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->singleElement( XML_numFmt,
+ XML_numFmtId, OString::number(mnXclNumFmt),
+ XML_formatCode, maNumFmtString );
+}
+
+XclExpNumFmtBuffer::XclExpNumFmtBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mxFormatter( new SvNumberFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US ) ),
+ mpKeywordTable( new NfKeywordTable ),
+ mnStdFmt( GetFormatter().GetStandardIndex( ScGlobal::eLnge ) )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5: mnXclOffset = EXC_FORMAT_OFFSET5; break;
+ case EXC_BIFF8: mnXclOffset = EXC_FORMAT_OFFSET8; break;
+ default: mnXclOffset = 0; DBG_ERROR_BIFF();
+ }
+
+ mxFormatter->FillKeywordTableForExcel( *mpKeywordTable );
+}
+
+XclExpNumFmtBuffer::~XclExpNumFmtBuffer()
+{
+}
+
+sal_uInt16 XclExpNumFmtBuffer::Insert( sal_uInt32 nScNumFmt )
+{
+ XclExpNumFmtVec::const_iterator aIt =
+ ::std::find_if( maFormatMap.begin(), maFormatMap.end(), XclExpNumFmtPred( nScNumFmt ) );
+ if( aIt != maFormatMap.end() )
+ return aIt->mnXclNumFmt;
+
+ size_t nSize = maFormatMap.size();
+ if( nSize < o3tl::make_unsigned( 0xFFFF - mnXclOffset ) )
+ {
+ sal_uInt16 nXclNumFmt = static_cast< sal_uInt16 >( nSize + mnXclOffset );
+ maFormatMap.emplace_back( nScNumFmt, nXclNumFmt, GetFormatCode( nScNumFmt ) );
+ return nXclNumFmt;
+ }
+
+ return 0;
+}
+
+void XclExpNumFmtBuffer::Save( XclExpStream& rStrm )
+{
+ for( const auto& rEntry : maFormatMap )
+ WriteFormatRecord( rStrm, rEntry );
+}
+
+void XclExpNumFmtBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maFormatMap.empty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_numFmts, XML_count, OString::number(maFormatMap.size()));
+ for( auto& rEntry : maFormatMap )
+ {
+ rEntry.SaveXml( rStrm );
+ }
+ rStyleSheet->endElement( XML_numFmts );
+}
+
+void XclExpNumFmtBuffer::WriteFormatRecord( XclExpStream& rStrm, sal_uInt16 nXclNumFmt, const OUString& rFormatStr )
+{
+ XclExpString aExpStr;
+ if( GetBiff() <= EXC_BIFF5 )
+ aExpStr.AssignByte( rFormatStr, GetTextEncoding(), XclStrFlags::EightBitLength );
+ else
+ aExpStr.Assign( rFormatStr );
+
+ rStrm.StartRecord( EXC_ID4_FORMAT, 2 + aExpStr.GetSize() );
+ rStrm << nXclNumFmt << aExpStr;
+ rStrm.EndRecord();
+}
+
+void XclExpNumFmtBuffer::WriteFormatRecord( XclExpStream& rStrm, const XclExpNumFmt& rFormat )
+{
+ WriteFormatRecord( rStrm, rFormat.mnXclNumFmt, GetFormatCode( rFormat.mnScNumFmt ) );
+}
+
+namespace {
+
+OUString GetNumberFormatCode(const XclRoot& rRoot, const sal_uInt32 nScNumFmt, SvNumberFormatter* pFormatter, const NfKeywordTable* pKeywordTable)
+{
+ return rRoot.GetFormatter().GetFormatStringForExcel( nScNumFmt, *pKeywordTable, *pFormatter);
+}
+
+}
+
+OUString XclExpNumFmtBuffer::GetFormatCode( sal_uInt32 nScNumFmt )
+{
+ return GetNumberFormatCode( *this, nScNumFmt, mxFormatter.get(), mpKeywordTable.get() );
+}
+
+// XF, STYLE record - Cell formatting =========================================
+
+bool XclExpCellProt::FillFromItemSet( const SfxItemSet& rItemSet, bool bStyle )
+{
+ const ScProtectionAttr& rProtItem = rItemSet.Get( ATTR_PROTECTION );
+ mbLocked = rProtItem.GetProtection();
+ mbHidden = rProtItem.GetHideFormula() || rProtItem.GetHideCell();
+ return ScfTools::CheckItem( rItemSet, ATTR_PROTECTION, bStyle );
+}
+
+void XclExpCellProt::FillToXF3( sal_uInt16& rnProt ) const
+{
+ ::set_flag( rnProt, EXC_XF_LOCKED, mbLocked );
+ ::set_flag( rnProt, EXC_XF_HIDDEN, mbHidden );
+}
+
+void XclExpCellProt::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ rStrm.GetCurrentStream()->singleElement( XML_protection,
+ XML_locked, ToPsz( mbLocked ),
+ XML_hidden, ToPsz( mbHidden ) );
+}
+
+bool XclExpCellAlign::FillFromItemSet(const XclRoot& rRoot, const SfxItemSet& rItemSet,
+ bool bForceLineBreak, XclBiff eBiff, bool bStyle)
+{
+ bool bUsed = false;
+ SvxCellHorJustify eHorAlign = rItemSet.Get( ATTR_HOR_JUSTIFY ).GetValue();
+ SvxCellVerJustify eVerAlign = rItemSet.Get( ATTR_VER_JUSTIFY ).GetValue();
+
+ switch( eBiff )
+ {
+ case EXC_BIFF8: // attributes new in BIFF8
+ {
+ // text indent
+ tools::Long nTmpIndent = rItemSet.Get( ATTR_INDENT ).GetValue(); // already in twips
+ tools::Long nSpaceWidth = rRoot.GetSpaceWidth();
+ sal_Int32 nIndent = static_cast<double>(nTmpIndent) / (3.0 * nSpaceWidth) + 0.5;
+ mnIndent = limit_cast< sal_uInt8 >( nIndent, 0, 15 );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_INDENT, bStyle );
+
+ // shrink to fit
+ mbShrink = rItemSet.Get( ATTR_SHRINKTOFIT ).GetValue();
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_SHRINKTOFIT, bStyle );
+
+ // CTL text direction
+ SetScFrameDir( rItemSet.Get( ATTR_WRITINGDIR ).GetValue() );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_WRITINGDIR, bStyle );
+
+ [[fallthrough]];
+ }
+
+ case EXC_BIFF5: // attributes new in BIFF5
+ case EXC_BIFF4: // attributes new in BIFF4
+ {
+ // vertical alignment
+ SetScVerAlign( eVerAlign );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_VER_JUSTIFY, bStyle );
+
+ // stacked/rotation
+ bool bStacked = rItemSet.Get( ATTR_STACKED ).GetValue();
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_STACKED, bStyle );
+ if( bStacked )
+ {
+ mnRotation = EXC_ROT_STACKED;
+ }
+ else
+ {
+ // rotation
+ Degree100 nScRot = rItemSet.Get( ATTR_ROTATE_VALUE ).GetValue();
+ mnRotation = XclTools::GetXclRotation( nScRot );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_ROTATE_VALUE, bStyle );
+ }
+ mnOrient = XclTools::GetXclOrientFromRot( mnRotation );
+
+ [[fallthrough]];
+ }
+
+ case EXC_BIFF3: // attributes new in BIFF3
+ {
+ // text wrap
+ mbLineBreak = bForceLineBreak || rItemSet.Get( ATTR_LINEBREAK ).GetValue();
+ bUsed |= bForceLineBreak || ScfTools::CheckItem( rItemSet, ATTR_LINEBREAK, bStyle );
+
+ [[fallthrough]];
+ }
+
+ case EXC_BIFF2: // attributes new in BIFF2
+ {
+ // horizontal alignment
+ SetScHorAlign( eHorAlign );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_HOR_JUSTIFY, bStyle );
+ }
+
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+
+ if (eBiff == EXC_BIFF8)
+ {
+ // Adjust for distributed alignments.
+ if (eHorAlign == SvxCellHorJustify::Block)
+ {
+ SvxCellJustifyMethod eHorJustMethod =
+ rItemSet.GetItem<SvxJustifyMethodItem>(ATTR_HOR_JUSTIFY_METHOD)->GetValue();
+ if (eHorJustMethod == SvxCellJustifyMethod::Distribute)
+ mnHorAlign = EXC_XF_HOR_DISTRIB;
+ }
+
+ if (eVerAlign == SvxCellVerJustify::Block)
+ {
+ SvxCellJustifyMethod eVerJustMethod =
+ rItemSet.GetItem<SvxJustifyMethodItem>(ATTR_VER_JUSTIFY_METHOD)->GetValue();
+ if (eVerJustMethod == SvxCellJustifyMethod::Distribute)
+ mnVerAlign = EXC_XF_VER_DISTRIB;
+ }
+ }
+
+ return bUsed;
+}
+
+void XclExpCellAlign::FillToXF5( sal_uInt16& rnAlign ) const
+{
+ ::insert_value( rnAlign, mnHorAlign, 0, 3 );
+ ::set_flag( rnAlign, EXC_XF_LINEBREAK, mbLineBreak );
+ ::insert_value( rnAlign, mnVerAlign, 4, 3 );
+ ::insert_value( rnAlign, mnOrient, 8, 2 );
+}
+
+void XclExpCellAlign::FillToXF8( sal_uInt16& rnAlign, sal_uInt16& rnMiscAttrib ) const
+{
+ ::insert_value( rnAlign, mnHorAlign, 0, 3 );
+ ::set_flag( rnAlign, EXC_XF_LINEBREAK, mbLineBreak );
+ ::insert_value( rnAlign, mnVerAlign, 4, 3 );
+ ::insert_value( rnAlign, mnRotation, 8, 8 );
+ ::insert_value( rnMiscAttrib, mnIndent, 0, 4 );
+ ::set_flag( rnMiscAttrib, EXC_XF8_SHRINK, mbShrink );
+ ::insert_value( rnMiscAttrib, mnTextDir, 6, 2 );
+}
+
+static const char* ToHorizontalAlignment( sal_uInt8 nHorAlign )
+{
+ switch( nHorAlign )
+ {
+ case EXC_XF_HOR_GENERAL: return "general";
+ case EXC_XF_HOR_LEFT: return "left";
+ case EXC_XF_HOR_CENTER: return "center";
+ case EXC_XF_HOR_RIGHT: return "right";
+ case EXC_XF_HOR_FILL: return "fill";
+ case EXC_XF_HOR_JUSTIFY: return "justify";
+ case EXC_XF_HOR_CENTER_AS: return "centerContinuous";
+ case EXC_XF_HOR_DISTRIB: return "distributed";
+ }
+ return "*unknown*";
+}
+
+static const char* ToVerticalAlignment( sal_uInt8 nVerAlign )
+{
+ switch( nVerAlign )
+ {
+ case EXC_XF_VER_TOP: return "top";
+ case EXC_XF_VER_CENTER: return "center";
+ case EXC_XF_VER_BOTTOM: return "bottom";
+ case EXC_XF_VER_JUSTIFY: return "justify";
+ case EXC_XF_VER_DISTRIB: return "distributed";
+ }
+ return "*unknown*";
+}
+
+void XclExpCellAlign::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ rStrm.GetCurrentStream()->singleElement( XML_alignment,
+ XML_horizontal, ToHorizontalAlignment( mnHorAlign ),
+ XML_vertical, ToVerticalAlignment( mnVerAlign ),
+ XML_textRotation, OString::number(mnRotation),
+ XML_wrapText, ToPsz( mbLineBreak ),
+ XML_indent, OString::number(mnIndent),
+ // OOXTODO: XML_relativeIndent, mnIndent?
+ // OOXTODO: XML_justifyLastLine,
+ XML_shrinkToFit, ToPsz( mbShrink ),
+ XML_readingOrder, sax_fastparser::UseIf(OString::number(mnTextDir), mnTextDir != EXC_XF_TEXTDIR_CONTEXT) );
+}
+
+namespace {
+
+void lclGetBorderLine(
+ sal_uInt8& rnXclLine, sal_uInt32& rnColorId,
+ const ::editeng::SvxBorderLine* pLine, XclExpPalette& rPalette, XclBiff eBiff )
+{
+ // Document: sc/qa/unit/data/README.cellborders
+
+ enum CalcLineIndex{Idx_None, Idx_Solid, Idx_Dotted, Idx_Dashed, Idx_FineDashed, Idx_DashDot, Idx_DashDotDot, Idx_DoubleThin, Idx_Last};
+ enum ExcelWidthIndex{Width_Hair, Width_Thin, Width_Medium, Width_Thick, Width_Last};
+ static sal_uInt8 Map_LineLO_toMS[Idx_Last][Width_Last] =
+ {
+ // 0,05 - 0,74 0,75 - 1,49 1,50 - 2,49 2,50 - 9,00 Width Range [pt]
+ // EXC_BORDER_HAIR EXC_BORDER_THIN EXC_BORDER_MEDIUM EXC_BORDER_THICK MS Width
+ {EXC_LINE_NONE , EXC_LINE_NONE , EXC_LINE_NONE , EXC_LINE_NONE }, // 0 BorderLineStyle::NONE
+ {EXC_LINE_HAIR , EXC_LINE_THIN , EXC_LINE_MEDIUM , EXC_LINE_THICK }, // 1 BorderLineStyle::SOLID
+ {EXC_LINE_DOTTED , EXC_LINE_DOTTED , EXC_LINE_MEDIUM_SLANT_DASHDOT, EXC_LINE_MEDIUM_SLANT_DASHDOT}, // 2 BorderLineStyle::DOTTED
+ {EXC_LINE_DOTTED , EXC_LINE_DASHED , EXC_LINE_MEDIUM_DASHED , EXC_LINE_MEDIUM_DASHED }, // 3 BorderLineStyle::DASHED
+ {EXC_LINE_DASHED , EXC_LINE_DASHED , EXC_LINE_MEDIUM_SLANT_DASHDOT, EXC_LINE_MEDIUM_SLANT_DASHDOT}, // 4 BorderLineStyle::FINE_DASHED
+ {EXC_LINE_DASHED , EXC_LINE_THIN_DASHDOT , EXC_LINE_MEDIUM_DASHDOT , EXC_LINE_MEDIUM_DASHDOT }, // 5 BorderLineStyle::DASH_DOT
+ {EXC_LINE_DASHED , EXC_LINE_THIN_DASHDOTDOT , EXC_LINE_MEDIUM_DASHDOTDOT , EXC_LINE_MEDIUM_DASHDOTDOT }, // 6 BorderLineStyle::DASH_DOT_DOT
+ {EXC_LINE_DOUBLE , EXC_LINE_DOUBLE , EXC_LINE_DOUBLE , EXC_LINE_DOUBLE } // 7 BorderLineStyle::DOUBLE_THIN
+ }; // Line Name
+
+ rnXclLine = EXC_LINE_NONE;
+ if( pLine )
+ {
+ sal_uInt16 nOuterWidth = pLine->GetOutWidth();
+ ExcelWidthIndex nOuterWidthIndx;
+ CalcLineIndex nStyleIndex;
+
+ switch (pLine->GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::NONE:
+ nStyleIndex = Idx_None;
+ break;
+ case SvxBorderLineStyle::SOLID:
+ nStyleIndex = Idx_Solid;
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ nStyleIndex = Idx_Dotted;
+ break;
+ case SvxBorderLineStyle::DASHED:
+ nStyleIndex = Idx_Dashed;
+ break;
+ case SvxBorderLineStyle::FINE_DASHED:
+ nStyleIndex = Idx_FineDashed;
+ break;
+ case SvxBorderLineStyle::DASH_DOT:
+ nStyleIndex = Idx_DashDot;
+ break;
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ nStyleIndex = Idx_DashDotDot;
+ break;
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ // the "nOuterWidth" is not right for this line type
+ // but at the moment width it not important for that
+ // the right function is nOuterWidth = (sal_uInt16) pLine->GetWidth();
+ nStyleIndex = Idx_DoubleThin;
+ break;
+ default:
+ nStyleIndex = Idx_Solid;
+ }
+
+ if( nOuterWidth >= EXC_BORDER_THICK )
+ nOuterWidthIndx = Width_Thick;
+ else if( nOuterWidth >= EXC_BORDER_MEDIUM )
+ nOuterWidthIndx = Width_Medium;
+ else if( nOuterWidth >= EXC_BORDER_THIN )
+ nOuterWidthIndx = Width_Thin;
+ else if ( nOuterWidth >= EXC_BORDER_HAIR )
+ nOuterWidthIndx = Width_Hair;
+ else
+ nOuterWidthIndx = Width_Thin;
+
+ rnXclLine = Map_LineLO_toMS[nStyleIndex][nOuterWidthIndx];
+ }
+
+ if( (eBiff == EXC_BIFF2) && (rnXclLine != EXC_LINE_NONE) )
+ rnXclLine = EXC_LINE_THIN;
+
+ rnColorId = (pLine && (rnXclLine != EXC_LINE_NONE)) ?
+ rPalette.InsertColor( pLine->GetColor(), EXC_COLOR_CELLBORDER ) :
+ XclExpPalette::GetColorIdFromIndex( 0 );
+}
+
+} // namespace
+
+XclExpCellBorder::XclExpCellBorder() :
+ mnLeftColorId( XclExpPalette::GetColorIdFromIndex( mnLeftColor ) ),
+ mnRightColorId( XclExpPalette::GetColorIdFromIndex( mnRightColor ) ),
+ mnTopColorId( XclExpPalette::GetColorIdFromIndex( mnTopColor ) ),
+ mnBottomColorId( XclExpPalette::GetColorIdFromIndex( mnBottomColor ) ),
+ mnDiagColorId( XclExpPalette::GetColorIdFromIndex( mnDiagColor ) )
+{
+}
+
+bool XclExpCellBorder::FillFromItemSet(
+ const SfxItemSet& rItemSet, XclExpPalette& rPalette, XclBiff eBiff, bool bStyle )
+{
+ bool bUsed = false;
+
+ switch( eBiff )
+ {
+ case EXC_BIFF8: // attributes new in BIFF8
+ {
+ const SvxLineItem& rTLBRItem = rItemSet.Get( ATTR_BORDER_TLBR );
+ sal_uInt8 nTLBRLine;
+ sal_uInt32 nTLBRColorId;
+ lclGetBorderLine( nTLBRLine, nTLBRColorId, rTLBRItem.GetLine(), rPalette, eBiff );
+ mbDiagTLtoBR = (nTLBRLine != EXC_LINE_NONE);
+
+ const SvxLineItem& rBLTRItem = rItemSet.Get( ATTR_BORDER_BLTR );
+ sal_uInt8 nBLTRLine;
+ sal_uInt32 nBLTRColorId;
+ lclGetBorderLine( nBLTRLine, nBLTRColorId, rBLTRItem.GetLine(), rPalette, eBiff );
+ mbDiagBLtoTR = (nBLTRLine != EXC_LINE_NONE);
+
+ if( ::ScHasPriority( rTLBRItem.GetLine(), rBLTRItem.GetLine() ) )
+ {
+ mnDiagLine = nTLBRLine;
+ mnDiagColorId = nTLBRColorId;
+ }
+ else
+ {
+ mnDiagLine = nBLTRLine;
+ mnDiagColorId = nBLTRColorId;
+ }
+
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_BORDER_TLBR, bStyle ) ||
+ ScfTools::CheckItem( rItemSet, ATTR_BORDER_BLTR, bStyle );
+
+ [[fallthrough]];
+ }
+
+ case EXC_BIFF5:
+ case EXC_BIFF4:
+ case EXC_BIFF3:
+ case EXC_BIFF2:
+ {
+ const SvxBoxItem& rBoxItem = rItemSet.Get( ATTR_BORDER );
+ lclGetBorderLine( mnLeftLine, mnLeftColorId, rBoxItem.GetLeft(), rPalette, eBiff );
+ lclGetBorderLine( mnRightLine, mnRightColorId, rBoxItem.GetRight(), rPalette, eBiff );
+ lclGetBorderLine( mnTopLine, mnTopColorId, rBoxItem.GetTop(), rPalette, eBiff );
+ lclGetBorderLine( mnBottomLine, mnBottomColorId, rBoxItem.GetBottom(), rPalette, eBiff );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_BORDER, bStyle );
+ }
+
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+
+ return bUsed;
+}
+
+void XclExpCellBorder::SetFinalColors( const XclExpPalette& rPalette )
+{
+ mnLeftColor = rPalette.GetColorIndex( mnLeftColorId );
+ mnRightColor = rPalette.GetColorIndex( mnRightColorId );
+ mnTopColor = rPalette.GetColorIndex( mnTopColorId );
+ mnBottomColor = rPalette.GetColorIndex( mnBottomColorId );
+ mnDiagColor = rPalette.GetColorIndex( mnDiagColorId );
+}
+
+void XclExpCellBorder::FillToXF5( sal_uInt32& rnBorder, sal_uInt32& rnArea ) const
+{
+ ::insert_value( rnBorder, mnTopLine, 0, 3 );
+ ::insert_value( rnBorder, mnLeftLine, 3, 3 );
+ ::insert_value( rnArea, mnBottomLine, 22, 3 );
+ ::insert_value( rnBorder, mnRightLine, 6, 3 );
+ ::insert_value( rnBorder, mnTopColor, 9, 7 );
+ ::insert_value( rnBorder, mnLeftColor, 16, 7 );
+ ::insert_value( rnArea, mnBottomColor, 25, 7 );
+ ::insert_value( rnBorder, mnRightColor, 23, 7 );
+}
+
+void XclExpCellBorder::FillToXF8( sal_uInt32& rnBorder1, sal_uInt32& rnBorder2 ) const
+{
+ ::insert_value( rnBorder1, mnLeftLine, 0, 4 );
+ ::insert_value( rnBorder1, mnRightLine, 4, 4 );
+ ::insert_value( rnBorder1, mnTopLine, 8, 4 );
+ ::insert_value( rnBorder1, mnBottomLine, 12, 4 );
+ ::insert_value( rnBorder1, mnLeftColor, 16, 7 );
+ ::insert_value( rnBorder1, mnRightColor, 23, 7 );
+ ::insert_value( rnBorder2, mnTopColor, 0, 7 );
+ ::insert_value( rnBorder2, mnBottomColor, 7, 7 );
+ ::insert_value( rnBorder2, mnDiagColor, 14, 7 );
+ ::insert_value( rnBorder2, mnDiagLine, 21, 4 );
+ ::set_flag( rnBorder1, EXC_XF_DIAGONAL_TL_TO_BR, mbDiagTLtoBR );
+ ::set_flag( rnBorder1, EXC_XF_DIAGONAL_BL_TO_TR, mbDiagBLtoTR );
+}
+
+void XclExpCellBorder::FillToCF8( sal_uInt16& rnLine, sal_uInt32& rnColor ) const
+{
+ ::insert_value( rnLine, mnLeftLine, 0, 4 );
+ ::insert_value( rnLine, mnRightLine, 4, 4 );
+ ::insert_value( rnLine, mnTopLine, 8, 4 );
+ ::insert_value( rnLine, mnBottomLine, 12, 4 );
+ ::insert_value( rnColor, mnLeftColor, 0, 7 );
+ ::insert_value( rnColor, mnRightColor, 7, 7 );
+ ::insert_value( rnColor, mnTopColor, 16, 7 );
+ ::insert_value( rnColor, mnBottomColor, 23, 7 );
+}
+
+static const char* ToLineStyle( sal_uInt8 nLineStyle )
+{
+ switch( nLineStyle )
+ {
+ case EXC_LINE_NONE: return "none";
+ case EXC_LINE_THIN: return "thin";
+ case EXC_LINE_MEDIUM: return "medium";
+ case EXC_LINE_THICK: return "thick";
+ case EXC_LINE_DOUBLE: return "double";
+ case EXC_LINE_HAIR: return "hair";
+ case EXC_LINE_DOTTED: return "dotted";
+ case EXC_LINE_DASHED: return "dashed";
+ case EXC_LINE_MEDIUM_DASHED: return "mediumDashed";
+ case EXC_LINE_THIN_DASHDOT: return "dashDot";
+ case EXC_LINE_THIN_DASHDOTDOT: return "dashDotDot";
+ case EXC_LINE_MEDIUM_DASHDOT: return "mediumDashDot";
+ case EXC_LINE_MEDIUM_DASHDOTDOT: return "mediumDashDotDot";
+ case EXC_LINE_MEDIUM_SLANT_DASHDOT: return "slantDashDot";
+ }
+ return "*unknown*";
+}
+
+static void lcl_WriteBorder( XclExpXmlStream& rStrm, sal_Int32 nElement, sal_uInt8 nLineStyle, const Color& rColor )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ if( nLineStyle == EXC_LINE_NONE )
+ rStyleSheet->singleElement(nElement);
+ else if( rColor == Color( 0, 0, 0 ) )
+ rStyleSheet->singleElement(nElement, XML_style, ToLineStyle(nLineStyle));
+ else
+ {
+ rStyleSheet->startElement(nElement, XML_style, ToLineStyle(nLineStyle));
+ rStyleSheet->singleElement(XML_color, XML_rgb, XclXmlUtils::ToOString(rColor));
+ rStyleSheet->endElement( nElement );
+ }
+}
+
+void XclExpCellBorder::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+
+ XclExpPalette& rPalette = rStrm.GetRoot().GetPalette();
+
+ rStyleSheet->startElement( XML_border,
+ XML_diagonalUp, ToPsz( mbDiagBLtoTR ),
+ XML_diagonalDown, ToPsz( mbDiagTLtoBR )
+ // OOXTODO: XML_outline
+ );
+ lcl_WriteBorder( rStrm, XML_left, mnLeftLine, rPalette.GetColor( mnLeftColor ) );
+ lcl_WriteBorder( rStrm, XML_right, mnRightLine, rPalette.GetColor( mnRightColor ) );
+ lcl_WriteBorder( rStrm, XML_top, mnTopLine, rPalette.GetColor( mnTopColor ) );
+ lcl_WriteBorder( rStrm, XML_bottom, mnBottomLine, rPalette.GetColor( mnBottomColor ) );
+ lcl_WriteBorder( rStrm, XML_diagonal, mnDiagLine, rPalette.GetColor( mnDiagColor ) );
+ // OOXTODO: XML_vertical, XML_horizontal
+ rStyleSheet->endElement( XML_border );
+}
+
+XclExpCellArea::XclExpCellArea() :
+ mnForeColorId( XclExpPalette::GetColorIdFromIndex( mnForeColor ) ),
+ mnBackColorId( XclExpPalette::GetColorIdFromIndex( mnBackColor ) ),
+ maForeColor(0),
+ maBackColor(0)
+{
+}
+
+XclExpCellArea::XclExpCellArea(Color aForeColor, Color aBackColor)
+ : XclCellArea(EXC_PATT_SOLID)
+ , mnForeColorId(0)
+ , mnBackColorId(0)
+ , maForeColor(aForeColor)
+ , maBackColor(aBackColor)
+{
+}
+
+bool XclExpCellArea::FillFromItemSet( const SfxItemSet& rItemSet, XclExpPalette& rPalette, bool bStyle )
+{
+ const SvxBrushItem& rBrushItem = rItemSet.Get( ATTR_BACKGROUND );
+ if( rBrushItem.GetColor().IsTransparent() )
+ {
+ mnPattern = EXC_PATT_NONE;
+ mnForeColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT );
+ mnBackColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWBACK );
+ }
+ else
+ {
+ mnPattern = EXC_PATT_SOLID;
+ mnForeColorId = rPalette.InsertColor( rBrushItem.GetColor(), EXC_COLOR_CELLAREA );
+ mnBackColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT );
+ }
+ return ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, bStyle );
+}
+
+void XclExpCellArea::SetFinalColors( const XclExpPalette& rPalette )
+{
+ rPalette.GetMixedColors( mnForeColor, mnBackColor, mnPattern, mnForeColorId, mnBackColorId );
+}
+
+void XclExpCellArea::FillToXF5( sal_uInt32& rnArea ) const
+{
+ ::insert_value( rnArea, mnPattern, 16, 6 );
+ ::insert_value( rnArea, mnForeColor, 0, 7 );
+ ::insert_value( rnArea, mnBackColor, 7, 7 );
+}
+
+void XclExpCellArea::FillToXF8( sal_uInt32& rnBorder2, sal_uInt16& rnArea ) const
+{
+ ::insert_value( rnBorder2, mnPattern, 26, 6 );
+ ::insert_value( rnArea, mnForeColor, 0, 7 );
+ ::insert_value( rnArea, mnBackColor, 7, 7 );
+}
+
+void XclExpCellArea::FillToCF8( sal_uInt16& rnPattern, sal_uInt16& rnColor ) const
+{
+ XclCellArea aTmp( *this );
+ if( !aTmp.IsTransparent() && (aTmp.mnBackColor == EXC_COLOR_WINDOWTEXT) )
+ aTmp.mnBackColor = 0;
+ if( aTmp.mnPattern == EXC_PATT_SOLID )
+ ::std::swap( aTmp.mnForeColor, aTmp.mnBackColor );
+ ::insert_value( rnColor, aTmp.mnForeColor, 0, 7 );
+ ::insert_value( rnColor, aTmp.mnBackColor, 7, 7 );
+ ::insert_value( rnPattern, aTmp.mnPattern, 10, 6 );
+}
+
+static const char* ToPatternType( sal_uInt8 nPattern )
+{
+ switch( nPattern )
+ {
+ case EXC_PATT_NONE: return "none";
+ case EXC_PATT_SOLID: return "solid";
+ case EXC_PATT_50_PERC: return "mediumGray";
+ case EXC_PATT_75_PERC: return "darkGray";
+ case EXC_PATT_25_PERC: return "lightGray";
+ case EXC_PATT_12_5_PERC: return "gray125";
+ case EXC_PATT_6_25_PERC: return "gray0625";
+ }
+ return "*unknown*";
+}
+
+void XclExpCellArea::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_fill);
+
+ // OOXTODO: XML_gradientFill
+
+ XclExpPalette& rPalette = rStrm.GetRoot().GetPalette();
+
+ if (mnPattern == EXC_PATT_NONE
+ || (mnForeColor == 0 && mnBackColor == 0 && maForeColor == 0 && maBackColor == 0))
+ {
+ rStyleSheet->singleElement(XML_patternFill, XML_patternType, ToPatternType(mnPattern));
+ }
+ else
+ {
+ rStyleSheet->startElement(XML_patternFill, XML_patternType, ToPatternType(mnPattern));
+ if (maForeColor != 0 || maBackColor != 0)
+ {
+ if (maForeColor != 0)
+ {
+ rStyleSheet->singleElement(XML_fgColor, XML_rgb,
+ XclXmlUtils::ToOString(maForeColor));
+ }
+
+ if (maBackColor != 0)
+ {
+ rStyleSheet->singleElement(XML_bgColor, XML_rgb,
+ XclXmlUtils::ToOString(maBackColor));
+ }
+ }
+ else
+ {
+ if (mnForeColor != 0)
+ {
+ rStyleSheet->singleElement(XML_fgColor, XML_rgb,
+ XclXmlUtils::ToOString(rPalette.GetColor(mnForeColor)));
+ }
+ if (mnBackColor != 0)
+ {
+ rStyleSheet->singleElement(XML_bgColor, XML_rgb,
+ XclXmlUtils::ToOString(rPalette.GetColor(mnBackColor)));
+ }
+ }
+
+ rStyleSheet->endElement( XML_patternFill );
+ }
+
+ rStyleSheet->endElement( XML_fill );
+}
+
+bool XclExpColor::FillFromItemSet( const SfxItemSet& rItemSet )
+{
+ if( !ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true ) )
+ return false;
+
+ const SvxBrushItem& rBrushItem = rItemSet.Get( ATTR_BACKGROUND );
+ maColor = rBrushItem.GetColor();
+
+ return true;
+}
+
+void XclExpColor::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_fill);
+ rStyleSheet->startElement(XML_patternFill);
+ rStyleSheet->singleElement(XML_bgColor, XML_rgb, XclXmlUtils::ToOString(maColor));
+
+ rStyleSheet->endElement( XML_patternFill );
+ rStyleSheet->endElement( XML_fill );
+}
+
+XclExpXFId::XclExpXFId() :
+ mnXFId( XclExpXFBuffer::GetDefCellXFId() ),
+ mnXFIndex( EXC_XF_DEFAULTCELL )
+{
+}
+
+void XclExpXFId::ConvertXFIndex( const XclExpRoot& rRoot )
+{
+ mnXFIndex = rRoot.GetXFBuffer().GetXFIndex( mnXFId );
+}
+
+XclExpXF::XclExpXF(
+ const XclExpRoot& rRoot, const ScPatternAttr& rPattern, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) :
+ XclXFBase( true ),
+ XclExpRoot( rRoot )
+{
+ mnParentXFId = GetXFBuffer().InsertStyle( rPattern.GetStyleSheet() );
+ Init( rPattern.GetItemSet(), nScript, nForceScNumFmt, nForceXclFont, bForceLineBreak, false );
+}
+
+XclExpXF::XclExpXF( const XclExpRoot& rRoot, const SfxStyleSheetBase& rStyleSheet ) :
+ XclXFBase( false ),
+ XclExpRoot( rRoot ),
+ mnParentXFId( XclExpXFBuffer::GetXFIdFromIndex( EXC_XF_STYLEPARENT ) )
+{
+ bool bDefStyle = (rStyleSheet.GetName() == ScResId( STR_STYLENAME_STANDARD ));
+ sal_Int16 nScript = bDefStyle ? GetDefApiScript() : css::i18n::ScriptType::WEAK;
+ Init( const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet(), nScript,
+ NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND, false, bDefStyle );
+}
+
+XclExpXF::XclExpXF( const XclExpRoot& rRoot, bool bCellXF ) :
+ XclXFBase( bCellXF ),
+ XclExpRoot( rRoot ),
+ mnParentXFId( XclExpXFBuffer::GetXFIdFromIndex( EXC_XF_STYLEPARENT ) )
+{
+ InitDefault();
+}
+
+bool XclExpXF::Equals( const ScPatternAttr& rPattern,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) const
+{
+ return IsCellXF() && (mpItemSet == &rPattern.GetItemSet()) &&
+ (!bForceLineBreak || maAlignment.mbLineBreak) &&
+ ((nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND) || (mnScNumFmt == nForceScNumFmt)) &&
+ ((nForceXclFont == EXC_FONT_NOTFOUND) || (mnXclFont == nForceXclFont));
+}
+
+bool XclExpXF::Equals( const SfxStyleSheetBase& rStyleSheet ) const
+{
+ return IsStyleXF() && (mpItemSet == &const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet());
+}
+
+void XclExpXF::SetFinalColors()
+{
+ maBorder.SetFinalColors( GetPalette() );
+ maArea.SetFinalColors( GetPalette() );
+}
+
+bool XclExpXF::Equals( const XclExpXF& rCmpXF ) const
+{
+ return XclXFBase::Equals( rCmpXF ) &&
+ (maProtection == rCmpXF.maProtection) && (maAlignment == rCmpXF.maAlignment) &&
+ (maBorder == rCmpXF.maBorder) && (maArea == rCmpXF.maArea) &&
+ (mnXclFont == rCmpXF.mnXclFont) && (mnXclNumFmt == rCmpXF.mnXclNumFmt) &&
+ (mnParentXFId == rCmpXF.mnParentXFId);
+}
+
+void XclExpXF::InitDefault()
+{
+ SetRecHeader( EXC_ID5_XF, (GetBiff() == EXC_BIFF8) ? 20 : 16 );
+ mpItemSet = nullptr;
+ mnScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ mnXclFont = mnXclNumFmt = 0;
+ SetXmlIds(0, 0);
+}
+
+void XclExpXF::Init( const SfxItemSet& rItemSet, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak, bool bDefStyle )
+{
+ InitDefault();
+ mpItemSet = &rItemSet;
+
+ // cell protection
+ mbProtUsed = maProtection.FillFromItemSet( rItemSet, IsStyleXF() );
+
+ // font
+ if( nForceXclFont == EXC_FONT_NOTFOUND )
+ {
+ mnXclFont = GetFontBuffer().Insert( rItemSet, nScript, EXC_COLOR_CELLTEXT, bDefStyle );
+ mbFontUsed = XclExpFontHelper::CheckItems( GetRoot(), rItemSet, nScript, IsStyleXF() );
+ }
+ else
+ {
+ mnXclFont = nForceXclFont;
+ mbFontUsed = true;
+ }
+
+ // number format
+ if (nForceScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ mnXclNumFmt = nForceScNumFmt;
+ else
+ {
+ // Built-in formats of dedicated languages may be attributed using the
+ // system language (or even other?) format with a language attribute,
+ // obtain the "real" format key.
+ mnScNumFmt = rItemSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ LanguageType nLang = rItemSet.Get( ATTR_LANGUAGE_FORMAT).GetLanguage();
+ if (mnScNumFmt >= SV_COUNTRY_LANGUAGE_OFFSET || nLang != LANGUAGE_SYSTEM)
+ mnScNumFmt = GetFormatter().GetFormatForLanguageIfBuiltIn( mnScNumFmt, nLang);
+ }
+ mnXclNumFmt = GetNumFmtBuffer().Insert( mnScNumFmt );
+ mbFmtUsed = ScfTools::CheckItem( rItemSet, ATTR_VALUE_FORMAT, IsStyleXF() );
+
+ // alignment
+ mbAlignUsed = maAlignment.FillFromItemSet(*this, rItemSet, bForceLineBreak, GetBiff(), IsStyleXF());
+
+ // cell border
+ mbBorderUsed = maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff(), IsStyleXF() );
+
+ // background area
+ mbAreaUsed = maArea.FillFromItemSet( rItemSet, GetPalette(), IsStyleXF() );
+
+ // set all b***Used flags to true in "Default"/"Normal" style
+ if( bDefStyle )
+ SetAllUsedFlags( true );
+}
+
+sal_uInt8 XclExpXF::GetUsedFlags() const
+{
+ sal_uInt8 nUsedFlags = 0;
+ /* In cell XFs a set bit means a used attribute, in style XFs a cleared bit.
+ "mbCellXF == mb***Used" evaluates to correct value in cell and style XFs. */
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_PROT, mbCellXF == mbProtUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_FONT, mbCellXF == mbFontUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_VALFMT, mbCellXF == mbFmtUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_ALIGN, mbCellXF == mbAlignUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_BORDER, mbCellXF == mbBorderUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_AREA, mbCellXF == mbAreaUsed );
+ return nUsedFlags;
+}
+
+void XclExpXF::WriteBody5( XclExpStream& rStrm )
+{
+ sal_uInt16 nTypeProt = 0, nAlign = 0;
+ sal_uInt32 nArea = 0, nBorder = 0;
+
+ ::set_flag( nTypeProt, EXC_XF_STYLE, IsStyleXF() );
+ ::insert_value( nTypeProt, mnParent, 4, 12 );
+ ::insert_value( nAlign, GetUsedFlags(), 10, 6 );
+
+ maProtection.FillToXF3( nTypeProt );
+ maAlignment.FillToXF5( nAlign );
+ maBorder.FillToXF5( nBorder, nArea );
+ maArea.FillToXF5( nArea );
+
+ rStrm << mnXclFont << mnXclNumFmt << nTypeProt << nAlign << nArea << nBorder;
+}
+
+void XclExpXF::WriteBody8( XclExpStream& rStrm )
+{
+ sal_uInt16 nTypeProt = 0, nAlign = 0, nMiscAttrib = 0, nArea = 0;
+ sal_uInt32 nBorder1 = 0, nBorder2 = 0;
+
+ ::set_flag( nTypeProt, EXC_XF_STYLE, IsStyleXF() );
+ ::insert_value( nTypeProt, mnParent, 4, 12 );
+ ::insert_value( nMiscAttrib, GetUsedFlags(), 10, 6 );
+
+ maProtection.FillToXF3( nTypeProt );
+ maAlignment.FillToXF8( nAlign, nMiscAttrib );
+ maBorder.FillToXF8( nBorder1, nBorder2 );
+ maArea.FillToXF8( nBorder2, nArea );
+
+ rStrm << mnXclFont << mnXclNumFmt << nTypeProt << nAlign << nMiscAttrib << nBorder1 << nBorder2 << nArea;
+}
+
+void XclExpXF::WriteBody( XclExpStream& rStrm )
+{
+ XclExpXFId aParentId( mnParentXFId );
+ aParentId.ConvertXFIndex( GetRoot() );
+ mnParent = aParentId.mnXFIndex;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5: WriteBody5( rStrm ); break;
+ case EXC_BIFF8: WriteBody8( rStrm ); break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpXF::SetXmlIds( sal_uInt32 nBorderId, sal_uInt32 nFillId )
+{
+ mnBorderId = nBorderId;
+ mnFillId = nFillId;
+}
+
+void XclExpXF::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+
+ sal_Int32 nXfId = 0;
+ const XclExpXF* pStyleXF = nullptr;
+ if( IsCellXF() )
+ {
+ sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( mnParentXFId );
+ nXfId = rStrm.GetRoot().GetXFBuffer().GetXmlStyleIndex( nXFIndex );
+ pStyleXF = rStrm.GetRoot().GetXFBuffer().GetXFById( mnParentXFId );
+ }
+
+ rStyleSheet->startElement( XML_xf,
+ XML_numFmtId, OString::number(mnXclNumFmt),
+ XML_fontId, OString::number(mnXclFont),
+ XML_fillId, OString::number(mnFillId),
+ XML_borderId, OString::number(mnBorderId),
+ XML_xfId, sax_fastparser::UseIf(OString::number(nXfId), !IsStyleXF()),
+ // OOXTODO: XML_quotePrefix,
+ // OOXTODO: XML_pivotButton,
+ // OOXTODO: XML_applyNumberFormat, ;
+ XML_applyFont, ToPsz( mbFontUsed ),
+ // OOXTODO: XML_applyFill,
+ XML_applyBorder, ToPsz( mbBorderUsed ),
+ XML_applyAlignment, ToPsz( mbAlignUsed ),
+ XML_applyProtection, ToPsz( mbProtUsed ) );
+ if( mbAlignUsed )
+ maAlignment.SaveXml( rStrm );
+ else if ( pStyleXF )
+ pStyleXF->GetAlignmentData().SaveXml( rStrm );
+ if( mbProtUsed )
+ maProtection.SaveXml( rStrm );
+ else if ( pStyleXF )
+ pStyleXF->GetProtectionData().SaveXml( rStrm );
+
+ // OOXTODO: XML_extLst
+ rStyleSheet->endElement( XML_xf );
+}
+
+XclExpDefaultXF::XclExpDefaultXF( const XclExpRoot& rRoot, bool bCellXF ) :
+ XclExpXF( rRoot, bCellXF )
+{
+}
+
+void XclExpDefaultXF::SetFont( sal_uInt16 nXclFont )
+{
+ mnXclFont = nXclFont;
+ mbFontUsed = true;
+}
+
+void XclExpDefaultXF::SetNumFmt( sal_uInt16 nXclNumFmt )
+{
+ mnXclNumFmt = nXclNumFmt;
+ mbFmtUsed = true;
+}
+
+XclExpStyle::XclExpStyle( sal_uInt32 nXFId, const OUString& rStyleName ) :
+ XclExpRecord( EXC_ID_STYLE, 4 ),
+ maName( rStyleName ),
+ maXFId( nXFId ),
+ mnStyleId( EXC_STYLE_USERDEF ),
+ mnLevel( EXC_STYLE_NOLEVEL )
+{
+ OSL_ENSURE( !maName.isEmpty(), "XclExpStyle::XclExpStyle - empty style name" );
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt8 nStyleId, nLevel; // do not use members for debug tests
+ OSL_ENSURE( !XclTools::GetBuiltInStyleId( nStyleId, nLevel, maName ),
+ "XclExpStyle::XclExpStyle - this is a built-in style" );
+#endif
+}
+
+XclExpStyle::XclExpStyle( sal_uInt32 nXFId, sal_uInt8 nStyleId, sal_uInt8 nLevel ) :
+ XclExpRecord( EXC_ID_STYLE, 4 ),
+ maXFId( nXFId ),
+ mnStyleId( nStyleId ),
+ mnLevel( nLevel )
+{
+}
+
+void XclExpStyle::WriteBody( XclExpStream& rStrm )
+{
+ maXFId.ConvertXFIndex( rStrm.GetRoot() );
+ ::set_flag( maXFId.mnXFIndex, EXC_STYLE_BUILTIN, IsBuiltIn() );
+ rStrm << maXFId.mnXFIndex;
+
+ if( IsBuiltIn() )
+ {
+ rStrm << mnStyleId << mnLevel;
+ }
+ else
+ {
+ XclExpString aNameEx;
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ aNameEx.Assign( maName );
+ else
+ aNameEx.AssignByte( maName, rStrm.GetRoot().GetTextEncoding(), XclStrFlags::EightBitLength );
+ rStrm << aNameEx;
+ }
+}
+
+static const char* lcl_StyleNameFromId( sal_Int32 nStyleId )
+{
+ switch( nStyleId )
+ {
+ case 0: return "Normal";
+ case 3: return "Comma";
+ case 4: return "Currency";
+ case 5: return "Percent";
+ case 6: return "Comma [0]";
+ case 7: return "Currency [0]";
+ }
+ return "*unknown*";
+}
+
+void XclExpStyle::SaveXml( XclExpXmlStream& rStrm )
+{
+ constexpr sal_Int32 CELL_STYLE_MAX_BUILTIN_ID = 54;
+ OString sName;
+ OString sBuiltinId;
+ const char* pBuiltinId = nullptr;
+ if( IsBuiltIn() )
+ {
+ sName = OString( lcl_StyleNameFromId( mnStyleId ) );
+ sBuiltinId = OString::number( std::min( static_cast<sal_Int32>( CELL_STYLE_MAX_BUILTIN_ID - 1 ), static_cast <sal_Int32>( mnStyleId ) ) );
+ pBuiltinId = sBuiltinId.getStr();
+ }
+ else
+ sName = maName.toUtf8();
+
+ // get the index in sortedlist associated with the mnXId
+ sal_Int32 nXFId = rStrm.GetRoot().GetXFBuffer().GetXFIndex( maXFId.mnXFId );
+ // get the style index associated with index into sortedlist
+ nXFId = rStrm.GetRoot().GetXFBuffer().GetXmlStyleIndex( nXFId );
+ rStrm.GetCurrentStream()->singleElement( XML_cellStyle,
+ XML_name, sName,
+ XML_xfId, OString::number(nXFId),
+// builtinId of 54 or above is invalid according to OpenXML SDK validator.
+ XML_builtinId, pBuiltinId
+ // OOXTODO: XML_iLevel,
+ // OOXTODO: XML_hidden,
+ // XML_customBuiltin, ToPsz( ! IsBuiltIn() )
+ );
+ // OOXTODO: XML_extLst
+}
+
+namespace {
+
+const sal_uInt32 EXC_XFLIST_INDEXBASE = 0xFFFE0000;
+/** Maximum count of XF records to store in the XF list (performance). */
+const sal_uInt32 EXC_XFLIST_HARDLIMIT = 256 * 1024;
+
+bool lclIsBuiltInStyle( const OUString& rStyleName )
+{
+ return
+ XclTools::IsBuiltInStyleName( rStyleName ) ||
+ XclTools::IsCondFormatStyleName( rStyleName );
+}
+
+} // namespace
+
+XclExpXFBuffer::XclExpBuiltInInfo::XclExpBuiltInInfo() :
+ mnStyleId( EXC_STYLE_USERDEF ),
+ mnLevel( EXC_STYLE_NOLEVEL ),
+ mbPredefined( true ),
+ mbHasStyleRec( false )
+{
+}
+
+namespace {
+
+/** Predicate for search algorithm. */
+struct XclExpBorderPred
+{
+ const XclExpCellBorder&
+ mrBorder;
+ explicit XclExpBorderPred( const XclExpCellBorder& rBorder ) : mrBorder( rBorder ) {}
+ bool operator()( const XclExpCellBorder& rBorder ) const;
+};
+
+}
+
+bool XclExpBorderPred::operator()( const XclExpCellBorder& rBorder ) const
+{
+ return
+ mrBorder.mnLeftColor == rBorder.mnLeftColor &&
+ mrBorder.mnRightColor == rBorder.mnRightColor &&
+ mrBorder.mnTopColor == rBorder.mnTopColor &&
+ mrBorder.mnBottomColor == rBorder.mnBottomColor &&
+ mrBorder.mnDiagColor == rBorder.mnDiagColor &&
+ mrBorder.mnLeftLine == rBorder.mnLeftLine &&
+ mrBorder.mnRightLine == rBorder.mnRightLine &&
+ mrBorder.mnTopLine == rBorder.mnTopLine &&
+ mrBorder.mnBottomLine == rBorder.mnBottomLine &&
+ mrBorder.mnDiagLine == rBorder.mnDiagLine &&
+ mrBorder.mbDiagTLtoBR == rBorder.mbDiagTLtoBR &&
+ mrBorder.mbDiagBLtoTR == rBorder.mbDiagBLtoTR &&
+ mrBorder.mnLeftColorId == rBorder.mnLeftColorId &&
+ mrBorder.mnRightColorId == rBorder.mnRightColorId &&
+ mrBorder.mnTopColorId == rBorder.mnTopColorId &&
+ mrBorder.mnBottomColorId == rBorder.mnBottomColorId &&
+ mrBorder.mnDiagColorId == rBorder.mnDiagColorId;
+}
+
+namespace {
+
+struct XclExpFillPred
+{
+ const XclExpCellArea&
+ mrFill;
+ explicit XclExpFillPred( const XclExpCellArea& rFill ) : mrFill( rFill ) {}
+ bool operator()( const XclExpCellArea& rFill ) const;
+};
+
+}
+
+bool XclExpFillPred::operator()( const XclExpCellArea& rFill ) const
+{
+ return
+ mrFill.mnForeColor == rFill.mnForeColor &&
+ mrFill.mnBackColor == rFill.mnBackColor &&
+ mrFill.mnPattern == rFill.mnPattern &&
+ mrFill.mnForeColorId == rFill.mnForeColorId &&
+ mrFill.mnBackColorId == rFill.mnBackColorId;
+}
+
+XclExpXFBuffer::XclExpXFBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpXFBuffer::Initialize()
+{
+ InsertDefaultRecords();
+ InsertUserStyles();
+}
+
+sal_uInt32 XclExpXFBuffer::Insert( const ScPatternAttr* pPattern, sal_Int16 nScript )
+{
+ return InsertCellXF( pPattern, nScript, NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND, false );
+}
+
+sal_uInt32 XclExpXFBuffer::InsertWithFont( const ScPatternAttr* pPattern, sal_Int16 nScript,
+ sal_uInt16 nForceXclFont, bool bForceLineBreak )
+{
+ return InsertCellXF( pPattern, nScript, NUMBERFORMAT_ENTRY_NOT_FOUND, nForceXclFont, bForceLineBreak );
+}
+
+sal_uInt32 XclExpXFBuffer::InsertWithNumFmt( const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForceScNumFmt, bool bForceLineBreak )
+{
+ return InsertCellXF( pPattern, nScript, nForceScNumFmt, EXC_FONT_NOTFOUND, bForceLineBreak );
+}
+
+sal_uInt32 XclExpXFBuffer::InsertStyle( const SfxStyleSheetBase* pStyleSheet )
+{
+ return pStyleSheet ? InsertStyleXF( *pStyleSheet ) : GetXFIdFromIndex( EXC_XF_DEFAULTSTYLE );
+}
+
+sal_uInt32 XclExpXFBuffer::GetXFIdFromIndex( sal_uInt16 nXFIndex )
+{
+ return EXC_XFLIST_INDEXBASE | nXFIndex;
+}
+
+sal_uInt32 XclExpXFBuffer::GetDefCellXFId()
+{
+ return GetXFIdFromIndex( EXC_XF_DEFAULTCELL );
+}
+
+const XclExpXF* XclExpXFBuffer::GetXFById( sal_uInt32 nXFId ) const
+{
+ return maXFList.GetRecord( nXFId );
+}
+
+void XclExpXFBuffer::Finalize()
+{
+ for( size_t nPos = 0, nSize = maXFList.GetSize(); nPos < nSize; ++nPos )
+ maXFList.GetRecord( nPos )->SetFinalColors();
+
+ sal_uInt32 nTotalCount = static_cast< sal_uInt32 >( maXFList.GetSize() );
+ sal_uInt32 nId;
+ maXFIndexVec.resize( nTotalCount, EXC_XF_DEFAULTCELL );
+ maStyleIndexes.resize( nTotalCount, EXC_XF_DEFAULTCELL );
+ maCellIndexes.resize( nTotalCount, EXC_XF_DEFAULTCELL );
+
+ XclExpBuiltInMap::const_iterator aBuiltInEnd = maBuiltInMap.end();
+ /* nMaxBuiltInXFId used to decide faster whether an XF record is
+ user-defined. If the current XF ID is greater than this value,
+ maBuiltInMap doesn't need to be searched. */
+ sal_uInt32 nMaxBuiltInXFId = maBuiltInMap.empty() ? 0 : maBuiltInMap.rbegin()->first;
+
+ // *** map all built-in XF records (cell and style) *** -------------------
+
+ // do not change XF order -> std::map<> iterates elements in ascending order
+ for( const auto& rEntry : maBuiltInMap )
+ AppendXFIndex( rEntry.first );
+
+ // *** insert all user-defined style XF records, without reduce *** -------
+
+ sal_uInt32 nStyleXFCount = 0; // counts up to EXC_XF_MAXSTYLECOUNT limit
+
+ for( nId = 0; nId < nTotalCount; ++nId )
+ {
+ XclExpXFRef xXF = maXFList.GetRecord( nId );
+ if( xXF->IsStyleXF() && ((nId > nMaxBuiltInXFId) || (maBuiltInMap.find( nId ) == aBuiltInEnd)) )
+ {
+ if( nStyleXFCount < EXC_XF_MAXSTYLECOUNT )
+ {
+ // maximum count of styles not reached
+ AppendXFIndex( nId );
+ ++nStyleXFCount;
+ }
+ else
+ {
+ /* Maximum count of styles reached - do not append more
+ pointers to XFs; use default style XF instead; do not break
+ the loop to initialize all maXFIndexVec elements. */
+ maXFIndexVec[ nId ] = EXC_XF_DEFAULTSTYLE;
+ }
+ }
+ }
+
+ // *** insert all cell XF records *** -------------------------------------
+
+ // start position to search for equal inserted XF records
+ size_t nSearchStart = maSortedXFList.GetSize();
+
+ // break the loop if XF limit reached - maXFIndexVec is already initialized with default index
+ XclExpXFRef xDefCellXF = maXFList.GetRecord( EXC_XF_DEFAULTCELL );
+ for( nId = 0; (nId < nTotalCount) && (maSortedXFList.GetSize() < EXC_XF_MAXCOUNT); ++nId )
+ {
+ XclExpXFRef xXF = maXFList.GetRecord( nId );
+ if( xXF->IsCellXF() && ((nId > nMaxBuiltInXFId) || (maBuiltInMap.find( nId ) == aBuiltInEnd)) )
+ {
+ // try to find an XF record equal to *xXF, which is already inserted
+ sal_uInt16 nFoundIndex = EXC_XF_NOTFOUND;
+
+ // first try if it is equal to the default cell XF
+ if( xDefCellXF->Equals( *xXF ) )
+ {
+ nFoundIndex = EXC_XF_DEFAULTCELL;
+ }
+ else for( size_t nSearchPos = nSearchStart, nSearchEnd = maSortedXFList.GetSize();
+ (nSearchPos < nSearchEnd) && (nFoundIndex == EXC_XF_NOTFOUND); ++nSearchPos )
+ {
+ if( maSortedXFList.GetRecord( nSearchPos )->Equals( *xXF ) )
+ nFoundIndex = static_cast< sal_uInt16 >( nSearchPos );
+ }
+
+ if( nFoundIndex != EXC_XF_NOTFOUND )
+ // equal XF already in the list, use its resulting XF index
+ maXFIndexVec[ nId ] = nFoundIndex;
+ else
+ AppendXFIndex( nId );
+ }
+ }
+
+ sal_uInt16 nXmlStyleIndex = 0;
+ sal_uInt16 nXmlCellIndex = 0;
+
+ size_t nXFCount = maSortedXFList.GetSize();
+ for( size_t i = 0; i < nXFCount; ++i )
+ {
+ XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i );
+ if( xXF->IsStyleXF() )
+ maStyleIndexes[ i ] = nXmlStyleIndex++;
+ else
+ maCellIndexes[ i ] = nXmlCellIndex++;
+ }
+}
+
+sal_uInt16 XclExpXFBuffer::GetXFIndex( sal_uInt32 nXFId ) const
+{
+ sal_uInt16 nXFIndex = EXC_XF_DEFAULTSTYLE;
+ if( nXFId >= EXC_XFLIST_INDEXBASE )
+ nXFIndex = static_cast< sal_uInt16 >( nXFId & ~EXC_XFLIST_INDEXBASE );
+ else if( nXFId < maXFIndexVec.size() )
+ nXFIndex = maXFIndexVec[ nXFId ];
+ return nXFIndex;
+}
+
+sal_Int32 XclExpXFBuffer::GetXmlStyleIndex( sal_uInt32 nXFIndex ) const
+{
+ OSL_ENSURE( nXFIndex < maStyleIndexes.size(), "XclExpXFBuffer::GetXmlStyleIndex - invalid index!" );
+ if( nXFIndex >= maStyleIndexes.size() )
+ return 0; // should be caught/debugged via above assert; return "valid" index.
+ return maStyleIndexes[ nXFIndex ];
+}
+
+sal_Int32 XclExpXFBuffer::GetXmlCellIndex( sal_uInt32 nXFIndex ) const
+{
+ OSL_ENSURE( nXFIndex < maCellIndexes.size(), "XclExpXFBuffer::GetXmlStyleIndex - invalid index!" );
+ if( nXFIndex >= maCellIndexes.size() )
+ return 0; // should be caught/debugged via above assert; return "valid" index.
+ return maCellIndexes[ nXFIndex ];
+}
+
+void XclExpXFBuffer::Save( XclExpStream& rStrm )
+{
+ // save all XF records contained in the maSortedXFList vector (sorted by XF index)
+ maSortedXFList.Save( rStrm );
+ // save all STYLE records
+ maStyleList.Save( rStrm );
+}
+
+static void lcl_GetCellCounts( const XclExpRecordList< XclExpXF >& rXFList, sal_Int32& rCells, sal_Int32& rStyles )
+{
+ rCells = 0;
+ rStyles = 0;
+ size_t nXFCount = rXFList.GetSize();
+ for( size_t i = 0; i < nXFCount; ++i )
+ {
+ XclExpRecordList< XclExpXF >::RecordRefType xXF = rXFList.GetRecord( i );
+ if( xXF->IsCellXF() )
+ ++rCells;
+ else if( xXF->IsStyleXF() )
+ ++rStyles;
+ }
+}
+
+void XclExpXFBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+
+ rStyleSheet->startElement(XML_fills, XML_count, OString::number(maFills.size()));
+ for( const auto& rFill : maFills )
+ {
+ rFill.SaveXml( rStrm );
+ }
+ rStyleSheet->endElement( XML_fills );
+
+ rStyleSheet->startElement(XML_borders, XML_count, OString::number(maBorders.size()));
+ for( const auto& rBorder : maBorders )
+ {
+ rBorder.SaveXml( rStrm );
+ }
+ rStyleSheet->endElement( XML_borders );
+
+ // save all XF records contained in the maSortedXFList vector (sorted by XF index)
+ sal_Int32 nCells, nStyles;
+ lcl_GetCellCounts( maSortedXFList, nCells, nStyles );
+
+ if( nStyles > 0 )
+ {
+ rStyleSheet->startElement(XML_cellStyleXfs, XML_count, OString::number(nStyles));
+ size_t nXFCount = maSortedXFList.GetSize();
+ for( size_t i = 0; i < nXFCount; ++i )
+ {
+ XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i );
+ if( ! xXF->IsStyleXF() )
+ continue;
+ SaveXFXml( rStrm, *xXF );
+ }
+ rStyleSheet->endElement( XML_cellStyleXfs );
+ }
+
+ if( nCells > 0 )
+ {
+ rStyleSheet->startElement(XML_cellXfs, XML_count, OString::number(nCells));
+ size_t nXFCount = maSortedXFList.GetSize();
+ for( size_t i = 0; i < nXFCount; ++i )
+ {
+ XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i );
+ if( ! xXF->IsCellXF() )
+ continue;
+ SaveXFXml( rStrm, *xXF );
+ }
+ rStyleSheet->endElement( XML_cellXfs );
+ }
+
+ // save all STYLE records
+ rStyleSheet->startElement(XML_cellStyles, XML_count, OString::number(maStyleList.GetSize()));
+ maStyleList.SaveXml( rStrm );
+ rStyleSheet->endElement( XML_cellStyles );
+}
+
+void XclExpXFBuffer::SaveXFXml( XclExpXmlStream& rStrm, XclExpXF& rXF )
+{
+ XclExpBorderList::iterator aBorderPos =
+ std::find_if( maBorders.begin(), maBorders.end(), XclExpBorderPred( rXF.GetBorderData() ) );
+ OSL_ENSURE( aBorderPos != maBorders.end(), "XclExpXFBuffer::SaveXml - Invalid @borderId!" );
+ XclExpFillList::iterator aFillPos =
+ std::find_if( maFills.begin(), maFills.end(), XclExpFillPred( rXF.GetAreaData() ) );
+ OSL_ENSURE( aFillPos != maFills.end(), "XclExpXFBuffer::SaveXml - Invalid @fillId!" );
+
+ sal_Int32 nBorderId = 0, nFillId = 0;
+ if( aBorderPos != maBorders.end() )
+ nBorderId = std::distance( maBorders.begin(), aBorderPos );
+ if( aFillPos != maFills.end() )
+ nFillId = std::distance( maFills.begin(), aFillPos );
+
+ rXF.SetXmlIds( nBorderId, nFillId );
+ rXF.SaveXml( rStrm );
+}
+
+sal_uInt32 XclExpXFBuffer::FindXF( const ScPatternAttr& rPattern,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) const
+{
+ if (nForceScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND && nForceXclFont == EXC_FONT_NOTFOUND)
+ {
+ FindKey key1 { /*mbCellXF*/true, &rPattern.GetItemSet(), nForceScNumFmt, 0 };
+ FindKey key2 { /*mbCellXF*/true, &rPattern.GetItemSet(), nForceScNumFmt, EXC_FONT_NOTFOUND };
+ auto it1 = maXFFindMap.lower_bound(key1);
+ if (it1 != maXFFindMap.end())
+ {
+ auto it2 = maXFFindMap.upper_bound(key2);
+ for (auto it = it1; it != it2; ++it)
+ for (auto const & nPos : it->second)
+ if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
+ return nPos;
+ }
+ }
+ else if (nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND || nForceXclFont == EXC_FONT_NOTFOUND)
+ {
+ FindKey key1 { /*mbCellXF*/true, &rPattern.GetItemSet(), 0, 0 };
+ FindKey key2 { /*mbCellXF*/true, &rPattern.GetItemSet(), NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND };
+ auto it1 = maXFFindMap.lower_bound(key1);
+ if (it1 != maXFFindMap.end())
+ {
+ auto it2 = maXFFindMap.upper_bound(key2);
+ for (auto it = it1; it != it2; ++it)
+ for (auto const & nPos : it->second)
+ if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
+ return nPos;
+ }
+ }
+ else
+ {
+ FindKey key { /*mbCellXF*/true, &rPattern.GetItemSet(), nForceScNumFmt, nForceXclFont };
+ auto it = maXFFindMap.find(key);
+ if (it == maXFFindMap.end())
+ return EXC_XFID_NOTFOUND;
+ for (auto const & nPos : it->second)
+ if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
+ return nPos;
+ }
+ return EXC_XFID_NOTFOUND;
+}
+
+sal_uInt32 XclExpXFBuffer::FindXF( const SfxStyleSheetBase& rStyleSheet ) const
+{
+ const SfxItemSet* pItemSet = &const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet();
+ FindKey key1 { /*mbCellXF*/false, pItemSet, 0, 0 };
+ FindKey key2 { /*mbCellXF*/false, pItemSet, NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND };
+ auto it1 = maXFFindMap.lower_bound(key1);
+ auto it2 = maXFFindMap.upper_bound(key2);
+ for (auto it = it1; it != it2; ++it)
+ for (auto const & nPos : it->second)
+ if( maXFList.GetRecord( nPos )->Equals( rStyleSheet ) )
+ return nPos;
+ return EXC_XFID_NOTFOUND;
+}
+
+sal_uInt32 XclExpXFBuffer::FindBuiltInXF( sal_uInt8 nStyleId, sal_uInt8 nLevel ) const
+{
+ auto aIt = std::find_if(maBuiltInMap.begin(), maBuiltInMap.end(),
+ [&nStyleId, nLevel](const XclExpBuiltInMap::value_type& rEntry) {
+ return (rEntry.second.mnStyleId == nStyleId) && (rEntry.second.mnLevel == nLevel);
+ });
+ if (aIt != maBuiltInMap.end())
+ return aIt->first;
+ return EXC_XFID_NOTFOUND;
+}
+
+XclExpXFBuffer::FindKey XclExpXFBuffer::ToFindKey(XclExpXF const & rRec)
+{
+ return { rRec.IsCellXF(), rRec.GetItemSet(), rRec.GetScNumFmt(), rRec.GetXclFont() };
+}
+
+sal_uInt32 XclExpXFBuffer::InsertCellXF( const ScPatternAttr* pPattern, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak )
+{
+ const ScPatternAttr* pDefPattern = GetDoc().GetDefPattern();
+ if( !pPattern )
+ pPattern = pDefPattern;
+
+ // special handling for default cell formatting
+ if( (pPattern == pDefPattern) && !bForceLineBreak &&
+ (nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND) &&
+ (nForceXclFont == EXC_FONT_NOTFOUND) )
+ {
+ // Is it the first try to insert the default cell format?
+ bool& rbPredefined = maBuiltInMap[ EXC_XF_DEFAULTCELL ].mbPredefined;
+ if( rbPredefined )
+ {
+ // remove old entry in find-map
+ auto & rPositions = maXFFindMap[ToFindKey(*maXFList.GetRecord(EXC_XF_DEFAULTCELL))];
+ auto it = std::find(rPositions.begin(), rPositions.end(), EXC_XF_DEFAULTCELL);
+ rPositions.erase(it);
+ // replace default cell pattern
+ XclExpXFRef xNewXF = new XclExpXF( GetRoot(), *pPattern, nScript );
+ maXFList.ReplaceRecord( xNewXF, EXC_XF_DEFAULTCELL );
+ // and add new entry in find-map
+ maXFFindMap[ToFindKey(*xNewXF)].push_back(EXC_XF_DEFAULTCELL);
+ rbPredefined = false;
+ }
+ return GetDefCellXFId();
+ }
+
+ sal_uInt32 nXFId = FindXF( *pPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak );
+ if( nXFId == EXC_XFID_NOTFOUND )
+ {
+ // not found - insert new cell XF
+ if( maXFList.GetSize() < EXC_XFLIST_HARDLIMIT )
+ {
+ auto pNewExp = new XclExpXF(
+ GetRoot(), *pPattern, nScript, nForceScNumFmt, nForceXclFont, bForceLineBreak );
+ maXFList.AppendNewRecord( pNewExp );
+ // do not set nXFId before the AppendNewRecord() call - it may insert 2 XFs (style+cell)
+ nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() - 1 );
+ maXFFindMap[ToFindKey(*pNewExp)].push_back(nXFId);
+ }
+ else
+ {
+ // list full - fall back to default cell XF
+ nXFId = GetDefCellXFId();
+ }
+ }
+ return nXFId;
+}
+
+sal_uInt32 XclExpXFBuffer::InsertStyleXF( const SfxStyleSheetBase& rStyleSheet )
+{
+ // *** try, if it is a built-in style - create new XF or replace existing predefined XF ***
+
+ sal_uInt8 nStyleId, nLevel;
+ if( XclTools::GetBuiltInStyleId( nStyleId, nLevel, rStyleSheet.GetName() ) )
+ {
+ // try to find the built-in XF record (if already created in InsertDefaultRecords())
+ sal_uInt32 nXFId = FindBuiltInXF( nStyleId, nLevel );
+ if( nXFId == EXC_XFID_NOTFOUND )
+ {
+ // built-in style XF not yet created - do it now
+ XclExpXFRef xXF = new XclExpXF( GetRoot(), rStyleSheet );
+ nXFId = AppendBuiltInXFWithStyle( xXF, nStyleId, nLevel );
+ // this new XF record is not predefined
+ maBuiltInMap[ nXFId ].mbPredefined = false;
+ }
+ else
+ {
+ OSL_ENSURE( maXFList.HasRecord( nXFId ), "XclExpXFBuffer::InsertStyleXF - built-in XF not found" );
+ // XF record still predefined? -> Replace with real XF
+ bool& rbPredefined = maBuiltInMap[ nXFId ].mbPredefined;
+ if( rbPredefined )
+ {
+ // remove old entry in find-map
+ auto & rPositions = maXFFindMap[ToFindKey(*maXFList.GetRecord(nXFId))];
+ auto it = std::find(rPositions.begin(), rPositions.end(), nXFId);
+ rPositions.erase(it);
+ // replace predefined built-in style (ReplaceRecord() deletes old record)
+ XclExpXFRef pNewExp = new XclExpXF( GetRoot(), rStyleSheet );
+ maXFList.ReplaceRecord( pNewExp, nXFId );
+ // and add new entry in find-map
+ maXFFindMap[ToFindKey(*pNewExp)].push_back(nXFId);
+ rbPredefined = false;
+ }
+ }
+
+ // STYLE already inserted? (may be not, i.e. for RowLevel/ColLevel or Hyperlink styles)
+ bool& rbHasStyleRec = maBuiltInMap[ nXFId ].mbHasStyleRec;
+ if( !rbHasStyleRec )
+ {
+ maStyleList.AppendNewRecord( new XclExpStyle( nXFId, nStyleId, nLevel ) );
+ rbHasStyleRec = true;
+ }
+
+ return nXFId;
+ }
+
+ // *** try to find the XF record of a user-defined style ***
+
+ sal_uInt32 nXFId = FindXF( rStyleSheet );
+ if( nXFId == EXC_XFID_NOTFOUND )
+ {
+ // not found - insert new style XF and STYLE
+ nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() );
+ if( nXFId < EXC_XFLIST_HARDLIMIT )
+ {
+ auto pNewExp = new XclExpXF( GetRoot(), rStyleSheet );
+ maXFList.AppendNewRecord( pNewExp );
+ // create the STYLE record
+ if( !rStyleSheet.GetName().isEmpty() )
+ maStyleList.AppendNewRecord( new XclExpStyle( nXFId, rStyleSheet.GetName() ) );
+ maXFFindMap[ToFindKey(*pNewExp)].push_back(nXFId);
+ }
+ else
+ // list full - fall back to default style XF
+ nXFId = GetXFIdFromIndex( EXC_XF_DEFAULTSTYLE );
+ }
+ return nXFId;
+}
+
+void XclExpXFBuffer::InsertUserStyles()
+{
+ SfxStyleSheetIterator aStyleIter( GetDoc().GetStyleSheetPool(), SfxStyleFamily::Para );
+ for( SfxStyleSheetBase* pStyleSheet = aStyleIter.First(); pStyleSheet; pStyleSheet = aStyleIter.Next() )
+ if( pStyleSheet->IsUserDefined() && !lclIsBuiltInStyle( pStyleSheet->GetName() ) )
+ InsertStyleXF( *pStyleSheet );
+}
+
+sal_uInt32 XclExpXFBuffer::AppendBuiltInXF( XclExpXFRef const & xXF, sal_uInt8 nStyleId, sal_uInt8 nLevel )
+{
+ sal_uInt32 nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() );
+ maXFList.AppendRecord( xXF );
+ maXFFindMap[ToFindKey(*xXF)].push_back(nXFId);
+ XclExpBuiltInInfo& rInfo = maBuiltInMap[ nXFId ];
+ rInfo.mnStyleId = nStyleId;
+ rInfo.mnLevel = nLevel;
+ rInfo.mbPredefined = true;
+ return nXFId;
+}
+
+sal_uInt32 XclExpXFBuffer::AppendBuiltInXFWithStyle( XclExpXFRef const & xXF, sal_uInt8 nStyleId, sal_uInt8 nLevel )
+{
+ sal_uInt32 nXFId = AppendBuiltInXF( xXF, nStyleId, nLevel );
+ maStyleList.AppendNewRecord( new XclExpStyle( nXFId, nStyleId, nLevel ) );
+ maBuiltInMap[ nXFId ].mbHasStyleRec = true; // mark existing STYLE record
+ return nXFId;
+}
+
+static XclExpCellArea lcl_GetPatternFill_None()
+{
+ XclExpCellArea aFill;
+ aFill.mnPattern = EXC_PATT_NONE;
+ return aFill;
+}
+
+static XclExpCellArea lcl_GetPatternFill_Gray125()
+{
+ XclExpCellArea aFill;
+ aFill.mnPattern = EXC_PATT_12_5_PERC;
+ aFill.mnForeColor = 0;
+ aFill.mnBackColor = 0;
+ return aFill;
+}
+
+void XclExpXFBuffer::InsertDefaultRecords()
+{
+ maFills.push_back( lcl_GetPatternFill_None() );
+ maFills.push_back( lcl_GetPatternFill_Gray125() );
+
+ // index 0: default style
+ if( SfxStyleSheetBase* pDefStyleSheet = GetStyleSheetPool().Find( ScResId( STR_STYLENAME_STANDARD ), SfxStyleFamily::Para ) )
+ {
+ XclExpXFRef xDefStyle = new XclExpXF( GetRoot(), *pDefStyleSheet );
+ sal_uInt32 nXFId = AppendBuiltInXFWithStyle( xDefStyle, EXC_STYLE_NORMAL );
+ // mark this XF as not predefined, prevents overwriting
+ maBuiltInMap[ nXFId ].mbPredefined = false;
+ }
+ else
+ {
+ OSL_FAIL( "XclExpXFBuffer::InsertDefaultRecords - default style not found" );
+ XclExpXFRef xDefStyle = new XclExpDefaultXF( GetRoot(), false );
+ xDefStyle->SetAllUsedFlags( true );
+ AppendBuiltInXFWithStyle( xDefStyle, EXC_STYLE_NORMAL );
+ }
+
+ // index 1-14: RowLevel and ColLevel styles (without STYLE records)
+ XclExpDefaultXF aLevelStyle( GetRoot(), false );
+ // RowLevel_1, ColLevel_1
+ aLevelStyle.SetFont( 1 );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_ROWLEVEL, 0 );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_COLLEVEL, 0 );
+ // RowLevel_2, ColLevel_2
+ aLevelStyle.SetFont( 2 );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_ROWLEVEL, 1 );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_COLLEVEL, 1 );
+ // RowLevel_3, ColLevel_3 ... RowLevel_7, ColLevel_7
+ aLevelStyle.SetFont( 0 );
+ for( sal_uInt8 nLevel = 2; nLevel < EXC_STYLE_LEVELCOUNT; ++nLevel )
+ {
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_ROWLEVEL, nLevel );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_COLLEVEL, nLevel );
+ }
+
+ // index 15: default hard cell format, placeholder to be able to add more built-in styles
+ maXFList.AppendNewRecord( new XclExpDefaultXF( GetRoot(), true ) );
+ maXFFindMap[ToFindKey(*maXFList.GetRecord(maXFList.GetSize()-1))].push_back(maXFList.GetSize()-1);
+ maBuiltInMap[ EXC_XF_DEFAULTCELL ].mbPredefined = true;
+
+ // index 16-20: other built-in styles
+ XclExpDefaultXF aFormatStyle( GetRoot(), false );
+ aFormatStyle.SetFont( 1 );
+ aFormatStyle.SetNumFmt( 43 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_COMMA );
+ aFormatStyle.SetNumFmt( 41 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_COMMA_0 );
+ aFormatStyle.SetNumFmt( 44 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_CURRENCY );
+ aFormatStyle.SetNumFmt( 42 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_CURRENCY_0 );
+ aFormatStyle.SetNumFmt( 9 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_PERCENT );
+
+ // other built-in style XF records (i.e. Hyperlink styles) are created on demand
+
+ /* Insert the real default hard cell format -> 0 is document default pattern.
+ Do it here (and not already above) to really have all built-in styles. */
+ Insert( nullptr, GetDefApiScript() );
+}
+
+void XclExpXFBuffer::AppendXFIndex( sal_uInt32 nXFId )
+{
+ OSL_ENSURE( nXFId < maXFIndexVec.size(), "XclExpXFBuffer::AppendXFIndex - XF ID out of range" );
+ maXFIndexVec[ nXFId ] = static_cast< sal_uInt16 >( maSortedXFList.GetSize() );
+ XclExpXFRef xXF = maXFList.GetRecord( nXFId );
+ AddBorderAndFill( *xXF );
+ maSortedXFList.AppendRecord( xXF );
+ OSL_ENSURE( maXFList.HasRecord( nXFId ), "XclExpXFBuffer::AppendXFIndex - XF not found" );
+}
+
+void XclExpXFBuffer::AddBorderAndFill( const XclExpXF& rXF )
+{
+ if( std::none_of( maBorders.begin(), maBorders.end(), XclExpBorderPred( rXF.GetBorderData() ) ) )
+ {
+ maBorders.push_back( rXF.GetBorderData() );
+ }
+
+ if( std::none_of( maFills.begin(), maFills.end(), XclExpFillPred( rXF.GetAreaData() ) ) )
+ {
+ maFills.push_back( rXF.GetAreaData() );
+ }
+}
+
+XclExpDxfs::XclExpDxfs( const XclExpRoot& rRoot )
+ : XclExpRoot( rRoot ),
+ mpKeywordTable( new NfKeywordTable )
+{
+ // Special number formatter for conversion.
+ SvNumberFormatterPtr xFormatter(new SvNumberFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US ));
+ xFormatter->FillKeywordTableForExcel( *mpKeywordTable );
+
+ SCTAB nTables = rRoot.GetDoc().GetTableCount();
+ sal_Int32 nDxfId = 0;
+ for(SCTAB nTab = 0; nTab < nTables; ++nTab)
+ {
+ // Color filters
+ std::vector<ScDBData*> pDBData = rRoot.GetDoc().GetDBCollection()->GetAllDBsFromTab(nTab);
+ for (auto& pData : pDBData)
+ {
+ ScRange aRange;
+ pData->GetArea(aRange);
+ for (auto nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); nCol++)
+ {
+ ScFilterEntries aFilterEntries;
+ rRoot.GetDoc().GetFilterEntriesArea(nCol, aRange.aStart.Row(),
+ aRange.aEnd.Row(), nTab, true, aFilterEntries);
+
+ // Excel has all filter values stored as foreground colors
+ // Does not matter it is text color or cell background color
+ for (auto& rColor : aFilterEntries.getBackgroundColors())
+ {
+ if (!maColorToDxfId.emplace(rColor, nDxfId).second)
+ continue;
+
+ std::unique_ptr<XclExpCellArea> pExpCellArea(new XclExpCellArea(rColor, 0));
+ maDxf.push_back(std::make_unique<XclExpDxf>(rRoot, std::move(pExpCellArea)));
+ nDxfId++;
+ }
+ for (auto& rColor : aFilterEntries.getTextColors())
+ {
+ if (!maColorToDxfId.emplace(rColor, nDxfId).second)
+ continue;
+
+ std::unique_ptr<XclExpCellArea> pExpCellArea(new XclExpCellArea(rColor, 0));
+ maDxf.push_back(std::make_unique<XclExpDxf>(rRoot, std::move(pExpCellArea)));
+ nDxfId++;
+ }
+ }
+ }
+
+ // Conditional formatting
+ ScConditionalFormatList* pList = rRoot.GetDoc().GetCondFormList(nTab);
+ if (pList)
+ {
+ for (const auto& rxItem : *pList)
+ {
+ size_t nEntryCount = rxItem->size();
+ for (size_t nFormatEntry = 0; nFormatEntry < nEntryCount; ++nFormatEntry)
+ {
+ const ScFormatEntry* pFormatEntry = rxItem->GetEntry(nFormatEntry);
+ if (!pFormatEntry
+ || (pFormatEntry->GetType() != ScFormatEntry::Type::Condition
+ && pFormatEntry->GetType() != ScFormatEntry::Type::Date
+ && pFormatEntry->GetType() != ScFormatEntry::Type::ExtCondition))
+ continue;
+
+ OUString aStyleName;
+ if (pFormatEntry->GetType() == ScFormatEntry::Type::Condition
+ || pFormatEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ {
+ const ScCondFormatEntry* pEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry);
+ aStyleName= pEntry->GetStyle();
+ }
+ else
+ {
+ const ScCondDateFormatEntry* pEntry = static_cast<const ScCondDateFormatEntry*>(pFormatEntry);
+ aStyleName = pEntry->GetStyleName();
+ }
+
+ if (maStyleNameToDxfId.emplace(aStyleName, nDxfId).second)
+ {
+ SfxStyleSheetBase* pStyle = rRoot.GetDoc().GetStyleSheetPool()->Find(aStyleName, SfxStyleFamily::Para);
+ if(!pStyle)
+ continue;
+
+ SfxItemSet& rSet = pStyle->GetItemSet();
+
+ std::unique_ptr<XclExpCellBorder> pBorder(new XclExpCellBorder);
+ if (!pBorder->FillFromItemSet( rSet, GetPalette(), GetBiff()) )
+ {
+ pBorder.reset();
+ }
+
+ std::unique_ptr<XclExpCellAlign> pAlign(new XclExpCellAlign);
+ if (!pAlign->FillFromItemSet(rRoot, rSet, false, GetBiff()))
+ {
+ pAlign.reset();
+ }
+
+ std::unique_ptr<XclExpCellProt> pCellProt(new XclExpCellProt);
+ if (!pCellProt->FillFromItemSet( rSet ))
+ {
+ pCellProt.reset();
+ }
+
+ std::unique_ptr<XclExpColor> pColor(new XclExpColor);
+ if(!pColor->FillFromItemSet( rSet ))
+ {
+ pColor.reset();
+ }
+
+ std::unique_ptr<XclExpDxfFont> pFont(new XclExpDxfFont(rRoot, rSet));
+
+ std::unique_ptr<XclExpNumFmt> pNumFormat;
+ if( const SfxUInt32Item *pPoolItem = rSet.GetItemIfSet( ATTR_VALUE_FORMAT ) )
+ {
+ sal_uInt32 nScNumFmt = pPoolItem->GetValue();
+ sal_Int32 nXclNumFmt = GetRoot().GetNumFmtBuffer().Insert(nScNumFmt);
+ pNumFormat.reset(new XclExpNumFmt( nScNumFmt, nXclNumFmt, GetNumberFormatCode( *this, nScNumFmt, xFormatter.get(), mpKeywordTable.get() )));
+ }
+
+ maDxf.push_back(std::make_unique<XclExpDxf>( rRoot, std::move(pAlign), std::move(pBorder),
+ std::move(pFont), std::move(pNumFormat), std::move(pCellProt), std::move(pColor) ));
+ ++nDxfId;
+ }
+
+ }
+ }
+ }
+ }
+}
+
+sal_Int32 XclExpDxfs::GetDxfId( const OUString& rStyleName ) const
+{
+ std::map<OUString, sal_Int32>::const_iterator itr = maStyleNameToDxfId.find(rStyleName);
+ if(itr!= maStyleNameToDxfId.end())
+ return itr->second;
+ return -1;
+}
+
+sal_Int32 XclExpDxfs::GetDxfByColor(Color aColor) const
+{
+ std::map<Color, sal_Int32>::const_iterator itr = maColorToDxfId.find(aColor);
+ if (itr != maColorToDxfId.end())
+ return itr->second;
+ return -1;
+}
+
+void XclExpDxfs::AddColor(Color aColor)
+{
+ maColorToDxfId.emplace(aColor, maDxf.size());
+
+ std::unique_ptr<XclExpCellArea> pExpCellArea(new XclExpCellArea(aColor, 0));
+ maDxf.push_back(std::make_unique<XclExpDxf>(GetRoot(), std::move(pExpCellArea)));
+}
+
+void XclExpDxfs::SaveXml( XclExpXmlStream& rStrm )
+{
+ if(maDxf.empty())
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_dxfs, XML_count, OString::number(maDxf.size()));
+
+ for ( auto& rxDxf : maDxf )
+ {
+ rxDxf->SaveXml( rStrm );
+ }
+
+ rStyleSheet->endElement( XML_dxfs );
+}
+
+XclExpDxf::XclExpDxf( const XclExpRoot& rRoot, std::unique_ptr<XclExpCellAlign> pAlign, std::unique_ptr<XclExpCellBorder> pBorder,
+ std::unique_ptr<XclExpDxfFont> pFont, std::unique_ptr<XclExpNumFmt> pNumberFmt, std::unique_ptr<XclExpCellProt> pProt,
+ std::unique_ptr<XclExpColor> pColor)
+ : XclExpRoot( rRoot ),
+ mpAlign(std::move(pAlign)),
+ mpBorder(std::move(pBorder)),
+ mpFont(std::move(pFont)),
+ mpNumberFmt(std::move(pNumberFmt)),
+ mpProt(std::move(pProt)),
+ mpColor(std::move(pColor))
+{
+}
+
+XclExpDxf::XclExpDxf(const XclExpRoot& rRoot, std::unique_ptr<XclExpCellArea> pCellArea)
+ : XclExpRoot(rRoot)
+ , mpCellArea(std::move(pCellArea))
+{
+}
+
+XclExpDxf::~XclExpDxf()
+{
+}
+
+void XclExpDxf::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_dxf);
+
+ if (mpFont)
+ mpFont->SaveXml(rStrm);
+ if (mpNumberFmt)
+ mpNumberFmt->SaveXml(rStrm);
+ if (mpColor)
+ mpColor->SaveXml(rStrm);
+ if (mpAlign)
+ mpAlign->SaveXml(rStrm);
+ if (mpBorder)
+ mpBorder->SaveXml(rStrm);
+ if (mpProt)
+ mpProt->SaveXml(rStrm);
+ if (mpCellArea)
+ mpCellArea->SaveXml(rStrm);
+ rStyleSheet->endElement( XML_dxf );
+}
+
+void XclExpDxf::SaveXmlExt( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElementNS( XML_x14, XML_dxf );
+
+ if (mpFont)
+ mpFont->SaveXml(rStrm);
+ if (mpNumberFmt)
+ mpNumberFmt->SaveXml(rStrm);
+ if (mpColor)
+ mpColor->SaveXml(rStrm);
+ if (mpAlign)
+ mpAlign->SaveXml(rStrm);
+ if (mpBorder)
+ mpBorder->SaveXml(rStrm);
+ if (mpProt)
+ mpProt->SaveXml(rStrm);
+ rStyleSheet->endElementNS( XML_x14, XML_dxf );
+}
+
+
+XclExpXmlStyleSheet::XclExpXmlStyleSheet( const XclExpRoot& rRoot )
+ : XclExpRoot( rRoot )
+{
+}
+
+void XclExpXmlStyleSheet::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr aStyleSheet = rStrm.CreateOutputStream(
+ "xl/styles.xml",
+ u"styles.xml",
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
+ oox::getRelationship(Relationship::STYLES));
+ rStrm.PushStream( aStyleSheet );
+
+ aStyleSheet->startElement(XML_styleSheet, XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)));
+
+ CreateRecord( EXC_ID_FORMATLIST )->SaveXml( rStrm );
+ CreateRecord( EXC_ID_FONTLIST )->SaveXml( rStrm );
+ CreateRecord( EXC_ID_XFLIST )->SaveXml( rStrm );
+ CreateRecord( EXC_ID_DXFS )->SaveXml( rStrm );
+ CreateRecord( EXC_ID_PALETTE )->SaveXml( rStrm );
+
+ aStyleSheet->endElement( XML_styleSheet );
+
+ rStrm.PopStream();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xetable.cxx b/sc/source/filter/excel/xetable.cxx
new file mode 100644
index 000000000..728ea2339
--- /dev/null
+++ b/sc/source/filter/excel/xetable.cxx
@@ -0,0 +1,2841 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xetable.hxx>
+
+#include <map>
+#include <numeric>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <scitems.hxx>
+#include <svl/intitem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/stritem.hxx>
+#include <tools/UnitConversion.hxx>
+#include <editeng/flditem.hxx>
+#include <document.hxx>
+#include <dociter.hxx>
+#include <olinetab.hxx>
+#include <formulacell.hxx>
+#include <patattr.hxx>
+#include <attrib.hxx>
+#include <xehelper.hxx>
+#include <xecontent.hxx>
+#include <xeescher.hxx>
+#include <xeextlst.hxx>
+#include <xeformula.hxx>
+#include <xlcontent.hxx>
+#include <xltools.hxx>
+#include <tokenarray.hxx>
+#include <formula/errorcodes.hxx>
+#include <comphelper/threadpool.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/export/utils.hxx>
+
+using namespace ::oox;
+
+namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+
+// Helper records for cell records
+
+XclExpStringRec::XclExpStringRec( const XclExpRoot& rRoot, const OUString& rResult ) :
+ XclExpRecord( EXC_ID3_STRING ),
+ mxResult( XclExpStringHelper::CreateString( rRoot, rResult ) )
+{
+ OSL_ENSURE( (rRoot.GetBiff() <= EXC_BIFF5) || (mxResult->Len() > 0),
+ "XclExpStringRec::XclExpStringRec - empty result not allowed in BIFF8+" );
+ SetRecSize( mxResult->GetSize() );
+}
+
+void XclExpStringRec::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << *mxResult;
+}
+
+// Additional records for special formula ranges ==============================
+
+XclExpRangeFmlaBase::XclExpRangeFmlaBase(
+ sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScAddress& rScPos ) :
+ XclExpRecord( nRecId, nRecSize ),
+ maXclRange( ScAddress::UNINITIALIZED ),
+ maBaseXclPos( ScAddress::UNINITIALIZED )
+{
+ maBaseXclPos.Set( static_cast< sal_uInt16 >( rScPos.Col() ), static_cast< sal_uInt16 >( rScPos.Row() ) );
+ maXclRange.maFirst = maXclRange.maLast = maBaseXclPos;
+}
+
+XclExpRangeFmlaBase::XclExpRangeFmlaBase(
+ sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScRange& rScRange ) :
+ XclExpRecord( nRecId, nRecSize ),
+ maXclRange( ScAddress::UNINITIALIZED ),
+ maBaseXclPos( ScAddress::UNINITIALIZED )
+{
+ maXclRange.Set(
+ static_cast< sal_uInt16 >( rScRange.aStart.Col() ),
+ static_cast< sal_uInt16 >( rScRange.aStart.Row() ),
+ static_cast< sal_uInt16 >( rScRange.aEnd.Col() ),
+ static_cast< sal_uInt16 >( rScRange.aEnd.Row() ) );
+ maBaseXclPos = maXclRange.maFirst;
+}
+
+bool XclExpRangeFmlaBase::IsBasePos( sal_uInt16 nXclCol, sal_uInt32 nXclRow ) const
+{
+ return (maBaseXclPos.mnCol == nXclCol) && (maBaseXclPos.mnRow == nXclRow);
+}
+
+void XclExpRangeFmlaBase::Extend( const ScAddress& rScPos )
+{
+ sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
+ sal_uInt32 nXclRow = static_cast< sal_uInt32 >( rScPos.Row() );
+ maXclRange.maFirst.mnCol = ::std::min( maXclRange.maFirst.mnCol, nXclCol );
+ maXclRange.maFirst.mnRow = ::std::min( maXclRange.maFirst.mnRow, nXclRow );
+ maXclRange.maLast.mnCol = ::std::max( maXclRange.maLast.mnCol, nXclCol );
+ maXclRange.maLast.mnRow = ::std::max( maXclRange.maLast.mnRow, nXclRow );
+}
+
+void XclExpRangeFmlaBase::WriteRangeAddress( XclExpStream& rStrm ) const
+{
+ maXclRange.Write( rStrm, false );
+}
+
+// Array formulas =============================================================
+
+XclExpArray::XclExpArray( const XclTokenArrayRef& xTokArr, const ScRange& rScRange ) :
+ XclExpRangeFmlaBase( EXC_ID3_ARRAY, 14 + xTokArr->GetSize(), rScRange ),
+ mxTokArr( xTokArr )
+{
+}
+
+XclTokenArrayRef XclExpArray::CreateCellTokenArray( const XclExpRoot& rRoot ) const
+{
+ return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
+}
+
+bool XclExpArray::IsVolatile() const
+{
+ return mxTokArr->IsVolatile();
+}
+
+void XclExpArray::WriteBody( XclExpStream& rStrm )
+{
+ WriteRangeAddress( rStrm );
+ sal_uInt16 nFlags = EXC_ARRAY_DEFAULTFLAGS;
+ ::set_flag( nFlags, EXC_ARRAY_RECALC_ALWAYS, IsVolatile() );
+ rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
+}
+
+XclExpArrayBuffer::XclExpArrayBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpArrayRef XclExpArrayBuffer::CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange )
+{
+ const ScAddress& rScPos = rScRange.aStart;
+ XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_MATRIX, rScTokArr, &rScPos );
+
+ OSL_ENSURE( maRecMap.find( rScPos ) == maRecMap.end(), "XclExpArrayBuffer::CreateArray - array exists already" );
+ XclExpArrayRef& rxRec = maRecMap[ rScPos ];
+ rxRec = new XclExpArray( xTokArr, rScRange );
+ return rxRec;
+}
+
+XclExpArrayRef XclExpArrayBuffer::FindArray( const ScTokenArray& rScTokArr, const ScAddress& rBasePos ) const
+{
+ XclExpArrayRef xRec;
+ // try to extract a matrix reference token
+ if (rScTokArr.GetLen() != 1)
+ // Must consist of a single reference token.
+ return xRec;
+
+ const formula::FormulaToken* pToken = rScTokArr.GetArray()[0];
+ if (!pToken || pToken->GetOpCode() != ocMatRef)
+ // not a matrix reference token.
+ return xRec;
+
+ const ScSingleRefData& rRef = *pToken->GetSingleRef();
+ ScAddress aAbsPos = rRef.toAbs(GetRoot().GetDoc(), rBasePos);
+ XclExpArrayMap::const_iterator it = maRecMap.find(aAbsPos);
+
+ if (it != maRecMap.end())
+ xRec = it->second;
+ return xRec;
+}
+
+// Shared formulas ============================================================
+
+XclExpShrfmla::XclExpShrfmla( const XclTokenArrayRef& xTokArr, const ScAddress& rScPos ) :
+ XclExpRangeFmlaBase( EXC_ID_SHRFMLA, 10 + xTokArr->GetSize(), rScPos ),
+ mxTokArr( xTokArr ),
+ mnUsedCount( 1 )
+{
+}
+
+void XclExpShrfmla::ExtendRange( const ScAddress& rScPos )
+{
+ Extend( rScPos );
+ ++mnUsedCount;
+}
+
+XclTokenArrayRef XclExpShrfmla::CreateCellTokenArray( const XclExpRoot& rRoot ) const
+{
+ return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
+}
+
+bool XclExpShrfmla::IsVolatile() const
+{
+ return mxTokArr->IsVolatile();
+}
+
+void XclExpShrfmla::WriteBody( XclExpStream& rStrm )
+{
+ WriteRangeAddress( rStrm );
+ rStrm << sal_uInt8( 0 ) << mnUsedCount << *mxTokArr;
+}
+
+XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+bool XclExpShrfmlaBuffer::IsValidTokenArray( const ScTokenArray& rArray ) const
+{
+ using namespace formula;
+
+ FormulaToken** pTokens = rArray.GetArray();
+ sal_uInt16 nLen = rArray.GetLen();
+ for (sal_uInt16 i = 0; i < nLen; ++i)
+ {
+ const FormulaToken* p = pTokens[i];
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ const ScSingleRefData& rRefData = *p->GetSingleRef();
+ if (!GetFormulaCompiler().IsRef2D(rRefData))
+ // Excel's shared formula cannot include 3D reference.
+ return false;
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRefData = *p->GetDoubleRef();
+ if (!GetFormulaCompiler().IsRef2D(rRefData))
+ // Excel's shared formula cannot include 3D reference.
+ return false;
+ }
+ break;
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svExternalName:
+ // External references aren't allowed.
+ return false;
+ default:
+ ;
+ }
+ }
+ return true;
+}
+
+XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
+ const ScFormulaCell& rScCell, const ScAddress& rScPos )
+{
+ XclExpShrfmlaRef xRec;
+ const ScTokenArray* pShrdScTokArr = rScCell.GetSharedCode();
+ if (!pShrdScTokArr)
+ // This formula cell is not shared formula cell.
+ return xRec;
+
+ // Check to see if this shared formula contains any tokens that Excel's shared formula cannot handle.
+ if (maBadTokens.count(pShrdScTokArr) > 0)
+ // Already on the black list. Skip it.
+ return xRec;
+
+ if (!IsValidTokenArray(*pShrdScTokArr))
+ {
+ // We can't export this as shared formula.
+ maBadTokens.insert(pShrdScTokArr);
+ return xRec;
+ }
+
+ TokensType::iterator aIt = maRecMap.find(pShrdScTokArr);
+ if( aIt == maRecMap.end() )
+ {
+ // create a new record
+ XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_SHARED, *pShrdScTokArr, &rScPos );
+ xRec = new XclExpShrfmla( xTokArr, rScPos );
+ maRecMap[ pShrdScTokArr ] = xRec;
+ }
+ else
+ {
+ // extend existing record
+ OSL_ENSURE( aIt->second, "XclExpShrfmlaBuffer::CreateOrExtendShrfmla - missing record" );
+ xRec = aIt->second;
+ xRec->ExtendRange( rScPos );
+ }
+
+ return xRec;
+}
+
+// Multiple operations ========================================================
+
+XclExpTableop::XclExpTableop( const ScAddress& rScPos,
+ const XclMultipleOpRefs& rRefs, sal_uInt8 nScMode ) :
+ XclExpRangeFmlaBase( EXC_ID3_TABLEOP, 16, rScPos ),
+ mnLastAppXclCol( static_cast< sal_uInt16 >( rScPos.Col() ) ),
+ mnColInpXclCol( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Col() ) ),
+ mnColInpXclRow( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Row() ) ),
+ mnRowInpXclCol( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Col() ) ),
+ mnRowInpXclRow( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Row() ) ),
+ mnScMode( nScMode ),
+ mbValid( false )
+{
+}
+
+bool XclExpTableop::TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
+{
+ sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
+ sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() );
+
+ bool bOk = IsAppendable( nXclCol, nXclRow );
+ if( bOk )
+ {
+ SCCOL nFirstScCol = static_cast< SCCOL >( maXclRange.maFirst.mnCol );
+ SCROW nFirstScRow = static_cast< SCROW >( maXclRange.maFirst.mnRow );
+ SCCOL nColInpScCol = static_cast< SCCOL >( mnColInpXclCol );
+ SCROW nColInpScRow = static_cast< SCROW >( mnColInpXclRow );
+ SCCOL nRowInpScCol = static_cast< SCCOL >( mnRowInpXclCol );
+ SCROW nRowInpScRow = static_cast< SCROW >( mnRowInpXclRow );
+
+ bOk = ((mnScMode == 2) == rRefs.mbDblRefMode) &&
+ (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
+ (nColInpScCol == rRefs.maColFirstScPos.Col()) &&
+ (nColInpScRow == rRefs.maColFirstScPos.Row()) &&
+ (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
+ (rScPos.Tab() == rRefs.maColRelScPos.Tab());
+
+ if( bOk ) switch( mnScMode )
+ {
+ case 0:
+ bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
+ (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
+ (nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row());
+ break;
+ case 1:
+ bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
+ (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
+ (nFirstScRow == rRefs.maColRelScPos.Row() + 1);
+ break;
+ case 2:
+ bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
+ (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
+ (nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
+ (nRowInpScCol == rRefs.maRowFirstScPos.Col()) &&
+ (nRowInpScRow == rRefs.maRowFirstScPos.Row()) &&
+ (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
+ (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
+ (nFirstScRow == rRefs.maRowRelScPos.Row() + 1) &&
+ (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
+ break;
+ default:
+ bOk = false;
+ }
+
+ if( bOk )
+ {
+ // extend the cell range
+ OSL_ENSURE( IsAppendable( nXclCol, nXclRow ), "XclExpTableop::TryExtend - wrong cell address" );
+ Extend( rScPos );
+ mnLastAppXclCol = nXclCol;
+ }
+ }
+
+ return bOk;
+}
+
+void XclExpTableop::Finalize()
+{
+ // is the range complete? (last appended cell is in last column)
+ mbValid = maXclRange.maLast.mnCol == mnLastAppXclCol;
+ // if last row is incomplete, try to shorten the used range
+ if( !mbValid && (maXclRange.maFirst.mnRow < maXclRange.maLast.mnRow) )
+ {
+ --maXclRange.maLast.mnRow;
+ mbValid = true;
+ }
+
+ // check if referred cells are outside of own range
+ if( !mbValid )
+ return;
+
+ switch( mnScMode )
+ {
+ case 0:
+ mbValid = (mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
+ (mnColInpXclRow < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
+ break;
+ case 1:
+ mbValid = (mnColInpXclCol < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
+ (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
+ break;
+ case 2:
+ mbValid = ((mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
+ (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow)) &&
+ ((mnRowInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnRowInpXclCol > maXclRange.maLast.mnCol) ||
+ (mnRowInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnRowInpXclRow > maXclRange.maLast.mnRow));
+ break;
+ }
+}
+
+XclTokenArrayRef XclExpTableop::CreateCellTokenArray( const XclExpRoot& rRoot ) const
+{
+ XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
+ return mbValid ?
+ rFmlaComp.CreateSpecialRefFormula( EXC_TOKID_TBL, maBaseXclPos ) :
+ rFmlaComp.CreateErrorFormula( EXC_ERR_NA );
+}
+
+bool XclExpTableop::IsVolatile() const
+{
+ return true;
+}
+
+void XclExpTableop::Save( XclExpStream& rStrm )
+{
+ if( mbValid )
+ XclExpRangeFmlaBase::Save( rStrm );
+}
+
+bool XclExpTableop::IsAppendable( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const
+{
+ return ((nXclCol == mnLastAppXclCol + 1) && (nXclRow == maXclRange.maFirst.mnRow)) ||
+ ((nXclCol == mnLastAppXclCol + 1) && (nXclCol <= maXclRange.maLast.mnCol) && (nXclRow == maXclRange.maLast.mnRow)) ||
+ ((mnLastAppXclCol == maXclRange.maLast.mnCol) && (nXclCol == maXclRange.maFirst.mnCol) && (nXclRow == maXclRange.maLast.mnRow + 1));
+}
+
+void XclExpTableop::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nFlags = EXC_TABLEOP_DEFAULTFLAGS;
+ ::set_flag( nFlags, EXC_TABLEOP_RECALC_ALWAYS, IsVolatile() );
+ switch( mnScMode )
+ {
+ case 1: ::set_flag( nFlags, EXC_TABLEOP_ROW ); break;
+ case 2: ::set_flag( nFlags, EXC_TABLEOP_BOTH ); break;
+ }
+
+ WriteRangeAddress( rStrm );
+ rStrm << nFlags;
+ if( mnScMode == 2 )
+ rStrm << mnRowInpXclRow << mnRowInpXclCol << mnColInpXclRow << mnColInpXclCol;
+ else
+ rStrm << mnColInpXclRow << mnColInpXclCol << sal_uInt32( 0 );
+}
+
+XclExpTableopBuffer::XclExpTableopBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpTableopRef XclExpTableopBuffer::CreateOrExtendTableop(
+ const ScTokenArray& rScTokArr, const ScAddress& rScPos )
+{
+ XclExpTableopRef xRec;
+
+ // try to extract cell references of a multiple operations formula
+ XclMultipleOpRefs aRefs;
+ if (XclTokenArrayHelper::GetMultipleOpRefs(GetDoc(), aRefs, rScTokArr, rScPos))
+ {
+ // try to find an existing TABLEOP record for this cell position
+ for( size_t nPos = 0, nSize = maTableopList.GetSize(); !xRec && (nPos < nSize); ++nPos )
+ {
+ XclExpTableop* xTempRec = maTableopList.GetRecord( nPos );
+ if( xTempRec->TryExtend( rScPos, aRefs ) )
+ xRec = xTempRec;
+ }
+
+ // no record found, or found record not extensible
+ if( !xRec )
+ xRec = TryCreate( rScPos, aRefs );
+ }
+
+ return xRec;
+}
+
+void XclExpTableopBuffer::Finalize()
+{
+ for( size_t nPos = 0, nSize = maTableopList.GetSize(); nPos < nSize; ++nPos )
+ maTableopList.GetRecord( nPos )->Finalize();
+}
+
+XclExpTableopRef XclExpTableopBuffer::TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
+{
+ sal_uInt8 nScMode = 0;
+ bool bOk = (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
+ (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
+ (rScPos.Tab() == rRefs.maColRelScPos.Tab());
+
+ if( bOk )
+ {
+ if( rRefs.mbDblRefMode )
+ {
+ nScMode = 2;
+ bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
+ (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
+ (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
+ (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
+ (rScPos.Row() == rRefs.maRowRelScPos.Row() + 1) &&
+ (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
+ }
+ else if( (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
+ (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
+ (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row()) )
+ {
+ nScMode = 0;
+ }
+ else if( (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
+ (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row() + 1) )
+ {
+ nScMode = 1;
+ }
+ else
+ {
+ bOk = false;
+ }
+ }
+
+ XclExpTableopRef xRec;
+ if( bOk )
+ {
+ xRec = new XclExpTableop( rScPos, rRefs, nScMode );
+ maTableopList.AppendRecord( xRec );
+ }
+
+ return xRec;
+}
+
+// Cell records
+
+XclExpCellBase::XclExpCellBase(
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos ) :
+ XclExpRecord( nRecId, nContSize + 4 ),
+ maXclPos( rXclPos )
+{
+}
+
+bool XclExpCellBase::IsMultiLineText() const
+{
+ return false;
+}
+
+bool XclExpCellBase::TryMerge( const XclExpCellBase& /*rCell*/ )
+{
+ return false;
+}
+
+void XclExpCellBase::GetBlankXFIndexes( ScfUInt16Vec& /*rXFIndexes*/ ) const
+{
+ // default: do nothing
+}
+
+void XclExpCellBase::RemoveUnusedBlankCells( const ScfUInt16Vec& /*rXFIndexes*/, size_t /*nStartAllNotFound*/ )
+{
+ // default: do nothing
+}
+
+// Single cell records ========================================================
+
+XclExpSingleCellBase::XclExpSingleCellBase(
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos, sal_uInt32 nXFId ) :
+ XclExpCellBase( nRecId, 2, rXclPos ),
+ maXFId( nXFId ),
+ mnContSize( nContSize )
+{
+}
+
+XclExpSingleCellBase::XclExpSingleCellBase( const XclExpRoot& rRoot,
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForcedXFId ) :
+ XclExpCellBase( nRecId, 2, rXclPos ),
+ maXFId( nForcedXFId ),
+ mnContSize( nContSize )
+{
+ if( GetXFId() == EXC_XFID_NOTFOUND )
+ SetXFId( rRoot.GetXFBuffer().Insert( pPattern, nScript ) );
+}
+
+sal_uInt16 XclExpSingleCellBase::GetLastXclCol() const
+{
+ return GetXclCol();
+}
+
+sal_uInt32 XclExpSingleCellBase::GetFirstXFId() const
+{
+ return GetXFId();
+}
+
+bool XclExpSingleCellBase::IsEmpty() const
+{
+ return false;
+}
+
+void XclExpSingleCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
+{
+ maXFId.ConvertXFIndex( rRoot );
+}
+
+void XclExpSingleCellBase::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
+ AddRecSize( mnContSize );
+ XclExpCellBase::Save( rStrm );
+}
+
+void XclExpSingleCellBase::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16> (GetXclRow()) << GetXclCol() << maXFId.mnXFIndex;
+ WriteContents( rStrm );
+}
+
+XclExpNumberCell::XclExpNumberCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, double fValue ) :
+ // #i41210# always use latin script for number cells - may look wrong for special number formats...
+ XclExpSingleCellBase( rRoot, EXC_ID3_NUMBER, 8, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
+ mfValue( fValue )
+{
+}
+
+static OString lcl_GetStyleId( const XclExpXmlStream& rStrm, sal_uInt32 nXFIndex )
+{
+ return OString::number( rStrm.GetRoot().GetXFBuffer()
+ .GetXmlCellIndex( nXFIndex ) );
+}
+
+static OString lcl_GetStyleId( const XclExpXmlStream& rStrm, const XclExpCellBase& rCell )
+{
+ sal_uInt32 nXFId = rCell.GetFirstXFId();
+ sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( nXFId );
+ return lcl_GetStyleId( rStrm, nXFIndex );
+}
+
+void XclExpNumberCell::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, *this),
+ XML_t, "n"
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+ rWorksheet->startElement(XML_v);
+ rWorksheet->write( mfValue );
+ rWorksheet->endElement( XML_v );
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpNumberCell::WriteContents( XclExpStream& rStrm )
+{
+ rStrm << mfValue;
+}
+
+XclExpBooleanCell::XclExpBooleanCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, bool bValue ) :
+ // #i41210# always use latin script for boolean cells
+ XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
+ mbValue( bValue )
+{
+}
+
+void XclExpBooleanCell::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, *this),
+ XML_t, "b"
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+ rWorksheet->startElement( XML_v );
+ rWorksheet->write( mbValue ? "1" : "0" );
+ rWorksheet->endElement( XML_v );
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpBooleanCell::WriteContents( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16( mbValue ? 1 : 0 ) << EXC_BOOLERR_BOOL;
+}
+
+XclExpLabelCell::XclExpLabelCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, const OUString& rStr ) :
+ XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
+{
+ sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
+ XclExpStringRef xText = XclExpStringHelper::CreateCellString(
+ rRoot, rStr, pPattern, XclStrFlags::NONE, nMaxLen);
+ Init( rRoot, pPattern, xText );
+}
+
+XclExpLabelCell::XclExpLabelCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ const EditTextObject* pEditText, XclExpHyperlinkHelper& rLinkHelper ) :
+ XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
+{
+ sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
+
+ XclExpStringRef xText;
+ if (pEditText)
+ xText = XclExpStringHelper::CreateCellString(
+ rRoot, *pEditText, pPattern, rLinkHelper, XclStrFlags::NONE, nMaxLen);
+ else
+ xText = XclExpStringHelper::CreateCellString(
+ rRoot, OUString(), pPattern, XclStrFlags::NONE, nMaxLen);
+
+ Init( rRoot, pPattern, xText );
+}
+
+bool XclExpLabelCell::IsMultiLineText() const
+{
+ return mbLineBreak || mxText->IsWrapped();
+}
+
+void XclExpLabelCell::Init( const XclExpRoot& rRoot,
+ const ScPatternAttr* pPattern, XclExpStringRef const & xText )
+{
+ OSL_ENSURE( xText && xText->Len(), "XclExpLabelCell::XclExpLabelCell - empty string passed" );
+ mxText = xText;
+ mnSstIndex = 0;
+
+ const XclFormatRunVec& rFormats = mxText->GetFormats();
+ // remove formatting of the leading run if the entire string
+ // is equally formatted
+ sal_uInt16 nXclFont = EXC_FONT_NOTFOUND;
+ if( rFormats.size() == 1 )
+ nXclFont = mxText->RemoveLeadingFont();
+ else
+ nXclFont = mxText->GetLeadingFont();
+
+ // create cell format
+ if( GetXFId() == EXC_XFID_NOTFOUND )
+ {
+ OSL_ENSURE( nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found" );
+ bool bForceLineBreak = mxText->IsWrapped();
+ SetXFId( rRoot.GetXFBuffer().InsertWithFont( pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak ) );
+ }
+
+ // get auto-wrap attribute from cell format
+ const XclExpXF* pXF = rRoot.GetXFBuffer().GetXFById( GetXFId() );
+ mbLineBreak = pXF && pXF->GetAlignmentData().mbLineBreak;
+
+ // initialize the record contents
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF5:
+ // BIFF5-BIFF7: create a LABEL or RSTRING record
+ OSL_ENSURE( mxText->Len() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::XclExpLabelCell - string too long" );
+ SetContSize( mxText->GetSize() );
+ // formatted string is exported in an RSTRING record
+ if( mxText->IsRich() )
+ {
+ OSL_ENSURE( mxText->GetFormatsCount() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::WriteContents - too many formats" );
+ mxText->LimitFormatCount( EXC_LABEL_MAXLEN );
+ SetRecId( EXC_ID_RSTRING );
+ SetContSize( GetContSize() + 1 + 2 * mxText->GetFormatsCount() );
+ }
+ break;
+ case EXC_BIFF8:
+ // BIFF8+: create a LABELSST record
+ mnSstIndex = rRoot.GetSst().Insert( xText );
+ SetRecId( EXC_ID_LABELSST );
+ SetContSize( 4 );
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpLabelCell::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, *this),
+ XML_t, "s"
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+ rWorksheet->startElement( XML_v );
+ rWorksheet->write( static_cast<sal_Int32>(mnSstIndex) );
+ rWorksheet->endElement( XML_v );
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpLabelCell::WriteContents( XclExpStream& rStrm )
+{
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF5:
+ rStrm << *mxText;
+ if( mxText->IsRich() )
+ {
+ rStrm << static_cast< sal_uInt8 >( mxText->GetFormatsCount() );
+ mxText->WriteFormats( rStrm );
+ }
+ break;
+ case EXC_BIFF8:
+ rStrm << mnSstIndex;
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+XclExpFormulaCell::XclExpFormulaCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ const ScFormulaCell& rScFmlaCell,
+ XclExpArrayBuffer& rArrayBfr,
+ XclExpShrfmlaBuffer& rShrfmlaBfr,
+ XclExpTableopBuffer& rTableopBfr ) :
+ XclExpSingleCellBase( EXC_ID2_FORMULA, 0, rXclPos, nForcedXFId ),
+ mrScFmlaCell( const_cast< ScFormulaCell& >( rScFmlaCell ) )
+{
+ // *** Find result number format overwriting cell number format *** -------
+
+ if( GetXFId() == EXC_XFID_NOTFOUND )
+ {
+ SvNumberFormatter& rFormatter = rRoot.GetFormatter();
+ XclExpNumFmtBuffer& rNumFmtBfr = rRoot.GetNumFmtBuffer();
+
+ // current cell number format
+ sal_uInt32 nScNumFmt = pPattern ?
+ pPattern->GetItemSet().Get( ATTR_VALUE_FORMAT ).GetValue() :
+ rNumFmtBfr.GetStandardFormat();
+
+ // alternative number format passed to XF buffer
+ sal_uInt32 nAltScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ /* Xcl doesn't know Boolean number formats, we write
+ "TRUE";"FALSE" (language dependent). Don't do it for automatic
+ formula formats, because Excel gets them right. */
+ /* #i8640# Don't set text format, if we have string results. */
+ SvNumFormatType nFormatType = mrScFmlaCell.GetFormatType();
+ if( ((nScNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0) &&
+ (nFormatType != SvNumFormatType::LOGICAL) &&
+ (nFormatType != SvNumFormatType::TEXT) )
+ nAltScNumFmt = nScNumFmt;
+ /* If cell number format is Boolean and automatic formula
+ format is Boolean don't write that ugly special format. */
+ else if( (nFormatType == SvNumFormatType::LOGICAL) &&
+ (rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL) )
+ nAltScNumFmt = rNumFmtBfr.GetStandardFormat();
+
+ // #i41420# find script type according to result type (always latin for numeric results)
+ sal_Int16 nScript = ApiScriptType::LATIN;
+ bool bForceLineBreak = false;
+ if( nFormatType == SvNumFormatType::TEXT )
+ {
+ OUString aResult = mrScFmlaCell.GetString().getString();
+ bForceLineBreak = mrScFmlaCell.IsMultilineResult();
+ nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult );
+ }
+ SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) );
+ }
+
+ // *** Convert the formula token array *** --------------------------------
+
+ ScAddress aScPos( static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), rRoot.GetCurrScTab() );
+ const ScTokenArray& rScTokArr = *mrScFmlaCell.GetCode();
+
+ // first try to create multiple operations
+ mxAddRec = rTableopBfr.CreateOrExtendTableop( rScTokArr, aScPos );
+
+ // no multiple operation found - try to create matrix formula
+ if( !mxAddRec )
+ switch( mrScFmlaCell.GetMatrixFlag() )
+ {
+ case ScMatrixMode::Formula:
+ {
+ // origin of the matrix - find the used matrix range
+ SCCOL nMatWidth;
+ SCROW nMatHeight;
+ mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
+ OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
+ ScRange aMatScRange( aScPos );
+ ScAddress& rMatEnd = aMatScRange.aEnd;
+ rMatEnd.IncCol( static_cast< SCCOL >( nMatWidth - 1 ) );
+ rMatEnd.IncRow( static_cast< SCROW >( nMatHeight - 1 ) );
+ // reduce to valid range (range keeps valid, because start position IS valid)
+ rRoot.GetAddressConverter().ValidateRange( aMatScRange, true );
+ // create the ARRAY record
+ mxAddRec = rArrayBfr.CreateArray( rScTokArr, aMatScRange );
+ }
+ break;
+ case ScMatrixMode::Reference:
+ {
+ // other formula cell covered by a matrix - find the ARRAY record
+ mxAddRec = rArrayBfr.FindArray(rScTokArr, aScPos);
+ // should always be found, if Calc document is not broken
+ OSL_ENSURE( mxAddRec, "XclExpFormulaCell::XclExpFormulaCell - no matrix found" );
+ }
+ break;
+ default:;
+ }
+
+ // no matrix found - try to create shared formula
+ if( !mxAddRec )
+ mxAddRec = rShrfmlaBfr.CreateOrExtendShrfmla(mrScFmlaCell, aScPos);
+
+ // no shared formula found - create a simple cell formula
+ if( !mxAddRec )
+ mxTokArr = rRoot.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL, rScTokArr, &aScPos );
+}
+
+void XclExpFormulaCell::Save( XclExpStream& rStrm )
+{
+ // create token array for FORMULA cells with additional record
+ if( mxAddRec )
+ mxTokArr = mxAddRec->CreateCellTokenArray( rStrm.GetRoot() );
+
+ // FORMULA record itself
+ OSL_ENSURE( mxTokArr, "XclExpFormulaCell::Save - missing token array" );
+ if( !mxTokArr )
+ mxTokArr = rStrm.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA );
+ SetContSize( 16 + mxTokArr->GetSize() );
+ XclExpSingleCellBase::Save( rStrm );
+
+ // additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record
+ if( mxAddRec && mxAddRec->IsBasePos( GetXclCol(), GetXclRow() ) )
+ mxAddRec->Save( rStrm );
+
+ // STRING record for string result
+ if( mxStringRec )
+ mxStringRec->Save( rStrm );
+}
+
+void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
+{
+ const char* sType = nullptr;
+ OUString sValue;
+ XclXmlUtils::GetFormulaTypeAndValue( mrScFmlaCell, sType, sValue );
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, *this),
+ XML_t, sType
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+
+ bool bWriteFormula = true;
+ bool bTagStarted = false;
+ ScAddress aScPos( static_cast< SCCOL >( GetXclPos().mnCol ),
+ static_cast< SCROW >( GetXclPos().mnRow ), rStrm.GetRoot().GetCurrScTab() );
+
+ switch (mrScFmlaCell.GetMatrixFlag())
+ {
+ case ScMatrixMode::NONE:
+ break;
+ case ScMatrixMode::Reference:
+ bWriteFormula = false;
+ break;
+ case ScMatrixMode::Formula:
+ {
+ // origin of the matrix - find the used matrix range
+ SCCOL nMatWidth;
+ SCROW nMatHeight;
+ mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
+ OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
+ ScRange aMatScRange( aScPos );
+ ScAddress& rMatEnd = aMatScRange.aEnd;
+ rMatEnd.IncCol( static_cast< SCCOL >( nMatWidth - 1 ) );
+ rMatEnd.IncRow( static_cast< SCROW >( nMatHeight - 1 ) );
+ // reduce to valid range (range keeps valid, because start position IS valid
+ rStrm.GetRoot().GetAddressConverter().ValidateRange( aMatScRange, true );
+
+ OStringBuffer sFmlaCellRange;
+ if (rStrm.GetRoot().GetDoc().ValidRange(aMatScRange))
+ {
+ // calculate the cell range.
+ sFmlaCellRange.append( XclXmlUtils::ToOString(
+ rStrm.GetRoot().GetStringBuf(), aMatScRange.aStart ).getStr());
+ sFmlaCellRange.append(":");
+ sFmlaCellRange.append( XclXmlUtils::ToOString(
+ rStrm.GetRoot().GetStringBuf(), aMatScRange.aEnd ).getStr());
+ }
+
+ if ( aMatScRange.aStart.Col() == GetXclPos().mnCol &&
+ aMatScRange.aStart.Row() == static_cast<SCROW>(GetXclPos().mnRow))
+ {
+ rWorksheet->startElement( XML_f,
+ XML_aca, ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
+ (mxAddRec && mxAddRec->IsVolatile())),
+ XML_t, mxAddRec ? "array" : nullptr,
+ XML_ref, !sFmlaCellRange.isEmpty()? sFmlaCellRange.getStr() : nullptr
+ // OOXTODO: XML_dt2D, bool
+ // OOXTODO: XML_dtr, bool
+ // OOXTODO: XML_del1, bool
+ // OOXTODO: XML_del2, bool
+ // OOXTODO: XML_r1, ST_CellRef
+ // OOXTODO: XML_r2, ST_CellRef
+ // OOXTODO: XML_ca, bool
+ // OOXTODO: XML_si, uint
+ // OOXTODO: XML_bx bool
+ );
+ bTagStarted = true;
+ }
+ }
+ break;
+ }
+
+ if (bWriteFormula)
+ {
+ if (!bTagStarted)
+ {
+ rWorksheet->startElement( XML_f,
+ XML_aca, ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
+ (mxAddRec && mxAddRec->IsVolatile()) ) );
+ }
+ rWorksheet->writeEscaped( XclXmlUtils::ToOUString(
+ rStrm.GetRoot().GetCompileFormulaContext(), mrScFmlaCell.aPos, mrScFmlaCell.GetCode(),
+ mrScFmlaCell.GetErrCode()));
+ rWorksheet->endElement( XML_f );
+ }
+
+ if( strcmp( sType, "inlineStr" ) == 0 )
+ {
+ rWorksheet->startElement(XML_is);
+ rWorksheet->startElement(XML_t);
+ rWorksheet->writeEscaped( sValue );
+ rWorksheet->endElement( XML_t );
+ rWorksheet->endElement( XML_is );
+ }
+ else
+ {
+ rWorksheet->startElement(XML_v);
+ rWorksheet->writeEscaped( sValue );
+ rWorksheet->endElement( XML_v );
+ }
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpFormulaCell::WriteContents( XclExpStream& rStrm )
+{
+ FormulaError nScErrCode = mrScFmlaCell.GetErrCode();
+ if( nScErrCode != FormulaError::NONE )
+ {
+ rStrm << EXC_FORMULA_RES_ERROR << sal_uInt8( 0 )
+ << XclTools::GetXclErrorCode( nScErrCode )
+ << sal_uInt8( 0 ) << sal_uInt16( 0 )
+ << sal_uInt16( 0xFFFF );
+ }
+ else
+ {
+ // result of the formula
+ switch( mrScFmlaCell.GetFormatType() )
+ {
+ case SvNumFormatType::NUMBER:
+ {
+ // either value or error code
+ rStrm << mrScFmlaCell.GetValue();
+ }
+ break;
+
+ case SvNumFormatType::TEXT:
+ {
+ OUString aResult = mrScFmlaCell.GetString().getString();
+ if( !aResult.isEmpty() || (rStrm.GetRoot().GetBiff() <= EXC_BIFF5) )
+ {
+ rStrm << EXC_FORMULA_RES_STRING;
+ mxStringRec = new XclExpStringRec( rStrm.GetRoot(), aResult );
+ }
+ else
+ rStrm << EXC_FORMULA_RES_EMPTY; // BIFF8 only
+ rStrm << sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
+ }
+ break;
+
+ case SvNumFormatType::LOGICAL:
+ {
+ sal_uInt8 nXclValue = (mrScFmlaCell.GetValue() == 0.0) ? 0 : 1;
+ rStrm << EXC_FORMULA_RES_BOOL << sal_uInt8( 0 )
+ << nXclValue << sal_uInt8( 0 ) << sal_uInt16( 0 )
+ << sal_uInt16( 0xFFFF );
+ }
+ break;
+
+ default:
+ rStrm << mrScFmlaCell.GetValue();
+ }
+ }
+
+ // flags and formula token array
+ sal_uInt16 nFlags = EXC_FORMULA_DEFAULTFLAGS;
+ ::set_flag( nFlags, EXC_FORMULA_RECALC_ALWAYS, mxTokArr->IsVolatile() || (mxAddRec && mxAddRec->IsVolatile()) );
+ ::set_flag( nFlags, EXC_FORMULA_SHARED, mxAddRec && (mxAddRec->GetRecId() == EXC_ID_SHRFMLA) );
+ rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
+}
+
+// Multiple cell records ======================================================
+
+XclExpMultiCellBase::XclExpMultiCellBase(
+ sal_uInt16 nRecId, sal_uInt16 nMulRecId, std::size_t nContSize, const XclAddress& rXclPos ) :
+ XclExpCellBase( nRecId, 0, rXclPos ),
+ mnMulRecId( nMulRecId ),
+ mnContSize( nContSize )
+{
+}
+
+sal_uInt16 XclExpMultiCellBase::GetLastXclCol() const
+{
+ return GetXclCol() + GetCellCount() - 1;
+}
+
+sal_uInt32 XclExpMultiCellBase::GetFirstXFId() const
+{
+ return maXFIds.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds.front().mnXFId;
+}
+
+bool XclExpMultiCellBase::IsEmpty() const
+{
+ return maXFIds.empty();
+}
+
+void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
+{
+ for( auto& rXFId : maXFIds )
+ rXFId.ConvertXFIndex( rRoot );
+}
+
+void XclExpMultiCellBase::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
+
+ XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
+ XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
+ XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
+ sal_uInt16 nBegXclCol = GetXclCol();
+ sal_uInt16 nEndXclCol = nBegXclCol;
+
+ while( aRangeEnd != aEnd )
+ {
+ // find begin of next used XF range
+ aRangeBeg = aRangeEnd;
+ nBegXclCol = nEndXclCol;
+ while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
+ {
+ nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
+ ++aRangeBeg;
+ }
+ // find end of next used XF range
+ aRangeEnd = aRangeBeg;
+ nEndXclCol = nBegXclCol;
+ while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
+ {
+ nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
+ ++aRangeEnd;
+ }
+
+ // export this range as a record
+ if( aRangeBeg != aRangeEnd )
+ {
+ sal_uInt16 nCount = nEndXclCol - nBegXclCol;
+ bool bIsMulti = nCount > 1;
+ std::size_t nTotalSize = GetRecSize() + (2 + mnContSize) * nCount;
+ if( bIsMulti ) nTotalSize += 2;
+
+ rStrm.StartRecord( bIsMulti ? mnMulRecId : GetRecId(), nTotalSize );
+ rStrm << static_cast<sal_uInt16> (GetXclRow()) << nBegXclCol;
+
+ sal_uInt16 nRelCol = nBegXclCol - GetXclCol();
+ for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
+ {
+ for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
+ {
+ rStrm << aIt->mnXFIndex;
+ WriteContents( rStrm, nRelCol );
+ ++nRelCol;
+ }
+ }
+ if( bIsMulti )
+ rStrm << static_cast< sal_uInt16 >( nEndXclCol - 1 );
+ rStrm.EndRecord();
+ }
+ }
+}
+
+void XclExpMultiCellBase::SaveXml( XclExpXmlStream& rStrm )
+{
+ XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
+ XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
+ XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
+ sal_uInt16 nBegXclCol = GetXclCol();
+ sal_uInt16 nEndXclCol = nBegXclCol;
+
+ while( aRangeEnd != aEnd )
+ {
+ // find begin of next used XF range
+ aRangeBeg = aRangeEnd;
+ nBegXclCol = nEndXclCol;
+ while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
+ {
+ nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
+ ++aRangeBeg;
+ }
+ // find end of next used XF range
+ aRangeEnd = aRangeBeg;
+ nEndXclCol = nBegXclCol;
+ while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
+ {
+ nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
+ ++aRangeEnd;
+ }
+
+ // export this range as a record
+ if( aRangeBeg != aRangeEnd )
+ {
+ sal_uInt16 nRelColIdx = nBegXclCol - GetXclCol();
+ sal_Int32 nRelCol = 0;
+ for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
+ {
+ for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
+ {
+ WriteXmlContents(
+ rStrm,
+ XclAddress( static_cast<sal_uInt16>(nBegXclCol + nRelCol), GetXclRow() ),
+ aIt->mnXFIndex,
+ nRelColIdx );
+ ++nRelCol;
+ ++nRelColIdx;
+ }
+ }
+ }
+ }
+}
+
+sal_uInt16 XclExpMultiCellBase::GetCellCount() const
+{
+ return std::accumulate(maXFIds.begin(), maXFIds.end(), sal_uInt16(0),
+ [](const sal_uInt16& rSum, const XclExpMultiXFId& rXFId) { return rSum + rXFId.mnCount; });
+}
+
+void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId& rXFId )
+{
+ if( maXFIds.empty() || (maXFIds.back().mnXFId != rXFId.mnXFId) )
+ maXFIds.push_back( rXFId );
+ else
+ maXFIds.back().mnCount += rXFId.mnCount;
+}
+
+void XclExpMultiCellBase::AppendXFId( const XclExpRoot& rRoot,
+ const ScPatternAttr* pPattern, sal_uInt16 nScript, sal_uInt32 nForcedXFId, sal_uInt16 nCount )
+{
+ sal_uInt32 nXFId = (nForcedXFId == EXC_XFID_NOTFOUND) ?
+ rRoot.GetXFBuffer().Insert( pPattern, nScript ) : nForcedXFId;
+ AppendXFId( XclExpMultiXFId( nXFId, nCount ) );
+}
+
+bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase& rCell )
+{
+ if( GetLastXclCol() + 1 == rCell.GetXclCol() )
+ {
+ maXFIds.insert( maXFIds.end(), rCell.maXFIds.begin(), rCell.maXFIds.end() );
+ return true;
+ }
+ return false;
+}
+
+void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const
+{
+ OSL_ENSURE( GetLastXclCol() < rXFIndexes.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" );
+ ScfUInt16Vec::iterator aDestIt = rXFIndexes.begin() + GetXclCol();
+ for( const auto& rXFId : maXFIds )
+ {
+ ::std::fill( aDestIt, aDestIt + rXFId.mnCount, rXFId.mnXFIndex );
+ aDestIt += rXFId.mnCount;
+ }
+}
+
+void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound )
+{
+ // save last column before calling maXFIds.clear()
+ sal_uInt16 nLastXclCol = GetLastXclCol();
+ OSL_ENSURE( nLastXclCol < rXFIndexes.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" );
+
+ // build new XF index vector, containing passed XF indexes
+ maXFIds.clear();
+ // Process only all that possibly are not EXC_XF_NOTFOUND.
+ size_t nEnd = std::min<size_t>(nLastXclCol + 1, nStartAllNotFound);
+ for( size_t i = GetXclCol(); i < nEnd; ++i )
+ {
+ XclExpMultiXFId aXFId( 0 );
+ // AppendXFId() tests XclExpXFIndex::mnXFId, set it too
+ aXFId.mnXFId = aXFId.mnXFIndex = rXFIndexes[ i ];
+ AppendXFId( aXFId );
+ }
+
+ // remove leading and trailing unused XF indexes
+ if( !maXFIds.empty() && (maXFIds.front().mnXFIndex == EXC_XF_NOTFOUND) )
+ {
+ SetXclCol( GetXclCol() + maXFIds.front().mnCount );
+ maXFIds.erase(maXFIds.begin(), maXFIds.begin() + 1);
+ }
+ if( !maXFIds.empty() && (maXFIds.back().mnXFIndex == EXC_XF_NOTFOUND) )
+ maXFIds.pop_back();
+
+ // The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND.
+}
+
+sal_uInt16 XclExpMultiCellBase::GetStartColAllDefaultCell() const
+{
+ sal_uInt16 col = GetXclCol();
+ sal_uInt16 nMaxNonDefCol = col;
+ for( const auto& rXFId : maXFIds )
+ {
+ col += rXFId.mnCount;
+ if (rXFId.mnXFIndex != EXC_XF_DEFAULTCELL)
+ nMaxNonDefCol = col;
+ }
+ return nMaxNonDefCol;
+}
+
+XclExpBlankCell::XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId ) :
+ XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
+{
+ OSL_ENSURE( rXFId.mnCount > 0, "XclExpBlankCell::XclExpBlankCell - invalid count" );
+ AppendXFId( rXFId );
+}
+
+XclExpBlankCell::XclExpBlankCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId ) :
+ XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
+{
+ OSL_ENSURE( rXclPos.mnCol <= nLastXclCol, "XclExpBlankCell::XclExpBlankCell - invalid column range" );
+ // #i46627# use default script type instead of ApiScriptType::WEAK
+ AppendXFId( rRoot, pPattern, rRoot.GetDefApiScript(), nForcedXFId, nLastXclCol - rXclPos.mnCol + 1 );
+}
+
+bool XclExpBlankCell::TryMerge( const XclExpCellBase& rCell )
+{
+ const XclExpBlankCell* pBlankCell = dynamic_cast< const XclExpBlankCell* >( &rCell );
+ return pBlankCell && TryMergeXFIds( *pBlankCell );
+}
+
+void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const
+{
+ GetXFIndexes( rXFIndexes );
+}
+
+void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound )
+{
+ RemoveUnusedXFIndexes( rXFIndexes, nStartAllNotFound );
+}
+
+void XclExpBlankCell::WriteContents( XclExpStream& /*rStrm*/, sal_uInt16 /*nRelCol*/ )
+{
+}
+
+void XclExpBlankCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 /* nRelCol */ )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->singleElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, nXFId) );
+}
+
+XclExpRkCell::XclExpRkCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_Int32 nRkValue ) :
+ XclExpMultiCellBase( EXC_ID_RK, EXC_ID_MULRK, 4, rXclPos )
+{
+ // #i41210# always use latin script for number cells - may look wrong for special number formats...
+ AppendXFId( rRoot, pPattern, ApiScriptType::LATIN, nForcedXFId );
+ maRkValues.push_back( nRkValue );
+}
+
+bool XclExpRkCell::TryMerge( const XclExpCellBase& rCell )
+{
+ const XclExpRkCell* pRkCell = dynamic_cast< const XclExpRkCell* >( &rCell );
+ if( pRkCell && TryMergeXFIds( *pRkCell ) )
+ {
+ maRkValues.insert( maRkValues.end(), pRkCell->maRkValues.begin(), pRkCell->maRkValues.end() );
+ return true;
+ }
+ return false;
+}
+
+void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, nXFId),
+ XML_t, "n"
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+ rWorksheet->startElement( XML_v );
+ rWorksheet->write( XclTools::GetDoubleFromRK( maRkValues[ nRelCol ] ) );
+ rWorksheet->endElement( XML_v );
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpRkCell::WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol )
+{
+ OSL_ENSURE( nRelCol < maRkValues.size(), "XclExpRkCell::WriteContents - overflow error" );
+ rStrm << maRkValues[ nRelCol ];
+}
+
+// Rows and Columns
+
+XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows ) :
+ mpScOLArray( nullptr ),
+ maLevelInfos( SC_OL_MAXDEPTH ),
+ mnCurrLevel( 0 ),
+ mbCurrCollapse( false )
+{
+ if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
+ mpScOLArray = &(bRows ? pOutlineTable->GetRowArray() : pOutlineTable->GetColArray());
+
+ if( mpScOLArray )
+ for( size_t nLevel = 0; nLevel < SC_OL_MAXDEPTH; ++nLevel )
+ if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nLevel, 0 ) )
+ maLevelInfos[ nLevel ].mnScEndPos = pEntry->GetEnd();
+}
+
+void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos )
+{
+ if( !mpScOLArray )
+ return;
+
+ // find open level index for passed position
+ size_t nNewOpenScLevel = 0; // new open level (0-based Calc index)
+ sal_uInt8 nNewLevel = 0; // new open level (1-based Excel index)
+
+ if( mpScOLArray->FindTouchedLevel( nScPos, nScPos, nNewOpenScLevel ) )
+ nNewLevel = static_cast< sal_uInt8 >( nNewOpenScLevel + 1 );
+ // else nNewLevel keeps 0 to show that there are no groups
+
+ mbCurrCollapse = false;
+ if( nNewLevel >= mnCurrLevel )
+ {
+ // new level(s) opened, or no level closed - update all level infos
+ for( size_t nScLevel = 0; nScLevel <= nNewOpenScLevel; ++nScLevel )
+ {
+ /* In each level: check if a new group is started (there may be
+ neighbored groups without gap - therefore check ALL levels). */
+ if( maLevelInfos[ nScLevel ].mnScEndPos < nScPos )
+ {
+ if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nScLevel, nScPos ) )
+ {
+ maLevelInfos[ nScLevel ].mnScEndPos = pEntry->GetEnd();
+ maLevelInfos[ nScLevel ].mbHidden = pEntry->IsHidden();
+ }
+ }
+ }
+ }
+ else
+ {
+ // level(s) closed - check if any of the closed levels are collapsed
+ // Calc uses 0-based level indexes
+ sal_uInt16 nOldOpenScLevel = mnCurrLevel - 1;
+ for( sal_uInt16 nScLevel = nNewOpenScLevel + 1; !mbCurrCollapse && (nScLevel <= nOldOpenScLevel); ++nScLevel )
+ mbCurrCollapse = maLevelInfos[ nScLevel ].mbHidden;
+ }
+
+ // cache new opened level
+ mnCurrLevel = nNewLevel;
+}
+
+XclExpGuts::XclExpGuts( const XclExpRoot& rRoot ) :
+ XclExpRecord( EXC_ID_GUTS, 8 ),
+ mnColLevels( 0 ),
+ mnColWidth( 0 ),
+ mnRowLevels( 0 ),
+ mnRowWidth( 0 )
+{
+ const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() );
+ if(!pOutlineTable)
+ return;
+
+ // column outline groups
+ const ScOutlineArray& rColArray = pOutlineTable->GetColArray();
+ mnColLevels = ulimit_cast< sal_uInt16 >( rColArray.GetDepth(), EXC_OUTLINE_MAX );
+ if( mnColLevels )
+ {
+ ++mnColLevels;
+ mnColWidth = 12 * mnColLevels + 5;
+ }
+
+ // row outline groups
+ const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
+ mnRowLevels = ulimit_cast< sal_uInt16 >( rRowArray.GetDepth(), EXC_OUTLINE_MAX );
+ if( mnRowLevels )
+ {
+ ++mnRowLevels;
+ mnRowWidth = 12 * mnRowLevels + 5;
+ }
+}
+
+void XclExpGuts::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnRowWidth << mnColWidth << mnRowLevels << mnColLevels;
+}
+
+XclExpDimensions::XclExpDimensions( const XclExpRoot& rRoot ) :
+ mrRoot(rRoot),
+ mnFirstUsedXclRow( 0 ),
+ mnFirstFreeXclRow( 0 ),
+ mnFirstUsedXclCol( 0 ),
+ mnFirstFreeXclCol( 0 )
+{
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF2: SetRecHeader( EXC_ID2_DIMENSIONS, 8 ); break;
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5: SetRecHeader( EXC_ID3_DIMENSIONS, 10 ); break;
+ case EXC_BIFF8: SetRecHeader( EXC_ID3_DIMENSIONS, 14 ); break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpDimensions::SetDimensions(
+ sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
+ sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow )
+{
+ mnFirstUsedXclRow = nFirstUsedXclRow;
+ mnFirstFreeXclRow = nFirstFreeXclRow;
+ mnFirstUsedXclCol = nFirstUsedXclCol;
+ mnFirstFreeXclCol = nFirstFreeXclCol;
+}
+
+void XclExpDimensions::SaveXml( XclExpXmlStream& rStrm )
+{
+ ScRange aRange;
+ aRange.aStart.SetRow( static_cast<SCROW>(mnFirstUsedXclRow) );
+ aRange.aStart.SetCol( static_cast<SCCOL>(mnFirstUsedXclCol) );
+
+ if( mnFirstFreeXclRow != mnFirstUsedXclRow && mnFirstFreeXclCol != mnFirstUsedXclCol )
+ {
+ aRange.aEnd.SetRow( static_cast<SCROW>(mnFirstFreeXclRow-1) );
+ aRange.aEnd.SetCol( static_cast<SCCOL>(mnFirstFreeXclCol-1) );
+ }
+
+ aRange.PutInOrder();
+ rStrm.GetCurrentStream()->singleElement( XML_dimension,
+ // To be compatible with MS Office 2007,
+ // we need full address notation format
+ // e.g. "A1:AMJ177" and not partial like: "1:177".
+ XML_ref, XclXmlUtils::ToOString(mrRoot.GetDoc(), aRange, true) );
+}
+
+void XclExpDimensions::WriteBody( XclExpStream& rStrm )
+{
+ XclBiff eBiff = rStrm.GetRoot().GetBiff();
+ if( eBiff == EXC_BIFF8 )
+ rStrm << mnFirstUsedXclRow << mnFirstFreeXclRow;
+ else
+ rStrm << static_cast< sal_uInt16 >( mnFirstUsedXclRow ) << static_cast< sal_uInt16 >( mnFirstFreeXclRow );
+ rStrm << mnFirstUsedXclCol << mnFirstFreeXclCol;
+ if( eBiff >= EXC_BIFF3 )
+ rStrm << sal_uInt16( 0 );
+}
+
+namespace {
+
+double lclGetCChCorrection(const XclExpRoot& rRoot)
+{
+ // Convert the correction from 1/256ths of a character size to count of chars
+ // TODO: make to fit ECMA-376-1:2016 18.3.1.81 sheetFormatPr (Sheet Format Properties):
+ // 5 pixels are added to the base width: 2 for margin padding on each side, plus 1 for gridline
+ // So this should depend on rRoot.GetCharWidth(), not on font height
+
+ tools::Long nFontHt = rRoot.GetFontBuffer().GetAppFontData().mnHeight;
+ return XclTools::GetXclDefColWidthCorrection(nFontHt) / 256.0;
+}
+
+} // namespace
+
+XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot& rRoot ) :
+ XclExpDoubleRecord(EXC_ID_DEFCOLWIDTH, EXC_DEFCOLWIDTH_DEF + lclGetCChCorrection(rRoot)),
+ XclExpRoot( rRoot )
+{
+}
+
+bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth ) const
+{
+ // This formula is taking number of characters with GetValue()
+ // and it is translating it into default column width.
+ // https://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.column.aspx
+ double defaultColumnWidth = 256.0 * GetValue();
+
+ // exactly matched, if difference is less than 1/16 of a character to the left or to the right
+ return std::abs(defaultColumnWidth - nXclColWidth) < 256.0 * 1.0 / 16.0;
+}
+
+void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth, bool bXLS )
+{
+ 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)
+{
+ double fCorrectedCCh = GetValue() - lclGetCChCorrection(GetRoot());
+ // Convert double to sal_uInt16
+ XclExpUInt16Record aUInt16Rec(GetRecId(),
+ static_cast<sal_uInt16>(std::round(fCorrectedCCh)));
+ aUInt16Rec.Save(rStrm);
+}
+
+XclExpColinfo::XclExpColinfo( const XclExpRoot& rRoot,
+ SCCOL nScCol, SCROW nLastScRow, XclExpColOutlineBuffer& rOutlineBfr ) :
+ XclExpRecord( EXC_ID_COLINFO, 12 ),
+ XclExpRoot( rRoot ),
+ mbCustomWidth( false ),
+ mnWidth( 0 ),
+ mnScWidth( 0 ),
+ mnFlags( 0 ),
+ mnOutlineLevel( 0 ),
+ mnFirstXclCol( static_cast< sal_uInt16 >( nScCol ) ),
+ mnLastXclCol( static_cast< sal_uInt16 >( nScCol ) )
+{
+ ScDocument& rDoc = GetDoc();
+ SCTAB nScTab = GetCurrScTab();
+
+ // column default format
+ maXFId.mnXFId = GetXFBuffer().Insert(
+ rDoc.GetMostUsedPattern( nScCol, 0, nLastScRow, nScTab ), GetDefApiScript() );
+
+ // column width. If column is hidden then we should return real value (not zero)
+ sal_uInt16 nScWidth = rDoc.GetColWidth( nScCol, nScTab, false );
+ mnWidth = XclTools::GetXclColumnWidth( nScWidth, GetCharWidth() );
+ mnScWidth = convertTwipToMm100(nScWidth);
+
+ // column flags
+ ::set_flag( mnFlags, EXC_COLINFO_HIDDEN, rDoc.ColHidden(nScCol, nScTab) );
+
+ // outline data
+ rOutlineBfr.Update( nScCol );
+ ::set_flag( mnFlags, EXC_COLINFO_COLLAPSED, rOutlineBfr.IsCollapsed() );
+ ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 8, 3 );
+ mnOutlineLevel = rOutlineBfr.GetLevel();
+}
+
+void XclExpColinfo::ConvertXFIndexes()
+{
+ maXFId.ConvertXFIndex( GetRoot() );
+}
+
+bool XclExpColinfo::IsDefault( const XclExpDefcolwidth& rDefColWidth )
+{
+ mbCustomWidth = !rDefColWidth.IsDefWidth(mnWidth);
+ return (maXFId.mnXFIndex == EXC_XF_DEFAULTCELL) &&
+ (mnFlags == 0) &&
+ (mnOutlineLevel == 0) &&
+ !mbCustomWidth;
+}
+
+bool XclExpColinfo::TryMerge( const XclExpColinfo& rColInfo )
+{
+ if( (maXFId.mnXFIndex == rColInfo.maXFId.mnXFIndex) &&
+ (mnWidth == rColInfo.mnWidth) &&
+ (mnFlags == rColInfo.mnFlags) &&
+ (mnOutlineLevel == rColInfo.mnOutlineLevel) &&
+ (mnLastXclCol + 1 == rColInfo.mnFirstXclCol) )
+ {
+ mnLastXclCol = rColInfo.mnLastXclCol;
+ return true;
+ }
+ return false;
+}
+
+void XclExpColinfo::WriteBody( XclExpStream& rStrm )
+{
+ // if last column is equal to last possible column, Excel adds one more
+ sal_uInt16 nLastXclCol = mnLastXclCol;
+ if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
+ ++nLastXclCol;
+
+ rStrm << mnFirstXclCol
+ << nLastXclCol
+ << mnWidth
+ << maXFId.mnXFIndex
+ << mnFlags
+ << sal_uInt16( 0 );
+}
+
+void XclExpColinfo::SaveXml( XclExpXmlStream& rStrm )
+{
+ const double nExcelColumnWidth = mnScWidth / convertTwipToMm100<double>(GetCharWidth());
+
+ // tdf#101363 In MS specification the output value is set with double precision after delimiter:
+ // =Truncate(({width in pixels} - 5)/{Maximum Digit Width} * 100 + 0.5)/100
+ // Explanation of magic numbers:
+ // 5 number - are 4 pixels of margin padding (two on each side), plus 1 pixel padding for the gridlines.
+ // It is unknown if it should be applied during LibreOffice export
+ // 100 number - used to limit precision to 0.01 with formula =Truncate( {value}*100+0.5 ) / 100
+ // 0.5 number (0.005 to output value) - used to increase value before truncating,
+ // to avoid situation when 2.997 will be truncated to 2.99 and not to 3.00
+ const double nTruncatedExcelColumnWidth = std::trunc( nExcelColumnWidth * 100.0 + 0.5 ) / 100.0;
+ rStrm.GetCurrentStream()->singleElement( XML_col,
+ // OOXTODO: XML_bestFit,
+ XML_collapsed, ToPsz( ::get_flag( mnFlags, EXC_COLINFO_COLLAPSED ) ),
+ XML_customWidth, ToPsz( mbCustomWidth ),
+ XML_hidden, ToPsz( ::get_flag( mnFlags, EXC_COLINFO_HIDDEN ) ),
+ XML_outlineLevel, OString::number(mnOutlineLevel),
+ XML_max, OString::number(mnLastXclCol + 1),
+ XML_min, OString::number(mnFirstXclCol + 1),
+ // OOXTODO: XML_phonetic,
+ XML_style, lcl_GetStyleId(rStrm, maXFId.mnXFIndex),
+ XML_width, OString::number(nTruncatedExcelColumnWidth) );
+}
+
+XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ maDefcolwidth( rRoot ),
+ maOutlineBfr( rRoot ),
+ mnHighestOutlineLevel( 0 )
+{
+}
+
+void XclExpColinfoBuffer::Initialize( SCROW nLastScRow )
+{
+
+ for( sal_uInt16 nScCol = 0, nLastScCol = GetMaxPos().Col(); nScCol <= nLastScCol; ++nScCol )
+ {
+ maColInfos.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol, nLastScRow, maOutlineBfr ) );
+ if( maOutlineBfr.GetLevel() > mnHighestOutlineLevel )
+ {
+ mnHighestOutlineLevel = maOutlineBfr.GetLevel();
+ }
+ }
+}
+
+void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes, bool bXLS )
+{
+ rXFIndexes.clear();
+ rXFIndexes.reserve( maColInfos.GetSize() );
+
+ if( !maColInfos.IsEmpty())
+ {
+ XclExpColinfo* xPrevRec = maColInfos.GetRecord( 0 );
+ xPrevRec->ConvertXFIndexes();
+ for( size_t nPos = 1; nPos < maColInfos.GetSize(); ++nPos )
+ {
+ XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
+ xRec->ConvertXFIndexes();
+
+ // try to merge with previous record
+ if( xPrevRec->TryMerge( *xRec ) )
+ maColInfos.InvalidateRecord( nPos );
+ else
+ xPrevRec = xRec;
+ }
+ maColInfos.RemoveInvalidatedRecords();
+ }
+
+ // put XF indexes into passed vector, collect use count of all different widths
+ std::unordered_map< sal_uInt16, sal_uInt16 > aWidthMap;
+ sal_uInt16 nMaxColCount = 0;
+ sal_uInt16 nMaxUsedWidth = 0;
+ for( size_t nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
+ {
+ const XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
+ sal_uInt16 nColCount = xRec->GetColCount();
+
+ // add XF index to passed vector
+ rXFIndexes.resize( rXFIndexes.size() + nColCount, xRec->GetXFIndex() );
+
+ // collect use count of column width
+ sal_uInt16 nWidth = xRec->GetColWidth();
+ sal_uInt16& rnMapCount = aWidthMap[ nWidth ];
+ rnMapCount = rnMapCount + nColCount;
+ if( rnMapCount > nMaxColCount )
+ {
+ nMaxColCount = rnMapCount;
+ nMaxUsedWidth = nWidth;
+ }
+ }
+ maDefcolwidth.SetDefWidth( nMaxUsedWidth, bXLS );
+
+ // remove all default COLINFO records
+ for( size_t nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
+ {
+ XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
+ if( xRec->IsDefault( maDefcolwidth ) )
+ maColInfos.InvalidateRecord( nPos );
+ }
+ maColInfos.RemoveInvalidatedRecords();
+}
+
+void XclExpColinfoBuffer::Save( XclExpStream& rStrm )
+{
+ // DEFCOLWIDTH
+ maDefcolwidth.Save( rStrm );
+ // COLINFO records
+ maColInfos.Save( rStrm );
+}
+
+void XclExpColinfoBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maColInfos.IsEmpty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_cols);
+ maColInfos.SaveXml( rStrm );
+ rWorksheet->endElement( XML_cols );
+}
+
+XclExpDefaultRowData::XclExpDefaultRowData() :
+ mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
+ mnHeight( EXC_DEFROW_DEFAULTHEIGHT )
+{
+}
+
+XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow& rRow ) :
+ mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
+ mnHeight( rRow.GetHeight() )
+{
+ ::set_flag( mnFlags, EXC_DEFROW_HIDDEN, rRow.IsHidden() );
+ ::set_flag( mnFlags, EXC_DEFROW_UNSYNCED, rRow.IsUnsynced() );
+}
+
+static bool operator<( const XclExpDefaultRowData& rLeft, const XclExpDefaultRowData& rRight )
+{
+ return (rLeft.mnHeight < rRight.mnHeight) ||
+ ((rLeft.mnHeight == rRight.mnHeight) && (rLeft.mnFlags < rRight.mnFlags));
+}
+
+XclExpDefrowheight::XclExpDefrowheight() :
+ XclExpRecord( EXC_ID3_DEFROWHEIGHT, 4 )
+{
+}
+
+void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData& rDefData )
+{
+ maDefData = rDefData;
+}
+
+void XclExpDefrowheight::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
+ rStrm << maDefData.mnFlags << maDefData.mnHeight;
+}
+
+XclExpRow::XclExpRow( const XclExpRoot& rRoot, sal_uInt32 nXclRow,
+ XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty, bool bHidden, sal_uInt16 nHeight ) :
+ XclExpRecord( EXC_ID3_ROW, 16 ),
+ XclExpRoot( rRoot ),
+ mnXclRow( nXclRow ),
+ mnHeight( nHeight ),
+ mnFlags( EXC_ROW_DEFAULTFLAGS ),
+ mnXFIndex( EXC_XF_DEFAULTCELL ),
+ mnOutlineLevel( 0 ),
+ mnXclRowRpt( 1 ),
+ mnCurrentRow( nXclRow ),
+ mbAlwaysEmpty( bAlwaysEmpty ),
+ mbEnabled( true )
+{
+ SCTAB nScTab = GetCurrScTab();
+ SCROW nScRow = static_cast< SCROW >( mnXclRow );
+
+ // *** Row flags *** ------------------------------------------------------
+
+ CRFlags nRowFlags = GetDoc().GetRowFlags( nScRow, nScTab );
+ bool bUserHeight( nRowFlags & CRFlags::ManualSize );
+ ::set_flag( mnFlags, EXC_ROW_UNSYNCED, bUserHeight );
+ ::set_flag( mnFlags, EXC_ROW_HIDDEN, bHidden );
+
+ // *** Outline data *** ---------------------------------------------------
+
+ rOutlineBfr.Update( nScRow );
+ ::set_flag( mnFlags, EXC_ROW_COLLAPSED, rOutlineBfr.IsCollapsed() );
+ ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 0, 3 );
+ mnOutlineLevel = rOutlineBfr.GetLevel();
+
+ // *** Progress bar *** ---------------------------------------------------
+
+ XclExpProgressBar& rProgress = GetProgressBar();
+ rProgress.IncRowRecordCount();
+ rProgress.Progress();
+}
+
+static size_t findFirstAllSameUntilEnd( const ScfUInt16Vec& rIndexes, sal_uInt16 value,
+ size_t searchStart = std::numeric_limits<size_t>::max())
+{
+ for( size_t i = std::min(rIndexes.size(), searchStart); i >= 1; --i )
+ {
+ if( rIndexes[ i - 1 ] != value )
+ return i;
+ }
+ return 0;
+}
+
+void XclExpRow::AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase )
+{
+ OSL_ENSURE( !mbAlwaysEmpty, "XclExpRow::AppendCell - row is marked to be always empty" );
+ // try to merge with last existing cell
+ InsertCell( xCell, maCellList.GetSize(), bIsMergedBase );
+}
+
+void XclExpRow::Finalize( const ScfUInt16Vec& rColXFIndexes, ScfUInt16Vec& aXFIndexes, size_t nStartColAllDefault, bool bProgress )
+{
+ size_t nPos, nSize;
+
+ // *** Convert XF identifiers *** -----------------------------------------
+
+ // additionally collect the blank XF indexes
+ size_t nColCount = GetMaxPos().Col() + 1;
+ OSL_ENSURE( rColXFIndexes.size() == nColCount, "XclExpRow::Finalize - wrong column XF index count" );
+
+ // The vector should be preset to all items being EXC_XF_NOTFOUND, to avoid repeated allocations
+ // and clearing.
+ assert( aXFIndexes.size() == nColCount );
+ assert( aXFIndexes.front() == EXC_XF_NOTFOUND );
+ assert( aXFIndexes.back() == EXC_XF_NOTFOUND );
+ for( nPos = 0, nSize = maCellList.GetSize(); nPos < nSize; ++nPos )
+ {
+ XclExpCellBase* pCell = maCellList.GetRecord( nPos );
+ pCell->ConvertXFIndexes( GetRoot() );
+ pCell->GetBlankXFIndexes( aXFIndexes );
+ }
+
+ // *** Fill gaps with BLANK/MULBLANK cell records *** ---------------------
+
+ /* This is needed because nonexistent cells in Calc are not formatted at all,
+ but in Excel they would have the column default format. Blank cells that
+ are equal to the respective column default are removed later in this function. */
+ if( !mbAlwaysEmpty )
+ {
+ // XF identifier representing default cell XF
+ XclExpMultiXFId aXFId( XclExpXFBuffer::GetDefCellXFId() );
+ aXFId.ConvertXFIndex( GetRoot() );
+
+ nPos = 0;
+ while( nPos <= maCellList.GetSize() ) // don't cache list size, may change in the loop
+ {
+ // get column index that follows previous cell
+ sal_uInt16 nFirstFreeXclCol = (nPos > 0) ? (maCellList.GetRecord( nPos - 1 )->GetLastXclCol() + 1) : 0;
+ // get own column index
+ sal_uInt16 nNextUsedXclCol = (nPos < maCellList.GetSize()) ? maCellList.GetRecord( nPos )->GetXclCol() : (GetMaxPos().Col() + 1);
+
+ // is there a gap?
+ if( nFirstFreeXclCol < nNextUsedXclCol )
+ {
+ aXFId.mnCount = nNextUsedXclCol - nFirstFreeXclCol;
+ XclExpCellRef xNewCell = new XclExpBlankCell( XclAddress( nFirstFreeXclCol, mnXclRow ), aXFId );
+ // insert the cell, InsertCell() may merge it with existing BLANK records
+ InsertCell( xNewCell, nPos, false );
+ // insert default XF indexes into aXFIndexes
+ for( size_t i = nFirstFreeXclCol; i < nNextUsedXclCol; ++i )
+ aXFIndexes[ i ] = aXFId.mnXFIndex;
+ // don't step forward with nPos, InsertCell() may remove records
+ }
+ else
+ ++nPos;
+ }
+ }
+
+ // *** Find default row format *** ----------------------------------------
+
+ // Often there will be many EXC_XF_DEFAULTCELL at the end, optimize by ignoring them.
+ size_t nStartSearchAllDefault = aXFIndexes.size();
+ if( !maCellList.IsEmpty() && dynamic_cast< const XclExpBlankCell* >( maCellList.GetLastRecord()))
+ {
+ const XclExpBlankCell* pLastBlank = static_cast< const XclExpBlankCell* >( maCellList.GetLastRecord());
+ assert(pLastBlank->GetLastXclCol() == aXFIndexes.size() - 1);
+ nStartSearchAllDefault = pLastBlank->GetStartColAllDefaultCell();
+ }
+ size_t nStartAllDefault = findFirstAllSameUntilEnd( aXFIndexes, EXC_XF_DEFAULTCELL, nStartSearchAllDefault);
+
+ // find most used XF index in the row
+ sal_uInt16 nRowXFIndex = EXC_XF_DEFAULTCELL;
+ const size_t nHalfIndexes = aXFIndexes.size() / 2;
+ if( nStartAllDefault > nHalfIndexes ) // Otherwise most are EXC_XF_DEFAULTCELL.
+ {
+ // Very likely the most common one is going to be the last one.
+ nRowXFIndex = aXFIndexes.back();
+ size_t nStartLastSame = findFirstAllSameUntilEnd( aXFIndexes, nRowXFIndex );
+ if( nStartLastSame > nHalfIndexes ) // No, find out the most used one by counting.
+ {
+ std::unordered_map< sal_uInt16, size_t > aIndexMap;
+ size_t nMaxXFCount = 0;
+ for( const auto& rXFIndex : aXFIndexes )
+ {
+ if( rXFIndex != EXC_XF_NOTFOUND )
+ {
+ size_t& rnCount = aIndexMap[ rXFIndex ];
+ ++rnCount;
+ if( rnCount > nMaxXFCount )
+ {
+ nRowXFIndex = rXFIndex;
+ nMaxXFCount = rnCount;
+ if (nMaxXFCount > nHalfIndexes)
+ {
+ // No other XF index can have a greater usage count, we
+ // don't need to loop through the remaining cells.
+ // Specifically for the tail of unused default
+ // cells/columns this makes a difference.
+ break; // for
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // decide whether to use the row default XF index or column default XF indexes
+ bool bUseColDefXFs = nRowXFIndex == EXC_XF_DEFAULTCELL;
+ if( !bUseColDefXFs )
+ {
+ // count needed XF indexes for blank cells with and without row default XF index
+ size_t nXFCountWithRowDefXF = 0;
+ size_t nXFCountWithoutRowDefXF = 0;
+ ScfUInt16Vec::const_iterator aColIt = rColXFIndexes.begin();
+ for( const auto& rXFIndex : aXFIndexes )
+ {
+ sal_uInt16 nXFIndex = rXFIndex;
+ if( nXFIndex != EXC_XF_NOTFOUND )
+ {
+ if( nXFIndex != nRowXFIndex )
+ ++nXFCountWithRowDefXF; // with row default XF index
+ if( nXFIndex != *aColIt )
+ ++nXFCountWithoutRowDefXF; // without row default XF index
+ }
+ ++aColIt;
+ }
+
+ // use column XF indexes if this would cause less or equal number of BLANK records
+ bUseColDefXFs = nXFCountWithoutRowDefXF <= nXFCountWithRowDefXF;
+ }
+
+ // *** Remove unused BLANK cell records *** -------------------------------
+
+ size_t maxStartAllNotFound;
+ if( bUseColDefXFs )
+ {
+ size_t maxStartAllDefault = std::max( nStartAllDefault, nStartColAllDefault );
+ // use column default XF indexes
+ // #i194#: remove cell XF indexes equal to column default XF indexes
+ for( size_t i = 0; i < maxStartAllDefault; ++i )
+ {
+ if( aXFIndexes[ i ] == rColXFIndexes[ i ] )
+ aXFIndexes[ i ] = EXC_XF_NOTFOUND;
+ }
+ // They can differ only up to maxNonDefault, in the rest they are the same.
+ for( size_t i = maxStartAllDefault; i < aXFIndexes.size(); ++i )
+ aXFIndexes[ i ] = EXC_XF_NOTFOUND;
+ maxStartAllNotFound = maxStartAllDefault;
+ }
+ else
+ {
+ // use row default XF index
+ mnXFIndex = nRowXFIndex;
+ ::set_flag( mnFlags, EXC_ROW_USEDEFXF );
+ // #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index
+ for( auto& rXFIndex : aXFIndexes )
+ if( rXFIndex == nRowXFIndex )
+ rXFIndex = EXC_XF_NOTFOUND;
+ maxStartAllNotFound = aXFIndexes.size();
+ }
+
+ // remove unused parts of BLANK/MULBLANK cell records
+ size_t nStartAllNotFound = findFirstAllSameUntilEnd( aXFIndexes, EXC_XF_NOTFOUND, maxStartAllNotFound );
+ nPos = 0;
+ while( nPos < maCellList.GetSize() ) // do not cache list size, may change in the loop
+ {
+ XclExpCellBase* xCell = maCellList.GetRecord( nPos );
+ xCell->RemoveUnusedBlankCells( aXFIndexes, nStartAllNotFound );
+ if( xCell->IsEmpty() )
+ maCellList.RemoveRecord( nPos );
+ else
+ ++nPos;
+ }
+ // Ensure it's all EXC_XF_NOTFOUND again for next reuse.
+ for( size_t i = 0; i < nStartAllNotFound; ++i )
+ aXFIndexes[ i ] = EXC_XF_NOTFOUND;
+
+ // progress bar includes disabled rows; only update it in the lead thread.
+ if (bProgress)
+ GetProgressBar().Progress();
+}
+sal_uInt16 XclExpRow::GetFirstUsedXclCol() const
+{
+ return maCellList.IsEmpty() ? 0 : maCellList.GetFirstRecord()->GetXclCol();
+}
+
+sal_uInt16 XclExpRow::GetFirstFreeXclCol() const
+{
+ return maCellList.IsEmpty() ? 0 : (maCellList.GetLastRecord()->GetLastXclCol() + 1);
+}
+
+bool XclExpRow::IsDefaultable() const
+{
+ const sal_uInt16 nFlagsAlwaysMarkedAsDefault = EXC_ROW_DEFAULTFLAGS | EXC_ROW_HIDDEN | EXC_ROW_UNSYNCED;
+ return !::get_flag( mnFlags, static_cast< sal_uInt16 >( ~nFlagsAlwaysMarkedAsDefault ) ) &&
+ IsEmpty();
+}
+
+void XclExpRow::DisableIfDefault( const XclExpDefaultRowData& rDefRowData )
+{
+ mbEnabled = !IsDefaultable() ||
+ (mnHeight != rDefRowData.mnHeight) ||
+ (IsHidden() != rDefRowData.IsHidden()) ||
+ (IsUnsynced() != rDefRowData.IsUnsynced());
+}
+
+void XclExpRow::WriteCellList( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mbEnabled || maCellList.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" );
+ maCellList.Save( rStrm );
+}
+
+void XclExpRow::Save( XclExpStream& rStrm )
+{
+ if( mbEnabled )
+ {
+ mnCurrentRow = mnXclRow;
+ for ( sal_uInt32 i = 0; i < mnXclRowRpt; ++i, ++mnCurrentRow )
+ XclExpRecord::Save( rStrm );
+ }
+}
+
+void XclExpRow::InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase )
+{
+ OSL_ENSURE( xCell, "XclExpRow::InsertCell - missing cell" );
+
+ /* If we have a multi-line text in a merged cell, and the resulting
+ row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED
+ flag to be true to ensure Excel works correctly. */
+ if( bIsMergedBase && xCell->IsMultiLineText() )
+ ::set_flag( mnFlags, EXC_ROW_UNSYNCED );
+
+ // try to merge with previous cell, insert the new cell if not successful
+ XclExpCellBase* xPrevCell = maCellList.GetRecord( nPos - 1 );
+ if( xPrevCell && xPrevCell->TryMerge( *xCell ) )
+ xCell = xPrevCell;
+ else
+ maCellList.InsertRecord( xCell, nPos++ );
+ // nPos points now to following cell
+
+ // try to merge with following cell, remove it if successful
+ XclExpCellRef xNextCell = maCellList.GetRecord( nPos );
+ if( xNextCell && xCell->TryMerge( *xNextCell ) )
+ maCellList.RemoveRecord( nPos );
+}
+
+void XclExpRow::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast< sal_uInt16 >(mnCurrentRow)
+ << GetFirstUsedXclCol()
+ << GetFirstFreeXclCol()
+ << mnHeight
+ << sal_uInt32( 0 )
+ << mnFlags
+ << mnXFIndex;
+}
+
+void XclExpRow::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( !mbEnabled )
+ return;
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ bool haveFormat = ::get_flag( mnFlags, EXC_ROW_USEDEFXF );
+ mnCurrentRow = mnXclRow + 1;
+ for ( sal_uInt32 i=0; i<mnXclRowRpt; ++i )
+ {
+ rWorksheet->startElement( XML_row,
+ XML_r, OString::number(mnCurrentRow++),
+ // OOXTODO: XML_spans, optional
+ XML_s, haveFormat ? lcl_GetStyleId( rStrm, mnXFIndex ).getStr() : nullptr,
+ XML_customFormat, ToPsz( haveFormat ),
+ XML_ht, OString::number(static_cast<double>(mnHeight) / 20.0),
+ XML_hidden, ToPsz( ::get_flag( mnFlags, EXC_ROW_HIDDEN ) ),
+ XML_customHeight, ToPsz( ::get_flag( mnFlags, EXC_ROW_UNSYNCED ) ),
+ XML_outlineLevel, OString::number(mnOutlineLevel),
+ XML_collapsed, ToPsz( ::get_flag( mnFlags, EXC_ROW_COLLAPSED ) )
+ // OOXTODO: XML_thickTop, bool
+ // OOXTODO: XML_thickBot, bool
+ // OOXTODO: XML_ph, bool
+ );
+ // OOXTODO: XML_extLst
+ maCellList.SaveXml( rStrm );
+ rWorksheet->endElement( XML_row );
+ }
+}
+
+XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ maOutlineBfr( rRoot ),
+ maDimensions( rRoot ),
+ mnHighestOutlineLevel( 0 )
+{
+}
+
+void XclExpRowBuffer::AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase )
+{
+ OSL_ENSURE( xCell, "XclExpRowBuffer::AppendCell - missing cell" );
+ GetOrCreateRow( xCell->GetXclRow(), false ).AppendCell( xCell, bIsMergedBase );
+}
+
+void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow )
+{
+ if( nFirstFreeScRow > 0 )
+ GetOrCreateRow( ::std::max ( nFirstFreeScRow - 1, GetMaxPos().Row() ), true );
+}
+
+namespace {
+
+class RowFinalizeTask : public comphelper::ThreadTask
+{
+ bool mbProgress;
+ const ScfUInt16Vec& mrColXFIndexes;
+ size_t mnStartColAllDefault;
+ std::vector< XclExpRow * > maRows;
+public:
+ RowFinalizeTask( const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,
+ const ScfUInt16Vec& rColXFIndexes,
+ size_t nStartColAllDefault,
+ bool bProgress ) :
+ comphelper::ThreadTask( pTag ),
+ mbProgress( bProgress ),
+ mrColXFIndexes( rColXFIndexes ),
+ mnStartColAllDefault( nStartColAllDefault )
+ {}
+
+ void push_back( XclExpRow *pRow ) { maRows.push_back( pRow ); }
+ virtual void doWork() override
+ {
+ ScfUInt16Vec aXFIndexes( mrColXFIndexes.size(), EXC_XF_NOTFOUND );
+ for (XclExpRow* p : maRows)
+ p->Finalize( mrColXFIndexes, aXFIndexes, mnStartColAllDefault, mbProgress );
+ }
+};
+
+}
+
+void XclExpRowBuffer::Finalize( XclExpDefaultRowData& rDefRowData,
+ const ScfUInt16Vec& rColXFIndexes,
+ size_t nStartColAllDefault )
+{
+ // *** Finalize all rows *** ----------------------------------------------
+
+ GetProgressBar().ActivateFinalRowsSegment();
+
+#if 1
+ // This is staggeringly slow, and each element operates only
+ // on its own data.
+ const size_t nRows = maRowMap.size();
+ const size_t nThreads = nRows < 128 ? 1 : comphelper::ThreadPool::getPreferredConcurrency();
+#else
+ const size_t nThreads = 1; // globally disable multi-threading for now.
+#endif
+ if (nThreads == 1)
+ {
+ ScfUInt16Vec aXFIndexes( rColXFIndexes.size(), EXC_XF_NOTFOUND );
+ for (auto& rEntry : maRowMap)
+ rEntry.second->Finalize( rColXFIndexes, aXFIndexes, nStartColAllDefault, true );
+ }
+ else
+ {
+ comphelper::ThreadPool &rPool = comphelper::ThreadPool::getSharedOptimalPool();
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+ std::vector<std::unique_ptr<RowFinalizeTask>> aTasks(nThreads);
+ for ( size_t i = 0; i < nThreads; i++ )
+ aTasks[ i ].reset( new RowFinalizeTask( pTag, rColXFIndexes, nStartColAllDefault, i == 0 ) );
+
+ size_t nIdx = 0;
+ for ( const auto& rEntry : maRowMap )
+ {
+ aTasks[ nIdx % nThreads ]->push_back( rEntry.second.get() );
+ ++nIdx;
+ }
+
+ for ( size_t i = 1; i < nThreads; i++ )
+ rPool.pushTask( std::move(aTasks[ i ]) );
+
+ // Progress bar updates must be synchronous to avoid deadlock
+ aTasks[0]->doWork();
+
+ rPool.waitUntilDone(pTag);
+ }
+
+ // *** Default row format *** ---------------------------------------------
+
+ std::map< XclExpDefaultRowData, size_t > aDefRowMap;
+
+ XclExpDefaultRowData aMaxDefData;
+ size_t nMaxDefCount = 0;
+ // only look for default format in existing rows, if there are more than unused
+ // if the row is hidden, then row xml must be created even if it not contain cells
+ XclExpRow* pPrev = nullptr;
+ std::vector< XclExpRow* > aRepeated;
+ for (const auto& rEntry : maRowMap)
+ {
+ const RowRef& rRow = rEntry.second;
+ if ( rRow->IsDefaultable() )
+ {
+ XclExpDefaultRowData aDefData( *rRow );
+ size_t& rnDefCount = aDefRowMap[ aDefData ];
+ ++rnDefCount;
+ if( rnDefCount > nMaxDefCount )
+ {
+ nMaxDefCount = rnDefCount;
+ aMaxDefData = aDefData;
+ }
+ }
+ if ( pPrev )
+ {
+ if ( pPrev->IsDefaultable() )
+ {
+ // if the previous row we processed is not
+ // defaultable then afaict the rows in between are
+ // not used ( and not repeatable )
+ sal_uInt32 nRpt = rRow->GetXclRow() - pPrev->GetXclRow();
+ if ( nRpt > 1 )
+ aRepeated.push_back( pPrev );
+ pPrev->SetXclRowRpt( nRpt );
+ XclExpDefaultRowData aDefData( *pPrev );
+ size_t& rnDefCount = aDefRowMap[ aDefData ];
+ rnDefCount += ( pPrev->GetXclRowRpt() - 1 );
+ if( rnDefCount > nMaxDefCount )
+ {
+ nMaxDefCount = rnDefCount;
+ aMaxDefData = aDefData;
+ }
+ }
+ }
+ pPrev = rRow.get();
+ }
+ // return the default row format to caller
+ rDefRowData = aMaxDefData;
+
+ // now disable repeating extra (empty) rows that are equal to the default row
+ for (auto& rpRow : aRepeated)
+ {
+ if ( rpRow->GetXclRowRpt() > 1
+ && rpRow->GetHeight() == rDefRowData.mnHeight
+ && rpRow->IsHidden() == rDefRowData.IsHidden() )
+ {
+ rpRow->SetXclRowRpt( 1 );
+ }
+ }
+
+ // *** Disable unused ROW records, find used area *** ---------------------
+
+ sal_uInt16 nFirstUsedXclCol = SAL_MAX_UINT16;
+ sal_uInt16 nFirstFreeXclCol = 0;
+ sal_uInt32 nFirstUsedXclRow = SAL_MAX_UINT32;
+ sal_uInt32 nFirstFreeXclRow = 0;
+
+ for (const auto& rEntry : maRowMap)
+ {
+ const RowRef& rRow = rEntry.second;
+ // disable unused rows
+ rRow->DisableIfDefault( aMaxDefData );
+
+ // find used column range
+ if( !rRow->IsEmpty() ) // empty rows return (0...0) as used range
+ {
+ nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, rRow->GetFirstUsedXclCol() );
+ nFirstFreeXclCol = ::std::max( nFirstFreeXclCol, rRow->GetFirstFreeXclCol() );
+ }
+
+ // find used row range
+ if( rRow->IsEnabled() )
+ {
+ sal_uInt32 nXclRow = rRow->GetXclRow();
+ nFirstUsedXclRow = ::std::min< sal_uInt32 >( nFirstUsedXclRow, nXclRow );
+ nFirstFreeXclRow = ::std::max< sal_uInt32 >( nFirstFreeXclRow, nXclRow + 1 );
+ }
+ }
+
+ // adjust start position, if there are no or only empty/disabled ROW records
+ nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, nFirstFreeXclCol );
+ nFirstUsedXclRow = ::std::min( nFirstUsedXclRow, nFirstFreeXclRow );
+
+ // initialize the DIMENSIONS record
+ maDimensions.SetDimensions(
+ nFirstUsedXclCol, nFirstUsedXclRow, nFirstFreeXclCol, nFirstFreeXclRow );
+}
+
+void XclExpRowBuffer::Save( XclExpStream& rStrm )
+{
+ // DIMENSIONS record
+ maDimensions.Save( rStrm );
+
+ // save in blocks of 32 rows, each block contains first all ROWs, then all cells
+ size_t nSize = maRowMap.size();
+ RowMap::iterator itr = maRowMap.begin(), itrEnd = maRowMap.end();
+ RowMap::iterator itrBlkStart = maRowMap.begin(), itrBlkEnd = maRowMap.begin();
+ sal_uInt16 nStartXclRow = (nSize == 0) ? 0 : itr->second->GetXclRow();
+
+ for (; itr != itrEnd; ++itr)
+ {
+ // find end of row block
+ itrBlkEnd = std::find_if_not(itrBlkEnd, itrEnd,
+ [&nStartXclRow](const RowMap::value_type& rRow) { return rRow.second->GetXclRow() - nStartXclRow < EXC_ROW_ROWBLOCKSIZE; });
+
+ // write the ROW records
+ std::for_each(itrBlkStart, itrBlkEnd, [&rStrm](const RowMap::value_type& rRow) { rRow.second->Save( rStrm ); });
+
+ // write the cell records
+ std::for_each(itrBlkStart, itrBlkEnd, [&rStrm](const RowMap::value_type& rRow) { rRow.second->WriteCellList( rStrm ); });
+
+ itrBlkStart = (itrBlkEnd == itrEnd) ? itrBlkEnd : itrBlkEnd++;
+ nStartXclRow += EXC_ROW_ROWBLOCKSIZE;
+ }
+}
+
+void XclExpRowBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (std::none_of(maRowMap.begin(), maRowMap.end(), [](const RowMap::value_type& rRow) { return rRow.second->IsEnabled(); }))
+ {
+ rStrm.GetCurrentStream()->singleElement(XML_sheetData);
+ return;
+ }
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_sheetData);
+ for (const auto& rEntry : maRowMap)
+ rEntry.second->SaveXml(rStrm);
+ rWorksheet->endElement( XML_sheetData );
+}
+
+XclExpRow& XclExpRowBuffer::GetOrCreateRow( sal_uInt32 nXclRow, bool bRowAlwaysEmpty )
+{
+ // This is called rather often, so optimize for the most common case of saving row by row
+ // (so the requested row is often the last one in the map or belongs after the last one).
+ RowMap::iterator itr;
+ if(maRowMap.empty())
+ itr = maRowMap.end();
+ else
+ {
+ RowMap::reverse_iterator last = maRowMap.rbegin();
+ if( last->first == nXclRow )
+ return *last->second;
+ if( nXclRow > last->first )
+ itr = maRowMap.end();
+ else
+ itr = maRowMap.lower_bound( nXclRow );
+ }
+ const bool bFound = itr != maRowMap.end();
+ // bFoundHigher: nXclRow was identical to the previous entry, so not explicitly created earlier
+ // coverity[deref_iterator : FALSE] - clearly itr if only derefed if bFound which checks for valid itr
+ const bool bFoundHigher = bFound && itr->first != nXclRow;
+ if( bFound && !bFoundHigher )
+ return *itr->second;
+
+ size_t nFrom = 0;
+ RowRef pPrevEntry;
+ if( itr != maRowMap.begin() )
+ {
+ --itr;
+ pPrevEntry = itr->second;
+ if( bFoundHigher )
+ nFrom = nXclRow;
+ else
+ nFrom = itr->first + 1;
+ }
+
+ const ScDocument& rDoc = GetRoot().GetDoc();
+ const SCTAB nScTab = GetRoot().GetCurrScTab();
+ // Do not repeatedly call RowHidden() / GetRowHeight() for same values.
+ bool bHidden = false;
+ SCROW lastSameHiddenRow = -1;
+ sal_uInt16 nHeight = 0;
+ SCROW lastSameHeightRow = -1;
+ // create the missing rows first
+ while( nFrom <= nXclRow )
+ {
+ // only create RowMap entries if it is first row in spreadsheet,
+ // if it is the desired row, or for rows that differ from previous.
+ if( static_cast<SCROW>(nFrom) > lastSameHiddenRow )
+ bHidden = rDoc.RowHidden(nFrom, nScTab, nullptr, &lastSameHiddenRow);
+ // Always get the actual row height even if the manual size flag is
+ // not set, to correctly export the heights of rows with wrapped
+ // texts.
+ if( static_cast<SCROW>(nFrom) > lastSameHeightRow )
+ nHeight = rDoc.GetRowHeight(nFrom, nScTab, nullptr, &lastSameHeightRow, false);
+ if ( !pPrevEntry || ( nFrom == nXclRow ) ||
+ ( maOutlineBfr.IsCollapsed() ) ||
+ ( maOutlineBfr.GetLevel() != 0 ) ||
+ ( bRowAlwaysEmpty && !pPrevEntry->IsEmpty() ) ||
+ ( bHidden != pPrevEntry->IsHidden() ) ||
+ ( nHeight != pPrevEntry->GetHeight() ) )
+ {
+ if( maOutlineBfr.GetLevel() > mnHighestOutlineLevel )
+ {
+ mnHighestOutlineLevel = maOutlineBfr.GetLevel();
+ }
+ RowRef p = std::make_shared<XclExpRow>(GetRoot(), nFrom, maOutlineBfr, bRowAlwaysEmpty, bHidden, nHeight);
+ maRowMap.emplace(nFrom, p);
+ pPrevEntry = p;
+ }
+ ++nFrom;
+ }
+ itr = maRowMap.find(nXclRow);
+ return *itr->second;
+}
+
+// Cell Table
+
+XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ maColInfoBfr( rRoot ),
+ maRowBfr( rRoot ),
+ maArrayBfr( rRoot ),
+ maShrfmlaBfr( rRoot ),
+ maTableopBfr( rRoot ),
+ mxDefrowheight( new XclExpDefrowheight() ),
+ mxGuts( new XclExpGuts( rRoot ) ),
+ mxNoteList( new XclExpNoteList ),
+ mxMergedcells( new XclExpMergedcells( rRoot ) ),
+ mxHyperlinkList( new XclExpHyperlinkList ),
+ mxDval( new XclExpDval( rRoot ) ),
+ mxExtLst( new XclExtLst( rRoot ) )
+{
+ ScDocument& rDoc = GetDoc();
+ SCTAB nScTab = GetCurrScTab();
+ SvNumberFormatter& rFormatter = GetFormatter();
+
+ // maximum sheet limits
+ SCCOL nMaxScCol = GetMaxPos().Col();
+ SCROW nMaxScRow = GetMaxPos().Row();
+
+ // find used area (non-empty cells)
+ SCCOL nLastUsedScCol;
+ SCROW nLastUsedScRow;
+ rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
+
+ if(nLastUsedScCol > nMaxScCol)
+ nLastUsedScCol = nMaxScCol;
+
+ // check extra blank rows to avoid of losing their not default settings (workaround for tdf#41425)
+ nLastUsedScRow += 1000;
+
+ if(nLastUsedScRow > nMaxScRow)
+ nLastUsedScRow = nMaxScRow;
+
+ ScRange aUsedRange( 0, 0, nScTab, nLastUsedScCol, nLastUsedScRow, nScTab );
+ GetAddressConverter().ValidateRange( aUsedRange, true );
+ nLastUsedScRow = aUsedRange.aEnd.Row();
+
+ // first row without any set attributes (height/hidden/...)
+ SCROW nFirstUnflaggedScRow = rDoc.GetLastFlaggedRow( nScTab ) + 1;
+
+ // find range of outlines
+ SCROW nFirstUngroupedScRow = 0;
+ if( const ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable( nScTab ) )
+ {
+ SCCOLROW nScStartPos, nScEndPos;
+ const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
+ rRowArray.GetRange( nScStartPos, nScEndPos );
+ // +1 because open/close button is in next row in Excel, +1 for "end->first unused"
+ nFirstUngroupedScRow = static_cast< SCROW >( nScEndPos + 2 );
+ }
+
+ // column settings
+ /* #i30411# Files saved with SO7/OOo1.x with nonstandard default column
+ formatting cause big Excel files, because all rows from row 1 to row
+ 32000 are exported. Now, if the used area goes exactly to row 32000,
+ use this row as default and ignore all rows >32000.
+ #i59220# Tolerance of +-128 rows for inserted/removed rows. */
+ if( (31871 <= nLastUsedScRow) && (nLastUsedScRow <= 32127) && (nFirstUnflaggedScRow < nLastUsedScRow) && (nFirstUngroupedScRow <= nLastUsedScRow) )
+ nMaxScRow = nLastUsedScRow;
+ maColInfoBfr.Initialize( nMaxScRow );
+
+ // range for cell iterator
+ SCCOL nLastIterScCol = nMaxScCol;
+ SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow, nMaxScRow );
+ ScUsedAreaIterator aIt( rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow );
+
+ // activate the correct segment and sub segment at the progress bar
+ GetProgressBar().ActivateCreateRowsSegment();
+
+ for( bool bIt = aIt.GetNext(); bIt; bIt = aIt.GetNext() )
+ {
+ SCCOL nScCol = aIt.GetStartCol();
+ SCROW nScRow = aIt.GetRow();
+ SCCOL nLastScCol = aIt.GetEndCol();
+ ScAddress aScPos( nScCol, nScRow, nScTab );
+
+ XclAddress aXclPos( static_cast< sal_uInt16 >( nScCol ), static_cast< sal_uInt32 >( nScRow ) );
+ sal_uInt16 nLastXclCol = static_cast< sal_uInt16 >( nLastScCol );
+
+ const ScRefCellValue& rScCell = aIt.GetCell();
+ XclExpCellRef xCell;
+
+ const ScPatternAttr* pPattern = aIt.GetPattern();
+
+ // handle overlapped merged cells before creating the cell record
+ sal_uInt32 nMergeBaseXFId = EXC_XFID_NOTFOUND;
+ bool bIsMergedBase = false;
+ if( pPattern )
+ {
+ const SfxItemSet& rItemSet = pPattern->GetItemSet();
+ // base cell in a merged range
+ const ScMergeAttr& rMergeItem = rItemSet.Get( ATTR_MERGE );
+ bIsMergedBase = rMergeItem.IsMerged();
+ /* overlapped cell in a merged range; in Excel all merged cells
+ must contain same XF index, for correct border */
+ const ScMergeFlagAttr& rMergeFlagItem = rItemSet.Get( ATTR_MERGE_FLAG );
+ if( rMergeFlagItem.IsOverlapped() )
+ nMergeBaseXFId = mxMergedcells->GetBaseXFId( aScPos );
+ }
+
+ OUString aAddNoteText; // additional text to be appended to a note
+
+ switch (rScCell.meType)
+ {
+ case CELLTYPE_VALUE:
+ {
+ double fValue = rScCell.mfValue;
+
+ if (pPattern)
+ {
+ OUString aUrl = pPattern->GetItemSet().Get(ATTR_HYPERLINK).GetValue();
+ if (!aUrl.isEmpty())
+ {
+ rtl::Reference<XclExpHyperlink> aLink =
+ new XclExpHyperlink(GetRoot(), SvxURLField(aUrl, aUrl), aScPos);
+ mxHyperlinkList->AppendRecord(aLink);
+ }
+ }
+
+ // try to create a Boolean cell
+ if( pPattern && ((fValue == 0.0) || (fValue == 1.0)) )
+ {
+ sal_uInt32 nScNumFmt = pPattern->GetItemSet().Get( ATTR_VALUE_FORMAT ).GetValue();
+ if( rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL )
+ xCell = new XclExpBooleanCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue != 0.0 );
+ }
+
+ // try to create an RK value (compressed floating-point number)
+ sal_Int32 nRkValue;
+ if( !xCell && XclTools::GetRKFromDouble( nRkValue, fValue ) )
+ xCell = new XclExpRkCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, nRkValue );
+
+ // else: simple floating-point number cell
+ if( !xCell )
+ xCell = new XclExpNumberCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue );
+ }
+ break;
+
+ case CELLTYPE_STRING:
+ {
+ xCell = new XclExpLabelCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.mpString->getString());
+ }
+ break;
+
+ case CELLTYPE_EDIT:
+ {
+ XclExpHyperlinkHelper aLinkHelper( GetRoot(), aScPos );
+ xCell = new XclExpLabelCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.mpEditText, aLinkHelper);
+
+ // add a single created HLINK record to the record list
+ if( aLinkHelper.HasLinkRecord() )
+ mxHyperlinkList->AppendRecord( aLinkHelper.GetLinkRecord() );
+ // add list of multiple URLs to the additional cell note text
+ if( aLinkHelper.HasMultipleUrls() )
+ aAddNoteText = ScGlobal::addToken( aAddNoteText, aLinkHelper.GetUrlList(), '\n', 2 );
+ }
+ break;
+
+ case CELLTYPE_FORMULA:
+ {
+ if (pPattern)
+ {
+ OUString aUrl = pPattern->GetItemSet().Get(ATTR_HYPERLINK).GetValue();
+ if (!aUrl.isEmpty())
+ {
+ rtl::Reference<XclExpHyperlink> aLink =
+ new XclExpHyperlink(GetRoot(), SvxURLField(aUrl, aUrl), aScPos);
+ mxHyperlinkList->AppendRecord(aLink);
+ }
+ }
+
+ xCell = new XclExpFormulaCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId,
+ *rScCell.mpFormula, maArrayBfr, maShrfmlaBfr, maTableopBfr);
+ }
+ break;
+
+ default:
+ OSL_FAIL( "XclExpCellTable::XclExpCellTable - unknown cell type" );
+ [[fallthrough]];
+ case CELLTYPE_NONE:
+ {
+ xCell = new XclExpBlankCell(
+ GetRoot(), aXclPos, nLastXclCol, pPattern, nMergeBaseXFId );
+ }
+ break;
+ }
+
+ assert(xCell && "can only reach here with xCell set");
+
+ // insert the cell into the current row
+ maRowBfr.AppendCell( xCell, bIsMergedBase );
+
+ if ( !aAddNoteText.isEmpty() )
+ mxNoteList->AppendNewRecord( new XclExpNote( GetRoot(), aScPos, nullptr, aAddNoteText ) );
+
+ // other sheet contents
+ if( pPattern )
+ {
+ const SfxItemSet& rItemSet = pPattern->GetItemSet();
+
+ // base cell in a merged range
+ if( bIsMergedBase )
+ {
+ const ScMergeAttr& rMergeItem = rItemSet.Get( ATTR_MERGE );
+ ScRange aScRange( aScPos );
+ aScRange.aEnd.IncCol( rMergeItem.GetColMerge() - 1 );
+ aScRange.aEnd.IncRow( rMergeItem.GetRowMerge() - 1 );
+ sal_uInt32 nXFId = xCell->GetFirstXFId();
+ // blank cells merged vertically may occur repeatedly
+ OSL_ENSURE( (aScRange.aStart.Col() == aScRange.aEnd.Col()) || (nScCol == nLastScCol),
+ "XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" );
+ for( SCCOL nIndex = nScCol; nIndex <= nLastScCol; ++nIndex )
+ {
+ mxMergedcells->AppendRange( aScRange, nXFId );
+ aScRange.aStart.IncCol();
+ aScRange.aEnd.IncCol();
+ }
+ }
+
+ // data validation
+ if( ScfTools::CheckItem( rItemSet, ATTR_VALIDDATA, false ) )
+ {
+ sal_uLong nScHandle = rItemSet.Get( ATTR_VALIDDATA ).GetValue();
+ ScRange aScRange( aScPos );
+ aScRange.aEnd.SetCol( nLastScCol );
+ mxDval->InsertCellRange( aScRange, nScHandle );
+ }
+ }
+ }
+
+ // create missing row settings for rows anyhow flagged or with outlines
+ maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, nFirstUngroupedScRow ) );
+}
+
+void XclExpCellTable::Finalize(bool bXLS)
+{
+ // Finalize multiple operations.
+ maTableopBfr.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, bXLS );
+
+ // Usually many indexes towards the end will be EXC_XF_DEFAULTCELL, find
+ // the index that starts all EXC_XF_DEFAULTCELL until the end.
+ size_t nStartColAllDefault = findFirstAllSameUntilEnd( aColXFIndexes, EXC_XF_DEFAULTCELL );
+
+ /* Finalize row buffer. This calculates all cell XF indexes from the XF
+ identifiers. Then the XF index vector aColXFIndexes (filled above) is
+ used to calculate the row default formats. With this, all unneeded blank
+ cell records (equal to row default or column default) will be removed.
+ The function returns the (most used) default row format in aDefRowData. */
+ XclExpDefaultRowData aDefRowData;
+ maRowBfr.Finalize( aDefRowData, aColXFIndexes, nStartColAllDefault );
+
+ // Initialize the DEFROWHEIGHT record.
+ mxDefrowheight->SetDefaultData( aDefRowData );
+}
+
+XclExpRecordRef XclExpCellTable::CreateRecord( sal_uInt16 nRecId ) const
+{
+ XclExpRecordRef xRec;
+ switch( nRecId )
+ {
+ case EXC_ID3_DIMENSIONS: xRec = new XclExpDelegatingRecord( &const_cast<XclExpRowBuffer*>(&maRowBfr)->GetDimensions() ); break;
+ case EXC_ID2_DEFROWHEIGHT: xRec = mxDefrowheight; break;
+ case EXC_ID_GUTS: xRec = mxGuts; break;
+ case EXC_ID_NOTE: xRec = mxNoteList; break;
+ case EXC_ID_MERGEDCELLS: xRec = mxMergedcells; break;
+ case EXC_ID_HLINK: xRec = mxHyperlinkList; break;
+ case EXC_ID_DVAL: xRec = mxDval; break;
+ case EXC_ID_EXTLST: xRec = mxExtLst; break;
+ default: OSL_FAIL( "XclExpCellTable::CreateRecord - unknown record id" );
+ }
+ return xRec;
+}
+
+void XclExpCellTable::Save( XclExpStream& rStrm )
+{
+ // DEFCOLWIDTH and COLINFOs
+ maColInfoBfr.Save( rStrm );
+ // ROWs and cell records
+ maRowBfr.Save( rStrm );
+}
+
+void XclExpCellTable::SaveXml( XclExpXmlStream& rStrm )
+{
+ // DEFAULT row height
+ XclExpDefaultRowData& rDefData = mxDefrowheight->GetDefaultData();
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_sheetFormatPr,
+ // OOXTODO: XML_baseColWidth
+ XML_defaultColWidth, OString::number(maColInfoBfr.GetDefColWidth()),
+ // OOXTODO: XML_customHeight
+ // OOXTODO: XML_thickTop
+ // OOXTODO: XML_thickBottom
+ XML_defaultRowHeight, OString::number(static_cast<double> (rDefData.mnHeight) / 20.0),
+ XML_zeroHeight, ToPsz( rDefData.IsHidden() ),
+ XML_outlineLevelRow, OString::number(maRowBfr.GetHighestOutlineLevel()),
+ XML_outlineLevelCol, OString::number(maColInfoBfr.GetHighestOutlineLevel()) );
+ rWorksheet->endElement( XML_sheetFormatPr );
+
+ maColInfoBfr.SaveXml( rStrm );
+ maRowBfr.SaveXml( rStrm );
+ mxExtLst->SaveXml( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeview.cxx b/sc/source/filter/excel/xeview.cxx
new file mode 100644
index 000000000..d94a94407
--- /dev/null
+++ b/sc/source/filter/excel/xeview.cxx
@@ -0,0 +1,537 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xeview.hxx>
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <viewopti.hxx>
+#include <xelink.hxx>
+#include <xestyle.hxx>
+#include <xehelper.hxx>
+#include <xltools.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/export/utils.hxx>
+
+using namespace ::oox;
+
+// Workbook view settings records =============================================
+
+XclExpWindow1::XclExpWindow1( const XclExpRoot& rRoot )
+ : XclExpRecord(EXC_ID_WINDOW1, 18)
+ , mnFlags(0)
+ , mnTabBarSize(600)
+{
+ const ScViewOptions& rViewOpt = rRoot.GetDoc().GetViewOptions();
+ ::set_flag( mnFlags, EXC_WIN1_HOR_SCROLLBAR, rViewOpt.GetOption( VOPT_HSCROLL ) );
+ ::set_flag( mnFlags, EXC_WIN1_VER_SCROLLBAR, rViewOpt.GetOption( VOPT_VSCROLL ) );
+ ::set_flag( mnFlags, EXC_WIN1_TABBAR, rViewOpt.GetOption( VOPT_TABCONTROLS ) );
+
+ double fTabBarWidth = rRoot.GetExtDocOptions().GetDocSettings().mfTabBarWidth;
+ if( (0.0 <= fTabBarWidth) && (fTabBarWidth <= 1.0) )
+ mnTabBarSize = static_cast< sal_uInt16 >( fTabBarWidth * 1000.0 + 0.5 );
+}
+
+void XclExpWindow1::SaveXml( XclExpXmlStream& rStrm )
+{
+ const XclExpTabInfo& rTabInfo = rStrm.GetRoot().GetTabInfo();
+
+ rStrm.GetCurrentStream()->singleElement( XML_workbookView,
+ // OOXTODO: XML_visibility, // ST_visibility
+ // OOXTODO: XML_minimized, // bool
+ XML_showHorizontalScroll, ToPsz( ::get_flag( mnFlags, EXC_WIN1_HOR_SCROLLBAR ) ),
+ XML_showVerticalScroll, ToPsz( ::get_flag( mnFlags, EXC_WIN1_VER_SCROLLBAR ) ),
+ XML_showSheetTabs, ToPsz( ::get_flag( mnFlags, EXC_WIN1_TABBAR ) ),
+ XML_xWindow, "0",
+ XML_yWindow, "0",
+ XML_windowWidth, OString::number(0x4000),
+ XML_windowHeight, OString::number(0x2000),
+ XML_tabRatio, OString::number(mnTabBarSize),
+ XML_firstSheet, OString::number(rTabInfo.GetFirstVisXclTab()),
+ XML_activeTab, OString::number(rTabInfo.GetDisplayedXclTab())
+ // OOXTODO: XML_autoFilterDateGrouping, // bool; AUTOFILTER12? 87Eh
+ );
+}
+
+void XclExpWindow1::WriteBody( XclExpStream& rStrm )
+{
+ const XclExpTabInfo& rTabInfo = rStrm.GetRoot().GetTabInfo();
+
+ rStrm << sal_uInt16( 0 ) // X position of the window
+ << sal_uInt16( 0 ) // Y position of the window
+ << sal_uInt16( 0x4000 ) // width of the window
+ << sal_uInt16( 0x2000 ) // height of the window
+ << mnFlags
+ << rTabInfo.GetDisplayedXclTab()
+ << rTabInfo.GetFirstVisXclTab()
+ << rTabInfo.GetXclSelectedCount()
+ << mnTabBarSize;
+}
+
+// Sheet view settings records ================================================
+
+XclExpWindow2::XclExpWindow2( const XclExpRoot& rRoot,
+ const XclTabViewData& rData, sal_uInt32 nGridColorId ) :
+ XclExpRecord( EXC_ID_WINDOW2, (rRoot.GetBiff() == EXC_BIFF8) ? 18 : 10 ),
+ maGridColor( rData.maGridColor ),
+ mnGridColorId( nGridColorId ),
+ mnFlags( 0 ),
+ maFirstXclPos( rData.maFirstXclPos ),
+ mnNormalZoom( rData.mnNormalZoom ),
+ mnPageZoom( rData.mnPageZoom )
+{
+ ::set_flag( mnFlags, EXC_WIN2_SHOWFORMULAS, rData.mbShowFormulas );
+ ::set_flag( mnFlags, EXC_WIN2_SHOWGRID, rData.mbShowGrid );
+ ::set_flag( mnFlags, EXC_WIN2_SHOWHEADINGS, rData.mbShowHeadings );
+ ::set_flag( mnFlags, EXC_WIN2_FROZEN, rData.mbFrozenPanes );
+ ::set_flag( mnFlags, EXC_WIN2_SHOWZEROS, rData.mbShowZeros );
+ ::set_flag( mnFlags, EXC_WIN2_DEFGRIDCOLOR, rData.mbDefGridColor );
+ ::set_flag( mnFlags, EXC_WIN2_MIRRORED, rData.mbMirrored );
+ ::set_flag( mnFlags, EXC_WIN2_SHOWOUTLINE, rData.mbShowOutline );
+ ::set_flag( mnFlags, EXC_WIN2_FROZENNOSPLIT, rData.mbFrozenPanes );
+ ::set_flag( mnFlags, EXC_WIN2_SELECTED, rData.mbSelected );
+ ::set_flag( mnFlags, EXC_WIN2_DISPLAYED, rData.mbDisplayed );
+ ::set_flag( mnFlags, EXC_WIN2_PAGEBREAKMODE, rData.mbPageMode );
+}
+
+void XclExpWindow2::WriteBody( XclExpStream& rStrm )
+{
+ const XclExpRoot& rRoot = rStrm.GetRoot();
+
+ rStrm << mnFlags
+ << maFirstXclPos;
+
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ rStrm << maGridColor;
+ break;
+ case EXC_BIFF8:
+ rStrm << rRoot.GetPalette().GetColorIndex( mnGridColorId )
+ << sal_uInt16( 0 )
+ << mnPageZoom
+ << mnNormalZoom
+ << sal_uInt32( 0 );
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+XclExpScl::XclExpScl( sal_uInt16 nZoom ) :
+ XclExpRecord( EXC_ID_SCL, 4 ),
+ mnNum( nZoom ),
+ mnDenom( 100 )
+{
+ Shorten( 2 );
+ Shorten( 5 );
+}
+
+void XclExpScl::Shorten( sal_uInt16 nFactor )
+{
+ while( (mnNum % nFactor == 0) && (mnDenom % nFactor == 0) )
+ {
+ mnNum = mnNum / nFactor;
+ mnDenom = mnDenom / nFactor;
+ }
+}
+
+void XclExpScl::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF4 );
+ rStrm << mnNum << mnDenom;
+}
+
+XclExpPane::XclExpPane( const XclTabViewData& rData ) :
+ XclExpRecord( EXC_ID_PANE, 10 ),
+ mnSplitX( rData.mnSplitX ),
+ mnSplitY( rData.mnSplitY ),
+ maSecondXclPos( rData.maSecondXclPos ),
+ mnActivePane( rData.mnActivePane ),
+ mbFrozenPanes( rData.mbFrozenPanes )
+{
+ OSL_ENSURE( rData.IsSplit(), "XclExpPane::XclExpPane - no PANE record for unsplit view" );
+}
+
+static const char* lcl_GetActivePane( sal_uInt8 nActivePane )
+{
+ switch( nActivePane )
+ {
+ case EXC_PANE_TOPLEFT: return "topLeft";
+ case EXC_PANE_TOPRIGHT: return "topRight";
+ case EXC_PANE_BOTTOMLEFT: return "bottomLeft";
+ case EXC_PANE_BOTTOMRIGHT: return "bottomRight";
+ }
+ return "**error: lcl_GetActivePane";
+}
+
+void XclExpPane::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->singleElement( XML_pane,
+ XML_xSplit, OString::number(mnSplitX),
+ XML_ySplit, OString::number(mnSplitY),
+ XML_topLeftCell, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), maSecondXclPos ).getStr(),
+ XML_activePane, lcl_GetActivePane( mnActivePane ),
+ XML_state, mbFrozenPanes ? "frozen" : "split" );
+}
+
+void XclExpPane::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnSplitX
+ << static_cast<sal_uInt16>( mnSplitY )
+ << maSecondXclPos
+ << mnActivePane;
+ if( rStrm.GetRoot().GetBiff() >= EXC_BIFF5 )
+ rStrm << sal_uInt8( 0 );
+}
+
+XclExpSelection::XclExpSelection( const XclTabViewData& rData, sal_uInt8 nPane ) :
+ XclExpRecord( EXC_ID_SELECTION, 15 ),
+ mnPane( nPane )
+{
+ if( const XclSelectionData* pSelData = rData.GetSelectionData( nPane ) )
+ maSelData = *pSelData;
+
+ // find the cursor position in the selection list (or add it)
+ XclRangeList& rXclSel = maSelData.maXclSelection;
+ auto aIt = std::find_if(rXclSel.begin(), rXclSel.end(),
+ [this](const XclRange& rRange) { return rRange.Contains(maSelData.maXclCursor); });
+ if (aIt != rXclSel.end())
+ {
+ maSelData.mnCursorIdx = static_cast< sal_uInt16 >( std::distance(rXclSel.begin(), aIt) );
+ }
+ else
+ {
+ /* Cursor cell not found in list? (e.g. inactive pane, or removed in
+ ConvertRangeList(), because Calc cursor on invalid pos)
+ -> insert the valid Excel cursor. */
+ maSelData.mnCursorIdx = static_cast< sal_uInt16 >( rXclSel.size() );
+ rXclSel.push_back( XclRange( maSelData.maXclCursor ) );
+ }
+}
+
+void XclExpSelection::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->singleElement( XML_selection,
+ XML_pane, lcl_GetActivePane( mnPane ),
+ XML_activeCell, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), maSelData.maXclCursor ).getStr(),
+ XML_activeCellId, OString::number(maSelData.mnCursorIdx),
+ XML_sqref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maSelData.maXclSelection) );
+}
+
+void XclExpSelection::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnPane // pane for this selection
+ << maSelData.maXclCursor // cell cursor
+ << maSelData.mnCursorIdx; // index to range containing cursor
+ maSelData.maXclSelection.Write( rStrm, false );
+}
+
+XclExpTabBgColor::XclExpTabBgColor( const XclTabViewData& rTabViewData ) :
+ XclExpRecord( EXC_ID_SHEETEXT, 18 ),
+ mrTabViewData( rTabViewData )
+{
+}
+//TODO Fix savexml...
+/*void XclExpTabBgColor::SaveXml( XclExpXmlStream& rStrm )
+{
+}*/
+
+void XclExpTabBgColor::WriteBody( XclExpStream& rStrm )
+{
+ if ( mrTabViewData.IsDefaultTabBgColor() )
+ return;
+ sal_uInt16 const rt = 0x0862; //rt
+ sal_uInt16 const grbitFrt = 0x0000; //grbit must be set to 0
+ sal_uInt32 unused = 0x00000000; //Use twice...
+ sal_uInt32 const cb = 0x00000014; // Record Size, may be larger in future...
+ sal_uInt16 const reserved = 0x0000; //trailing bits are 0
+ sal_uInt16 TabBgColorIndex;
+ XclExpPalette& rPal = rStrm.GetRoot().GetPalette();
+ TabBgColorIndex = rPal.GetColorIndex(mrTabViewData.mnTabBgColorId);
+ if (TabBgColorIndex < 8 || TabBgColorIndex > 63 ) // only numbers 8 - 63 are valid numbers
+ TabBgColorIndex = 127; //Excel specs: 127 makes excel ignore tab color information.
+ rStrm << rt << grbitFrt << unused << unused << cb << TabBgColorIndex << reserved;
+}
+
+// Sheet view settings ========================================================
+
+namespace {
+
+/** Converts a Calc zoom factor into an Excel zoom factor. Returns 0 for a default zoom value. */
+sal_uInt16 lclGetXclZoom( tools::Long nScZoom, sal_uInt16 nDefXclZoom )
+{
+ sal_uInt16 nXclZoom = limit_cast< sal_uInt16 >( nScZoom, EXC_ZOOM_MIN, EXC_ZOOM_MAX );
+ return (nXclZoom == nDefXclZoom) ? 0 : nXclZoom;
+}
+
+} // namespace
+
+XclExpTabViewSettings::XclExpTabViewSettings( const XclExpRoot& rRoot, SCTAB nScTab ) :
+ XclExpRoot( rRoot ),
+ mnGridColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT ) ),
+ mbHasTabSettings(false)
+{
+ // *** sheet flags ***
+
+ const XclExpTabInfo& rTabInfo = GetTabInfo();
+ maData.mbSelected = rTabInfo.IsSelectedTab( nScTab );
+ maData.mbDisplayed = rTabInfo.IsDisplayedTab( nScTab );
+ maData.mbMirrored = rTabInfo.IsMirroredTab( nScTab );
+
+ const ScViewOptions& rViewOpt = GetDoc().GetViewOptions();
+ maData.mbShowFormulas = rViewOpt.GetOption( VOPT_FORMULAS );
+ maData.mbShowHeadings = rViewOpt.GetOption( VOPT_HEADER );
+ maData.mbShowZeros = rViewOpt.GetOption( VOPT_NULLVALS );
+ maData.mbShowOutline = rViewOpt.GetOption( VOPT_OUTLINER );
+
+ // *** sheet options: cursor, selection, splits, grid color, zoom ***
+
+ if( const ScExtTabSettings* pTabSett = GetExtDocOptions().GetTabSettings( nScTab ) )
+ {
+ mbHasTabSettings = true;
+ const ScExtTabSettings& rTabSett = *pTabSett;
+ XclExpAddressConverter& rAddrConv = GetAddressConverter();
+
+ // first visible cell in top-left pane
+ if( (rTabSett.maFirstVis.Col() >= 0) && (rTabSett.maFirstVis.Row() >= 0) )
+ maData.maFirstXclPos = rAddrConv.CreateValidAddress( rTabSett.maFirstVis, false );
+
+ // first visible cell in additional pane(s)
+ if( (rTabSett.maSecondVis.Col() >= 0) && (rTabSett.maSecondVis.Row() >= 0) )
+ maData.maSecondXclPos = rAddrConv.CreateValidAddress( rTabSett.maSecondVis, false );
+
+ // active pane
+ switch( rTabSett.meActivePane )
+ {
+ case SCEXT_PANE_TOPLEFT: maData.mnActivePane = EXC_PANE_TOPLEFT; break;
+ case SCEXT_PANE_TOPRIGHT: maData.mnActivePane = EXC_PANE_TOPRIGHT; break;
+ case SCEXT_PANE_BOTTOMLEFT: maData.mnActivePane = EXC_PANE_BOTTOMLEFT; break;
+ case SCEXT_PANE_BOTTOMRIGHT: maData.mnActivePane = EXC_PANE_BOTTOMRIGHT; break;
+ }
+
+ // freeze/split position
+ maData.mbFrozenPanes = rTabSett.mbFrozenPanes;
+ if( maData.mbFrozenPanes )
+ {
+ /* Frozen panes: handle split position as row/column positions.
+ #i35812# Excel uses number of visible rows/columns, Calc uses position of freeze. */
+ SCCOL nFreezeScCol = rTabSett.maFreezePos.Col();
+ if( (0 < nFreezeScCol) && (nFreezeScCol <= GetXclMaxPos().Col()) )
+ maData.mnSplitX = static_cast< sal_uInt16 >( nFreezeScCol ) - maData.maFirstXclPos.mnCol;
+ SCROW nFreezeScRow = rTabSett.maFreezePos.Row();
+ if( (0 < nFreezeScRow) && (nFreezeScRow <= GetXclMaxPos().Row()) )
+ maData.mnSplitY = static_cast< sal_uInt32 >( nFreezeScRow ) - maData.maFirstXclPos.mnRow;
+ // if both splits are left out (address overflow), remove the frozen flag
+ maData.mbFrozenPanes = maData.IsSplit();
+
+ // #i20671# frozen panes: mostright/mostbottom pane is active regardless of cursor position
+ if( maData.HasPane( EXC_PANE_BOTTOMRIGHT ) )
+ maData.mnActivePane = EXC_PANE_BOTTOMRIGHT;
+ else if( maData.HasPane( EXC_PANE_TOPRIGHT ) )
+ maData.mnActivePane = EXC_PANE_TOPRIGHT;
+ else if( maData.HasPane( EXC_PANE_BOTTOMLEFT ) )
+ maData.mnActivePane = EXC_PANE_BOTTOMLEFT;
+ }
+ else
+ {
+ // split window: position is in twips
+ maData.mnSplitX = static_cast<sal_uInt16>(rTabSett.maSplitPos.X());
+ maData.mnSplitY = static_cast<sal_uInt32>(rTabSett.maSplitPos.Y());
+ }
+
+ // selection
+ CreateSelectionData( EXC_PANE_TOPLEFT, rTabSett.maCursor, rTabSett.maSelection );
+ CreateSelectionData( EXC_PANE_TOPRIGHT, rTabSett.maCursor, rTabSett.maSelection );
+ CreateSelectionData( EXC_PANE_BOTTOMLEFT, rTabSett.maCursor, rTabSett.maSelection );
+ CreateSelectionData( EXC_PANE_BOTTOMRIGHT, rTabSett.maCursor, rTabSett.maSelection );
+
+ // grid color
+ const Color& rGridColor = rTabSett.maGridColor;
+ maData.mbDefGridColor = rGridColor == COL_AUTO;
+ if( !maData.mbDefGridColor )
+ {
+ if( GetBiff() == EXC_BIFF8 )
+ mnGridColorId = GetPalette().InsertColor( rGridColor, EXC_COLOR_GRID );
+ else
+ maData.maGridColor = rGridColor;
+ }
+ maData.mbShowGrid = rTabSett.mbShowGrid;
+
+ // view mode and zoom
+ maData.mbPageMode = (GetBiff() == EXC_BIFF8) && rTabSett.mbPageMode;
+ maData.mnNormalZoom = lclGetXclZoom( rTabSett.mnNormalZoom, EXC_WIN2_NORMALZOOM_DEF );
+ maData.mnPageZoom = lclGetXclZoom( rTabSett.mnPageZoom, EXC_WIN2_PAGEZOOM_DEF );
+ maData.mnCurrentZoom = maData.mbPageMode ? maData.mnPageZoom : maData.mnNormalZoom;
+ }
+
+ // Tab Bg Color
+ if ( GetBiff() == EXC_BIFF8 && !GetDoc().IsDefaultTabBgColor(nScTab) )
+ {
+ XclExpPalette& rPal = GetPalette();
+ maData.maTabBgColor = GetDoc().GetTabBgColor(nScTab);
+ maData.mnTabBgColorId = rPal.InsertColor(maData.maTabBgColor, EXC_COLOR_TABBG, EXC_COLOR_NOTABBG );
+ }
+}
+
+void XclExpTabViewSettings::Save( XclExpStream& rStrm )
+{
+ WriteWindow2( rStrm );
+ WriteScl( rStrm );
+ WritePane( rStrm );
+ WriteSelection( rStrm, EXC_PANE_TOPLEFT );
+ WriteSelection( rStrm, EXC_PANE_TOPRIGHT );
+ WriteSelection( rStrm, EXC_PANE_BOTTOMLEFT );
+ WriteSelection( rStrm, EXC_PANE_BOTTOMRIGHT );
+ WriteTabBgColor( rStrm );
+}
+
+static void lcl_WriteSelection( XclExpXmlStream& rStrm, const XclTabViewData& rData, sal_uInt8 nPane )
+{
+ if( rData.HasPane( nPane ) )
+ XclExpSelection( rData, nPane ).SaveXml( rStrm );
+}
+
+static OString lcl_GetZoom( sal_uInt16 nZoom )
+{
+ if( nZoom )
+ return OString::number( nZoom );
+ return "100";
+}
+
+void XclExpTabViewSettings::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_sheetViews);
+
+ // handle missing viewdata at embedded XLSX OLE objects
+ if( !mbHasTabSettings && maData.mbSelected )
+ {
+ SCCOL nPosLeft = rStrm.GetRoot().GetDoc().GetPosLeft();
+ SCROW nPosTop = rStrm.GetRoot().GetDoc().GetPosTop();
+ if (nPosLeft > 0 || nPosTop > 0)
+ {
+ ScAddress aLeftTop(nPosLeft, nPosTop, 0);
+ XclExpAddressConverter& rAddrConv = GetAddressConverter();
+ maData.maFirstXclPos = rAddrConv.CreateValidAddress( aLeftTop, false );
+ }
+ }
+
+ rWorksheet->startElement( XML_sheetView,
+ // OOXTODO: XML_windowProtection,
+ XML_showFormulas, ToPsz( maData.mbShowFormulas ),
+ XML_showGridLines, ToPsz( maData.mbShowGrid ),
+ XML_showRowColHeaders, ToPsz( maData.mbShowHeadings ),
+ XML_showZeros, ToPsz( maData.mbShowZeros ),
+ XML_rightToLeft, ToPsz( maData.mbMirrored ),
+ XML_tabSelected, ToPsz( maData.mbSelected ),
+ // OOXTODO: XML_showRuler,
+ XML_showOutlineSymbols, ToPsz( maData.mbShowOutline ),
+ XML_defaultGridColor, mnGridColorId == XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT ) ? "true" : "false",
+ // OOXTODO: XML_showWhiteSpace,
+ XML_view, maData.mbPageMode ? "pageBreakPreview" : "normal", // OOXTODO: pageLayout
+ XML_topLeftCell, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), maData.maFirstXclPos ).getStr(),
+ XML_colorId, OString::number(rStrm.GetRoot().GetPalette().GetColorIndex(mnGridColorId)),
+ XML_zoomScale, lcl_GetZoom(maData.mnCurrentZoom),
+ XML_zoomScaleNormal, lcl_GetZoom(maData.mnNormalZoom),
+ // OOXTODO: XML_zoomScaleSheetLayoutView,
+ XML_zoomScalePageLayoutView, lcl_GetZoom(maData.mnPageZoom),
+ XML_workbookViewId, "0" // OOXTODO? 0-based index of document(xl/workbook.xml)/workbook/bookviews/workbookView
+ // should always be 0, as we only generate 1 such element.
+ );
+ if( maData.IsSplit() )
+ {
+ XclExpPane aPane( maData );
+ aPane.SaveXml( rStrm );
+ }
+ lcl_WriteSelection( rStrm, maData, EXC_PANE_TOPLEFT );
+ lcl_WriteSelection( rStrm, maData, EXC_PANE_TOPRIGHT );
+ lcl_WriteSelection( rStrm, maData, EXC_PANE_BOTTOMLEFT );
+ lcl_WriteSelection( rStrm, maData, EXC_PANE_BOTTOMRIGHT );
+ rWorksheet->endElement( XML_sheetView );
+ // OOXTODO: XML_extLst
+ rWorksheet->endElement( XML_sheetViews );
+}
+
+// private --------------------------------------------------------------------
+
+void XclExpTabViewSettings::CreateSelectionData( sal_uInt8 nPane,
+ const ScAddress& rCursor, const ScRangeList& rSelection )
+{
+ if( !maData.HasPane( nPane ) )
+ return;
+
+ XclSelectionData& rSelData = maData.CreateSelectionData( nPane );
+
+ // first step: use top-left visible cell as cursor
+ rSelData.maXclCursor.mnCol = ((nPane == EXC_PANE_TOPLEFT) || (nPane == EXC_PANE_BOTTOMLEFT)) ?
+ maData.maFirstXclPos.mnCol : maData.maSecondXclPos.mnCol;
+ rSelData.maXclCursor.mnRow = ((nPane == EXC_PANE_TOPLEFT) || (nPane == EXC_PANE_TOPRIGHT)) ?
+ maData.maFirstXclPos.mnRow : maData.maSecondXclPos.mnRow;
+
+ // second step, active pane: create actual selection data with current cursor position
+ if( nPane == maData.mnActivePane )
+ {
+ XclExpAddressConverter& rAddrConv = GetAddressConverter();
+ // cursor position (keep top-left pane position from above, if rCursor is invalid)
+ if( (rCursor.Col() >= 0) && (rCursor.Row() >= 0) )
+ rSelData.maXclCursor = rAddrConv.CreateValidAddress( rCursor, false );
+ // selection
+ rAddrConv.ConvertRangeList( rSelData.maXclSelection, rSelection, false );
+ }
+}
+
+void XclExpTabViewSettings::WriteWindow2( XclExpStream& rStrm ) const
+{
+// #i43553# GCC 3.3 parse error
+// XclExpWindow2( GetRoot(), maData, mnGridColorId ).Save( rStrm );
+ XclExpWindow2 aWindow2( GetRoot(), maData, mnGridColorId );
+ aWindow2.Save( rStrm );
+}
+
+void XclExpTabViewSettings::WriteScl( XclExpStream& rStrm ) const
+{
+ if( maData.mnCurrentZoom != 0 )
+ XclExpScl( maData.mnCurrentZoom ).Save( rStrm );
+}
+
+void XclExpTabViewSettings::WritePane( XclExpStream& rStrm ) const
+{
+ if( maData.IsSplit() )
+// #i43553# GCC 3.3 parse error
+// XclExpPane( GetRoot(), maData ).Save( rStrm );
+ {
+ XclExpPane aPane( maData );
+ aPane.Save( rStrm );
+ }
+}
+
+void XclExpTabViewSettings::WriteSelection( XclExpStream& rStrm, sal_uInt8 nPane ) const
+{
+ if( maData.HasPane( nPane ) )
+ XclExpSelection( maData, nPane ).Save( rStrm );
+}
+
+void XclExpTabViewSettings::WriteTabBgColor( XclExpStream& rStrm ) const
+{
+ if ( !maData.IsDefaultTabBgColor() )
+ XclExpTabBgColor( maData ).Save( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xichart.cxx b/sc/source/filter/excel/xichart.cxx
new file mode 100644
index 000000000..dc14076b7
--- /dev/null
+++ b/sc/source/filter/excel/xichart.cxx
@@ -0,0 +1,4415 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xichart.hxx>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/drawing/Direction3D.hpp>
+#include <com/sun/star/drawing/ProjectionMode.hpp>
+#include <com/sun/star/drawing/ShadeMode.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/chart/ChartAxisArrangeOrderType.hpp>
+#include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
+#include <com/sun/star/chart/ChartAxisMarkPosition.hpp>
+#include <com/sun/star/chart/ChartAxisPosition.hpp>
+#include <com/sun/star/chart/ChartLegendExpansion.hpp>
+#include <com/sun/star/chart/TimeInterval.hpp>
+#include <com/sun/star/chart/TimeUnit.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart/XDiagramPositioning.hpp>
+#include <com/sun/star/chart/DataLabelPlacement.hpp>
+#include <com/sun/star/chart/ErrorBarStyle.hpp>
+#include <com/sun/star/chart/MissingValueTreatment.hpp>
+#include <com/sun/star/chart2/LinearRegressionCurve.hpp>
+#include <com/sun/star/chart2/ExponentialRegressionCurve.hpp>
+#include <com/sun/star/chart2/LogarithmicRegressionCurve.hpp>
+#include <com/sun/star/chart2/PotentialRegressionCurve.hpp>
+#include <com/sun/star/chart2/PolynomialRegressionCurve.hpp>
+#include <com/sun/star/chart2/MovingAverageRegressionCurve.hpp>
+#include <com/sun/star/chart2/CartesianCoordinateSystem2d.hpp>
+#include <com/sun/star/chart2/CartesianCoordinateSystem3d.hpp>
+#include <com/sun/star/chart2/FormattedString.hpp>
+#include <com/sun/star/chart2/LogarithmicScaling.hpp>
+#include <com/sun/star/chart2/LinearScaling.hpp>
+#include <com/sun/star/chart2/PolarCoordinateSystem2d.hpp>
+#include <com/sun/star/chart2/PolarCoordinateSystem3d.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/XDiagram.hpp>
+#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
+#include <com/sun/star/chart2/XChartTypeContainer.hpp>
+#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
+#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
+#include <com/sun/star/chart2/XTitled.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/chart2/CurveStyle.hpp>
+#include <com/sun/star/chart2/DataPointGeometry3D.hpp>
+#include <com/sun/star/chart2/DataPointLabel.hpp>
+#include <com/sun/star/chart2/LegendPosition.hpp>
+#include <com/sun/star/chart2/StackingDirection.hpp>
+#include <com/sun/star/chart2/TickmarkStyle.hpp>
+#include <com/sun/star/chart2/RelativePosition.hpp>
+#include <com/sun/star/chart2/RelativeSize.hpp>
+#include <com/sun/star/chart2/data/XDataProvider.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/chart2/data/XDataSink.hpp>
+#include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
+#include <comphelper/processfactory.hxx>
+#include <o3tl/numeric.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <sfx2/objsh.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unoapi.hxx>
+#include <sal/log.hxx>
+#include <tools/helpers.hxx>
+
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <tokenarray.hxx>
+#include <compiler.hxx>
+#include <reftokenhelper.hxx>
+#include <chartlis.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <xltracer.hxx>
+#include <xltools.hxx>
+#include <xistream.hxx>
+#include <xiformula.hxx>
+#include <xistyle.hxx>
+#include <xipage.hxx>
+#include <xiview.hxx>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::util::XNumberFormatsSupplier;
+using ::com::sun::star::drawing::XDrawPage;
+using ::com::sun::star::drawing::XDrawPageSupplier;
+using ::com::sun::star::drawing::XShape;
+
+using namespace ::com::sun::star::chart2;
+
+using ::com::sun::star::chart2::data::XDataProvider;
+using ::com::sun::star::chart2::data::XDataReceiver;
+using ::com::sun::star::chart2::data::XDataSequence;
+using ::com::sun::star::chart2::data::XDataSink;
+using ::com::sun::star::chart2::data::XLabeledDataSequence;
+using ::com::sun::star::chart2::data::LabeledDataSequence;
+
+using ::formula::FormulaToken;
+using ::formula::FormulaTokenArrayPlainIterator;
+using ::std::unique_ptr;
+
+namespace cssc = ::com::sun::star::chart;
+namespace cssc2 = ::com::sun::star::chart2;
+
+// Helpers ====================================================================
+
+namespace {
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclChRectangle& rRect )
+{
+ rRect.mnX = rStrm.ReadInt32();
+ rRect.mnY = rStrm.ReadInt32();
+ rRect.mnWidth = rStrm.ReadInt32();
+ rRect.mnHeight = rStrm.ReadInt32();
+ return rStrm;
+}
+
+void lclSetValueOrClearAny( Any& rAny, double fValue, bool bClear )
+{
+ if( bClear )
+ rAny.clear();
+ else
+ rAny <<= fValue;
+}
+
+void lclSetExpValueOrClearAny( Any& rAny, double fValue, bool bLogScale, bool bClear )
+{
+ if( !bClear && bLogScale )
+ fValue = pow( 10.0, fValue );
+ lclSetValueOrClearAny( rAny, fValue, bClear );
+}
+
+double lclGetSerialDay( const XclImpRoot& rRoot, sal_uInt16 nValue, sal_uInt16 nTimeUnit )
+{
+ switch( nTimeUnit )
+ {
+ case EXC_CHDATERANGE_DAYS:
+ return nValue;
+ case EXC_CHDATERANGE_MONTHS:
+ return rRoot.GetDoubleFromDateTime( Date( 1, static_cast< sal_uInt16 >( 1 + nValue % 12 ), static_cast< sal_uInt16 >( rRoot.GetBaseYear() + nValue / 12 ) ) );
+ case EXC_CHDATERANGE_YEARS:
+ return rRoot.GetDoubleFromDateTime( Date( 1, 1, static_cast< sal_uInt16 >( rRoot.GetBaseYear() + nValue ) ) );
+ default:
+ OSL_ENSURE( false, "lclGetSerialDay - unexpected time unit" );
+ }
+ return nValue;
+}
+
+void lclConvertTimeValue( const XclImpRoot& rRoot, Any& rAny, sal_uInt16 nValue, bool bAuto, sal_uInt16 nTimeUnit )
+{
+ if( bAuto )
+ rAny.clear();
+ else
+ rAny <<= lclGetSerialDay( rRoot, nValue, nTimeUnit );
+}
+
+sal_Int32 lclGetApiTimeUnit( sal_uInt16 nTimeUnit )
+{
+ switch( nTimeUnit )
+ {
+ case EXC_CHDATERANGE_DAYS: return cssc::TimeUnit::DAY;
+ case EXC_CHDATERANGE_MONTHS: return cssc::TimeUnit::MONTH;
+ case EXC_CHDATERANGE_YEARS: return cssc::TimeUnit::YEAR;
+ default: OSL_ENSURE( false, "lclGetApiTimeUnit - unexpected time unit" );
+ }
+ return cssc::TimeUnit::DAY;
+}
+
+void lclConvertTimeInterval( Any& rInterval, sal_uInt16 nValue, bool bAuto, sal_uInt16 nTimeUnit )
+{
+ if( bAuto || (nValue == 0) )
+ rInterval.clear();
+ else
+ rInterval <<= cssc::TimeInterval( nValue, lclGetApiTimeUnit( nTimeUnit ) );
+}
+
+} // namespace
+
+// Common =====================================================================
+
+/** Stores global data needed in various classes of the Chart import filter. */
+struct XclImpChRootData : public XclChRootData
+{
+ XclImpChChart& mrChartData; /// The chart data object.
+
+ explicit XclImpChRootData( XclImpChChart& rChartData ) : mrChartData( rChartData ) {}
+};
+
+XclImpChRoot::XclImpChRoot( const XclImpRoot& rRoot, XclImpChChart& rChartData ) :
+ XclImpRoot( rRoot ),
+ mxChData( std::make_shared<XclImpChRootData>( rChartData ) )
+{
+}
+
+XclImpChRoot::~XclImpChRoot()
+{
+}
+
+XclImpChChart& XclImpChRoot::GetChartData() const
+{
+ return mxChData->mrChartData;
+}
+
+const XclChTypeInfo& XclImpChRoot::GetChartTypeInfo( XclChTypeId eType ) const
+{
+ return mxChData->mxTypeInfoProv->GetTypeInfo( eType );
+}
+
+const XclChTypeInfo& XclImpChRoot::GetChartTypeInfo( sal_uInt16 nRecId ) const
+{
+ return mxChData->mxTypeInfoProv->GetTypeInfoFromRecId( nRecId );
+}
+
+const XclChFormatInfo& XclImpChRoot::GetFormatInfo( XclChObjectType eObjType ) const
+{
+ return mxChData->mxFmtInfoProv->GetFormatInfo( eObjType );
+}
+
+Color XclImpChRoot::GetFontAutoColor() const
+{
+ return GetPalette().GetColor( EXC_COLOR_CHWINDOWTEXT );
+}
+
+Color XclImpChRoot::GetSeriesLineAutoColor( sal_uInt16 nFormatIdx ) const
+{
+ return GetPalette().GetColor( XclChartHelper::GetSeriesLineAutoColorIdx( nFormatIdx ) );
+}
+
+Color XclImpChRoot::GetSeriesFillAutoColor( sal_uInt16 nFormatIdx ) const
+{
+ const XclImpPalette& rPal = GetPalette();
+ Color aColor = rPal.GetColor( XclChartHelper::GetSeriesFillAutoColorIdx( nFormatIdx ) );
+ sal_uInt8 nTrans = XclChartHelper::GetSeriesFillAutoTransp( nFormatIdx );
+ return ScfTools::GetMixedColor( aColor, rPal.GetColor( EXC_COLOR_CHWINDOWBACK ), nTrans );
+}
+
+void XclImpChRoot::InitConversion( const Reference<XChartDocument>& xChartDoc, const tools::Rectangle& rChartRect ) const
+{
+ // create formatting object tables
+ mxChData->InitConversion( GetRoot(), xChartDoc, rChartRect );
+
+ // lock the model to suppress any internal updates
+ if( xChartDoc.is() )
+ xChartDoc->lockControllers();
+
+ SfxObjectShell* pDocShell = GetDocShell();
+ Reference< XDataReceiver > xDataRec( xChartDoc, UNO_QUERY );
+ if( pDocShell && xDataRec.is() )
+ {
+ // create and register a data provider
+ Reference< XDataProvider > xDataProv(
+ ScfApiHelper::CreateInstance( pDocShell, SERVICE_CHART2_DATAPROVIDER ), UNO_QUERY );
+ if( xDataProv.is() )
+ xDataRec->attachDataProvider( xDataProv );
+ // attach the number formatter
+ Reference< XNumberFormatsSupplier > xNumFmtSupp( pDocShell->GetModel(), UNO_QUERY );
+ if( xNumFmtSupp.is() )
+ xDataRec->attachNumberFormatsSupplier( xNumFmtSupp );
+ }
+}
+
+void XclImpChRoot::FinishConversion( XclImpDffConverter& rDffConv ) const
+{
+ rDffConv.Progress( EXC_CHART_PROGRESS_SIZE );
+ // unlock the model
+ Reference< XModel > xModel = mxChData->mxChartDoc;
+ if( xModel.is() )
+ xModel->unlockControllers();
+ rDffConv.Progress( EXC_CHART_PROGRESS_SIZE );
+
+ mxChData->FinishConversion();
+}
+
+Reference< XDataProvider > XclImpChRoot::GetDataProvider() const
+{
+ return mxChData->mxChartDoc->getDataProvider();
+}
+
+Reference< XShape > XclImpChRoot::GetTitleShape( const XclChTextKey& rTitleKey ) const
+{
+ return mxChData->GetTitleShape( rTitleKey );
+}
+
+sal_Int32 XclImpChRoot::CalcHmmFromChartX( sal_Int32 nPosX ) const
+{
+ return static_cast< sal_Int32 >( mxChData->mfUnitSizeX * nPosX + mxChData->mnBorderGapX + 0.5 );
+}
+
+sal_Int32 XclImpChRoot::CalcHmmFromChartY( sal_Int32 nPosY ) const
+{
+ return static_cast< sal_Int32 >( mxChData->mfUnitSizeY * nPosY + mxChData->mnBorderGapY + 0.5 );
+}
+
+css::awt::Rectangle XclImpChRoot::CalcHmmFromChartRect( const XclChRectangle& rRect ) const
+{
+ return css::awt::Rectangle(
+ CalcHmmFromChartX( rRect.mnX ),
+ CalcHmmFromChartY( rRect.mnY ),
+ CalcHmmFromChartX( rRect.mnWidth ),
+ CalcHmmFromChartY( rRect.mnHeight ) );
+}
+
+double XclImpChRoot::CalcRelativeFromHmmX( sal_Int32 nPosX ) const
+{
+ const tools::Long nWidth = mxChData->maChartRect.GetWidth();
+ if (!nWidth)
+ throw o3tl::divide_by_zero();
+ return static_cast<double>(nPosX) / nWidth;
+}
+
+double XclImpChRoot::CalcRelativeFromHmmY( sal_Int32 nPosY ) const
+{
+ const tools::Long nHeight = mxChData->maChartRect.GetHeight();
+ if (!nHeight)
+ throw o3tl::divide_by_zero();
+ return static_cast<double >(nPosY) / nHeight;
+}
+
+double XclImpChRoot::CalcRelativeFromChartX( sal_Int32 nPosX ) const
+{
+ return CalcRelativeFromHmmX( CalcHmmFromChartX( nPosX ) );
+}
+
+double XclImpChRoot::CalcRelativeFromChartY( sal_Int32 nPosY ) const
+{
+ return CalcRelativeFromHmmY( CalcHmmFromChartY( nPosY ) );
+}
+
+void XclImpChRoot::ConvertLineFormat( ScfPropertySet& rPropSet,
+ const XclChLineFormat& rLineFmt, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().WriteLineProperties(
+ rPropSet, *mxChData->mxLineDashTable, rLineFmt, ePropMode );
+}
+
+void XclImpChRoot::ConvertAreaFormat( ScfPropertySet& rPropSet,
+ const XclChAreaFormat& rAreaFmt, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().WriteAreaProperties( rPropSet, rAreaFmt, ePropMode );
+}
+
+void XclImpChRoot::ConvertEscherFormat( ScfPropertySet& rPropSet,
+ const XclChEscherFormat& rEscherFmt, const XclChPicFormat* pPicFmt,
+ sal_uInt32 nDffFillType, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().WriteEscherProperties( rPropSet,
+ *mxChData->mxGradientTable, *mxChData->mxBitmapTable,
+ rEscherFmt, pPicFmt, nDffFillType, ePropMode );
+}
+
+void XclImpChRoot::ConvertFont( ScfPropertySet& rPropSet,
+ sal_uInt16 nFontIdx, const Color* pFontColor ) const
+{
+ GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CHART, nFontIdx, pFontColor );
+}
+
+void XclImpChRoot::ConvertPieRotation( ScfPropertySet& rPropSet, sal_uInt16 nAngle )
+{
+ sal_Int32 nApiRot = (450 - (nAngle % 360)) % 360;
+ rPropSet.SetProperty( EXC_CHPROP_STARTINGANGLE, nApiRot );
+}
+
+XclImpChGroupBase::~XclImpChGroupBase()
+{
+}
+
+void XclImpChGroupBase::ReadRecordGroup( XclImpStream& rStrm )
+{
+ // read contents of the header record
+ ReadHeaderRecord( rStrm );
+
+ // only read sub records, if the next record is a CHBEGIN
+ if( rStrm.GetNextRecId() != EXC_ID_CHBEGIN )
+ return;
+
+ // read the CHBEGIN record, may be used for special initial processing
+ rStrm.StartNextRecord();
+ ReadSubRecord( rStrm );
+
+ // read the nested records
+ bool bLoop = true;
+ while( bLoop && rStrm.StartNextRecord() )
+ {
+ sal_uInt16 nRecId = rStrm.GetRecId();
+ bLoop = nRecId != EXC_ID_CHEND;
+ // skip unsupported nested blocks
+ if( nRecId == EXC_ID_CHBEGIN )
+ SkipBlock( rStrm );
+ else
+ ReadSubRecord( rStrm );
+ }
+ /* Returns with current CHEND record or unchanged stream, if no record
+ group present. In every case another call to StartNextRecord() will go
+ to next record of interest. */
+}
+
+void XclImpChGroupBase::SkipBlock( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecId() == EXC_ID_CHBEGIN, "XclImpChGroupBase::SkipBlock - no CHBEGIN record" );
+ // do nothing if current record is not CHBEGIN
+ bool bLoop = rStrm.GetRecId() == EXC_ID_CHBEGIN;
+ while( bLoop && rStrm.StartNextRecord() )
+ {
+ sal_uInt16 nRecId = rStrm.GetRecId();
+ bLoop = nRecId != EXC_ID_CHEND;
+ // skip nested record groups
+ if( nRecId == EXC_ID_CHBEGIN )
+ SkipBlock( rStrm );
+ }
+}
+
+// Frame formatting ===========================================================
+
+void XclImpChFramePos::ReadChFramePos( XclImpStream& rStrm )
+{
+ maData.mnTLMode = rStrm.ReaduInt16();
+ maData.mnBRMode = rStrm.ReaduInt16();
+ /* According to the spec, the upper 16 bits of all members in the
+ rectangle are unused and may contain garbage. */
+ maData.maRect.mnX = rStrm.ReadInt16(); rStrm.Ignore( 2 );
+ maData.maRect.mnY = rStrm.ReadInt16(); rStrm.Ignore( 2 );
+ maData.maRect.mnWidth = rStrm.ReadInt16(); rStrm.Ignore( 2 );
+ maData.maRect.mnHeight = rStrm.ReadInt16(); rStrm.Ignore( 2 );
+}
+
+void XclImpChLineFormat::ReadChLineFormat( XclImpStream& rStrm )
+{
+ rStrm >> maData.maColor;
+ maData.mnPattern = rStrm.ReaduInt16();
+ maData.mnWeight = rStrm.ReadInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ // BIFF8: index into palette used instead of RGB data
+ maData.maColor = rRoot.GetPalette().GetColor( rStrm.ReaduInt16() );
+}
+
+void XclImpChLineFormat::Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ if( IsAuto() )
+ {
+ XclChLineFormat aLineFmt;
+ aLineFmt.maColor = (eObjType == EXC_CHOBJTYPE_LINEARSERIES) ?
+ rRoot.GetSeriesLineAutoColor( nFormatIdx ) :
+ rRoot.GetPalette().GetColor( rFmtInfo.mnAutoLineColorIdx );
+ aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ aLineFmt.mnWeight = rFmtInfo.mnAutoLineWeight;
+ rRoot.ConvertLineFormat( rPropSet, aLineFmt, rFmtInfo.mePropMode );
+ }
+ else
+ {
+ rRoot.ConvertLineFormat( rPropSet, maData, rFmtInfo.mePropMode );
+ }
+}
+
+void XclImpChAreaFormat::ReadChAreaFormat( XclImpStream& rStrm )
+{
+ rStrm >> maData.maPattColor >> maData.maBackColor;
+ maData.mnPattern = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ {
+ // BIFF8: index into palette used instead of RGB data
+ const XclImpPalette& rPal = rRoot.GetPalette();
+ maData.maPattColor = rPal.GetColor( rStrm.ReaduInt16() );
+ maData.maBackColor = rPal.GetColor( rStrm.ReaduInt16());
+ }
+}
+
+void XclImpChAreaFormat::Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ if( IsAuto() )
+ {
+ XclChAreaFormat aAreaFmt;
+ aAreaFmt.maPattColor = (eObjType == EXC_CHOBJTYPE_FILLEDSERIES) ?
+ rRoot.GetSeriesFillAutoColor( nFormatIdx ) :
+ rRoot.GetPalette().GetColor( rFmtInfo.mnAutoPattColorIdx );
+ aAreaFmt.mnPattern = EXC_PATT_SOLID;
+ rRoot.ConvertAreaFormat( rPropSet, aAreaFmt, rFmtInfo.mePropMode );
+ }
+ else
+ {
+ rRoot.ConvertAreaFormat( rPropSet, maData, rFmtInfo.mePropMode );
+ }
+}
+
+XclImpChEscherFormat::XclImpChEscherFormat( const XclImpRoot& rRoot ) :
+ mnDffFillType( mso_fillSolid )
+{
+ maData.mxItemSet =
+ std::make_shared<SfxItemSet>( rRoot.GetDoc().GetDrawLayer()->GetItemPool() );
+}
+
+void XclImpChEscherFormat::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ // read from stream - CHESCHERFORMAT uses own ID for record continuation
+ XclImpDffPropSet aPropSet( rStrm.GetRoot() );
+ rStrm.ResetRecord( true, rStrm.GetRecId() );
+ rStrm >> aPropSet;
+ // get the data
+ aPropSet.FillToItemSet( *maData.mxItemSet );
+ // get fill type from DFF property set
+ mnDffFillType = aPropSet.GetPropertyValue( DFF_Prop_fillType );
+}
+
+void XclImpChEscherFormat::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHPICFORMAT:
+ maPicFmt.mnBmpMode = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ maPicFmt.mnFlags = rStrm.ReaduInt16();
+ maPicFmt.mfScale = rStrm.ReadDouble();
+ break;
+ }
+}
+
+void XclImpChEscherFormat::Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, bool bUsePicFmt ) const
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ rRoot.ConvertEscherFormat( rPropSet, maData, bUsePicFmt ? &maPicFmt : nullptr, mnDffFillType, rFmtInfo.mePropMode );
+}
+
+XclImpChFrameBase::XclImpChFrameBase( const XclChFormatInfo& rFmtInfo )
+{
+ if( !rFmtInfo.mbCreateDefFrame )
+ return;
+
+ switch( rFmtInfo.meDefFrameType )
+ {
+ case EXC_CHFRAMETYPE_AUTO:
+ mxLineFmt = new XclImpChLineFormat();
+ if( rFmtInfo.mbIsFrame )
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
+ break;
+ case EXC_CHFRAMETYPE_INVISIBLE:
+ {
+ XclChLineFormat aLineFmt;
+ ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, false );
+ aLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE;
+ mxLineFmt = new XclImpChLineFormat( aLineFmt );
+ if( rFmtInfo.mbIsFrame )
+ {
+ XclChAreaFormat aAreaFmt;
+ ::set_flag( aAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, false );
+ aAreaFmt.mnPattern = EXC_PATT_NONE;
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>( aAreaFmt );
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclImpChFrameBase::XclImpChFrameBase - unknown frame type" );
+ }
+}
+
+void XclImpChFrameBase::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHLINEFORMAT:
+ mxLineFmt = new XclImpChLineFormat();
+ mxLineFmt->ReadChLineFormat( rStrm );
+ break;
+ case EXC_ID_CHAREAFORMAT:
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
+ mxAreaFmt->ReadChAreaFormat( rStrm );
+ break;
+ case EXC_ID_CHESCHERFORMAT:
+ mxEscherFmt = std::make_shared<XclImpChEscherFormat>( rStrm.GetRoot() );
+ mxEscherFmt->ReadRecordGroup( rStrm );
+ break;
+ }
+}
+
+void XclImpChFrameBase::ConvertLineBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
+{
+ if( mxLineFmt )
+ mxLineFmt->Convert( rRoot, rPropSet, eObjType, nFormatIdx );
+}
+
+void XclImpChFrameBase::ConvertAreaBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx, bool bUsePicFmt ) const
+{
+ if( rRoot.GetFormatInfo( eObjType ).mbIsFrame )
+ {
+ // CHESCHERFORMAT overrides CHAREAFORMAT (even if it is auto)
+ if( mxEscherFmt )
+ mxEscherFmt->Convert( rRoot, rPropSet, eObjType, bUsePicFmt );
+ else if( mxAreaFmt )
+ mxAreaFmt->Convert( rRoot, rPropSet, eObjType, nFormatIdx );
+ }
+}
+
+void XclImpChFrameBase::ConvertFrameBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx, bool bUsePicFmt ) const
+{
+ ConvertLineBase( rRoot, rPropSet, eObjType, nFormatIdx );
+ ConvertAreaBase( rRoot, rPropSet, eObjType, nFormatIdx, bUsePicFmt );
+}
+
+XclImpChFrame::XclImpChFrame( const XclImpChRoot& rRoot, XclChObjectType eObjType ) :
+ XclImpChFrameBase( rRoot.GetFormatInfo( eObjType ) ),
+ XclImpChRoot( rRoot ),
+ meObjType( eObjType )
+{
+}
+
+void XclImpChFrame::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnFormat = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChFrame::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
+{
+ const XclImpPalette& rPal = GetPalette();
+
+ if( rLineData.IsVisible() && (!mxLineFmt || !mxLineFmt->HasLine()) )
+ {
+ // line formatting
+ XclChLineFormat aLineFmt;
+ aLineFmt.maColor = rPal.GetColor( rLineData.mnColorIdx );
+ switch( rLineData.mnStyle )
+ {
+ case EXC_OBJ_LINE_SOLID: aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID; break;
+ case EXC_OBJ_LINE_DASH: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASH; break;
+ case EXC_OBJ_LINE_DOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DOT; break;
+ case EXC_OBJ_LINE_DASHDOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASHDOT; break;
+ case EXC_OBJ_LINE_DASHDOTDOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASHDOTDOT; break;
+ case EXC_OBJ_LINE_MEDTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_MEDTRANS; break;
+ case EXC_OBJ_LINE_DARKTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DARKTRANS; break;
+ case EXC_OBJ_LINE_LIGHTTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_LIGHTTRANS; break;
+ case EXC_OBJ_LINE_NONE: aLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE; break;
+ default: aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ }
+ switch( rLineData.mnWidth )
+ {
+ case EXC_OBJ_LINE_HAIR: aLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR; break;
+ case EXC_OBJ_LINE_THIN: aLineFmt.mnWeight = EXC_CHLINEFORMAT_SINGLE; break;
+ case EXC_OBJ_LINE_MEDIUM: aLineFmt.mnWeight = EXC_CHLINEFORMAT_DOUBLE; break;
+ case EXC_OBJ_LINE_THICK: aLineFmt.mnWeight = EXC_CHLINEFORMAT_TRIPLE; break;
+ default: aLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR;
+ }
+ ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, rLineData.IsAuto() );
+ mxLineFmt = new XclImpChLineFormat( aLineFmt );
+ }
+
+ if( rFillData.IsFilled() && (!mxAreaFmt || !mxAreaFmt->HasArea()) && !mxEscherFmt )
+ {
+ // area formatting
+ XclChAreaFormat aAreaFmt;
+ aAreaFmt.maPattColor = rPal.GetColor( rFillData.mnPattColorIdx );
+ aAreaFmt.maBackColor = rPal.GetColor( rFillData.mnBackColorIdx );
+ aAreaFmt.mnPattern = rFillData.mnPattern;
+ ::set_flag( aAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, rFillData.IsAuto() );
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>( aAreaFmt );
+ }
+}
+
+void XclImpChFrame::Convert( ScfPropertySet& rPropSet, bool bUsePicFmt ) const
+{
+ ConvertFrameBase( GetChRoot(), rPropSet, meObjType, EXC_CHDATAFORMAT_UNKNOWN, bUsePicFmt );
+}
+
+// Source links ===============================================================
+
+namespace {
+
+/** Creates a labeled data sequence object, adds link for series title if present. */
+Reference< XLabeledDataSequence > lclCreateLabeledDataSequence(
+ const XclImpChSourceLinkRef& xValueLink, const OUString& rValueRole,
+ const XclImpChSourceLink* pTitleLink = nullptr )
+{
+ // create data sequence for values and title
+ Reference< XDataSequence > xValueSeq;
+ if( xValueLink )
+ xValueSeq = xValueLink->CreateDataSequence( rValueRole );
+ Reference< XDataSequence > xTitleSeq;
+ if( pTitleLink )
+ xTitleSeq = pTitleLink->CreateDataSequence( EXC_CHPROP_ROLE_LABEL );
+
+ // create the labeled data sequence, if values or title are present
+ Reference< XLabeledDataSequence > xLabeledSeq;
+ if( xValueSeq.is() || xTitleSeq.is() )
+ xLabeledSeq = LabeledDataSequence::create(comphelper::getProcessComponentContext());
+ if( xLabeledSeq.is() )
+ {
+ if( xValueSeq.is() )
+ xLabeledSeq->setValues( xValueSeq );
+ if( xTitleSeq.is() )
+ xLabeledSeq->setLabel( xTitleSeq );
+ }
+ return xLabeledSeq;
+}
+
+} // namespace
+
+XclImpChSourceLink::XclImpChSourceLink( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+XclImpChSourceLink::~XclImpChSourceLink()
+{
+}
+
+void XclImpChSourceLink::ReadChSourceLink( XclImpStream& rStrm )
+{
+ maData.mnDestType = rStrm.ReaduInt8();
+ maData.mnLinkType = rStrm.ReaduInt8();
+ maData.mnFlags = rStrm.ReaduInt16();
+ maData.mnNumFmtIdx = rStrm.ReaduInt16();
+
+ mxTokenArray.reset();
+ if( GetLinkType() == EXC_CHSRCLINK_WORKSHEET )
+ {
+ // read token array
+ XclTokenArray aXclTokArr;
+ rStrm >> aXclTokArr;
+
+ // convert BIFF formula tokens to Calc token array
+ if( std::unique_ptr<ScTokenArray> pTokens = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CHART, aXclTokArr ) )
+ mxTokenArray = std::move( pTokens );
+ }
+
+ // try to read a following CHSTRING record
+ if( (rStrm.GetNextRecId() == EXC_ID_CHSTRING) && rStrm.StartNextRecord() )
+ {
+ mxString = std::make_shared<XclImpString>();
+ rStrm.Ignore( 2 );
+ mxString->Read( rStrm, XclStrFlags::EightBitLength | XclStrFlags::SeparateFormats );
+ }
+}
+
+void XclImpChSourceLink::SetString( const OUString& rString )
+{
+ if( !mxString )
+ mxString = std::make_shared<XclImpString>();
+ mxString->SetText( rString );
+}
+
+void XclImpChSourceLink::SetTextFormats( XclFormatRunVec&& rFormats )
+{
+ if( mxString )
+ mxString->SetFormats( std::move(rFormats) );
+}
+
+sal_uInt16 XclImpChSourceLink::GetCellCount() const
+{
+ sal_uInt32 nCellCount = 0;
+ if( mxTokenArray )
+ {
+ FormulaTokenArrayPlainIterator aIter(*mxTokenArray);
+ for( const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next() )
+ {
+ switch( pToken->GetType() )
+ {
+ case ::formula::svSingleRef:
+ case ::formula::svExternalSingleRef:
+ // single cell
+ ++nCellCount;
+ break;
+ case ::formula::svDoubleRef:
+ case ::formula::svExternalDoubleRef:
+ {
+ // cell range
+ const ScComplexRefData& rComplexRef = *pToken->GetDoubleRef();
+ ScAddress aAbs1 = rComplexRef.Ref1.toAbs(GetRoot().GetDoc(), ScAddress());
+ ScAddress aAbs2 = rComplexRef.Ref2.toAbs(GetRoot().GetDoc(), ScAddress());
+ sal_uInt32 nTabs = static_cast<sal_uInt32>(aAbs2.Tab() - aAbs1.Tab() + 1);
+ sal_uInt32 nCols = static_cast<sal_uInt32>(aAbs2.Col() - aAbs1.Col() + 1);
+ sal_uInt32 nRows = static_cast<sal_uInt32>(aAbs2.Row() - aAbs1.Row() + 1);
+ nCellCount += nCols * nRows * nTabs;
+ }
+ break;
+ default: ;
+ }
+ }
+ }
+ return limit_cast< sal_uInt16 >( nCellCount );
+}
+
+void XclImpChSourceLink::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const
+{
+ bool bLinkToSource = ::get_flag( maData.mnFlags, EXC_CHSRCLINK_NUMFMT );
+ sal_uInt32 nScNumFmt = bLinkToSource ? GetNumFmtBuffer().GetScFormat( maData.mnNumFmtIdx ) : NUMBERFORMAT_ENTRY_NOT_FOUND;
+ OUString aPropName = bPercent ? OUString( EXC_CHPROP_PERCENTAGENUMFMT ) : OUString( EXC_CHPROP_NUMBERFORMAT );
+ if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND )
+ rPropSet.SetProperty( aPropName, static_cast< sal_Int32 >( nScNumFmt ) );
+ else
+ // restore 'link to source' at data point (series may contain manual number format)
+ rPropSet.SetAnyProperty( aPropName, Any() );
+}
+
+Reference< XDataSequence > XclImpChSourceLink::CreateDataSequence( const OUString& rRole ) const
+{
+ Reference< XDataSequence > xDataSeq;
+ Reference< XDataProvider > xDataProv = GetDataProvider();
+ if( xDataProv.is() )
+ {
+ if ( mxTokenArray )
+ {
+ ScCompiler aComp( GetDoc(), ScAddress(), *mxTokenArray, GetDoc().GetGrammar() );
+ OUStringBuffer aRangeRep;
+ aComp.CreateStringFromTokenArray( aRangeRep );
+ try
+ {
+ xDataSeq = xDataProv->createDataSequenceByRangeRepresentation( aRangeRep.makeStringAndClear() );
+ // set sequence role
+ ScfPropertySet aSeqProp( xDataSeq );
+ aSeqProp.SetProperty( EXC_CHPROP_ROLE, rRole );
+ }
+ catch( Exception& )
+ {
+ // OSL_FAIL( "XclImpChSourceLink::CreateDataSequence - cannot create data sequence" );
+ }
+ }
+ else if( rRole == EXC_CHPROP_ROLE_LABEL && mxString && !mxString->GetText().isEmpty() )
+ {
+ try
+ {
+ OUString aString("\"");
+ xDataSeq = xDataProv->createDataSequenceByRangeRepresentation( aString + mxString->GetText() + aString );
+ // set sequence role
+ ScfPropertySet aSeqProp( xDataSeq );
+ aSeqProp.SetProperty( EXC_CHPROP_ROLE, rRole );
+ }
+ catch( Exception& ) { }
+ }
+ }
+ return xDataSeq;
+}
+
+Sequence< Reference< XFormattedString > > XclImpChSourceLink::CreateStringSequence(
+ const XclImpChRoot& rRoot, sal_uInt16 nLeadFontIdx, const Color& rLeadFontColor ) const
+{
+ ::std::vector< Reference< XFormattedString > > aStringVec;
+ if( mxString )
+ {
+ for( XclImpStringIterator aIt( *mxString ); aIt.Is(); ++aIt )
+ {
+ Reference< css::chart2::XFormattedString2 > xFmtStr = css::chart2::FormattedString::create( comphelper::getProcessComponentContext() );
+ // set text data
+ xFmtStr->setString( aIt.GetPortionText() );
+
+ // set font formatting and font color
+ ScfPropertySet aStringProp( xFmtStr );
+ sal_uInt16 nFontIdx = aIt.GetPortionFont();
+ if( (nFontIdx == EXC_FONT_NOTFOUND) && (aIt.GetPortionIndex() == 0) )
+ // leading unformatted portion - use passed font settings
+ rRoot.ConvertFont( aStringProp, nLeadFontIdx, &rLeadFontColor );
+ else
+ rRoot.ConvertFont( aStringProp, nFontIdx );
+
+ // add string to vector of strings
+ aStringVec.emplace_back(xFmtStr );
+ }
+ }
+ return ScfApiHelper::VectorToSequence( aStringVec );
+}
+
+void XclImpChSourceLink::FillSourceLink( ::std::vector< ScTokenRef >& rTokens ) const
+{
+ if( !mxTokenArray )
+ // no links to fill.
+ return;
+
+ FormulaTokenArrayPlainIterator aIter(*mxTokenArray);
+ for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ ScTokenRef pToken(p->Clone());
+ if (ScRefTokenHelper::isRef(pToken))
+ // This is a reference token. Store it.
+ ScRefTokenHelper::join(&GetRoot().GetDoc(), rTokens, pToken, ScAddress());
+ }
+}
+
+// Text =======================================================================
+
+XclImpChFontBase::~XclImpChFontBase()
+{
+}
+
+void XclImpChFontBase::ConvertFontBase( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const
+{
+ Color aFontColor = GetFontColor();
+ rRoot.ConvertFont( rPropSet, GetFontIndex(), &aFontColor );
+}
+
+void XclImpChFontBase::ConvertRotationBase( ScfPropertySet& rPropSet, bool bSupportsStacked ) const
+{
+ XclChPropSetHelper::WriteRotationProperties( rPropSet, GetRotation(), bSupportsStacked );
+}
+
+XclImpChFont::XclImpChFont() :
+ mnFontIdx( EXC_FONT_NOTFOUND )
+{
+}
+
+void XclImpChFont::ReadChFont( XclImpStream& rStrm )
+{
+ mnFontIdx = rStrm.ReaduInt16();
+}
+
+XclImpChText::XclImpChText( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChText::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnHAlign = rStrm.ReaduInt8();
+ maData.mnVAlign = rStrm.ReaduInt8();
+ maData.mnBackMode = rStrm.ReaduInt16();
+ rStrm >> maData.maTextColor
+ >> maData.maRect;
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ // BIFF8: index into palette used instead of RGB data
+ maData.maTextColor = GetPalette().GetColor( rStrm.ReaduInt16() );
+ // placement and rotation
+ maData.mnFlags2 = rStrm.ReaduInt16();
+ maData.mnRotation = rStrm.ReaduInt16();
+ }
+ else
+ {
+ // BIFF2-BIFF7: get rotation from text orientation
+ sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 8, 3 );
+ maData.mnRotation = XclTools::GetXclRotFromOrient( nOrient );
+ }
+}
+
+void XclImpChText::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHFRAMEPOS:
+ mxFramePos = std::make_shared<XclImpChFramePos>();
+ mxFramePos->ReadChFramePos( rStrm );
+ break;
+ case EXC_ID_CHFONT:
+ mxFont = std::make_shared<XclImpChFont>();
+ mxFont->ReadChFont( rStrm );
+ break;
+ case EXC_ID_CHFORMATRUNS:
+ if( GetBiff() == EXC_BIFF8 )
+ XclImpString::ReadFormats( rStrm, maFormats );
+ break;
+ case EXC_ID_CHSOURCELINK:
+ mxSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
+ mxSrcLink->ReadChSourceLink( rStrm );
+ break;
+ case EXC_ID_CHFRAME:
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_TEXT );
+ mxFrame->ReadRecordGroup( rStrm );
+ break;
+ case EXC_ID_CHOBJECTLINK:
+ maObjLink.mnTarget = rStrm.ReaduInt16();
+ maObjLink.maPointPos.mnSeriesIdx = rStrm.ReaduInt16();
+ maObjLink.maPointPos.mnPointIdx = rStrm.ReaduInt16();
+ break;
+ case EXC_ID_CHFRLABELPROPS:
+ ReadChFrLabelProps( rStrm );
+ break;
+ case EXC_ID_CHEND:
+ if( mxSrcLink && !maFormats.empty() )
+ mxSrcLink->SetTextFormats( std::vector(maFormats) );
+ break;
+ }
+}
+
+sal_uInt16 XclImpChText::GetFontIndex() const
+{
+ return mxFont ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND;
+}
+
+Color XclImpChText::GetFontColor() const
+{
+ return ::get_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR ) ? GetFontAutoColor() : maData.maTextColor;
+}
+
+sal_uInt16 XclImpChText::GetRotation() const
+{
+ return maData.mnRotation;
+}
+
+void XclImpChText::SetString( const OUString& rString )
+{
+ if( !mxSrcLink )
+ mxSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
+ mxSrcLink->SetString( rString );
+}
+
+void XclImpChText::UpdateText( const XclImpChText* pParentText )
+{
+ if( !pParentText )
+ return;
+
+ // update missing members
+ if( !mxFrame )
+ mxFrame = pParentText->mxFrame;
+ if( !mxFont )
+ {
+ mxFont = pParentText->mxFont;
+ // text color is taken from CHTEXT record, not from font in CHFONT
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR, ::get_flag( pParentText->maData.mnFlags, EXC_CHTEXT_AUTOCOLOR ) );
+ maData.maTextColor = pParentText->maData.maTextColor;
+ }
+}
+
+void XclImpChText::UpdateDataLabel( bool bCateg, bool bValue, bool bPercent )
+{
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG, bCateg );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE, bValue );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT, bPercent );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bCateg && bPercent );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bCateg && !bValue && !bPercent );
+}
+
+void XclImpChText::ConvertFont( ScfPropertySet& rPropSet ) const
+{
+ ConvertFontBase( GetChRoot(), rPropSet );
+}
+
+void XclImpChText::ConvertRotation( ScfPropertySet& rPropSet, bool bSupportsStacked ) const
+{
+ ConvertRotationBase( rPropSet, bSupportsStacked );
+}
+
+void XclImpChText::ConvertFrame( ScfPropertySet& rPropSet ) const
+{
+ if( mxFrame )
+ mxFrame->Convert( rPropSet );
+}
+
+void XclImpChText::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const
+{
+ if( mxSrcLink )
+ mxSrcLink->ConvertNumFmt( rPropSet, bPercent );
+}
+
+void XclImpChText::ConvertDataLabel( ScfPropertySet& rPropSet, const XclChTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const
+{
+ // existing CHFRLABELPROPS record wins over flags from CHTEXT
+ sal_uInt16 nShowFlags = mxLabelProps ? mxLabelProps->mnFlags : maData.mnFlags;
+ sal_uInt16 SHOWANYCATEG = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWCATEG : (EXC_CHTEXT_SHOWCATEGPERC | EXC_CHTEXT_SHOWCATEG);
+ sal_uInt16 SHOWANYVALUE = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWVALUE : EXC_CHTEXT_SHOWVALUE;
+ sal_uInt16 SHOWANYPERCENT = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWPERCENT : (EXC_CHTEXT_SHOWPERCENT | EXC_CHTEXT_SHOWCATEGPERC);
+ sal_uInt16 SHOWANYBUBBLE = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWBUBBLE : EXC_CHTEXT_SHOWBUBBLE;
+
+ // get raw flags for label values
+ bool bShowNone = IsDeleted();
+ bool bShowCateg = !bShowNone && ::get_flag( nShowFlags, SHOWANYCATEG );
+ bool bShowPercent = !bShowNone && ::get_flag( nShowFlags, SHOWANYPERCENT );
+ bool bShowValue = !bShowNone && ::get_flag( nShowFlags, SHOWANYVALUE );
+ bool bShowBubble = !bShowNone && ::get_flag( nShowFlags, SHOWANYBUBBLE );
+
+ // adjust to Chart2 behaviour
+ if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES )
+ bShowValue = bShowBubble; // Chart2 bubble charts show bubble size if 'ShowValue' is set
+
+ // other flags
+ bool bShowAny = bShowValue || bShowPercent || bShowCateg;
+ bool bShowSymbol = bShowAny && ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL );
+
+ // create API struct for label values, set API label separator
+ cssc2::DataPointLabel aPointLabel( bShowValue, bShowPercent, bShowCateg, bShowSymbol, false, false );
+ rPropSet.SetProperty( EXC_CHPROP_LABEL, aPointLabel );
+ OUString aSep = mxLabelProps ? mxLabelProps->maSeparator : OUString('\n');
+ if( aSep.isEmpty() )
+ aSep = "; ";
+ rPropSet.SetStringProperty( EXC_CHPROP_LABELSEPARATOR, aSep );
+
+ // text properties of attached label
+ if( !bShowAny )
+ return;
+
+ ConvertFont( rPropSet );
+ ConvertRotation( rPropSet, false );
+ // label placement
+ using namespace cssc::DataLabelPlacement;
+ sal_Int32 nPlacement = rTypeInfo.mnDefaultLabelPos;
+ switch( ::extract_value< sal_uInt16 >( maData.mnFlags2, 0, 4 ) )
+ {
+ case EXC_CHTEXT_POS_DEFAULT: nPlacement = rTypeInfo.mnDefaultLabelPos; break;
+ case EXC_CHTEXT_POS_OUTSIDE: nPlacement = OUTSIDE; break;
+ case EXC_CHTEXT_POS_INSIDE: nPlacement = INSIDE; break;
+ case EXC_CHTEXT_POS_CENTER: nPlacement = CENTER; break;
+ case EXC_CHTEXT_POS_AXIS: nPlacement = NEAR_ORIGIN; break;
+ case EXC_CHTEXT_POS_ABOVE: nPlacement = TOP; break;
+ case EXC_CHTEXT_POS_BELOW: nPlacement = BOTTOM; break;
+ case EXC_CHTEXT_POS_LEFT: nPlacement = LEFT; break;
+ case EXC_CHTEXT_POS_RIGHT: nPlacement = RIGHT; break;
+ case EXC_CHTEXT_POS_AUTO: nPlacement = AVOID_OVERLAP; break;
+ }
+ sal_Int32 nGlobalPlacement = 0;
+ if ( ( nPlacement == rTypeInfo.mnDefaultLabelPos ) && pGlobalPropSet &&
+ pGlobalPropSet->GetProperty( nGlobalPlacement, EXC_CHPROP_LABELPLACEMENT ) )
+ nPlacement = nGlobalPlacement;
+
+ rPropSet.SetProperty( EXC_CHPROP_LABELPLACEMENT, nPlacement );
+ // label number format (percentage format wins over value format)
+ if( bShowPercent || bShowValue )
+ ConvertNumFmt( rPropSet, bShowPercent );
+}
+
+Reference< XTitle > XclImpChText::CreateTitle() const
+{
+ Reference< XTitle > xTitle;
+ if( mxSrcLink && mxSrcLink->HasString() )
+ {
+ // create the formatted strings
+ Sequence< Reference< XFormattedString > > aStringSeq(
+ mxSrcLink->CreateStringSequence( GetChRoot(), GetFontIndex(), GetFontColor() ) );
+ if( aStringSeq.hasElements() )
+ {
+ // create the title object
+ xTitle.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_TITLE ), UNO_QUERY );
+ if( xTitle.is() )
+ {
+ // set the formatted strings
+ xTitle->setText( aStringSeq );
+ // more title formatting properties
+ ScfPropertySet aTitleProp( xTitle );
+ ConvertFrame( aTitleProp );
+ ConvertRotation( aTitleProp, true );
+ }
+ }
+ }
+ return xTitle;
+}
+
+void XclImpChText::ConvertTitlePosition( const XclChTextKey& rTitleKey ) const
+{
+ if( !mxFramePos ) return;
+
+ const XclChFramePos& rPosData = mxFramePos->GetFramePosData();
+ OSL_ENSURE( (rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rPosData.mnBRMode == EXC_CHFRAMEPOS_PARENT),
+ "XclImpChText::ConvertTitlePosition - unexpected frame position mode" );
+
+ /* Check if title is moved manually. To get the actual position of the
+ title, we do some kind of hack and use the values from the CHTEXT
+ record, effectively ignoring the contents of the CHFRAMEPOS record
+ which contains the position relative to the default title position
+ (according to the spec, the CHFRAMEPOS supersedes the CHTEXT record).
+ Especially when it comes to axis titles, things would become very
+ complicated here, because the relative title position is stored in a
+ measurement unit that is dependent on the size of the inner plot area,
+ the interpretation of the X and Y coordinate is dependent on the
+ direction of the axis, and in 3D charts, and the title default
+ positions are dependent on the 3D view settings (rotation, elevation,
+ and perspective). Thus, it is easier to assume that the creator has
+ written out the correct absolute position and size of the title in the
+ CHTEXT record. This is assured by checking that the shape size stored
+ in the CHTEXT record is non-zero. */
+ if( !((rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) &&
+ ((rPosData.maRect.mnX != 0) || (rPosData.maRect.mnY != 0)) &&
+ (maData.maRect.mnWidth > 0) && (maData.maRect.mnHeight > 0)) )
+ return;
+
+ try
+ {
+ Reference< XShape > xTitleShape( GetTitleShape( rTitleKey ), UNO_SET_THROW );
+ // the call to XShape.getSize() may recalc the chart view
+ css::awt::Size aTitleSize = xTitleShape->getSize();
+ // rotated titles need special handling...
+ Degree100 nScRot = XclTools::GetScRotation( GetRotation(), 0_deg100 );
+ double fRad = toRadians(nScRot);
+ double fSin = fabs( sin( fRad ) );
+ // calculate the title position from the values in the CHTEXT record
+ css::awt::Point aTitlePos(
+ CalcHmmFromChartX( maData.maRect.mnX ),
+ CalcHmmFromChartY( maData.maRect.mnY ) );
+ // add part of height to X direction, if title is rotated down (clockwise)
+ if( nScRot > 18000_deg100 )
+ aTitlePos.X += static_cast< sal_Int32 >( fSin * aTitleSize.Height + 0.5 );
+ // add part of width to Y direction, if title is rotated up (counterclockwise)
+ else if( nScRot > 0_deg100 )
+ aTitlePos.Y += static_cast< sal_Int32 >( fSin * aTitleSize.Width + 0.5 );
+ // set the resulting position at the title shape
+ xTitleShape->setPosition( aTitlePos );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void XclImpChText::ReadChFrLabelProps( XclImpStream& rStrm )
+{
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ mxLabelProps = std::make_shared<XclChFrLabelProps>();
+ sal_uInt16 nSepLen;
+ rStrm.Ignore( 12 );
+ mxLabelProps->mnFlags = rStrm.ReaduInt16();
+ nSepLen = rStrm.ReaduInt16();
+ if( nSepLen > 0 )
+ mxLabelProps->maSeparator = rStrm.ReadUniString( nSepLen );
+ }
+}
+
+namespace {
+
+void lclUpdateText( XclImpChTextRef& rxText, const XclImpChText* xDefText )
+{
+ if (rxText)
+ rxText->UpdateText( xDefText );
+ else if (xDefText)
+ {
+ rxText = std::make_shared<XclImpChText>(*xDefText);
+ }
+}
+
+void lclFinalizeTitle( XclImpChTextRef& rxTitle, const XclImpChText* pDefText, const OUString& rAutoTitle )
+{
+ /* Do not update a title, if it is not visible (if rxTitle is null).
+ Existing reference indicates enabled title. */
+ if( rxTitle )
+ {
+ if( !rxTitle->HasString() )
+ rxTitle->SetString( rAutoTitle );
+ if( rxTitle->HasString() )
+ rxTitle->UpdateText(pDefText);
+ else
+ rxTitle.reset();
+ }
+}
+
+} // namespace
+
+// Data series ================================================================
+
+void XclImpChMarkerFormat::ReadChMarkerFormat( XclImpStream& rStrm )
+{
+ rStrm >> maData.maLineColor >> maData.maFillColor;
+ maData.mnMarkerType = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ {
+ // BIFF8: index into palette used instead of RGB data
+ const XclImpPalette& rPal = rRoot.GetPalette();
+ maData.maLineColor = rPal.GetColor( rStrm.ReaduInt16() );
+ maData.maFillColor = rPal.GetColor( rStrm.ReaduInt16() );
+ // marker size
+ maData.mnMarkerSize = rStrm.ReaduInt32();
+ }
+}
+
+void XclImpChMarkerFormat::Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx, sal_Int16 nLineWeight ) const
+{
+ if( IsAuto() )
+ {
+ XclChMarkerFormat aMarkerFmt;
+ // line and fill color of the symbol are equal to series line color
+ //TODO: Excel sets no fill color for specific symbols (e.g. cross)
+ aMarkerFmt.maLineColor = aMarkerFmt.maFillColor = rRoot.GetSeriesLineAutoColor( nFormatIdx );
+ switch( nLineWeight )
+ {
+ case EXC_CHLINEFORMAT_HAIR: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_HAIRSIZE; break;
+ case EXC_CHLINEFORMAT_SINGLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_SINGLESIZE; break;
+ case EXC_CHLINEFORMAT_DOUBLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_DOUBLESIZE; break;
+ case EXC_CHLINEFORMAT_TRIPLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_TRIPLESIZE; break;
+ default: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_SINGLESIZE;
+ }
+ aMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx );
+ XclChPropSetHelper::WriteMarkerProperties( rPropSet, aMarkerFmt );
+ }
+ else
+ {
+ XclChPropSetHelper::WriteMarkerProperties( rPropSet, maData );
+ }
+}
+
+void XclImpChMarkerFormat::ConvertColor( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const
+{
+ Color aLineColor = IsAuto() ? rRoot.GetSeriesLineAutoColor( nFormatIdx ) : maData.maFillColor;
+ rPropSet.SetColorProperty( EXC_CHPROP_COLOR, aLineColor );
+}
+
+XclImpChPieFormat::XclImpChPieFormat() :
+ mnPieDist( 0 )
+{
+}
+
+void XclImpChPieFormat::ReadChPieFormat( XclImpStream& rStrm )
+{
+ mnPieDist = rStrm.ReaduInt16();
+}
+
+void XclImpChPieFormat::Convert( ScfPropertySet& rPropSet ) const
+{
+ double fApiDist = ::std::min< double >( mnPieDist / 100.0, 1.0 );
+ rPropSet.SetProperty( EXC_CHPROP_OFFSET, fApiDist );
+}
+
+XclImpChSeriesFormat::XclImpChSeriesFormat() :
+ mnFlags( 0 )
+{
+}
+
+void XclImpChSeriesFormat::ReadChSeriesFormat( XclImpStream& rStrm )
+{
+ mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpCh3dDataFormat::ReadCh3dDataFormat( XclImpStream& rStrm )
+{
+ maData.mnBase = rStrm.ReaduInt8();
+ maData.mnTop = rStrm.ReaduInt8();
+}
+
+void XclImpCh3dDataFormat::Convert( ScfPropertySet& rPropSet ) const
+{
+ using namespace ::com::sun::star::chart2::DataPointGeometry3D;
+ sal_Int32 nApiType = (maData.mnBase == EXC_CH3DDATAFORMAT_RECT) ?
+ ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CUBOID : PYRAMID) :
+ ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CYLINDER : CONE);
+ rPropSet.SetProperty( EXC_CHPROP_GEOMETRY3D, nApiType );
+}
+
+XclImpChAttachedLabel::XclImpChAttachedLabel( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot ),
+ mnFlags( 0 )
+{
+}
+
+void XclImpChAttachedLabel::ReadChAttachedLabel( XclImpStream& rStrm )
+{
+ mnFlags = rStrm.ReaduInt16();
+}
+
+XclImpChTextRef XclImpChAttachedLabel::CreateDataLabel( const XclImpChText* pParent ) const
+{
+ const sal_uInt16 EXC_CHATTLABEL_SHOWANYVALUE = EXC_CHATTLABEL_SHOWVALUE;
+ const sal_uInt16 EXC_CHATTLABEL_SHOWANYPERCENT = EXC_CHATTLABEL_SHOWPERCENT | EXC_CHATTLABEL_SHOWCATEGPERC;
+ const sal_uInt16 EXC_CHATTLABEL_SHOWANYCATEG = EXC_CHATTLABEL_SHOWCATEG | EXC_CHATTLABEL_SHOWCATEGPERC;
+
+ XclImpChTextRef xLabel;
+ if ( pParent )
+ xLabel = std::make_shared<XclImpChText>( *pParent );
+ else
+ xLabel = std::make_shared<XclImpChText>( GetChRoot() );
+ xLabel->UpdateDataLabel(
+ ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYCATEG ),
+ ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYVALUE ),
+ ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYPERCENT ) );
+ return xLabel;
+}
+
+XclImpChDataFormat::XclImpChDataFormat( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChDataFormat::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.maPointPos.mnPointIdx = rStrm.ReaduInt16();
+ maData.maPointPos.mnSeriesIdx = rStrm.ReaduInt16();
+ maData.mnFormatIdx = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChDataFormat::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHMARKERFORMAT:
+ mxMarkerFmt = std::make_shared<XclImpChMarkerFormat>();
+ mxMarkerFmt->ReadChMarkerFormat( rStrm );
+ break;
+ case EXC_ID_CHPIEFORMAT:
+ mxPieFmt = std::make_shared<XclImpChPieFormat>();
+ mxPieFmt->ReadChPieFormat( rStrm );
+ break;
+ case EXC_ID_CHSERIESFORMAT:
+ mxSeriesFmt = std::make_shared<XclImpChSeriesFormat>();
+ mxSeriesFmt->ReadChSeriesFormat( rStrm );
+ break;
+ case EXC_ID_CH3DDATAFORMAT:
+ mx3dDataFmt = std::make_shared<XclImpCh3dDataFormat>();
+ mx3dDataFmt->ReadCh3dDataFormat( rStrm );
+ break;
+ case EXC_ID_CHATTACHEDLABEL:
+ mxAttLabel = std::make_shared<XclImpChAttachedLabel>( GetChRoot() );
+ mxAttLabel->ReadChAttachedLabel( rStrm );
+ break;
+ default:
+ XclImpChFrameBase::ReadSubRecord( rStrm );
+ }
+}
+
+void XclImpChDataFormat::SetPointPos( const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx )
+{
+ maData.maPointPos = rPointPos;
+ maData.mnFormatIdx = nFormatIdx;
+}
+
+void XclImpChDataFormat::UpdateGroupFormat( const XclChExtTypeInfo& rTypeInfo )
+{
+ // remove formats not used for the current chart type
+ RemoveUnusedFormats( rTypeInfo );
+}
+
+void XclImpChDataFormat::UpdateSeriesFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pGroupFmt )
+{
+ // update missing formats from passed chart type group format
+ if( pGroupFmt )
+ {
+ if( !mxLineFmt )
+ mxLineFmt = pGroupFmt->mxLineFmt;
+ if( !mxAreaFmt && !mxEscherFmt )
+ {
+ mxAreaFmt = pGroupFmt->mxAreaFmt;
+ mxEscherFmt = pGroupFmt->mxEscherFmt;
+ }
+ if( !mxMarkerFmt )
+ mxMarkerFmt = pGroupFmt->mxMarkerFmt;
+ if( !mxPieFmt )
+ mxPieFmt = pGroupFmt->mxPieFmt;
+ if( !mxSeriesFmt )
+ mxSeriesFmt = pGroupFmt->mxSeriesFmt;
+ if( !mx3dDataFmt )
+ mx3dDataFmt = pGroupFmt->mx3dDataFmt;
+ if( !mxAttLabel )
+ mxAttLabel = pGroupFmt->mxAttLabel;
+ }
+
+ /* Create missing but required formats. Existing line, area, and marker
+ format objects are needed to create automatic series formatting. */
+ if( !mxLineFmt )
+ mxLineFmt = new XclImpChLineFormat();
+ if( !mxAreaFmt && !mxEscherFmt )
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
+ if( !mxMarkerFmt )
+ mxMarkerFmt = std::make_shared<XclImpChMarkerFormat>();
+
+ // remove formats not used for the current chart type
+ RemoveUnusedFormats( rTypeInfo );
+ // update data label
+ UpdateDataLabel( pGroupFmt );
+}
+
+void XclImpChDataFormat::UpdatePointFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pSeriesFmt )
+{
+ // remove formats if they are automatic in this and in the passed series format
+ if( pSeriesFmt )
+ {
+ if( IsAutoLine() && pSeriesFmt->IsAutoLine() )
+ mxLineFmt.clear();
+ if( IsAutoArea() && pSeriesFmt->IsAutoArea() )
+ mxAreaFmt.reset();
+ if( IsAutoMarker() && pSeriesFmt->IsAutoMarker() )
+ mxMarkerFmt.reset();
+ mxSeriesFmt.reset();
+ }
+
+ // Excel ignores 3D bar format for single data points
+ mx3dDataFmt.reset();
+ // remove point line formats for linear chart types, TODO: implement in OOChart
+ if( !rTypeInfo.IsSeriesFrameFormat() )
+ mxLineFmt.clear();
+
+ // remove formats not used for the current chart type
+ RemoveUnusedFormats( rTypeInfo );
+ // update data label
+ UpdateDataLabel( pSeriesFmt );
+}
+
+void XclImpChDataFormat::UpdateTrendLineFormat()
+{
+ if( !mxLineFmt )
+ mxLineFmt = new XclImpChLineFormat();
+ mxAreaFmt.reset();
+ mxEscherFmt.reset();
+ mxMarkerFmt.reset();
+ mxPieFmt.reset();
+ mxSeriesFmt.reset();
+ mx3dDataFmt.reset();
+ mxAttLabel.reset();
+ // update data label
+ UpdateDataLabel( nullptr );
+}
+
+void XclImpChDataFormat::Convert( ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const
+{
+ /* Line and area format.
+ #i71810# If the data points are filled with bitmaps, textures, or
+ patterns, then only bar charts will use the CHPICFORMAT record to
+ determine stacking/stretching mode. All other chart types ignore this
+ record and always use the property 'fill-type' from the DFF property
+ set (stretched for bitmaps, and stacked for textures and patterns). */
+ bool bUsePicFmt = rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR;
+ ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType(), maData.mnFormatIdx, bUsePicFmt );
+
+ // #i83151# only hair lines in 3D charts with filled data points
+ if( rTypeInfo.mb3dChart && rTypeInfo.IsSeriesFrameFormat() && mxLineFmt && mxLineFmt->HasLine() )
+ rPropSet.SetProperty< sal_Int32 >( "BorderWidth", 0 );
+
+ // other formatting
+ if( mxMarkerFmt )
+ mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx, GetLineWeight() );
+ if( mxPieFmt )
+ mxPieFmt->Convert( rPropSet );
+ if( mx3dDataFmt )
+ mx3dDataFmt->Convert( rPropSet );
+ if( mxLabel )
+ mxLabel->ConvertDataLabel( rPropSet, rTypeInfo, pGlobalPropSet );
+
+ // 3D settings
+ rPropSet.SetProperty< sal_Int16 >( EXC_CHPROP_PERCENTDIAGONAL, 0 );
+
+ /* Special case: set marker color as line color, if series line is not
+ visible. This makes the color visible in the marker area.
+ TODO: remove this if OOChart supports own colors in markers. */
+ if( !rTypeInfo.IsSeriesFrameFormat() && !HasLine() && mxMarkerFmt )
+ mxMarkerFmt->ConvertColor( GetChRoot(), rPropSet, maData.mnFormatIdx );
+}
+
+void XclImpChDataFormat::ConvertLine( ScfPropertySet& rPropSet, XclChObjectType eObjType ) const
+{
+ ConvertLineBase( GetChRoot(), rPropSet, eObjType );
+}
+
+void XclImpChDataFormat::ConvertArea( ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const
+{
+ ConvertAreaBase( GetChRoot(), rPropSet, EXC_CHOBJTYPE_FILLEDSERIES, nFormatIdx );
+}
+
+void XclImpChDataFormat::RemoveUnusedFormats( const XclChExtTypeInfo& rTypeInfo )
+{
+ // data point marker only in linear 2D charts
+ if( rTypeInfo.IsSeriesFrameFormat() )
+ mxMarkerFmt.reset();
+ // pie format only in pie/donut charts
+ if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE )
+ mxPieFmt.reset();
+ // 3D format only in 3D bar charts
+ if( !rTypeInfo.mb3dChart || (rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_BAR) )
+ mx3dDataFmt.reset();
+}
+
+void XclImpChDataFormat::UpdateDataLabel( const XclImpChDataFormat* pParentFmt )
+{
+ /* CHTEXT groups linked to data labels override existing CHATTACHEDLABEL
+ records. Only if there is a CHATTACHEDLABEL record without a CHTEXT
+ group, the contents of the CHATTACHEDLABEL record are used. In this
+ case a new CHTEXT group is created and filled with the settings from
+ the CHATTACHEDLABEL record. */
+ const XclImpChText* pDefText = nullptr;
+ if (pParentFmt)
+ pDefText = pParentFmt->GetDataLabel();
+ if (!pDefText)
+ pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_DATALABEL );
+ if (mxLabel)
+ mxLabel->UpdateText(pDefText);
+ else if (mxAttLabel)
+ mxLabel = mxAttLabel->CreateDataLabel( pDefText );
+}
+
+XclImpChSerTrendLine::XclImpChSerTrendLine( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChSerTrendLine::ReadChSerTrendLine( XclImpStream& rStrm )
+{
+ maData.mnLineType = rStrm.ReaduInt8();
+ maData.mnOrder = rStrm.ReaduInt8();
+ maData.mfIntercept = rStrm.ReadDouble();
+ maData.mnShowEquation = rStrm.ReaduInt8();
+ maData.mnShowRSquared = rStrm.ReaduInt8();
+ maData.mfForecastFor = rStrm.ReadDouble();
+ maData.mfForecastBack = rStrm.ReadDouble();
+}
+
+Reference< XRegressionCurve > XclImpChSerTrendLine::CreateRegressionCurve() const
+{
+ // trend line type
+ Reference< XRegressionCurve > xRegCurve;
+ switch( maData.mnLineType )
+ {
+ case EXC_CHSERTREND_POLYNOMIAL:
+ if( maData.mnOrder == 1 )
+ {
+ xRegCurve = LinearRegressionCurve::create( comphelper::getProcessComponentContext() );
+ } else {
+ xRegCurve = PolynomialRegressionCurve::create( comphelper::getProcessComponentContext() );
+ }
+ break;
+ case EXC_CHSERTREND_EXPONENTIAL:
+ xRegCurve = ExponentialRegressionCurve::create( comphelper::getProcessComponentContext() );
+ break;
+ case EXC_CHSERTREND_LOGARITHMIC:
+ xRegCurve = LogarithmicRegressionCurve::create( comphelper::getProcessComponentContext() );
+ break;
+ case EXC_CHSERTREND_POWER:
+ xRegCurve = PotentialRegressionCurve::create( comphelper::getProcessComponentContext() );
+ break;
+ case EXC_CHSERTREND_MOVING_AVG:
+ xRegCurve = MovingAverageRegressionCurve::create( comphelper::getProcessComponentContext() );
+ break;
+ }
+
+ // trend line formatting
+ if( xRegCurve.is() && mxDataFmt )
+ {
+ ScfPropertySet aPropSet( xRegCurve );
+ mxDataFmt->ConvertLine( aPropSet, EXC_CHOBJTYPE_TRENDLINE );
+
+ aPropSet.SetProperty(EXC_CHPROP_CURVENAME, maTrendLineName);
+ aPropSet.SetProperty(EXC_CHPROP_POLYNOMIAL_DEGREE, static_cast<sal_Int32> (maData.mnOrder) );
+ aPropSet.SetProperty(EXC_CHPROP_MOVING_AVERAGE_PERIOD, static_cast<sal_Int32> (maData.mnOrder) );
+ aPropSet.SetProperty(EXC_CHPROP_EXTRAPOLATE_FORWARD, maData.mfForecastFor);
+ aPropSet.SetProperty(EXC_CHPROP_EXTRAPOLATE_BACKWARD, maData.mfForecastBack);
+
+ bool bForceIntercept = std::isfinite(maData.mfIntercept);
+ aPropSet.SetProperty(EXC_CHPROP_FORCE_INTERCEPT, bForceIntercept);
+ if (bForceIntercept)
+ {
+ aPropSet.SetProperty(EXC_CHPROP_INTERCEPT_VALUE, maData.mfIntercept);
+ }
+
+ // #i83100# show equation and correlation coefficient
+ ScfPropertySet aLabelProp( xRegCurve->getEquationProperties() );
+ aLabelProp.SetBoolProperty( EXC_CHPROP_SHOWEQUATION, maData.mnShowEquation != 0 );
+ aLabelProp.SetBoolProperty( EXC_CHPROP_SHOWCORRELATION, maData.mnShowRSquared != 0 );
+
+ // #i83100# formatting of the equation text box
+ if (const XclImpChText* pLabel = mxDataFmt->GetDataLabel())
+ {
+ pLabel->ConvertFont( aLabelProp );
+ pLabel->ConvertFrame( aLabelProp );
+ pLabel->ConvertNumFmt( aLabelProp, false );
+ }
+ }
+
+ return xRegCurve;
+}
+
+XclImpChSerErrorBar::XclImpChSerErrorBar( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChSerErrorBar::ReadChSerErrorBar( XclImpStream& rStrm )
+{
+ maData.mnBarType = rStrm.ReaduInt8();
+ maData.mnSourceType = rStrm.ReaduInt8();
+ maData.mnLineEnd = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ maData.mfValue = rStrm.ReadDouble();
+ maData.mnValueCount = rStrm.ReaduInt16();
+}
+
+void XclImpChSerErrorBar::SetSeriesData( XclImpChSourceLinkRef const & xValueLink, XclImpChDataFormatRef const & xDataFmt )
+{
+ mxValueLink = xValueLink;
+ mxDataFmt = xDataFmt;
+}
+
+Reference< XLabeledDataSequence > XclImpChSerErrorBar::CreateValueSequence() const
+{
+ return lclCreateLabeledDataSequence( mxValueLink, XclChartHelper::GetErrorBarValuesRole( maData.mnBarType ) );
+}
+
+Reference< XPropertySet > XclImpChSerErrorBar::CreateErrorBar( const XclImpChSerErrorBar* pPosBar, const XclImpChSerErrorBar* pNegBar )
+{
+ Reference< XPropertySet > xErrorBar;
+
+ if( const XclImpChSerErrorBar* pPrimaryBar = pPosBar ? pPosBar : pNegBar )
+ {
+ xErrorBar.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_ERRORBAR ), UNO_QUERY );
+ ScfPropertySet aBarProp( xErrorBar );
+
+ // plus/minus bars visible?
+ aBarProp.SetBoolProperty( EXC_CHPROP_SHOWPOSITIVEERROR, pPosBar != nullptr );
+ aBarProp.SetBoolProperty( EXC_CHPROP_SHOWNEGATIVEERROR, pNegBar != nullptr );
+
+ // type of displayed error
+ switch( pPrimaryBar->maData.mnSourceType )
+ {
+ case EXC_CHSERERR_PERCENT:
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::RELATIVE );
+ aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue );
+ aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue );
+ break;
+ case EXC_CHSERERR_FIXED:
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::ABSOLUTE );
+ aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue );
+ aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue );
+ break;
+ case EXC_CHSERERR_STDDEV:
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_DEVIATION );
+ aBarProp.SetProperty( EXC_CHPROP_WEIGHT, pPrimaryBar->maData.mfValue );
+ break;
+ case EXC_CHSERERR_STDERR:
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_ERROR );
+ break;
+ case EXC_CHSERERR_CUSTOM:
+ {
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::FROM_DATA );
+ // attach data sequences to error bar
+ Reference< XDataSink > xDataSink( xErrorBar, UNO_QUERY );
+ if( xDataSink.is() )
+ {
+ // create vector of all value sequences
+ ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
+ // add positive values
+ if( pPosBar )
+ {
+ Reference< XLabeledDataSequence > xValueSeq = pPosBar->CreateValueSequence();
+ if( xValueSeq.is() )
+ aLabeledSeqVec.push_back( xValueSeq );
+ }
+ // add negative values
+ if( pNegBar )
+ {
+ Reference< XLabeledDataSequence > xValueSeq = pNegBar->CreateValueSequence();
+ if( xValueSeq.is() )
+ aLabeledSeqVec.push_back( xValueSeq );
+ }
+ // attach labeled data sequences to series
+ if( aLabeledSeqVec.empty() )
+ xErrorBar.clear();
+ else
+ xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
+ }
+ }
+ break;
+ default:
+ xErrorBar.clear();
+ }
+
+ // error bar formatting
+ if( pPrimaryBar->mxDataFmt && xErrorBar.is() )
+ pPrimaryBar->mxDataFmt->ConvertLine( aBarProp, EXC_CHOBJTYPE_ERRORBAR );
+ }
+
+ return xErrorBar;
+}
+
+XclImpChSeries::XclImpChSeries( const XclImpChRoot& rRoot, sal_uInt16 nSeriesIdx ) :
+ XclImpChRoot( rRoot ),
+ mnGroupIdx( EXC_CHSERGROUP_NONE ),
+ mnSeriesIdx( nSeriesIdx ),
+ mnParentIdx( EXC_CHSERIES_INVALID ),
+ mbLabelDeleted( false )
+{
+}
+
+void XclImpChSeries::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnCategType = rStrm.ReaduInt16();
+ maData.mnValueType = rStrm.ReaduInt16();
+ maData.mnCategCount = rStrm.ReaduInt16();
+ maData.mnValueCount = rStrm.ReaduInt16();
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ maData.mnBubbleType = rStrm.ReaduInt16();
+ maData.mnBubbleCount = rStrm.ReaduInt16();
+ }
+}
+
+void XclImpChSeries::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHSOURCELINK:
+ ReadChSourceLink( rStrm );
+ break;
+ case EXC_ID_CHDATAFORMAT:
+ ReadChDataFormat( rStrm );
+ break;
+ case EXC_ID_CHSERGROUP:
+ mnGroupIdx = rStrm.ReaduInt16();
+ break;
+ case EXC_ID_CHSERPARENT:
+ ReadChSerParent( rStrm );
+ break;
+ case EXC_ID_CHSERTRENDLINE:
+ ReadChSerTrendLine( rStrm );
+ break;
+ case EXC_ID_CHSERERRORBAR:
+ ReadChSerErrorBar( rStrm );
+ break;
+ case EXC_ID_CHLEGENDEXCEPTION:
+ ReadChLegendException( rStrm );
+ break;
+ }
+}
+
+void XclImpChSeries::SetDataFormat( const XclImpChDataFormatRef& xDataFmt )
+{
+ if (!xDataFmt)
+ return;
+
+ sal_uInt16 nPointIdx = xDataFmt->GetPointPos().mnPointIdx;
+ if (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS)
+ {
+ if (mxSeriesFmt)
+ // Don't overwrite the existing format.
+ return;
+
+ mxSeriesFmt = xDataFmt;
+ if (HasParentSeries())
+ return;
+
+ XclImpChTypeGroupRef pTypeGroup = GetChartData().GetTypeGroup(mnGroupIdx);
+ if (pTypeGroup)
+ pTypeGroup->SetUsedFormatIndex(xDataFmt->GetFormatIdx());
+
+ return;
+ }
+
+ if (nPointIdx >= EXC_CHDATAFORMAT_MAXPOINTCOUNT)
+ // Above the max point count. Bail out.
+ return;
+
+ XclImpChDataFormatMap::iterator itr = maPointFmts.lower_bound(nPointIdx);
+ if (itr == maPointFmts.end() || maPointFmts.key_comp()(nPointIdx, itr->first))
+ {
+ // No object exists at this point index position. Insert it.
+ itr = maPointFmts.insert(itr, XclImpChDataFormatMap::value_type(nPointIdx, xDataFmt));
+ }
+}
+
+void XclImpChSeries::SetDataLabel( const XclImpChTextRef& xLabel )
+{
+ if (!xLabel)
+ return;
+
+ sal_uInt16 nPointIdx = xLabel->GetPointPos().mnPointIdx;
+ if ((nPointIdx != EXC_CHDATAFORMAT_ALLPOINTS) && (nPointIdx >= EXC_CHDATAFORMAT_MAXPOINTCOUNT))
+ // Above the maximum allowed data points. Bail out.
+ return;
+
+ XclImpChTextMap::iterator itr = maLabels.lower_bound(nPointIdx);
+ if (itr == maLabels.end() || maLabels.key_comp()(nPointIdx, itr->first))
+ {
+ // No object exists at this point index position. Insert it.
+ itr = maLabels.insert(itr, XclImpChTextMap::value_type(nPointIdx, xLabel));
+ }
+}
+
+void XclImpChSeries::AddChildSeries( const XclImpChSeries& rSeries )
+{
+ OSL_ENSURE( !HasParentSeries(), "XclImpChSeries::AddChildSeries - not allowed for child series" );
+ if (&rSeries == this)
+ {
+ SAL_WARN("sc.filter", "self add attempt");
+ return;
+ }
+
+ /* In Excel, trend lines and error bars are stored as own series. In Calc,
+ these are properties of the parent series. This function adds the
+ settings of the passed series to this series. */
+ maTrendLines.insert( maTrendLines.end(), rSeries.maTrendLines.begin(), rSeries.maTrendLines.end() );
+ for (auto const& it : rSeries.m_ErrorBars)
+ {
+ m_ErrorBars.insert(std::make_pair(it.first, std::make_unique<XclImpChSerErrorBar>(*it.second)));
+ }
+}
+
+void XclImpChSeries::FinalizeDataFormats()
+{
+ if( HasParentSeries() )
+ {
+ // *** series is a child series, e.g. trend line or error bar ***
+
+ // create missing series format
+ if( !mxSeriesFmt )
+ mxSeriesFmt = CreateDataFormat( EXC_CHDATAFORMAT_ALLPOINTS, 0 );
+
+ if( mxSeriesFmt )
+ {
+ // #i83100# set text label format, e.g. for trend line equations
+ XclImpChTextRef xLabel;
+ XclImpChTextMap::iterator itr = maLabels.find(EXC_CHDATAFORMAT_ALLPOINTS);
+ if (itr != maLabels.end())
+ xLabel = itr->second;
+ mxSeriesFmt->SetDataLabel(xLabel);
+ // create missing automatic formats
+ mxSeriesFmt->UpdateTrendLineFormat();
+ }
+
+ // copy series formatting to child objects
+ for (auto const& trendLine : maTrendLines)
+ {
+ trendLine->SetDataFormat(mxSeriesFmt);
+ if (mxTitleLink && mxTitleLink->HasString())
+ {
+ trendLine->SetTrendlineName(mxTitleLink->GetString());
+ }
+ }
+ for (auto const& it : m_ErrorBars)
+ {
+ it.second->SetSeriesData( mxValueLink, mxSeriesFmt );
+ }
+ }
+ else if( XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() )
+ {
+ // *** series is a regular data series ***
+
+ // create missing series format
+ if( !mxSeriesFmt )
+ {
+ // #i51639# use a new unused format index to create series default format
+ sal_uInt16 nFormatIdx = pTypeGroup->PopUnusedFormatIndex();
+ mxSeriesFmt = CreateDataFormat( EXC_CHDATAFORMAT_ALLPOINTS, nFormatIdx );
+ }
+
+ // set text labels to data formats
+ for (auto const& label : maLabels)
+ {
+ sal_uInt16 nPointIdx = label.first;
+ if (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS)
+ {
+ if (!mxSeriesFmt)
+ mxSeriesFmt = CreateDataFormat(nPointIdx, EXC_CHDATAFORMAT_DEFAULT);
+ mxSeriesFmt->SetDataLabel(label.second);
+ }
+ else if (nPointIdx < EXC_CHDATAFORMAT_MAXPOINTCOUNT)
+ {
+ XclImpChDataFormatRef p;
+ XclImpChDataFormatMap::iterator itr = maPointFmts.lower_bound(nPointIdx);
+ if (itr == maPointFmts.end() || maPointFmts.key_comp()(nPointIdx, itr->first))
+ {
+ // No object exists at this point index position. Insert
+ // a new one.
+ p = CreateDataFormat(nPointIdx, EXC_CHDATAFORMAT_DEFAULT);
+ itr = maPointFmts.insert(
+ itr, XclImpChDataFormatMap::value_type(nPointIdx, p));
+ }
+ else
+ p = itr->second;
+ p->SetDataLabel(label.second);
+ }
+ }
+
+ // update series format (copy missing formatting from group default format)
+ if( mxSeriesFmt )
+ mxSeriesFmt->UpdateSeriesFormat( pTypeGroup->GetTypeInfo(), pTypeGroup->GetGroupFormat().get() );
+
+ // update data point formats (removes unchanged automatic formatting)
+ for (auto const& pointFormat : maPointFmts)
+ pointFormat.second->UpdatePointFormat( pTypeGroup->GetTypeInfo(), mxSeriesFmt.get() );
+ }
+}
+
+namespace {
+
+/** Returns the property set of the specified data point. */
+ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > const & xDataSeries, sal_uInt16 nPointIdx )
+{
+ ScfPropertySet aPropSet;
+ try
+ {
+ aPropSet.Set( xDataSeries->getDataPointByIndex( static_cast< sal_Int32 >( nPointIdx ) ) );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "lclGetPointPropSet - no data point property set" );
+ }
+ return aPropSet;
+}
+
+} // namespace
+
+Reference< XLabeledDataSequence > XclImpChSeries::CreateValueSequence( const OUString& rValueRole ) const
+{
+ return lclCreateLabeledDataSequence( mxValueLink, rValueRole, mxTitleLink.get() );
+}
+
+Reference< XLabeledDataSequence > XclImpChSeries::CreateCategSequence( const OUString& rCategRole ) const
+{
+ return lclCreateLabeledDataSequence( mxCategLink, rCategRole );
+}
+
+Reference< XDataSeries > XclImpChSeries::CreateDataSeries() const
+{
+ Reference< XDataSeries > xDataSeries;
+ if( const XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() )
+ {
+ const XclChExtTypeInfo& rTypeInfo = pTypeGroup->GetTypeInfo();
+
+ // create the data series object
+ xDataSeries.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY );
+
+ // attach data and title sequences to series
+ Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
+ if( xDataSink.is() )
+ {
+ // create vector of all value sequences
+ ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
+ // add Y values
+ Reference< XLabeledDataSequence > xYValueSeq =
+ CreateValueSequence( EXC_CHPROP_ROLE_YVALUES );
+ if( xYValueSeq.is() )
+ aLabeledSeqVec.push_back( xYValueSeq );
+ // add X values
+ if( !rTypeInfo.mbCategoryAxis )
+ {
+ Reference< XLabeledDataSequence > xXValueSeq =
+ CreateCategSequence( EXC_CHPROP_ROLE_XVALUES );
+ if( xXValueSeq.is() )
+ aLabeledSeqVec.push_back( xXValueSeq );
+ // add size values of bubble charts
+ if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES )
+ {
+ Reference< XLabeledDataSequence > xSizeValueSeq =
+ lclCreateLabeledDataSequence( mxBubbleLink, EXC_CHPROP_ROLE_SIZEVALUES, mxTitleLink.get() );
+ if( xSizeValueSeq.is() )
+ aLabeledSeqVec.push_back( xSizeValueSeq );
+ }
+ }
+ // attach labeled data sequences to series
+ if( !aLabeledSeqVec.empty() )
+ xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
+ }
+
+ // series formatting
+ ScfPropertySet aSeriesProp( xDataSeries );
+ if( mxSeriesFmt )
+ mxSeriesFmt->Convert( aSeriesProp, rTypeInfo );
+
+ if (mbLabelDeleted)
+ aSeriesProp.SetProperty(EXC_CHPROP_SHOWLEGENDENTRY, false);
+
+ // trend lines
+ ConvertTrendLines( xDataSeries );
+
+ // error bars
+ Reference< XPropertySet > xErrorBarX = CreateErrorBar( EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS );
+ if( xErrorBarX.is() )
+ aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARX, xErrorBarX );
+ Reference< XPropertySet > xErrorBarY = CreateErrorBar( EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS );
+ if( xErrorBarY.is() )
+ aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARY, xErrorBarY );
+
+ // own area formatting for every data point (TODO: varying line color not supported)
+ bool bVarPointFmt = pTypeGroup->HasVarPointFormat() && rTypeInfo.IsSeriesFrameFormat();
+ aSeriesProp.SetBoolProperty( EXC_CHPROP_VARYCOLORSBY, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE );
+ // #i91271# always set area formatting for every point in pie/doughnut charts
+ if (mxSeriesFmt && mxValueLink && ((bVarPointFmt && mxSeriesFmt->IsAutoArea()) || (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE)))
+ {
+ for( sal_uInt16 nPointIdx = 0, nPointCount = mxValueLink->GetCellCount(); nPointIdx < nPointCount; ++nPointIdx )
+ {
+ ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx );
+ mxSeriesFmt->ConvertArea( aPointProp, bVarPointFmt ? nPointIdx : mnSeriesIdx );
+ }
+ }
+
+ // data point formatting
+ for (auto const& pointFormat : maPointFmts)
+ {
+ ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, pointFormat.first );
+ pointFormat.second->Convert( aPointProp, rTypeInfo, &aSeriesProp );
+ }
+ }
+ return xDataSeries;
+}
+
+void XclImpChSeries::FillAllSourceLinks( ::std::vector< ScTokenRef >& rTokens ) const
+{
+ if( mxValueLink )
+ mxValueLink->FillSourceLink( rTokens );
+ if( mxCategLink )
+ mxCategLink->FillSourceLink( rTokens );
+ if( mxTitleLink )
+ mxTitleLink->FillSourceLink( rTokens );
+ if( mxBubbleLink )
+ mxBubbleLink->FillSourceLink( rTokens );
+}
+
+void XclImpChSeries::ReadChSourceLink( XclImpStream& rStrm )
+{
+ XclImpChSourceLinkRef xSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
+ xSrcLink->ReadChSourceLink( rStrm );
+ switch( xSrcLink->GetDestType() )
+ {
+ case EXC_CHSRCLINK_TITLE: mxTitleLink = xSrcLink; break;
+ case EXC_CHSRCLINK_VALUES: mxValueLink = xSrcLink; break;
+ case EXC_CHSRCLINK_CATEGORY: mxCategLink = xSrcLink; break;
+ case EXC_CHSRCLINK_BUBBLES: mxBubbleLink = xSrcLink; break;
+ }
+}
+
+void XclImpChSeries::ReadChDataFormat( XclImpStream& rStrm )
+{
+ // #i51639# chart stores all data formats and assigns them later to the series
+ GetChartData().ReadChDataFormat( rStrm );
+}
+
+void XclImpChSeries::ReadChSerParent( XclImpStream& rStrm )
+{
+ mnParentIdx = rStrm.ReaduInt16();
+ // index to parent series is 1-based, convert it to 0-based
+ if( mnParentIdx > 0 )
+ --mnParentIdx;
+ else
+ mnParentIdx = EXC_CHSERIES_INVALID;
+}
+
+void XclImpChSeries::ReadChSerTrendLine( XclImpStream& rStrm )
+{
+ XclImpChSerTrendLineRef xTrendLine = std::make_shared<XclImpChSerTrendLine>( GetChRoot() );
+ xTrendLine->ReadChSerTrendLine( rStrm );
+ maTrendLines.push_back( xTrendLine );
+}
+
+void XclImpChSeries::ReadChSerErrorBar( XclImpStream& rStrm )
+{
+ unique_ptr<XclImpChSerErrorBar> pErrorBar(new XclImpChSerErrorBar(GetChRoot()));
+ pErrorBar->ReadChSerErrorBar(rStrm);
+ sal_uInt8 nBarType = pErrorBar->GetBarType();
+ m_ErrorBars.insert(std::make_pair(nBarType, std::move(pErrorBar)));
+}
+
+XclImpChDataFormatRef XclImpChSeries::CreateDataFormat( sal_uInt16 nPointIdx, sal_uInt16 nFormatIdx )
+{
+ XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
+ xDataFmt->SetPointPos( XclChDataPointPos( mnSeriesIdx, nPointIdx ), nFormatIdx );
+ return xDataFmt;
+}
+
+void XclImpChSeries::ConvertTrendLines( Reference< XDataSeries > const & xDataSeries ) const
+{
+ Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY );
+ if( !xRegCurveCont.is() )
+ return;
+
+ for (auto const& trendLine : maTrendLines)
+ {
+ try
+ {
+ Reference< XRegressionCurve > xRegCurve = trendLine->CreateRegressionCurve();
+ if( xRegCurve.is() )
+ {
+ xRegCurveCont->addRegressionCurve( xRegCurve );
+ }
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChSeries::ConvertTrendLines - cannot add regression curve" );
+ }
+ }
+}
+
+Reference< XPropertySet > XclImpChSeries::CreateErrorBar( sal_uInt8 nPosBarId, sal_uInt8 nNegBarId ) const
+{
+ XclImpChSerErrorBarMap::const_iterator itrPosBar = m_ErrorBars.find(nPosBarId);
+ XclImpChSerErrorBarMap::const_iterator itrNegBar = m_ErrorBars.find(nNegBarId);
+ XclImpChSerErrorBarMap::const_iterator itrEnd = m_ErrorBars.end();
+ if (itrPosBar == itrEnd || itrNegBar == itrEnd)
+ return Reference<XPropertySet>();
+
+ return XclImpChSerErrorBar::CreateErrorBar(itrPosBar->second.get(), itrNegBar->second.get());
+}
+
+void XclImpChSeries::ReadChLegendException(XclImpStream& rStrm)
+{
+ rStrm.Ignore(2);
+ sal_uInt16 nFlags = rStrm.ReaduInt16();
+ mbLabelDeleted = (nFlags & EXC_CHLEGENDEXCEPTION_DELETED);
+}
+
+// Chart type groups ==========================================================
+
+XclImpChType::XclImpChType( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot ),
+ mnRecId( EXC_ID_CHUNKNOWN ),
+ maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) )
+{
+}
+
+void XclImpChType::ReadChType( XclImpStream& rStrm )
+{
+ sal_uInt16 nRecId = rStrm.GetRecId();
+ bool bKnownType = true;
+
+ switch( nRecId )
+ {
+ case EXC_ID_CHBAR:
+ maData.mnOverlap = rStrm.ReadInt16();
+ maData.mnGap = rStrm.ReadInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+ break;
+
+ case EXC_ID_CHLINE:
+ case EXC_ID_CHAREA:
+ case EXC_ID_CHRADARLINE:
+ case EXC_ID_CHRADARAREA:
+ maData.mnFlags = rStrm.ReaduInt16();
+ break;
+
+ case EXC_ID_CHPIE:
+ maData.mnRotation = rStrm.ReaduInt16();
+ maData.mnPieHole = rStrm.ReaduInt16();
+ if( GetBiff() == EXC_BIFF8 )
+ maData.mnFlags = rStrm.ReaduInt16();
+ else
+ maData.mnFlags = 0;
+ break;
+
+ case EXC_ID_CHPIEEXT:
+ maData.mnRotation = 0;
+ maData.mnPieHole = 0;
+ maData.mnFlags = 0;
+ break;
+
+ case EXC_ID_CHSCATTER:
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ maData.mnBubbleSize = rStrm.ReaduInt16();
+ maData.mnBubbleType = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+ }
+ else
+ maData.mnFlags = 0;
+ break;
+
+ case EXC_ID_CHSURFACE:
+ maData.mnFlags = rStrm.ReaduInt16();
+ break;
+
+ default:
+ bKnownType = false;
+ }
+
+ if( bKnownType )
+ mnRecId = nRecId;
+}
+
+void XclImpChType::Finalize( bool bStockChart )
+{
+ switch( mnRecId )
+ {
+ case EXC_ID_CHLINE:
+ maTypeInfo = GetChartTypeInfo( bStockChart ?
+ EXC_CHTYPEID_STOCK : EXC_CHTYPEID_LINE );
+ break;
+ case EXC_ID_CHBAR:
+ maTypeInfo = GetChartTypeInfo( ::get_flagvalue(
+ maData.mnFlags, EXC_CHBAR_HORIZONTAL,
+ EXC_CHTYPEID_HORBAR, EXC_CHTYPEID_BAR ) );
+ break;
+ case EXC_ID_CHPIE:
+ maTypeInfo = GetChartTypeInfo( (maData.mnPieHole > 0) ?
+ EXC_CHTYPEID_DONUT : EXC_CHTYPEID_PIE );
+ break;
+ case EXC_ID_CHSCATTER:
+ maTypeInfo = GetChartTypeInfo( ::get_flagvalue(
+ maData.mnFlags, EXC_CHSCATTER_BUBBLES,
+ EXC_CHTYPEID_BUBBLES, EXC_CHTYPEID_SCATTER ) );
+ break;
+ default:
+ maTypeInfo = GetChartTypeInfo( mnRecId );
+ }
+
+ switch( maTypeInfo.meTypeId )
+ {
+ case EXC_CHTYPEID_PIEEXT:
+ case EXC_CHTYPEID_BUBBLES:
+ case EXC_CHTYPEID_SURFACE:
+ case EXC_CHTYPEID_UNKNOWN:
+ GetTracer().TraceChartUnKnownType();
+ break;
+ default:;
+ }
+}
+
+bool XclImpChType::IsStacked() const
+{
+ bool bStacked = false;
+ if( maTypeInfo.mbSupportsStacking ) switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_LINE:
+ bStacked =
+ ::get_flag( maData.mnFlags, EXC_CHLINE_STACKED ) &&
+ !::get_flag( maData.mnFlags, EXC_CHLINE_PERCENT );
+ break;
+ case EXC_CHTYPECATEG_BAR:
+ bStacked =
+ ::get_flag( maData.mnFlags, EXC_CHBAR_STACKED ) &&
+ !::get_flag( maData.mnFlags, EXC_CHBAR_PERCENT );
+ break;
+ default:;
+ }
+ return bStacked;
+}
+
+bool XclImpChType::IsPercent() const
+{
+ bool bPercent = false;
+ if( maTypeInfo.mbSupportsStacking ) switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_LINE:
+ bPercent =
+ ::get_flag( maData.mnFlags, EXC_CHLINE_STACKED ) &&
+ ::get_flag( maData.mnFlags, EXC_CHLINE_PERCENT );
+ break;
+ case EXC_CHTYPECATEG_BAR:
+ bPercent =
+ ::get_flag( maData.mnFlags, EXC_CHBAR_STACKED ) &&
+ ::get_flag( maData.mnFlags, EXC_CHBAR_PERCENT );
+ break;
+ default:;
+ }
+ return bPercent;
+}
+
+bool XclImpChType::HasCategoryLabels() const
+{
+ // radar charts disable category labels in chart type, not in CHTICK of X axis
+ return (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_RADAR) || ::get_flag( maData.mnFlags, EXC_CHRADAR_AXISLABELS );
+}
+
+Reference< XCoordinateSystem > XclImpChType::CreateCoordSystem( bool b3dChart ) const
+{
+ // create the coordinate system object
+ Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+ Reference< XCoordinateSystem > xCoordSystem;
+ if( maTypeInfo.mbPolarCoordSystem )
+ {
+ if( b3dChart )
+ xCoordSystem = css::chart2::PolarCoordinateSystem3d::create(xContext);
+ else
+ xCoordSystem = css::chart2::PolarCoordinateSystem2d::create(xContext);
+ }
+ else
+ {
+ if( b3dChart )
+ xCoordSystem = css::chart2::CartesianCoordinateSystem3d::create(xContext);
+ else
+ xCoordSystem = css::chart2::CartesianCoordinateSystem2d::create(xContext);
+ }
+
+ // swap X and Y axis
+ if( maTypeInfo.mbSwappedAxesSet )
+ {
+ ScfPropertySet aCoordSysProp( xCoordSystem );
+ aCoordSysProp.SetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS, true );
+ }
+
+ return xCoordSystem;
+}
+
+Reference< XChartType > XclImpChType::CreateChartType( Reference< XDiagram > const & xDiagram, bool b3dChart ) const
+{
+ OUString aService = OUString::createFromAscii( maTypeInfo.mpcServiceName );
+ Reference< XChartType > xChartType( ScfApiHelper::CreateInstance( aService ), UNO_QUERY );
+
+ // additional properties
+ switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_BAR:
+ {
+ ScfPropertySet aTypeProp( xChartType );
+ Sequence< sal_Int32 > aInt32Seq{ -maData.mnOverlap, -maData.mnOverlap };
+ aTypeProp.SetProperty( EXC_CHPROP_OVERLAPSEQ, aInt32Seq );
+ aInt32Seq = { maData.mnGap, maData.mnGap };
+ aTypeProp.SetProperty( EXC_CHPROP_GAPWIDTHSEQ, aInt32Seq );
+ }
+ break;
+ case EXC_CHTYPECATEG_PIE:
+ {
+ ScfPropertySet aTypeProp( xChartType );
+ aTypeProp.SetBoolProperty( EXC_CHPROP_USERINGS, maTypeInfo.meTypeId == EXC_CHTYPEID_DONUT );
+ /* #i85166# starting angle of first pie slice. 3D pie charts use Y
+ rotation setting in view3D element. Of-pie charts do not
+ support pie rotation. */
+ if( !b3dChart && (maTypeInfo.meTypeId != EXC_CHTYPEID_PIEEXT) )
+ {
+ ScfPropertySet aDiaProp( xDiagram );
+ XclImpChRoot::ConvertPieRotation( aDiaProp, maData.mnRotation );
+ }
+ }
+ break;
+ default:;
+ }
+
+ return xChartType;
+}
+
+void XclImpChChart3d::ReadChChart3d( XclImpStream& rStrm )
+{
+ maData.mnRotation = rStrm.ReaduInt16();
+ maData.mnElevation = rStrm.ReadInt16();
+ maData.mnEyeDist = rStrm.ReaduInt16();
+ maData.mnRelHeight = rStrm.ReaduInt16();
+ maData.mnRelDepth = rStrm.ReaduInt16();
+ maData.mnDepthGap = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChChart3d::Convert( ScfPropertySet& rPropSet, bool b3dWallChart ) const
+{
+ namespace cssd = ::com::sun::star::drawing;
+
+// #i104057# do not assert this, written by broken external generators
+// OSL_ENSURE( ::get_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS ) == b3dWallChart, "XclImpChChart3d::Convert - wrong wall flag" );
+
+ sal_Int32 nRotationY = 0;
+ sal_Int32 nRotationX = 0;
+ sal_Int32 nPerspective = 15;
+ bool bRightAngled = false;
+ cssd::ProjectionMode eProjMode = cssd::ProjectionMode_PERSPECTIVE;
+ Color aAmbientColor, aLightColor;
+
+ if( b3dWallChart )
+ {
+ // Y rotation (Excel [0..359], Chart2 [-179,180])
+ nRotationY = NormAngle180<sal_Int32>(maData.mnRotation);
+ // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180])
+ nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, -90, 90 );
+ // perspective (Excel and Chart2 [0,100])
+ nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 );
+ // right-angled axes
+ bRightAngled = !::get_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D );
+ // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%)
+ bool bParallel = bRightAngled || (nPerspective == 0);
+ eProjMode = bParallel ? cssd::ProjectionMode_PARALLEL : cssd::ProjectionMode_PERSPECTIVE;
+ // ambient color (Gray 20%)
+ aAmbientColor = Color( 204, 204, 204 );
+ // light color (Gray 60%)
+ aLightColor = Color( 102, 102, 102 );
+ }
+ else
+ {
+ // Y rotation not used in pie charts, but 'first pie slice angle'
+ nRotationY = 0;
+ XclImpChRoot::ConvertPieRotation( rPropSet, maData.mnRotation );
+ // X rotation a.k.a. elevation (map Excel [10..80] to Chart2 [-80,-10])
+ nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, 10, 80 ) - 90;
+ // perspective (Excel and Chart2 [0,100])
+ nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 );
+ // no right-angled axes in pie charts, but parallel projection
+ bRightAngled = false;
+ eProjMode = cssd::ProjectionMode_PARALLEL;
+ // ambient color (Gray 30%)
+ aAmbientColor = Color( 179, 179, 179 );
+ // light color (Gray 70%)
+ aLightColor = Color( 76, 76, 76 );
+ }
+
+ // properties
+ rPropSet.SetProperty( EXC_CHPROP_3DRELATIVEHEIGHT, static_cast<sal_Int32>(maData.mnRelHeight / 2)); // seems to be 200%, change to 100%
+ rPropSet.SetProperty( EXC_CHPROP_ROTATIONVERTICAL, nRotationY );
+ rPropSet.SetProperty( EXC_CHPROP_ROTATIONHORIZONTAL, nRotationX );
+ rPropSet.SetProperty( EXC_CHPROP_PERSPECTIVE, nPerspective );
+ rPropSet.SetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES, bRightAngled );
+ rPropSet.SetProperty( EXC_CHPROP_D3DSCENEPERSPECTIVE, eProjMode );
+
+ // light settings
+ rPropSet.SetProperty( EXC_CHPROP_D3DSCENESHADEMODE, cssd::ShadeMode_FLAT );
+ rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENEAMBIENTCOLOR, aAmbientColor );
+ rPropSet.SetBoolProperty( EXC_CHPROP_D3DSCENELIGHTON1, false );
+ rPropSet.SetBoolProperty( EXC_CHPROP_D3DSCENELIGHTON2, true );
+ rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENELIGHTCOLOR2, aLightColor );
+ rPropSet.SetProperty( EXC_CHPROP_D3DSCENELIGHTDIR2, cssd::Direction3D( 0.2, 0.4, 1.0 ) );
+}
+
+XclImpChLegend::XclImpChLegend( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChLegend::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ rStrm >> maData.maRect;
+ maData.mnDockMode = rStrm.ReaduInt8();
+ maData.mnSpacing = rStrm.ReaduInt8();
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ // trace unsupported features
+ if( GetTracer().IsEnabled() )
+ {
+ if( maData.mnDockMode == EXC_CHLEGEND_NOTDOCKED )
+ GetTracer().TraceChartLegendPosition();
+ if( ::get_flag( maData.mnFlags, EXC_CHLEGEND_DATATABLE ) )
+ GetTracer().TraceChartDataTable();
+ }
+}
+
+void XclImpChLegend::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHFRAMEPOS:
+ mxFramePos = std::make_shared<XclImpChFramePos>();
+ mxFramePos->ReadChFramePos( rStrm );
+ break;
+ case EXC_ID_CHTEXT:
+ mxText = std::make_shared<XclImpChText>( GetChRoot() );
+ mxText->ReadRecordGroup( rStrm );
+ break;
+ case EXC_ID_CHFRAME:
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
+ mxFrame->ReadRecordGroup( rStrm );
+ break;
+ }
+}
+
+void XclImpChLegend::Finalize()
+{
+ // legend default formatting differs in OOChart and Excel, missing frame means automatic
+ if( !mxFrame )
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
+ // Update text formatting. If mxText is empty, the passed default text is used.
+ lclUpdateText( mxText, GetChartData().GetDefaultText( EXC_CHTEXTTYPE_LEGEND ) );
+}
+
+Reference< XLegend > XclImpChLegend::CreateLegend() const
+{
+ Reference< XLegend > xLegend( ScfApiHelper::CreateInstance( SERVICE_CHART2_LEGEND ), UNO_QUERY );
+ if( xLegend.is() )
+ {
+ ScfPropertySet aLegendProp( xLegend );
+ aLegendProp.SetBoolProperty( EXC_CHPROP_SHOW, true );
+
+ // frame properties
+ if( mxFrame )
+ mxFrame->Convert( aLegendProp );
+ // text properties
+ if( mxText )
+ mxText->ConvertFont( aLegendProp );
+
+ /* Legend position and size. Default positions are used only if the
+ plot area is positioned automatically (Excel sets the plot area to
+ manual mode, if the legend is moved or resized). With manual plot
+ areas, Excel ignores the value in maData.mnDockMode completely. */
+ cssc2::LegendPosition eApiPos = cssc2::LegendPosition_LINE_END;
+ cssc::ChartLegendExpansion eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
+ if( !GetChartData().IsManualPlotArea() ) switch( maData.mnDockMode )
+ {
+ case EXC_CHLEGEND_LEFT:
+ eApiPos = cssc2::LegendPosition_LINE_START;
+ eApiExpand = cssc::ChartLegendExpansion_HIGH;
+ break;
+ case EXC_CHLEGEND_RIGHT:
+ // top-right not supported
+ case EXC_CHLEGEND_CORNER:
+ eApiPos = cssc2::LegendPosition_LINE_END;
+ eApiExpand = cssc::ChartLegendExpansion_HIGH;
+ break;
+ case EXC_CHLEGEND_TOP:
+ eApiPos = cssc2::LegendPosition_PAGE_START;
+ eApiExpand = cssc::ChartLegendExpansion_WIDE;
+ break;
+ case EXC_CHLEGEND_BOTTOM:
+ eApiPos = cssc2::LegendPosition_PAGE_END;
+ eApiExpand = cssc::ChartLegendExpansion_WIDE;
+ break;
+ }
+
+ // no automatic position/size: try to find the correct position and size
+ if( GetChartData().IsManualPlotArea() || maData.mnDockMode == EXC_CHLEGEND_NOTDOCKED )
+ {
+ const XclChFramePos* pFramePos = mxFramePos ? &mxFramePos->GetFramePosData() : nullptr;
+
+ /* Legend position. Only the settings from the CHFRAMEPOS record
+ are used by Excel, the position in the CHLEGEND record will be
+ ignored. */
+ if( pFramePos )
+ {
+ RelativePosition aRelPos(
+ CalcRelativeFromChartX( pFramePos->maRect.mnX ),
+ CalcRelativeFromChartY( pFramePos->maRect.mnY ),
+ css::drawing::Alignment_TOP_LEFT );
+ aLegendProp.SetProperty( EXC_CHPROP_RELATIVEPOSITION, aRelPos );
+ }
+ else
+ {
+ // no manual position/size found, just go for the default
+ eApiPos = cssc2::LegendPosition_LINE_END;
+ }
+
+ /* Legend size. The member mnBRMode specifies whether size is
+ automatic or changes manually. Manual size is given in points,
+ not in chart units. */
+ if( pFramePos && (pFramePos->mnBRMode == EXC_CHFRAMEPOS_ABSSIZE_POINTS) &&
+ (pFramePos->maRect.mnWidth > 0) && (pFramePos->maRect.mnHeight > 0) )
+ {
+ eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
+ sal_Int32 nWidthHmm = o3tl::convert(pFramePos->maRect.mnWidth, o3tl::Length::pt, o3tl::Length::mm100);
+ sal_Int32 nHeightHmm = o3tl::convert(pFramePos->maRect.mnHeight, o3tl::Length::pt, o3tl::Length::mm100);
+ RelativeSize aRelSize( CalcRelativeFromHmmX( nWidthHmm ), CalcRelativeFromHmmY( nHeightHmm ) );
+ aLegendProp.SetProperty( EXC_CHPROP_RELATIVESIZE, aRelSize );
+ }
+ else
+ {
+ // automatic size: determine entry direction from flags
+ eApiExpand = ::get_flagvalue( maData.mnFlags, EXC_CHLEGEND_STACKED,
+ cssc::ChartLegendExpansion_HIGH, cssc::ChartLegendExpansion_WIDE );
+ }
+ }
+ aLegendProp.SetProperty( EXC_CHPROP_ANCHORPOSITION, eApiPos );
+ aLegendProp.SetProperty( EXC_CHPROP_EXPANSION, eApiExpand );
+ }
+ return xLegend;
+}
+
+XclImpChDropBar::XclImpChDropBar( sal_uInt16 nDropBar ) :
+ mnDropBar( nDropBar ),
+ mnBarDist( 0 )
+{
+}
+
+void XclImpChDropBar::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ mnBarDist = rStrm.ReaduInt16();
+}
+
+void XclImpChDropBar::Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const
+{
+ XclChObjectType eObjType = EXC_CHOBJTYPE_BACKGROUND;
+ switch( mnDropBar )
+ {
+ case EXC_CHDROPBAR_UP: eObjType = EXC_CHOBJTYPE_WHITEDROPBAR; break;
+ case EXC_CHDROPBAR_DOWN: eObjType = EXC_CHOBJTYPE_BLACKDROPBAR; break;
+ }
+ ConvertFrameBase( rRoot, rPropSet, eObjType );
+}
+
+XclImpChTypeGroup::XclImpChTypeGroup( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot ),
+ maType( rRoot ),
+ maTypeInfo( maType.GetTypeInfo() )
+{
+ // Initialize unused format indexes set. At this time, all formats are unused.
+ for( sal_uInt16 nFormatIdx = 0; nFormatIdx <= EXC_CHSERIES_MAXSERIES; ++nFormatIdx )
+ maUnusedFormats.insert( maUnusedFormats.end(), nFormatIdx );
+}
+
+void XclImpChTypeGroup::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 16 );
+ maData.mnFlags = rStrm.ReaduInt16();
+ maData.mnGroupIdx = rStrm.ReaduInt16();
+}
+
+void XclImpChTypeGroup::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHCHART3D:
+ mxChart3d = std::make_shared<XclImpChChart3d>();
+ mxChart3d->ReadChChart3d( rStrm );
+ break;
+ case EXC_ID_CHLEGEND:
+ mxLegend = std::make_shared<XclImpChLegend>( GetChRoot() );
+ mxLegend->ReadRecordGroup( rStrm );
+ break;
+ case EXC_ID_CHDEFAULTTEXT:
+ GetChartData().ReadChDefaultText( rStrm );
+ break;
+ case EXC_ID_CHDROPBAR:
+ ReadChDropBar( rStrm );
+ break;
+ case EXC_ID_CHCHARTLINE:
+ ReadChChartLine( rStrm );
+ break;
+ case EXC_ID_CHDATAFORMAT:
+ ReadChDataFormat( rStrm );
+ break;
+ default:
+ maType.ReadChType( rStrm );
+ }
+}
+
+void XclImpChTypeGroup::Finalize()
+{
+ // check and set valid chart type
+ bool bStockChart =
+ (maType.GetRecId() == EXC_ID_CHLINE) && // must be a line chart
+ !mxChart3d && // must be a 2d chart
+ m_ChartLines.find(EXC_CHCHARTLINE_HILO) != m_ChartLines.end() && // must contain hi-lo lines
+ (maSeries.size() == static_cast<XclImpChSeriesVec::size_type>(HasDropBars() ? 4 : 3)); // correct series count
+ maType.Finalize( bStockChart );
+
+ // extended type info
+ maTypeInfo.Set( maType.GetTypeInfo(), static_cast< bool >(mxChart3d), false );
+
+ // reverse series order for some unstacked 2D chart types
+ if( maTypeInfo.mbReverseSeries && !Is3dChart() && !maType.IsStacked() && !maType.IsPercent() )
+ ::std::reverse( maSeries.begin(), maSeries.end() );
+
+ // update chart type group format, may depend on chart type finalized above
+ if( mxGroupFmt )
+ mxGroupFmt->UpdateGroupFormat( maTypeInfo );
+}
+
+void XclImpChTypeGroup::AddSeries( XclImpChSeriesRef const & xSeries )
+{
+ if( xSeries )
+ maSeries.push_back( xSeries );
+ // store first inserted series separately, series order may be reversed later
+ if( !mxFirstSeries )
+ mxFirstSeries = xSeries;
+}
+
+void XclImpChTypeGroup::SetUsedFormatIndex( sal_uInt16 nFormatIdx )
+{
+ maUnusedFormats.erase( nFormatIdx );
+}
+
+sal_uInt16 XclImpChTypeGroup::PopUnusedFormatIndex()
+{
+ OSL_ENSURE( !maUnusedFormats.empty(), "XclImpChTypeGroup::PopUnusedFormatIndex - no more format indexes available" );
+ sal_uInt16 nFormatIdx = maUnusedFormats.empty() ? 0 : *maUnusedFormats.begin();
+ SetUsedFormatIndex( nFormatIdx );
+ return nFormatIdx;
+}
+
+bool XclImpChTypeGroup::HasVarPointFormat() const
+{
+ return ::get_flag( maData.mnFlags, EXC_CHTYPEGROUP_VARIEDCOLORS ) &&
+ ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_MULTI) || // multiple series allowed
+ ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_SINGLE) && // or exactly 1 series?
+ (maSeries.size() == 1)));
+}
+
+bool XclImpChTypeGroup::HasConnectorLines() const
+{
+ // existence of connector lines (only in stacked bar charts)
+ if ( !(maType.IsStacked() || maType.IsPercent()) || (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_BAR) )
+ return false;
+ XclImpChLineFormatMap::const_iterator aConLine = m_ChartLines.find(EXC_CHCHARTLINE_CONNECT);
+ return (aConLine != m_ChartLines.end() && aConLine->second.HasLine());
+}
+
+OUString XclImpChTypeGroup::GetSingleSeriesTitle() const
+{
+ // no automatic title for series with trendlines or error bars
+ // pie charts always show an automatic title, even if more series exist
+ return (mxFirstSeries && !mxFirstSeries->HasChildSeries() && (maTypeInfo.mbSingleSeriesVis || (maSeries.size() == 1))) ?
+ mxFirstSeries->GetTitle() : OUString();
+}
+
+void XclImpChTypeGroup::ConvertChart3d( ScfPropertySet& rPropSet ) const
+{
+ if( mxChart3d )
+ mxChart3d->Convert( rPropSet, Is3dWallChart() );
+}
+
+Reference< XCoordinateSystem > XclImpChTypeGroup::CreateCoordSystem() const
+{
+ return maType.CreateCoordSystem( Is3dChart() );
+}
+
+Reference< XChartType > XclImpChTypeGroup::CreateChartType( Reference< XDiagram > const & xDiagram, sal_Int32 nApiAxesSetIdx ) const
+{
+ OSL_ENSURE( IsValidGroup(), "XclImpChTypeGroup::CreateChartType - type group without series" );
+
+ // create the chart type object
+ Reference< XChartType > xChartType = maType.CreateChartType( xDiagram, Is3dChart() );
+
+ // bar chart connector lines
+ if( HasConnectorLines() )
+ {
+ ScfPropertySet aDiaProp( xDiagram );
+ aDiaProp.SetBoolProperty( EXC_CHPROP_CONNECTBARS, true );
+ }
+
+ /* Stock chart needs special processing. Create one 'big' series with
+ data sequences of different roles. */
+ if( maTypeInfo.meTypeId == EXC_CHTYPEID_STOCK )
+ CreateStockSeries( xChartType, nApiAxesSetIdx );
+ else
+ CreateDataSeries( xChartType, nApiAxesSetIdx );
+
+ return xChartType;
+}
+
+Reference< XLabeledDataSequence > XclImpChTypeGroup::CreateCategSequence() const
+{
+ Reference< XLabeledDataSequence > xLabeledSeq;
+ // create category sequence from first visible series
+ if( mxFirstSeries )
+ xLabeledSeq = mxFirstSeries->CreateCategSequence( EXC_CHPROP_ROLE_CATEG );
+ return xLabeledSeq;
+}
+
+void XclImpChTypeGroup::ReadChDropBar( XclImpStream& rStrm )
+{
+ if (m_DropBars.find(EXC_CHDROPBAR_UP) == m_DropBars.end())
+ {
+ unique_ptr<XclImpChDropBar> p(new XclImpChDropBar(EXC_CHDROPBAR_UP));
+ p->ReadRecordGroup(rStrm);
+ m_DropBars.insert(std::make_pair(EXC_CHDROPBAR_UP, std::move(p)));
+ }
+ else if (m_DropBars.find(EXC_CHDROPBAR_DOWN) == m_DropBars.end())
+ {
+ unique_ptr<XclImpChDropBar> p(new XclImpChDropBar(EXC_CHDROPBAR_DOWN));
+ p->ReadRecordGroup(rStrm);
+ m_DropBars.insert(std::make_pair(EXC_CHDROPBAR_DOWN, std::move(p)));
+ }
+}
+
+void XclImpChTypeGroup::ReadChChartLine( XclImpStream& rStrm )
+{
+ sal_uInt16 nLineId = rStrm.ReaduInt16();
+ if( (rStrm.GetNextRecId() == EXC_ID_CHLINEFORMAT) && rStrm.StartNextRecord() )
+ {
+ XclImpChLineFormat aLineFmt;
+ aLineFmt.ReadChLineFormat( rStrm );
+ m_ChartLines[ nLineId ] = aLineFmt;
+ }
+}
+
+void XclImpChTypeGroup::ReadChDataFormat( XclImpStream& rStrm )
+{
+ // global series and data point format
+ XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
+ xDataFmt->ReadRecordGroup( rStrm );
+ const XclChDataPointPos& rPos = xDataFmt->GetPointPos();
+ if( (rPos.mnSeriesIdx == 0) && (rPos.mnPointIdx == 0) &&
+ (xDataFmt->GetFormatIdx() == EXC_CHDATAFORMAT_DEFAULT) )
+ mxGroupFmt = xDataFmt;
+}
+
+void XclImpChTypeGroup::InsertDataSeries( Reference< XChartType > const & xChartType,
+ Reference< XDataSeries > const & xSeries, sal_Int32 nApiAxesSetIdx ) const
+{
+ Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY );
+ if( !(xSeriesCont.is() && xSeries.is()) )
+ return;
+
+ // series stacking mode
+ cssc2::StackingDirection eStacking = cssc2::StackingDirection_NO_STACKING;
+ // stacked overrides deep-3d
+ if( maType.IsStacked() || maType.IsPercent() )
+ eStacking = cssc2::StackingDirection_Y_STACKING;
+ else if( Is3dDeepChart() )
+ eStacking = cssc2::StackingDirection_Z_STACKING;
+
+ // additional series properties
+ ScfPropertySet aSeriesProp( xSeries );
+ aSeriesProp.SetProperty( EXC_CHPROP_STACKINGDIR, eStacking );
+ aSeriesProp.SetProperty( EXC_CHPROP_ATTAXISINDEX, nApiAxesSetIdx );
+
+ // insert series into container
+ try
+ {
+ xSeriesCont->addDataSeries( xSeries );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChTypeGroup::InsertDataSeries - cannot add data series" );
+ }
+}
+
+void XclImpChTypeGroup::CreateDataSeries( Reference< XChartType > const & xChartType, sal_Int32 nApiAxesSetIdx ) const
+{
+ bool bSpline = false;
+ for (auto const& elem : maSeries)
+ {
+ Reference< XDataSeries > xDataSeries = elem->CreateDataSeries();
+ InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx );
+ bSpline |= elem->HasSpline();
+ }
+ // spline - TODO: set at single series (#i66858#)
+ if( bSpline && !maTypeInfo.IsSeriesFrameFormat() && (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_RADAR) )
+ {
+ ScfPropertySet aTypeProp( xChartType );
+ aTypeProp.SetProperty( EXC_CHPROP_CURVESTYLE, css::chart2::CurveStyle_CUBIC_SPLINES );
+ }
+}
+
+void XclImpChTypeGroup::CreateStockSeries( Reference< XChartType > const & xChartType, sal_Int32 nApiAxesSetIdx ) const
+{
+ // create the data series object
+ Reference< XDataSeries > xDataSeries( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY );
+ Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
+ if( !xDataSink.is() )
+ return;
+
+ // create a list of data sequences from all series
+ ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
+ OSL_ENSURE( maSeries.size() >= 3, "XclImpChTypeGroup::CreateChartType - missing stock series" );
+ int nRoleIdx = (maSeries.size() == 3) ? 1 : 0;
+ for( const auto& rxSeries : maSeries )
+ {
+ // create a data sequence with a specific role
+ OUString aRole;
+ switch( nRoleIdx )
+ {
+ case 0: aRole = EXC_CHPROP_ROLE_OPENVALUES; break;
+ case 1: aRole = EXC_CHPROP_ROLE_HIGHVALUES; break;
+ case 2: aRole = EXC_CHPROP_ROLE_LOWVALUES; break;
+ case 3: aRole = EXC_CHPROP_ROLE_CLOSEVALUES; break;
+ }
+ Reference< XLabeledDataSequence > xDataSeq = rxSeries->CreateValueSequence( aRole );
+ if( xDataSeq.is() )
+ aLabeledSeqVec.push_back( xDataSeq );
+ ++nRoleIdx;
+ if (nRoleIdx >= 4)
+ break;
+ }
+
+ // attach labeled data sequences to series and insert series into chart type
+ xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
+
+ // formatting of special stock chart elements
+ ScfPropertySet aTypeProp( xChartType );
+ aTypeProp.SetBoolProperty( EXC_CHPROP_JAPANESE, HasDropBars() );
+ aTypeProp.SetBoolProperty( EXC_CHPROP_SHOWFIRST, HasDropBars() );
+ aTypeProp.SetBoolProperty( EXC_CHPROP_SHOWHIGHLOW, true );
+ // hi-lo line format
+ XclImpChLineFormatMap::const_iterator aHiLoLine = m_ChartLines.find( EXC_CHCHARTLINE_HILO );
+ if (aHiLoLine != m_ChartLines.end())
+ {
+ ScfPropertySet aSeriesProp( xDataSeries );
+ aHiLoLine->second.Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE );
+ }
+ // white dropbar format
+ XclImpChDropBarMap::const_iterator itr = m_DropBars.find(EXC_CHDROPBAR_UP);
+ Reference<XPropertySet> xWhitePropSet;
+ if (itr != m_DropBars.end() && aTypeProp.GetProperty(xWhitePropSet, EXC_CHPROP_WHITEDAY))
+ {
+ ScfPropertySet aBarProp( xWhitePropSet );
+ itr->second->Convert(GetChRoot(), aBarProp);
+ }
+ // black dropbar format
+ itr = m_DropBars.find(EXC_CHDROPBAR_DOWN);
+ Reference<XPropertySet> xBlackPropSet;
+ if (itr != m_DropBars.end() && aTypeProp.GetProperty(xBlackPropSet, EXC_CHPROP_BLACKDAY))
+ {
+ ScfPropertySet aBarProp( xBlackPropSet );
+ itr->second->Convert(GetChRoot(), aBarProp);
+ }
+
+ // insert the series into the chart type object
+ InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx );
+}
+
+// Axes =======================================================================
+
+XclImpChLabelRange::XclImpChLabelRange( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChLabelRange::ReadChLabelRange( XclImpStream& rStrm )
+{
+ maLabelData.mnCross = rStrm.ReaduInt16();
+ maLabelData.mnLabelFreq = rStrm.ReaduInt16();
+ maLabelData.mnTickFreq = rStrm.ReaduInt16();
+ maLabelData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChLabelRange::ReadChDateRange( XclImpStream& rStrm )
+{
+ maDateData.mnMinDate = rStrm.ReaduInt16();
+ maDateData.mnMaxDate = rStrm.ReaduInt16();
+ maDateData.mnMajorStep = rStrm.ReaduInt16();
+ maDateData.mnMajorUnit = rStrm.ReaduInt16();
+ maDateData.mnMinorStep = rStrm.ReaduInt16();
+ maDateData.mnMinorUnit = rStrm.ReaduInt16();
+ maDateData.mnBaseUnit = rStrm.ReaduInt16();
+ maDateData.mnCross = rStrm.ReaduInt16();
+ maDateData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChLabelRange::Convert( ScfPropertySet& rPropSet, ScaleData& rScaleData, bool bMirrorOrient ) const
+{
+ // automatic axis type detection
+ rScaleData.AutoDateAxis = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTODATE );
+
+ // the flag EXC_CHDATERANGE_DATEAXIS specifies whether this is a date axis
+ if( ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS ) )
+ {
+ /* Chart2 requires axis type CATEGORY for automatic category/date axis
+ (even if it is a date axis currently). */
+ rScaleData.AxisType = rScaleData.AutoDateAxis ? cssc2::AxisType::CATEGORY : cssc2::AxisType::DATE;
+ rScaleData.Scaling = css::chart2::LinearScaling::create( comphelper::getProcessComponentContext() );
+ /* Min/max values depend on base time unit, they specify the number of
+ days, months, or years starting from null date. */
+ lclConvertTimeValue( GetRoot(), rScaleData.Minimum, maDateData.mnMinDate, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMIN ), maDateData.mnBaseUnit );
+ lclConvertTimeValue( GetRoot(), rScaleData.Maximum, maDateData.mnMaxDate, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAX ), maDateData.mnBaseUnit );
+ // increment
+ cssc::TimeIncrement& rTimeIncrement = rScaleData.TimeIncrement;
+ lclConvertTimeInterval( rTimeIncrement.MajorTimeInterval, maDateData.mnMajorStep, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAJOR ), maDateData.mnMajorUnit );
+ lclConvertTimeInterval( rTimeIncrement.MinorTimeInterval, maDateData.mnMinorStep, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMINOR ), maDateData.mnMinorUnit );
+ // base unit
+ if( ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOBASE ) )
+ rTimeIncrement.TimeResolution.clear();
+ else
+ rTimeIncrement.TimeResolution <<= lclGetApiTimeUnit( maDateData.mnBaseUnit );
+ }
+ else
+ {
+ // do not overlap text unless all labels are visible
+ rPropSet.SetBoolProperty( EXC_CHPROP_TEXTOVERLAP, maLabelData.mnLabelFreq == 1 );
+ // do not break text into several lines unless all labels are visible
+ rPropSet.SetBoolProperty( EXC_CHPROP_TEXTBREAK, maLabelData.mnLabelFreq == 1 );
+ // do not stagger labels in two lines
+ rPropSet.SetProperty( EXC_CHPROP_ARRANGEORDER, cssc::ChartAxisArrangeOrderType_SIDE_BY_SIDE );
+ }
+
+ // reverse order
+ bool bReverse = ::get_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_REVERSE ) != bMirrorOrient;
+ rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL;
+
+ //TODO #i58731# show n-th category
+}
+
+void XclImpChLabelRange::ConvertAxisPosition( ScfPropertySet& rPropSet, bool b3dChart ) const
+{
+ /* Crossing mode (max-cross flag overrides other crossing settings). Excel
+ does not move the Y axis in 3D charts, regardless of actual settings.
+ But: the Y axis has to be moved to "end", if the X axis is mirrored,
+ to keep it at the left end of the chart. */
+ bool bMaxCross = ::get_flag( maLabelData.mnFlags, b3dChart ? EXC_CHLABELRANGE_REVERSE : EXC_CHLABELRANGE_MAXCROSS );
+ cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE;
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos );
+
+ // crossing position (depending on axis type text/date)
+ if( ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS ) )
+ {
+ bool bAutoCross = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
+ /* Crossing position value depends on base time unit, it specifies the
+ number of days, months, or years from null date. Note that Excel
+ 2007/2010 write broken BIFF8 files, they always stores the number
+ of days regardless of the base time unit (and they are reading it
+ the same way, thus wrongly displaying files written by Excel
+ 97-2003). This filter sticks to the correct behaviour of Excel
+ 97-2003. */
+ double fCrossingPos = bAutoCross ? 1.0 : lclGetSerialDay( GetRoot(), maDateData.mnCross, maDateData.mnBaseUnit );
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
+ }
+ else
+ {
+ double fCrossingPos = b3dChart ? 1.0 : maLabelData.mnCross;
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
+ }
+}
+
+XclImpChValueRange::XclImpChValueRange( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChValueRange::ReadChValueRange( XclImpStream& rStrm )
+{
+ maData.mfMin = rStrm.ReadDouble();
+ maData.mfMax = rStrm.ReadDouble();
+ maData.mfMajorStep = rStrm.ReadDouble();
+ maData.mfMinorStep = rStrm.ReadDouble();
+ maData.mfCross = rStrm.ReadDouble();
+ maData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChValueRange::Convert( ScaleData& rScaleData, bool bMirrorOrient ) const
+{
+ // scaling algorithm
+ const bool bLogScale = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE );
+ if( bLogScale )
+ rScaleData.Scaling = css::chart2::LogarithmicScaling::create( comphelper::getProcessComponentContext() );
+ else
+ rScaleData.Scaling = css::chart2::LinearScaling::create( comphelper::getProcessComponentContext() );
+
+ // min/max
+ lclSetExpValueOrClearAny( rScaleData.Minimum, maData.mfMin, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN ) );
+ lclSetExpValueOrClearAny( rScaleData.Maximum, maData.mfMax, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX ) );
+
+ // increment
+ bool bAutoMajor = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAJOR );
+ bool bAutoMinor = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMINOR );
+ // major increment
+ IncrementData& rIncrementData = rScaleData.IncrementData;
+ lclSetValueOrClearAny( rIncrementData.Distance, maData.mfMajorStep, bAutoMajor );
+ // minor increment
+ Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements;
+ rSubIncrementSeq.realloc( 1 );
+ Any& rIntervalCount = rSubIncrementSeq.getArray()[ 0 ].IntervalCount;
+ rIntervalCount.clear();
+ if( bLogScale )
+ {
+ if( !bAutoMinor )
+ rIntervalCount <<= sal_Int32( 9 );
+ }
+ else if( !bAutoMajor && !bAutoMinor && (0.0 < maData.mfMinorStep) && (maData.mfMinorStep <= maData.mfMajorStep) )
+ {
+ double fCount = maData.mfMajorStep / maData.mfMinorStep + 0.5;
+ if( (1.0 <= fCount) && (fCount < 1001.0) )
+ rIntervalCount <<= static_cast< sal_Int32 >( fCount );
+ }
+ else if( bAutoMinor )
+ {
+ // tdf#114168 If minor unit is not set then set interval to 5, as MS Excel do.
+ rIntervalCount <<= static_cast< sal_Int32 >( 5 );
+ }
+
+ // reverse order
+ bool bReverse = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE ) != bMirrorOrient;
+ rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL;
+}
+
+void XclImpChValueRange::ConvertAxisPosition( ScfPropertySet& rPropSet ) const
+{
+ bool bMaxCross = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_MAXCROSS );
+ bool bAutoCross = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS );
+ bool bLogScale = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE );
+
+ // crossing mode (max-cross flag overrides other crossing settings)
+ cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE;
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos );
+
+ // crossing position
+ double fCrossingPos = bAutoCross ? 0.0 : maData.mfCross;
+ if( bLogScale ) fCrossingPos = pow( 10.0, fCrossingPos );
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
+}
+
+namespace {
+
+sal_Int32 lclGetApiTickmarks( sal_uInt8 nXclTickPos )
+{
+ using namespace ::com::sun::star::chart2::TickmarkStyle;
+ sal_Int32 nApiTickmarks = css::chart2::TickmarkStyle::NONE;
+ ::set_flag( nApiTickmarks, INNER, ::get_flag( nXclTickPos, EXC_CHTICK_INSIDE ) );
+ ::set_flag( nApiTickmarks, OUTER, ::get_flag( nXclTickPos, EXC_CHTICK_OUTSIDE ) );
+ return nApiTickmarks;
+}
+
+cssc::ChartAxisLabelPosition lclGetApiLabelPosition( sal_Int8 nXclLabelPos )
+{
+ using namespace ::com::sun::star::chart;
+ switch( nXclLabelPos )
+ {
+ case EXC_CHTICK_LOW: return ChartAxisLabelPosition_OUTSIDE_START;
+ case EXC_CHTICK_HIGH: return ChartAxisLabelPosition_OUTSIDE_END;
+ case EXC_CHTICK_NEXT: return ChartAxisLabelPosition_NEAR_AXIS;
+ }
+ return ChartAxisLabelPosition_NEAR_AXIS;
+}
+
+} // namespace
+
+XclImpChTick::XclImpChTick( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChTick::ReadChTick( XclImpStream& rStrm )
+{
+ maData.mnMajor = rStrm.ReaduInt8();
+ maData.mnMinor = rStrm.ReaduInt8();
+ maData.mnLabelPos = rStrm.ReaduInt8();
+ maData.mnBackMode = rStrm.ReaduInt8();
+ rStrm.Ignore( 16 );
+ rStrm >> maData.maTextColor;
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ // BIFF8: index into palette used instead of RGB data
+ maData.maTextColor = GetPalette().GetColor( rStrm.ReaduInt16() );
+ // rotation
+ maData.mnRotation = rStrm.ReaduInt16();
+ }
+ else
+ {
+ // BIFF2-BIFF7: get rotation from text orientation
+ sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 2, 3 );
+ maData.mnRotation = XclTools::GetXclRotFromOrient( nOrient );
+ }
+}
+
+Color XclImpChTick::GetFontColor() const
+{
+ return ::get_flag( maData.mnFlags, EXC_CHTICK_AUTOCOLOR ) ? GetFontAutoColor() : maData.maTextColor;
+}
+
+sal_uInt16 XclImpChTick::GetRotation() const
+{
+ /* n#720443: Ignore auto-rotation if there is a suggested rotation.
+ * Better fix would be to improve our axis auto rotation algorithm.
+ */
+ if( maData.mnRotation != EXC_ROT_NONE )
+ return maData.mnRotation;
+ return ::get_flag( maData.mnFlags, EXC_CHTICK_AUTOROT ) ? EXC_CHART_AUTOROTATION : maData.mnRotation;
+}
+
+void XclImpChTick::Convert( ScfPropertySet& rPropSet ) const
+{
+ rPropSet.SetProperty( EXC_CHPROP_MAJORTICKS, lclGetApiTickmarks( maData.mnMajor ) );
+ rPropSet.SetProperty( EXC_CHPROP_MINORTICKS, lclGetApiTickmarks( maData.mnMinor ) );
+ rPropSet.SetProperty( EXC_CHPROP_LABELPOSITION, lclGetApiLabelPosition( maData.mnLabelPos ) );
+ rPropSet.SetProperty( EXC_CHPROP_MARKPOSITION, cssc::ChartAxisMarkPosition_AT_AXIS );
+}
+
+XclImpChAxis::XclImpChAxis( const XclImpChRoot& rRoot, sal_uInt16 nAxisType ) :
+ XclImpChRoot( rRoot ),
+ mnNumFmtIdx( EXC_FORMAT_NOTFOUND )
+{
+ maData.mnType = nAxisType;
+}
+
+void XclImpChAxis::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnType = rStrm.ReaduInt16();
+}
+
+void XclImpChAxis::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHLABELRANGE:
+ mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
+ mxLabelRange->ReadChLabelRange( rStrm );
+ break;
+ case EXC_ID_CHDATERANGE:
+ if( !mxLabelRange )
+ mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
+ mxLabelRange->ReadChDateRange( rStrm );
+ break;
+ case EXC_ID_CHVALUERANGE:
+ mxValueRange = std::make_shared<XclImpChValueRange>( GetChRoot() );
+ mxValueRange->ReadChValueRange( rStrm );
+ break;
+ case EXC_ID_CHFORMAT:
+ mnNumFmtIdx = rStrm.ReaduInt16();
+ break;
+ case EXC_ID_CHTICK:
+ mxTick = std::make_shared<XclImpChTick>( GetChRoot() );
+ mxTick->ReadChTick( rStrm );
+ break;
+ case EXC_ID_CHFONT:
+ mxFont = std::make_shared<XclImpChFont>();
+ mxFont->ReadChFont( rStrm );
+ break;
+ case EXC_ID_CHAXISLINE:
+ ReadChAxisLine( rStrm );
+ break;
+ }
+}
+
+void XclImpChAxis::Finalize()
+{
+ // add default scaling, needed e.g. to adjust rotation direction of pie and radar charts
+ if( !mxLabelRange )
+ mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
+ if( !mxValueRange )
+ mxValueRange = std::make_shared<XclImpChValueRange>( GetChRoot() );
+ // remove invisible grid lines completely
+ if( mxMajorGrid && !mxMajorGrid->HasLine() )
+ mxMajorGrid.clear();
+ if( mxMinorGrid && !mxMinorGrid->HasLine() )
+ mxMinorGrid.clear();
+ // default tick settings different in OOChart and Excel
+ if( !mxTick )
+ mxTick = std::make_shared<XclImpChTick>( GetChRoot() );
+ // #i4140# different default axis line color
+ if( !mxAxisLine )
+ {
+ XclChLineFormat aLineFmt;
+ // set "show axis" flag, default if line format record is missing
+ ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_SHOWAXIS );
+ mxAxisLine = new XclImpChLineFormat( aLineFmt );
+ }
+ // add wall/floor frame for 3d charts
+ if( !mxWallFrame )
+ CreateWallFrame();
+}
+
+sal_uInt16 XclImpChAxis::GetFontIndex() const
+{
+ return mxFont ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND;
+}
+
+Color XclImpChAxis::GetFontColor() const
+{
+ return mxTick ? mxTick->GetFontColor() : GetFontAutoColor();
+}
+
+sal_uInt16 XclImpChAxis::GetRotation() const
+{
+ return mxTick ? mxTick->GetRotation() : EXC_CHART_AUTOROTATION;
+}
+
+Reference< XAxis > XclImpChAxis::CreateAxis( const XclImpChTypeGroup& rTypeGroup, const XclImpChAxis* pCrossingAxis ) const
+{
+ // create the axis object (always)
+ Reference< XAxis > xAxis( ScfApiHelper::CreateInstance( SERVICE_CHART2_AXIS ), UNO_QUERY );
+ if( xAxis.is() )
+ {
+ ScfPropertySet aAxisProp( xAxis );
+ // #i58688# axis enabled
+ aAxisProp.SetBoolProperty( EXC_CHPROP_SHOW, !mxAxisLine || mxAxisLine->IsShowAxis() );
+
+ // axis line properties
+ if( mxAxisLine )
+ mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE );
+ // axis ticks properties
+ if( mxTick )
+ mxTick->Convert( aAxisProp );
+
+ // axis caption text --------------------------------------------------
+
+ // radar charts disable their category labels via chart type, not via axis
+ bool bHasLabels = (!mxTick || mxTick->HasLabels()) &&
+ ((GetAxisType() != EXC_CHAXIS_X) || rTypeGroup.HasCategoryLabels());
+ aAxisProp.SetBoolProperty( EXC_CHPROP_DISPLAYLABELS, bHasLabels );
+ if( bHasLabels )
+ {
+ // font settings from CHFONT record or from default text
+ if( mxFont )
+ ConvertFontBase( GetChRoot(), aAxisProp );
+ else if( const XclImpChText* pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_AXISLABEL ) )
+ pDefText->ConvertFont( aAxisProp );
+ // label text rotation
+ ConvertRotationBase( aAxisProp, true );
+ // number format
+ bool bLinkNumberFmtToSource = true;
+ if ( mnNumFmtIdx != EXC_FORMAT_NOTFOUND )
+ {
+ sal_uInt32 nScNumFmt = GetNumFmtBuffer().GetScFormat( mnNumFmtIdx );
+ if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND )
+ {
+ aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT, static_cast< sal_Int32 >( nScNumFmt ) );
+ bLinkNumberFmtToSource = false;
+ }
+ }
+
+ aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT_LINKSRC, bLinkNumberFmtToSource );
+ }
+
+ // axis scaling and increment -----------------------------------------
+
+ const XclChExtTypeInfo& rTypeInfo = rTypeGroup.GetTypeInfo();
+ ScaleData aScaleData = xAxis->getScaleData();
+ // set axis type
+ switch( GetAxisType() )
+ {
+ case EXC_CHAXIS_X:
+ if( rTypeInfo.mbCategoryAxis )
+ {
+ aScaleData.AxisType = cssc2::AxisType::CATEGORY;
+ aScaleData.Categories = rTypeGroup.CreateCategSequence();
+ }
+ else
+ aScaleData.AxisType = cssc2::AxisType::REALNUMBER;
+ break;
+ case EXC_CHAXIS_Y:
+ aScaleData.AxisType = rTypeGroup.IsPercent() ?
+ cssc2::AxisType::PERCENT : cssc2::AxisType::REALNUMBER;
+ break;
+ case EXC_CHAXIS_Z:
+ aScaleData.AxisType = cssc2::AxisType::SERIES;
+ break;
+ }
+ // axis scaling settings, dependent on axis type
+ switch( aScaleData.AxisType )
+ {
+ case cssc2::AxisType::CATEGORY:
+ case cssc2::AxisType::SERIES:
+ // #i71684# radar charts have reversed rotation direction
+ if (mxLabelRange)
+ mxLabelRange->Convert( aAxisProp, aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR );
+ else
+ SAL_WARN("sc.filter", "missing LabelRange");
+ break;
+ case cssc2::AxisType::REALNUMBER:
+ case cssc2::AxisType::PERCENT:
+ // #i85167# pie/donut charts have reversed rotation direction (at Y axis!)
+ if (mxValueRange)
+ mxValueRange->Convert( aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE );
+ else
+ SAL_WARN("sc.filter", "missing ValueRange");
+ break;
+ default:
+ OSL_FAIL( "XclImpChAxis::CreateAxis - unknown axis type" );
+ }
+
+ /* Do not set a value to the Origin member anymore (will be done via
+ new axis properties 'CrossoverPosition' and 'CrossoverValue'). */
+ aScaleData.Origin.clear();
+
+ // write back
+ xAxis->setScaleData( aScaleData );
+
+ // grid ---------------------------------------------------------------
+
+ // main grid
+ ScfPropertySet aGridProp( xAxis->getGridProperties() );
+ aGridProp.SetBoolProperty( EXC_CHPROP_SHOW, static_cast<bool>(mxMajorGrid) );
+ if( mxMajorGrid )
+ mxMajorGrid->Convert( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE );
+ // sub grid
+ Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties();
+ if( aSubGridPropSeq.hasElements() )
+ {
+ ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] );
+ aSubGridProp.SetBoolProperty( EXC_CHPROP_SHOW, static_cast<bool>(mxMinorGrid) );
+ if( mxMinorGrid )
+ mxMinorGrid->Convert( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE );
+ }
+
+ // position of crossing axis ------------------------------------------
+
+ if( pCrossingAxis )
+ pCrossingAxis->ConvertAxisPosition( aAxisProp, rTypeGroup );
+ }
+ return xAxis;
+}
+
+void XclImpChAxis::ConvertWall( ScfPropertySet& rPropSet ) const
+{
+ // #i71810# walls and floor in 3D charts use the CHPICFORMAT record for bitmap mode
+ if( mxWallFrame )
+ mxWallFrame->Convert( rPropSet, true );
+}
+
+void XclImpChAxis::ConvertAxisPosition( ScfPropertySet& rPropSet, const XclImpChTypeGroup& rTypeGroup ) const
+{
+ if( ((GetAxisType() == EXC_CHAXIS_X) && rTypeGroup.GetTypeInfo().mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z) )
+ {
+ if (mxLabelRange)
+ mxLabelRange->ConvertAxisPosition( rPropSet, rTypeGroup.Is3dChart() );
+ else
+ SAL_WARN("sc.filter", "missing LabelRange");
+ }
+ else
+ {
+ if (mxValueRange)
+ mxValueRange->ConvertAxisPosition( rPropSet );
+ else
+ SAL_WARN("sc.filter", "missing ValueRange");
+ }
+}
+
+void XclImpChAxis::ReadChAxisLine( XclImpStream& rStrm )
+{
+ XclImpChLineFormatRef* pxLineFmt = nullptr;
+ bool bWallFrame = false;
+ switch( rStrm.ReaduInt16() )
+ {
+ case EXC_CHAXISLINE_AXISLINE: pxLineFmt = &mxAxisLine; break;
+ case EXC_CHAXISLINE_MAJORGRID: pxLineFmt = &mxMajorGrid; break;
+ case EXC_CHAXISLINE_MINORGRID: pxLineFmt = &mxMinorGrid; break;
+ case EXC_CHAXISLINE_WALLS: bWallFrame = true; break;
+ }
+ if( bWallFrame )
+ CreateWallFrame();
+
+ bool bLoop = pxLineFmt || bWallFrame;
+ while( bLoop )
+ {
+ sal_uInt16 nRecId = rStrm.GetNextRecId();
+ bLoop = ((nRecId == EXC_ID_CHLINEFORMAT) ||
+ (nRecId == EXC_ID_CHAREAFORMAT) ||
+ (nRecId == EXC_ID_CHESCHERFORMAT))
+ && rStrm.StartNextRecord();
+ if( bLoop )
+ {
+ if( pxLineFmt && (nRecId == EXC_ID_CHLINEFORMAT) )
+ {
+ (*pxLineFmt) = new XclImpChLineFormat();
+ (*pxLineFmt)->ReadChLineFormat( rStrm );
+ }
+ else if( bWallFrame && mxWallFrame )
+ {
+ mxWallFrame->ReadSubRecord( rStrm );
+ }
+ }
+ }
+}
+
+void XclImpChAxis::CreateWallFrame()
+{
+ switch( GetAxisType() )
+ {
+ case EXC_CHAXIS_X:
+ mxWallFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_WALL3D );
+ break;
+ case EXC_CHAXIS_Y:
+ mxWallFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_FLOOR3D );
+ break;
+ default:
+ mxWallFrame.reset();
+ }
+}
+
+XclImpChAxesSet::XclImpChAxesSet( const XclImpChRoot& rRoot, sal_uInt16 nAxesSetId ) :
+ XclImpChRoot( rRoot )
+{
+ maData.mnAxesSetId = nAxesSetId;
+}
+
+void XclImpChAxesSet::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnAxesSetId = rStrm.ReaduInt16();
+ rStrm >> maData.maRect;
+}
+
+void XclImpChAxesSet::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHFRAMEPOS:
+ mxFramePos = std::make_shared<XclImpChFramePos>();
+ mxFramePos->ReadChFramePos( rStrm );
+ break;
+ case EXC_ID_CHAXIS:
+ ReadChAxis( rStrm );
+ break;
+ case EXC_ID_CHTEXT:
+ ReadChText( rStrm );
+ break;
+ case EXC_ID_CHPLOTFRAME:
+ ReadChPlotFrame( rStrm );
+ break;
+ case EXC_ID_CHTYPEGROUP:
+ ReadChTypeGroup( rStrm );
+ break;
+ }
+}
+
+void XclImpChAxesSet::Finalize()
+{
+ if( IsValidAxesSet() )
+ {
+ // finalize chart type groups, erase empty groups without series
+ XclImpChTypeGroupMap aValidGroups;
+ for (auto const& typeGroup : maTypeGroups)
+ {
+ XclImpChTypeGroupRef xTypeGroup = typeGroup.second;
+ xTypeGroup->Finalize();
+ if( xTypeGroup->IsValidGroup() )
+ aValidGroups.emplace(typeGroup.first, xTypeGroup);
+ }
+ maTypeGroups.swap( aValidGroups );
+ }
+
+ // invalid chart type groups are deleted now, check again with IsValidAxesSet()
+ if( !IsValidAxesSet() )
+ return;
+
+ // always create missing axis objects
+ if( !mxXAxis )
+ mxXAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_X );
+ if( !mxYAxis )
+ mxYAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_Y );
+ if( !mxZAxis && GetFirstTypeGroup()->Is3dDeepChart() )
+ mxZAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_Z );
+
+ // finalize axes
+ if( mxXAxis ) mxXAxis->Finalize();
+ if( mxYAxis ) mxYAxis->Finalize();
+ if( mxZAxis ) mxZAxis->Finalize();
+
+ // finalize axis titles
+ const XclImpChText* pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_AXISTITLE );
+ OUString aAutoTitle(ScResId(STR_AXISTITLE));
+ lclFinalizeTitle( mxXAxisTitle, pDefText, aAutoTitle );
+ lclFinalizeTitle( mxYAxisTitle, pDefText, aAutoTitle );
+ lclFinalizeTitle( mxZAxisTitle, pDefText, aAutoTitle );
+
+ // #i47745# missing plot frame -> invisible border and area
+ if( !mxPlotFrame )
+ mxPlotFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME );
+}
+
+XclImpChTypeGroupRef XclImpChAxesSet::GetTypeGroup( sal_uInt16 nGroupIdx ) const
+{
+ XclImpChTypeGroupMap::const_iterator itr = maTypeGroups.find(nGroupIdx);
+ return itr == maTypeGroups.end() ? XclImpChTypeGroupRef() : itr->second;
+}
+
+XclImpChTypeGroupRef XclImpChAxesSet::GetFirstTypeGroup() const
+{
+ XclImpChTypeGroupRef xTypeGroup;
+ if( !maTypeGroups.empty() )
+ xTypeGroup = maTypeGroups.begin()->second;
+ return xTypeGroup;
+}
+
+XclImpChLegendRef XclImpChAxesSet::GetLegend() const
+{
+ XclImpChLegendRef xLegend;
+ for( const auto& rEntry : maTypeGroups )
+ {
+ xLegend = rEntry.second->GetLegend();
+ if (xLegend)
+ break;
+ }
+ return xLegend;
+}
+
+OUString XclImpChAxesSet::GetSingleSeriesTitle() const
+{
+ return (maTypeGroups.size() == 1) ? maTypeGroups.begin()->second->GetSingleSeriesTitle() : OUString();
+}
+
+void XclImpChAxesSet::Convert( Reference< XDiagram > const & xDiagram ) const
+{
+ if( !(IsValidAxesSet() && xDiagram.is()) )
+ return;
+
+ // diagram background formatting
+ if( GetAxesSetId() == EXC_CHAXESSET_PRIMARY )
+ ConvertBackground( xDiagram );
+
+ // create the coordinate system, this inserts all chart types and series
+ Reference< XCoordinateSystem > xCoordSystem = CreateCoordSystem( xDiagram );
+ if( !xCoordSystem.is() )
+ return;
+
+ // insert coordinate system, if not already done
+ try
+ {
+ Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY_THROW );
+ Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
+ if( !aCoordSystems.hasElements() )
+ xCoordSystemCont->addCoordinateSystem( xCoordSystem );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChAxesSet::Convert - cannot insert coordinate system" );
+ }
+
+ // create the axes with grids and axis titles and insert them into the diagram
+ ConvertAxis( mxXAxis, mxXAxisTitle, xCoordSystem, mxYAxis.get() );
+ ConvertAxis( mxYAxis, mxYAxisTitle, xCoordSystem, mxXAxis.get() );
+ ConvertAxis( mxZAxis, mxZAxisTitle, xCoordSystem, nullptr );
+}
+
+void XclImpChAxesSet::ConvertTitlePositions() const
+{
+ if( mxXAxisTitle )
+ mxXAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_X ) );
+ if( mxYAxisTitle )
+ mxYAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_Y ) );
+ if( mxZAxisTitle )
+ mxZAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_Z ) );
+}
+
+void XclImpChAxesSet::ReadChAxis( XclImpStream& rStrm )
+{
+ XclImpChAxisRef xAxis = std::make_shared<XclImpChAxis>( GetChRoot() );
+ xAxis->ReadRecordGroup( rStrm );
+
+ switch( xAxis->GetAxisType() )
+ {
+ case EXC_CHAXIS_X: mxXAxis = xAxis; break;
+ case EXC_CHAXIS_Y: mxYAxis = xAxis; break;
+ case EXC_CHAXIS_Z: mxZAxis = xAxis; break;
+ }
+}
+
+void XclImpChAxesSet::ReadChText( XclImpStream& rStrm )
+{
+ XclImpChTextRef xText = std::make_shared<XclImpChText>( GetChRoot() );
+ xText->ReadRecordGroup( rStrm );
+
+ switch( xText->GetLinkTarget() )
+ {
+ case EXC_CHOBJLINK_XAXIS: mxXAxisTitle = xText; break;
+ case EXC_CHOBJLINK_YAXIS: mxYAxisTitle = xText; break;
+ case EXC_CHOBJLINK_ZAXIS: mxZAxisTitle = xText; break;
+ }
+}
+
+void XclImpChAxesSet::ReadChPlotFrame( XclImpStream& rStrm )
+{
+ if( (rStrm.GetNextRecId() == EXC_ID_CHFRAME) && rStrm.StartNextRecord() )
+ {
+ mxPlotFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME );
+ mxPlotFrame->ReadRecordGroup( rStrm );
+ }
+}
+
+void XclImpChAxesSet::ReadChTypeGroup( XclImpStream& rStrm )
+{
+ XclImpChTypeGroupRef xTypeGroup = std::make_shared<XclImpChTypeGroup>( GetChRoot() );
+ xTypeGroup->ReadRecordGroup( rStrm );
+ sal_uInt16 nGroupIdx = xTypeGroup->GetGroupIdx();
+ XclImpChTypeGroupMap::iterator itr = maTypeGroups.lower_bound(nGroupIdx);
+ if (itr != maTypeGroups.end() && !maTypeGroups.key_comp()(nGroupIdx, itr->first))
+ // Overwrite the existing element.
+ itr->second = xTypeGroup;
+ else
+ maTypeGroups.insert(
+ itr, XclImpChTypeGroupMap::value_type(nGroupIdx, xTypeGroup));
+}
+
+Reference< XCoordinateSystem > XclImpChAxesSet::CreateCoordSystem( Reference< XDiagram > const & xDiagram ) const
+{
+ Reference< XCoordinateSystem > xCoordSystem;
+
+ /* Try to get existing coordinate system. For now, all series from primary
+ and secondary axes sets are inserted into one coordinate system. Later,
+ this should be changed to use one coordinate system for each axes set. */
+ Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY );
+ if( xCoordSystemCont.is() )
+ {
+ Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
+ OSL_ENSURE( aCoordSystems.getLength() <= 1, "XclImpChAxesSet::CreateCoordSystem - too many existing coordinate systems" );
+ if( aCoordSystems.hasElements() )
+ xCoordSystem = aCoordSystems[ 0 ];
+ }
+
+ // create the coordinate system according to the first chart type
+ if( !xCoordSystem.is() )
+ {
+ XclImpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
+ if( xTypeGroup )
+ {
+ xCoordSystem = xTypeGroup->CreateCoordSystem();
+ // convert 3d chart settings
+ ScfPropertySet aDiaProp( xDiagram );
+ xTypeGroup->ConvertChart3d( aDiaProp );
+ }
+ }
+
+ /* Create XChartType objects for all chart type groups. Each group will
+ add its series to the data provider attached to the chart document. */
+ Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY );
+ if( xChartTypeCont.is() )
+ {
+ sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
+ for( const auto& rEntry : maTypeGroups )
+ {
+ try
+ {
+ Reference< XChartType > xChartType = rEntry.second->CreateChartType( xDiagram, nApiAxesSetIdx );
+ if( xChartType.is() )
+ xChartTypeCont->addChartType( xChartType );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChAxesSet::CreateCoordSystem - cannot add chart type" );
+ }
+ }
+ }
+
+ return xCoordSystem;
+}
+
+void XclImpChAxesSet::ConvertAxis(
+ XclImpChAxisRef const & xChAxis, XclImpChTextRef const & xChAxisTitle,
+ Reference< XCoordinateSystem > const & xCoordSystem, const XclImpChAxis* pCrossingAxis ) const
+{
+ if( !xChAxis )
+ return;
+
+ // create and attach the axis object
+ Reference< XAxis > xAxis = CreateAxis( *xChAxis, pCrossingAxis );
+ if( !xAxis.is() )
+ return;
+
+ // create and attach the axis title
+ if( xChAxisTitle ) try
+ {
+ Reference< XTitled > xTitled( xAxis, UNO_QUERY_THROW );
+ Reference< XTitle > xTitle( xChAxisTitle->CreateTitle(), UNO_SET_THROW );
+ xTitled->setTitleObject( xTitle );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChAxesSet::ConvertAxis - cannot set axis title" );
+ }
+
+ // insert axis into coordinate system
+ try
+ {
+ sal_Int32 nApiAxisDim = xChAxis->GetApiAxisDimension();
+ sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
+ xCoordSystem->setAxisByDimension( nApiAxisDim, xAxis, nApiAxesSetIdx );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChAxesSet::ConvertAxis - cannot set axis" );
+ }
+}
+
+Reference< XAxis > XclImpChAxesSet::CreateAxis( const XclImpChAxis& rChAxis, const XclImpChAxis* pCrossingAxis ) const
+{
+ Reference< XAxis > xAxis;
+ if( const XclImpChTypeGroup* pTypeGroup = GetFirstTypeGroup().get() )
+ xAxis = rChAxis.CreateAxis( *pTypeGroup, pCrossingAxis );
+ return xAxis;
+}
+
+void XclImpChAxesSet::ConvertBackground( Reference< XDiagram > const & xDiagram ) const
+{
+ XclImpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
+ if( xTypeGroup && xTypeGroup->Is3dWallChart() )
+ {
+ // wall/floor formatting (3D charts)
+ if( mxXAxis )
+ {
+ ScfPropertySet aWallProp( xDiagram->getWall() );
+ mxXAxis->ConvertWall( aWallProp );
+ }
+ if( mxYAxis )
+ {
+ ScfPropertySet aFloorProp( xDiagram->getFloor() );
+ mxYAxis->ConvertWall( aFloorProp );
+ }
+ }
+ else if( mxPlotFrame )
+ {
+ // diagram background formatting
+ ScfPropertySet aWallProp( xDiagram->getWall() );
+ mxPlotFrame->Convert( aWallProp );
+ }
+}
+
+// The chart object ===========================================================
+
+XclImpChChart::XclImpChChart( const XclImpRoot& rRoot ) :
+ XclImpChRoot( rRoot, *this )
+{
+ mxPrimAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_PRIMARY );
+ mxSecnAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_SECONDARY );
+}
+
+XclImpChChart::~XclImpChChart()
+{
+}
+
+void XclImpChChart::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ // coordinates are stored as 16.16 fixed point
+ rStrm >> maRect;
+}
+
+void XclImpChChart::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHFRAME:
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
+ mxFrame->ReadRecordGroup( rStrm );
+ break;
+ case EXC_ID_CHSERIES:
+ ReadChSeries( rStrm );
+ break;
+ case EXC_ID_CHPROPERTIES:
+ ReadChProperties( rStrm );
+ break;
+ case EXC_ID_CHDEFAULTTEXT:
+ ReadChDefaultText( rStrm );
+ break;
+ case EXC_ID_CHAXESSET:
+ ReadChAxesSet( rStrm );
+ break;
+ case EXC_ID_CHTEXT:
+ ReadChText( rStrm );
+ break;
+ case EXC_ID_CHEND:
+ Finalize(); // finalize the entire chart object
+ break;
+ }
+}
+
+void XclImpChChart::ReadChDefaultText( XclImpStream& rStrm )
+{
+ sal_uInt16 nTextId = rStrm.ReaduInt16();
+ if( (rStrm.GetNextRecId() == EXC_ID_CHTEXT) && rStrm.StartNextRecord() )
+ {
+ unique_ptr<XclImpChText> pText(new XclImpChText(GetChRoot()));
+ pText->ReadRecordGroup(rStrm);
+ m_DefTexts.insert(std::make_pair(nTextId, std::move(pText)));
+ }
+}
+
+void XclImpChChart::ReadChDataFormat( XclImpStream& rStrm )
+{
+ XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
+ xDataFmt->ReadRecordGroup( rStrm );
+ if( xDataFmt->GetPointPos().mnSeriesIdx <= EXC_CHSERIES_MAXSERIES )
+ {
+ const XclChDataPointPos& rPos = xDataFmt->GetPointPos();
+ XclImpChDataFormatMap::iterator itr = maDataFmts.lower_bound(rPos);
+ if (itr == maDataFmts.end() || maDataFmts.key_comp()(rPos, itr->first))
+ // No element exists for this data point. Insert it.
+ maDataFmts.insert(
+ itr, XclImpChDataFormatMap::value_type(rPos, xDataFmt));
+
+ /* Do not overwrite existing data format group, Excel always uses the
+ first data format group occurring in any CHSERIES group. */
+ }
+}
+
+void XclImpChChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
+{
+ if( !mxFrame )
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
+ mxFrame->UpdateObjFrame( rLineData, rFillData );
+}
+
+XclImpChTypeGroupRef XclImpChChart::GetTypeGroup( sal_uInt16 nGroupIdx ) const
+{
+ XclImpChTypeGroupRef xTypeGroup = mxPrimAxesSet->GetTypeGroup( nGroupIdx );
+ if( !xTypeGroup ) xTypeGroup = mxSecnAxesSet->GetTypeGroup( nGroupIdx );
+ if( !xTypeGroup ) xTypeGroup = mxPrimAxesSet->GetFirstTypeGroup();
+ return xTypeGroup;
+}
+
+const XclImpChText* XclImpChChart::GetDefaultText( XclChTextType eTextType ) const
+{
+ sal_uInt16 nDefTextId = EXC_CHDEFTEXT_GLOBAL;
+ bool bBiff8 = GetBiff() == EXC_BIFF8;
+ switch( eTextType )
+ {
+ case EXC_CHTEXTTYPE_TITLE: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break;
+ case EXC_CHTEXTTYPE_LEGEND: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break;
+ case EXC_CHTEXTTYPE_AXISTITLE: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
+ case EXC_CHTEXTTYPE_AXISLABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
+ case EXC_CHTEXTTYPE_DATALABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
+ }
+
+ XclImpChTextMap::const_iterator const itr = m_DefTexts.find(nDefTextId);
+ return itr == m_DefTexts.end() ? nullptr : itr->second.get();
+}
+
+bool XclImpChChart::IsManualPlotArea() const
+{
+ // there is no real automatic mode in BIFF5 charts
+ return (GetBiff() <= EXC_BIFF5) || ::get_flag( maProps.mnFlags, EXC_CHPROPS_USEMANPLOTAREA );
+}
+
+void XclImpChChart::Convert( const Reference<XChartDocument>& xChartDoc,
+ XclImpDffConverter& rDffConv, const OUString& rObjName, const tools::Rectangle& rChartRect ) const
+{
+ // initialize conversion (locks the model to suppress any internal updates)
+ InitConversion( xChartDoc, rChartRect );
+
+ // chart frame formatting
+ if( mxFrame )
+ {
+ ScfPropertySet aFrameProp( xChartDoc->getPageBackground() );
+ mxFrame->Convert( aFrameProp );
+ }
+
+ // chart title
+ if( mxTitle ) try
+ {
+ Reference< XTitled > xTitled( xChartDoc, UNO_QUERY_THROW );
+ Reference< XTitle > xTitle( mxTitle->CreateTitle(), UNO_SET_THROW );
+ xTitled->setTitleObject( xTitle );
+ }
+ catch( Exception& )
+ {
+ }
+
+ /* Create the diagram object and attach it to the chart document. Currently,
+ one diagram is used to carry all coordinate systems and data series. */
+ Reference< XDiagram > xDiagram = CreateDiagram();
+ xChartDoc->setFirstDiagram( xDiagram );
+
+ // coordinate systems and chart types, convert axis settings
+ mxPrimAxesSet->Convert( xDiagram );
+ mxSecnAxesSet->Convert( xDiagram );
+
+ // legend
+ if( xDiagram.is() && mxLegend )
+ xDiagram->setLegend( mxLegend->CreateLegend() );
+
+ /* Following all conversions needing the old Chart1 API that involves full
+ initialization of the chart view. */
+ Reference< cssc::XChartDocument > xChart1Doc( xChartDoc, UNO_QUERY );
+ if( xChart1Doc.is() )
+ {
+ Reference< cssc::XDiagram > xDiagram1 = xChart1Doc->getDiagram();
+
+ /* Set the 'IncludeHiddenCells' property via the old API as only this
+ ensures that the data provider and all created sequences get this
+ flag correctly. */
+ ScfPropertySet aDiaProp( xDiagram1 );
+ bool bShowVisCells = ::get_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY );
+ aDiaProp.SetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS, !bShowVisCells );
+
+ // plot area position and size (there is no real automatic mode in BIFF5 charts)
+ XclImpChFramePosRef xPlotAreaPos = mxPrimAxesSet->GetPlotAreaFramePos();
+ if( IsManualPlotArea() && xPlotAreaPos ) try
+ {
+ const XclChFramePos& rFramePos = xPlotAreaPos->GetFramePosData();
+ if( (rFramePos.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rFramePos.mnBRMode == EXC_CHFRAMEPOS_PARENT) )
+ {
+ Reference< cssc::XDiagramPositioning > xPositioning( xDiagram1, UNO_QUERY_THROW );
+ css::awt::Rectangle aDiagramRect = CalcHmmFromChartRect( rFramePos.maRect );
+ // for pie charts, always set inner plot area size to exclude the data labels as Excel does
+ const XclImpChTypeGroup* pFirstTypeGroup = mxPrimAxesSet->GetFirstTypeGroup().get();
+ if( pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE) )
+ xPositioning->setDiagramPositionExcludingAxes( aDiagramRect );
+ else if( pFirstTypeGroup && pFirstTypeGroup->Is3dChart() )
+ xPositioning->setDiagramPositionIncludingAxesAndAxisTitles( aDiagramRect );
+ else
+ xPositioning->setDiagramPositionIncludingAxes( aDiagramRect );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ // positions of all title objects
+ if( mxTitle )
+ mxTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_TITLE ) );
+ mxPrimAxesSet->ConvertTitlePositions();
+ mxSecnAxesSet->ConvertTitlePositions();
+ }
+
+ // unlock the model
+ FinishConversion( rDffConv );
+
+ // start listening to this chart
+ ScDocument& rDoc = GetRoot().GetDoc();
+ ScChartListenerCollection* pChartCollection = rDoc.GetChartListenerCollection();
+ if(!pChartCollection)
+ return;
+
+ std::vector< ScTokenRef > aRefTokens;
+ for( const auto& rxSeries : maSeries )
+ rxSeries->FillAllSourceLinks( aRefTokens );
+ if( !aRefTokens.empty() )
+ {
+ ::std::unique_ptr< ScChartListener > xListener( new ScChartListener( rObjName, rDoc, std::move(aRefTokens) ) );
+ xListener->SetUsed( true );
+ xListener->StartListeningTo();
+ pChartCollection->insert( xListener.release() );
+ }
+}
+
+void XclImpChChart::ReadChSeries( XclImpStream& rStrm )
+{
+ sal_uInt16 nNewSeriesIdx = static_cast< sal_uInt16 >( maSeries.size() );
+ XclImpChSeriesRef xSeries = std::make_shared<XclImpChSeries>( GetChRoot(), nNewSeriesIdx );
+ xSeries->ReadRecordGroup( rStrm );
+ maSeries.push_back( xSeries );
+}
+
+void XclImpChChart::ReadChProperties( XclImpStream& rStrm )
+{
+ maProps.mnFlags = rStrm.ReaduInt16();
+ maProps.mnEmptyMode = rStrm.ReaduInt8();
+}
+
+void XclImpChChart::ReadChAxesSet( XclImpStream& rStrm )
+{
+ XclImpChAxesSetRef xAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_NONE );
+ xAxesSet->ReadRecordGroup( rStrm );
+ switch( xAxesSet->GetAxesSetId() )
+ {
+ case EXC_CHAXESSET_PRIMARY: mxPrimAxesSet = xAxesSet; break;
+ case EXC_CHAXESSET_SECONDARY: mxSecnAxesSet = xAxesSet; break;
+ }
+}
+
+void XclImpChChart::ReadChText( XclImpStream& rStrm )
+{
+ XclImpChTextRef xText = std::make_shared<XclImpChText>( GetChRoot() );
+ xText->ReadRecordGroup( rStrm );
+ switch( xText->GetLinkTarget() )
+ {
+ case EXC_CHOBJLINK_TITLE:
+ mxTitle = xText;
+ break;
+ case EXC_CHOBJLINK_DATA:
+ {
+ sal_uInt16 nSeriesIdx = xText->GetPointPos().mnSeriesIdx;
+ if( nSeriesIdx < maSeries.size() )
+ maSeries[ nSeriesIdx ]->SetDataLabel( xText );
+ }
+ break;
+ }
+}
+
+void XclImpChChart::Finalize()
+{
+ // finalize series (must be done first)
+ FinalizeSeries();
+ // #i49218# legend may be attached to primary or secondary axes set
+ mxLegend = mxPrimAxesSet->GetLegend();
+ if( !mxLegend )
+ mxLegend = mxSecnAxesSet->GetLegend();
+ if( mxLegend )
+ mxLegend->Finalize();
+ // axes sets, updates chart type group default formats -> must be called before FinalizeDataFormats()
+ mxPrimAxesSet->Finalize();
+ mxSecnAxesSet->Finalize();
+ // formatting of all series
+ FinalizeDataFormats();
+ // #i47745# missing frame -> invisible border and area
+ if( !mxFrame )
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
+ // chart title
+ FinalizeTitle();
+}
+
+void XclImpChChart::FinalizeSeries()
+{
+ for( const XclImpChSeriesRef& xSeries : maSeries )
+ {
+ if( xSeries->HasParentSeries() )
+ {
+ /* Process child series (trend lines and error bars). Data of
+ child series will be set at the connected parent series. */
+ if( xSeries->GetParentIdx() < maSeries.size() )
+ maSeries[ xSeries->GetParentIdx() ]->AddChildSeries( *xSeries );
+ }
+ else
+ {
+ // insert the series into the related chart type group
+ if( XclImpChTypeGroup* pTypeGroup = GetTypeGroup( xSeries->GetGroupIdx() ).get() )
+ pTypeGroup->AddSeries( xSeries );
+ }
+ }
+}
+
+void XclImpChChart::FinalizeDataFormats()
+{
+ /* #i51639# (part 1): CHDATAFORMAT groups are part of CHSERIES groups.
+ Each CHDATAFORMAT group specifies the series and data point it is
+ assigned to. This makes it possible to have a data format that is
+ related to another series, e.g. a CHDATAFORMAT group for series 2 is
+ part of a CHSERIES group that describes series 1. Therefore the chart
+ itself has collected all CHDATAFORMAT groups to be able to store data
+ format groups for series that have not been imported at that time. This
+ loop finally assigns these groups to the related series. */
+ for( const auto& [rPos, rDataFmt] : maDataFmts )
+ {
+ sal_uInt16 nSeriesIdx = rPos.mnSeriesIdx;
+ if( nSeriesIdx < maSeries.size() )
+ maSeries[ nSeriesIdx ]->SetDataFormat( rDataFmt );
+ }
+
+ /* #i51639# (part 2): Finalize data formats of all series. This adds for
+ example missing CHDATAFORMAT groups for entire series that are needed
+ for automatic colors of lines and areas. */
+ for( auto& rxSeries : maSeries )
+ rxSeries->FinalizeDataFormats();
+}
+
+void XclImpChChart::FinalizeTitle()
+{
+ // special handling for auto-generated title
+ OUString aAutoTitle;
+ if( !mxTitle || (!mxTitle->IsDeleted() && !mxTitle->HasString()) )
+ {
+ // automatic title from first series name (if there are no series on secondary axes set)
+ if( !mxSecnAxesSet->IsValidAxesSet() )
+ aAutoTitle = mxPrimAxesSet->GetSingleSeriesTitle();
+ if( mxTitle || (!aAutoTitle.isEmpty()) )
+ {
+ if( !mxTitle )
+ mxTitle = std::make_shared<XclImpChText>( GetChRoot() );
+ if( aAutoTitle.isEmpty() )
+ aAutoTitle = ScResId(STR_CHARTTITLE);
+ }
+ }
+
+ // will reset mxTitle, if it does not contain a string and no auto title exists
+ lclFinalizeTitle( mxTitle, GetDefaultText( EXC_CHTEXTTYPE_TITLE ), aAutoTitle );
+}
+
+Reference< XDiagram > XclImpChChart::CreateDiagram() const
+{
+ // create a diagram object
+ Reference< XDiagram > xDiagram( ScfApiHelper::CreateInstance( SERVICE_CHART2_DIAGRAM ), UNO_QUERY );
+
+ // convert global chart settings
+ ScfPropertySet aDiaProp( xDiagram );
+
+ // treatment of missing values
+ using namespace cssc::MissingValueTreatment;
+ sal_Int32 nMissingValues = LEAVE_GAP;
+ switch( maProps.mnEmptyMode )
+ {
+ case EXC_CHPROPS_EMPTY_SKIP: nMissingValues = LEAVE_GAP; break;
+ case EXC_CHPROPS_EMPTY_ZERO: nMissingValues = USE_ZERO; break;
+ case EXC_CHPROPS_EMPTY_INTERPOLATE: nMissingValues = CONTINUE; break;
+ }
+ aDiaProp.SetProperty( EXC_CHPROP_MISSINGVALUETREATMENT, nMissingValues );
+
+ return xDiagram;
+}
+
+XclImpChartDrawing::XclImpChartDrawing( const XclImpRoot& rRoot, bool bOwnTab ) :
+ XclImpDrawing( rRoot, bOwnTab ), // sheet charts may contain OLE objects
+ mnScTab( rRoot.GetCurrScTab() ),
+ mbOwnTab( bOwnTab )
+{
+}
+
+void XclImpChartDrawing::ConvertObjects( XclImpDffConverter& rDffConv,
+ const Reference< XModel >& rxModel, const tools::Rectangle& rChartRect )
+{
+ maChartRect = rChartRect; // needed in CalcAnchorRect() callback
+
+ SdrModel* pSdrModel = nullptr;
+ SdrPage* pSdrPage = nullptr;
+ if( mbOwnTab )
+ {
+ // chart sheet: insert all shapes into the sheet, not into the chart object
+ pSdrModel = GetDoc().GetDrawLayer();
+ pSdrPage = GetSdrPage( mnScTab );
+ }
+ else
+ {
+ // embedded chart object: insert all shapes into the chart
+ try
+ {
+ Reference< XDrawPageSupplier > xDrawPageSupp( rxModel, UNO_QUERY_THROW );
+ Reference< XDrawPage > xDrawPage( xDrawPageSupp->getDrawPage(), UNO_SET_THROW );
+ pSdrPage = ::GetSdrPageFromXDrawPage( xDrawPage );
+ pSdrModel = pSdrPage ? &pSdrPage->getSdrModelFromSdrPage() : nullptr;
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ if( pSdrModel && pSdrPage )
+ ImplConvertObjects( rDffConv, *pSdrModel, *pSdrPage );
+}
+
+tools::Rectangle XclImpChartDrawing::CalcAnchorRect( const XclObjAnchor& rAnchor, bool bDffAnchor ) const
+{
+ /* In objects with DFF client anchor, the position of the shape is stored
+ in the cell address components of the client anchor. In old BIFF3-BIFF5
+ objects, the position is stored in the offset components of the anchor. */
+ tools::Rectangle aRect(
+ static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnCol : rAnchor.mnLX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ),
+ static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnRow : rAnchor.mnTY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ),
+ static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnCol : rAnchor.mnRX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ),
+ static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnRow : rAnchor.mnBY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ) );
+ aRect.Justify();
+ // move shapes into chart area for sheet charts
+ if( mbOwnTab )
+ aRect.Move( maChartRect.Left(), maChartRect.Top() );
+ return aRect;
+}
+
+void XclImpChartDrawing::OnObjectInserted( const XclImpDrawObjBase& )
+{
+}
+
+XclImpChart::XclImpChart( const XclImpRoot& rRoot, bool bOwnTab ) :
+ XclImpRoot( rRoot ),
+ mbOwnTab( bOwnTab ),
+ mbIsPivotChart( false )
+{
+}
+
+XclImpChart::~XclImpChart()
+{
+}
+
+void XclImpChart::ReadChartSubStream( XclImpStream& rStrm )
+{
+ XclImpPageSettings& rPageSett = GetPageSettings();
+ XclImpTabViewSettings& rTabViewSett = GetTabViewSettings();
+
+ bool bLoop = true;
+ while( bLoop && rStrm.StartNextRecord() )
+ {
+ // page settings - only for charts in entire sheet
+ if( mbOwnTab ) switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_HORPAGEBREAKS:
+ case EXC_ID_VERPAGEBREAKS: rPageSett.ReadPageBreaks( rStrm ); break;
+ case EXC_ID_HEADER:
+ case EXC_ID_FOOTER: rPageSett.ReadHeaderFooter( rStrm ); break;
+ case EXC_ID_LEFTMARGIN:
+ case EXC_ID_RIGHTMARGIN:
+ case EXC_ID_TOPMARGIN:
+ case EXC_ID_BOTTOMMARGIN: rPageSett.ReadMargin( rStrm ); break;
+ case EXC_ID_PRINTHEADERS: rPageSett.ReadPrintHeaders( rStrm ); break;
+ case EXC_ID_PRINTGRIDLINES: rPageSett.ReadPrintGridLines( rStrm ); break;
+ case EXC_ID_HCENTER:
+ case EXC_ID_VCENTER: rPageSett.ReadCenter( rStrm ); break;
+ case EXC_ID_SETUP: rPageSett.ReadSetup( rStrm ); break;
+ case EXC_ID8_IMGDATA: rPageSett.ReadImgData( rStrm ); break;
+
+ case EXC_ID_WINDOW2: rTabViewSett.ReadWindow2( rStrm, true );break;
+ case EXC_ID_SCL: rTabViewSett.ReadScl( rStrm ); break;
+
+ case EXC_ID_SHEETEXT: //0x0862
+ {
+ // FIXME: do not need to pass palette, XclImpTabVieSettings is derived from root
+ XclImpPalette& rPal = GetPalette();
+ rTabViewSett.ReadTabBgColor( rStrm, rPal);
+ }
+ break;
+
+ case EXC_ID_CODENAME: ReadCodeName( rStrm, false ); break;
+ }
+
+ // common records
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_EOF: bLoop = false; break;
+
+ // #i31882# ignore embedded chart objects
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( rStrm ); break;
+
+ case EXC_ID_CHCHART: ReadChChart( rStrm ); break;
+
+ case EXC_ID8_CHPIVOTREF:
+ GetTracer().TracePivotChartExists();
+ mbIsPivotChart = true;
+ break;
+
+ // BIFF specific records
+ default: switch( GetBiff() )
+ {
+ case EXC_BIFF5: switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break;
+ }
+ break;
+ case EXC_BIFF8: switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_MSODRAWING: GetChartDrawing().ReadMsoDrawing( rStrm ); break;
+ // #i61786# weird documents: OBJ without MSODRAWING -> read in BIFF5 format
+ case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break;
+ }
+ break;
+ default:;
+ }
+ }
+ }
+}
+
+void XclImpChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
+{
+ if( !mxChartData )
+ mxChartData = std::make_shared<XclImpChChart>( GetRoot() );
+ mxChartData->UpdateObjFrame( rLineData, rFillData );
+}
+
+std::size_t XclImpChart::GetProgressSize() const
+{
+ return
+ (mxChartData ? XclImpChChart::GetProgressSize() : 0) +
+ (mxChartDrawing ? mxChartDrawing->GetProgressSize() : 0);
+}
+
+void XclImpChart::Convert( Reference< XModel > const & xModel, XclImpDffConverter& rDffConv, const OUString& rObjName, const tools::Rectangle& rChartRect ) const
+{
+ Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY );
+ if( xChartDoc.is() )
+ {
+ if( mxChartData )
+ mxChartData->Convert( xChartDoc, rDffConv, rObjName, rChartRect );
+ if( mxChartDrawing )
+ mxChartDrawing->ConvertObjects( rDffConv, xModel, rChartRect );
+ }
+}
+
+XclImpChartDrawing& XclImpChart::GetChartDrawing()
+{
+ if( !mxChartDrawing )
+ mxChartDrawing = std::make_shared<XclImpChartDrawing>( GetRoot(), mbOwnTab );
+ return *mxChartDrawing;
+}
+
+void XclImpChart::ReadChChart( XclImpStream& rStrm )
+{
+ mxChartData = std::make_shared<XclImpChChart>( GetRoot() );
+ mxChartData->ReadRecordGroup( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xicontent.cxx b/sc/source/filter/excel/xicontent.cxx
new file mode 100644
index 000000000..872632a1c
--- /dev/null
+++ b/sc/source/filter/excel/xicontent.cxx
@@ -0,0 +1,1442 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <xicontent.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <svl/itemset.hxx>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/editobj.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/configmgr.hxx>
+#include <stringutil.hxx>
+#include <cellform.hxx>
+#include <cellvalue.hxx>
+#include <document.hxx>
+#include <editutil.hxx>
+#include <validat.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <rangenam.hxx>
+#include <arealink.hxx>
+#include <stlsheet.hxx>
+#include <xlcontent.hxx>
+#include <xlformula.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xistyle.hxx>
+#include <xiescher.hxx>
+#include <xiname.hxx>
+
+#include <excform.hxx>
+#include <tabprotection.hxx>
+#include <documentimport.hxx>
+
+#include <memory>
+#include <utility>
+#include <oox/helper/helper.hxx>
+#include <sal/log.hxx>
+
+using ::com::sun::star::uno::Sequence;
+using ::std::unique_ptr;
+
+// Shared string table ========================================================
+
+XclImpSst::XclImpSst( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpSst::ReadSst( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 4 );
+ sal_uInt32 nStrCount = rStrm.ReaduInt32();
+ auto nBytesAvailable = rStrm.GetRecLeft();
+ if (nStrCount > nBytesAvailable)
+ {
+ SAL_WARN("sc.filter", "xls claimed to have " << nStrCount << " strings, but only " << nBytesAvailable << " bytes available, truncating");
+ nStrCount = nBytesAvailable;
+ }
+ maStrings.clear();
+ maStrings.reserve(nStrCount);
+ while( (nStrCount > 0) && rStrm.IsValid() )
+ {
+ XclImpString aString;
+ aString.Read( rStrm );
+ maStrings.push_back( aString );
+ --nStrCount;
+ }
+}
+
+const XclImpString* XclImpSst::GetString( sal_uInt32 nSstIndex ) const
+{
+ return (nSstIndex < maStrings.size()) ? &maStrings[ nSstIndex ] : nullptr;
+}
+
+// Hyperlinks =================================================================
+
+namespace {
+
+/** Reads character array and stores it into rString.
+ @param nChars Number of following characters (not byte count!).
+ @param b16Bit true = 16-bit characters, false = 8-bit characters. */
+void lclAppendString32( OUString& rString, XclImpStream& rStrm, sal_uInt32 nChars, bool b16Bit )
+{
+ sal_uInt16 nReadChars = ulimit_cast< sal_uInt16 >( nChars );
+ rString += rStrm.ReadRawUniString( nReadChars, b16Bit );
+ // ignore remaining chars
+ std::size_t nIgnore = nChars - nReadChars;
+ if( b16Bit )
+ nIgnore *= 2;
+ rStrm.Ignore( nIgnore );
+}
+
+/** Reads 32-bit string length and the character array and stores it into rString.
+ @param b16Bit true = 16-bit characters, false = 8-bit characters. */
+void lclAppendString32( OUString& rString, XclImpStream& rStrm, bool b16Bit )
+{
+ lclAppendString32( rString, rStrm, rStrm.ReaduInt32(), b16Bit );
+}
+
+/** Reads 32-bit string length and ignores following 16-bit character array. */
+void lclIgnoreString32( XclImpStream& rStrm )
+{
+ sal_uInt32 nChars = rStrm.ReaduInt32();
+ nChars *= 2;
+ rStrm.Ignore( nChars );
+}
+
+/** Converts a path to an absolute path.
+ @param rPath The source path. The resulting path is returned here.
+ @param nLevel Number of parent directories to add in front of the path. */
+void lclGetAbsPath( OUString& rPath, sal_uInt16 nLevel, const SfxObjectShell* pDocShell )
+{
+ OUStringBuffer aTmpStr;
+ while( nLevel )
+ {
+ aTmpStr.append( "../" );
+ --nLevel;
+ }
+ aTmpStr.append( rPath );
+
+ if( pDocShell )
+ {
+ bool bWasAbs = false;
+ rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr.makeStringAndClear(), bWasAbs ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ // full path as stored in SvxURLField must be encoded
+ }
+ else
+ rPath = aTmpStr.makeStringAndClear();
+}
+
+/** Inserts the URL into a text cell. Does not modify value or formula cells. */
+void lclInsertUrl( XclImpRoot& rRoot, const OUString& rUrl, SCCOL nScCol, SCROW nScRow, SCTAB nScTab )
+{
+ ScDocumentImport& rDoc = rRoot.GetDocImport();
+ ScAddress aScPos( nScCol, nScRow, nScTab );
+ ScRefCellValue aCell(rDoc.getDoc(), aScPos);
+ switch( aCell.meType )
+ {
+ // #i54261# hyperlinks in string cells
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ {
+ sal_uInt32 nNumFmt = rDoc.getDoc().GetNumberFormat(rDoc.getDoc().GetNonThreadedContext(), aScPos);
+ SvNumberFormatter* pFormatter = rDoc.getDoc().GetFormatTable();
+ const Color* pColor;
+ OUString aDisplText = ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pFormatter, rDoc.getDoc());
+ if (aDisplText.isEmpty())
+ aDisplText = rUrl;
+
+ ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
+ SvxURLField aUrlField( rUrl, aDisplText, SvxURLFormat::AppDefault );
+
+ if( aCell.meType == CELLTYPE_EDIT )
+ {
+ const EditTextObject* pEditObj = aCell.mpEditText;
+ rEE.SetTextCurrentDefaults( *pEditObj );
+ rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0, 0, EE_PARA_ALL, 0 ) );
+ }
+ else
+ {
+ rEE.SetTextCurrentDefaults( OUString() );
+ rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection() );
+ if( const ScPatternAttr* pPattern = rDoc.getDoc().GetPattern( aScPos.Col(), aScPos.Row(), nScTab ) )
+ {
+ SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
+ pPattern->FillEditItemSet( &aItemSet );
+ rEE.QuickSetAttribs( aItemSet, ESelection( 0, 0, EE_PARA_ALL, 0 ) );
+ }
+ }
+
+ // The cell will own the text object instance.
+ rDoc.setEditCell(aScPos, rEE.CreateTextObject());
+ }
+ break;
+
+ default:
+ // Handle other cell types e.g. formulas ( and ? ) that have associated
+ // hyperlinks.
+ // Ideally all hyperlinks should be treated as below. For the moment,
+ // given the current absence of ods support lets just handle what we
+ // previously didn't handle the new way.
+ // Unfortunately we won't be able to preserve such hyperlinks when
+ // saving to ods. Note: when we are able to save such hyperlinks to ods
+ // we should handle *all* imported hyperlinks as below ( e.g. as cell
+ // attribute ) for better interoperability.
+ {
+ SfxStringItem aItem( ATTR_HYPERLINK, rUrl );
+ rDoc.getDoc().ApplyAttr(nScCol, nScRow, nScTab, aItem);
+ break;
+ }
+ }
+}
+
+} // namespace
+
+void XclImpHyperlink::ReadHlink( XclImpStream& rStrm )
+{
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ rStrm >> aXclRange;
+ // #i80006# Excel silently ignores invalid hi-byte of column index (TODO: everywhere?)
+ aXclRange.maFirst.mnCol &= 0xFF;
+ aXclRange.maLast.mnCol &= 0xFF;
+ OUString aString = ReadEmbeddedData( rStrm );
+ if ( !aString.isEmpty() )
+ rStrm.GetRoot().GetXFRangeBuffer().SetHyperlink( aXclRange, aString );
+}
+
+OUString XclImpHyperlink::ReadEmbeddedData( XclImpStream& rStrm )
+{
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ SfxObjectShell* pDocShell = rRoot.GetDocShell();
+
+ OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
+
+ XclGuid aGuid;
+ rStrm >> aGuid;
+ rStrm.Ignore( 4 );
+ sal_uInt32 nFlags = rStrm.ReaduInt32();
+
+ OSL_ENSURE( aGuid == XclTools::maGuidStdLink, "XclImpHyperlink::ReadEmbeddedData - unknown header GUID" );
+
+ ::std::unique_ptr< OUString > xLongName; // link / file name
+ ::std::unique_ptr< OUString > xShortName; // 8.3-representation of file name
+ ::std::unique_ptr< OUString > xTextMark; // text mark
+
+ // description (ignore)
+ if( ::get_flag( nFlags, EXC_HLINK_DESCR ) )
+ lclIgnoreString32( rStrm );
+ // target frame (ignore) !! DESCR/FRAME - is this the right order? (never seen them together)
+ if( ::get_flag( nFlags, EXC_HLINK_FRAME ) )
+ lclIgnoreString32( rStrm );
+
+ // URL fields are zero-terminated - do not let the stream replace them
+ // in the lclAppendString32() with the '?' character.
+ rStrm.SetNulSubstChar( '\0' );
+
+ // UNC path
+ if( ::get_flag( nFlags, EXC_HLINK_UNC ) )
+ {
+ xLongName.reset( new OUString );
+ lclAppendString32( *xLongName, rStrm, true );
+ lclGetAbsPath( *xLongName, 0, pDocShell );
+ }
+ // file link or URL
+ else if( ::get_flag( nFlags, EXC_HLINK_BODY ) )
+ {
+ rStrm >> aGuid;
+
+ if( aGuid == XclTools::maGuidFileMoniker )
+ {
+ sal_uInt16 nLevel = rStrm.ReaduInt16(); // counter for level to climb down in path
+ xShortName.reset( new OUString );
+ lclAppendString32( *xShortName, rStrm, false );
+ rStrm.Ignore( 24 );
+
+ sal_uInt32 nStrLen = rStrm.ReaduInt32();
+ if( nStrLen )
+ {
+ nStrLen = rStrm.ReaduInt32();
+ nStrLen /= 2; // it's byte count here...
+ rStrm.Ignore( 2 );
+ xLongName.reset( new OUString );
+ lclAppendString32( *xLongName, rStrm, nStrLen, true );
+ lclGetAbsPath( *xLongName, nLevel, pDocShell );
+ }
+ else
+ lclGetAbsPath( *xShortName, nLevel, pDocShell );
+ }
+ else if( aGuid == XclTools::maGuidUrlMoniker )
+ {
+ sal_uInt32 nStrLen = rStrm.ReaduInt32();
+ nStrLen /= 2; // it's byte count here...
+ xLongName.reset( new OUString );
+ lclAppendString32( *xLongName, rStrm, nStrLen, true );
+ if( !::get_flag( nFlags, EXC_HLINK_ABS ) )
+ lclGetAbsPath( *xLongName, 0, pDocShell );
+ }
+ else
+ {
+ OSL_FAIL( "XclImpHyperlink::ReadEmbeddedData - unknown content GUID" );
+ }
+ }
+
+ // text mark
+ if( ::get_flag( nFlags, EXC_HLINK_MARK ) )
+ {
+ xTextMark.reset( new OUString );
+ lclAppendString32( *xTextMark, rStrm, true );
+ }
+
+ rStrm.SetNulSubstChar(); // back to default
+
+ OSL_ENSURE( rStrm.GetRecLeft() == 0, "XclImpHyperlink::ReadEmbeddedData - record size mismatch" );
+
+ if (!xLongName && xShortName)
+ xLongName = std::move(xShortName);
+ else if (!xLongName && xTextMark)
+ xLongName.reset( new OUString );
+
+ if (xLongName)
+ {
+ if (xTextMark)
+ {
+ if( xLongName->isEmpty() )
+ {
+ sal_Int32 nSepPos = xTextMark->lastIndexOf( '!' );
+ if( nSepPos > 0 )
+ {
+ // Do not attempt to blindly convert '#SheetName!A1' to
+ // '#SheetName.A1', it can be #SheetName!R1C1 as well.
+ // Hyperlink handler has to handle all, but prefer
+ // '#SheetName.A1' if possible.
+ if (nSepPos < xTextMark->getLength() - 1)
+ {
+ ScDocument& rDoc = rRoot.GetDoc();
+ ScRange aRange;
+ if ((aRange.ParseAny( xTextMark->copy( nSepPos + 1 ), rDoc, formula::FormulaGrammar::CONV_XL_R1C1)
+ & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ xTextMark.reset( new OUString( xTextMark->replaceAt( nSepPos, 1, rtl::OUStringChar( '.' ))));
+ }
+ }
+ }
+ xLongName.reset( new OUString( *xLongName + "#" + *xTextMark ) );
+ }
+ return( *xLongName );
+ }
+ return( OUString() );
+}
+
+void XclImpHyperlink::ConvertToValidTabName(OUString& rUrl)
+{
+ sal_Int32 n = rUrl.getLength();
+ if (n < 4)
+ // Needs at least 4 characters.
+ return;
+
+ if (rUrl[0] != '#')
+ // the 1st character must be '#'.
+ return;
+
+ OUStringBuffer aNewUrl("#");
+ OUStringBuffer aTabName;
+
+ bool bInQuote = false;
+ bool bQuoteTabName = false;
+ for( sal_Int32 i = 1; i < n; ++i )
+ {
+ sal_Unicode c = rUrl[i];
+ if (c == '\'')
+ {
+ if (bInQuote && i+1 < n && rUrl[i+1] == '\'')
+ {
+ // Two consecutive single quotes ('') signify a single literal
+ // quite. When this occurs, the whole table name needs to be
+ // quoted.
+ bQuoteTabName = true;
+ aTabName.append(c).append(c);
+ ++i;
+ continue;
+ }
+
+ bInQuote = !bInQuote;
+ if (!bInQuote && !aTabName.isEmpty())
+ {
+ if (bQuoteTabName)
+ aNewUrl.append("'");
+ aNewUrl.append(aTabName);
+ if (bQuoteTabName)
+ aNewUrl.append("'");
+ }
+ }
+ else if (bInQuote)
+ aTabName.append(c);
+ else
+ aNewUrl.append(c);
+ }
+
+ if (bInQuote)
+ // It should be outside the quotes!
+ return;
+
+ // All is good. Pass the new URL.
+ rUrl = aNewUrl.makeStringAndClear();
+}
+
+void XclImpHyperlink::InsertUrl( XclImpRoot& rRoot, const XclRange& rXclRange, const OUString& rUrl )
+{
+ OUString aUrl(rUrl);
+ ConvertToValidTabName(aUrl);
+
+ SCTAB nScTab = rRoot.GetCurrScTab();
+ ScRange aScRange( ScAddress::UNINITIALIZED );
+ if( rRoot.GetAddressConverter().ConvertRange( aScRange, rXclRange, nScTab, nScTab, true ) )
+ {
+ SCCOL nScCol1, nScCol2;
+ SCROW nScRow1, nScRow2;
+ aScRange.GetVars( nScCol1, nScRow1, nScTab, nScCol2, nScRow2, nScTab );
+
+ if (utl::ConfigManager::IsFuzzing())
+ {
+ SCROW nRows = nScRow2 - nScRow1;
+ if (nRows > 1024)
+ {
+ SAL_WARN("sc.filter", "for fuzzing performance, clamped hyperlink apply range end row from " << nScRow2 << " to " << nScRow1 + 1024);
+ nScRow2 = nScRow1 + 1024;
+ }
+ }
+
+ for( SCCOL nScCol = nScCol1; nScCol <= nScCol2; ++nScCol )
+ for( SCROW nScRow = nScRow1; nScRow <= nScRow2; ++nScRow )
+ lclInsertUrl( rRoot, aUrl, nScCol, nScRow, nScTab );
+ }
+}
+
+// Label ranges ===============================================================
+
+void XclImpLabelranges::ReadLabelranges( XclImpStream& rStrm )
+{
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
+
+ ScDocument& rDoc = rRoot.GetDoc();
+ SCTAB nScTab = rRoot.GetCurrScTab();
+ XclImpAddressConverter& rAddrConv = rRoot.GetAddressConverter();
+ ScRangePairListRef xLabelRangesRef;
+
+ XclRangeList aRowXclRanges, aColXclRanges;
+ rStrm >> aRowXclRanges >> aColXclRanges;
+
+ // row label ranges
+ ScRangeList aRowScRanges;
+ rAddrConv.ConvertRangeList( aRowScRanges, aRowXclRanges, nScTab, false );
+ xLabelRangesRef = rDoc.GetRowNameRangesRef();
+ for ( size_t i = 0, nRanges = aRowScRanges.size(); i < nRanges; ++i )
+ {
+ const ScRange & rScRange = aRowScRanges[ i ];
+ ScRange aDataRange( rScRange );
+ if( aDataRange.aEnd.Col() < rDoc.MaxCol() )
+ {
+ aDataRange.aStart.SetCol( aDataRange.aEnd.Col() + 1 );
+ aDataRange.aEnd.SetCol( rDoc.MaxCol() );
+ }
+ else if( aDataRange.aStart.Col() > 0 )
+ {
+ aDataRange.aEnd.SetCol( aDataRange.aStart.Col() - 1 );
+ aDataRange.aStart.SetCol( 0 );
+ }
+ xLabelRangesRef->Append( ScRangePair( rScRange, aDataRange ) );
+ }
+
+ // column label ranges
+ ScRangeList aColScRanges;
+ rAddrConv.ConvertRangeList( aColScRanges, aColXclRanges, nScTab, false );
+ xLabelRangesRef = rDoc.GetColNameRangesRef();
+
+ for ( size_t i = 0, nRanges = aColScRanges.size(); i < nRanges; ++i )
+ {
+ const ScRange & rScRange = aColScRanges[ i ];
+ ScRange aDataRange( rScRange );
+ if( aDataRange.aEnd.Row() < rDoc.MaxRow() )
+ {
+ aDataRange.aStart.SetRow( aDataRange.aEnd.Row() + 1 );
+ aDataRange.aEnd.SetRow( rDoc.MaxRow() );
+ }
+ else if( aDataRange.aStart.Row() > 0 )
+ {
+ aDataRange.aEnd.SetRow( aDataRange.aStart.Row() - 1 );
+ aDataRange.aStart.SetRow( 0 );
+ }
+ xLabelRangesRef->Append( ScRangePair( rScRange, aDataRange ) );
+ }
+}
+
+// Conditional formatting =====================================================
+
+XclImpCondFormat::XclImpCondFormat( const XclImpRoot& rRoot, sal_uInt32 nFormatIndex ) :
+ XclImpRoot( rRoot ),
+ mnFormatIndex( nFormatIndex ),
+ mnCondCount( 0 ),
+ mnCondIndex( 0 )
+{
+}
+
+XclImpCondFormat::~XclImpCondFormat()
+{
+}
+
+void XclImpCondFormat::ReadCondfmt( XclImpStream& rStrm )
+{
+ OSL_ENSURE( !mnCondCount, "XclImpCondFormat::ReadCondfmt - already initialized" );
+ XclRangeList aXclRanges;
+ mnCondCount = rStrm.ReaduInt16();
+ rStrm.Ignore( 10 );
+ rStrm >> aXclRanges;
+ GetAddressConverter().ConvertRangeList( maRanges, aXclRanges, GetCurrScTab(), true );
+}
+
+void XclImpCondFormat::ReadCF( XclImpStream& rStrm )
+{
+ if( mnCondIndex >= mnCondCount )
+ {
+ OSL_FAIL( "XclImpCondFormat::ReadCF - CF without leading CONDFMT" );
+ return;
+ }
+
+ // entire conditional format outside of valid range?
+ if( maRanges.empty() )
+ return;
+
+ sal_uInt8 nType = rStrm.ReaduInt8();
+ sal_uInt8 nOperator = rStrm.ReaduInt8();
+ sal_uInt16 nFmlaSize1 = rStrm.ReaduInt16();
+ sal_uInt16 nFmlaSize2 = rStrm.ReaduInt16();
+ sal_uInt32 nFlags = rStrm.ReaduInt32();
+ rStrm.Ignore( 2 ); //nFlagsExtended
+
+ // *** mode and comparison operator ***
+
+ ScConditionMode eMode = ScConditionMode::NONE;
+ switch( nType )
+ {
+ case EXC_CF_TYPE_CELL:
+ {
+ switch( nOperator )
+ {
+ case EXC_CF_CMP_BETWEEN: eMode = ScConditionMode::Between; break;
+ case EXC_CF_CMP_NOT_BETWEEN: eMode = ScConditionMode::NotBetween; break;
+ case EXC_CF_CMP_EQUAL: eMode = ScConditionMode::Equal; break;
+ case EXC_CF_CMP_NOT_EQUAL: eMode = ScConditionMode::NotEqual; break;
+ case EXC_CF_CMP_GREATER: eMode = ScConditionMode::Greater; break;
+ case EXC_CF_CMP_LESS: eMode = ScConditionMode::Less; break;
+ case EXC_CF_CMP_GREATER_EQUAL: eMode = ScConditionMode::EqGreater; break;
+ case EXC_CF_CMP_LESS_EQUAL: eMode = ScConditionMode::EqLess; break;
+ default:
+ SAL_INFO(
+ "sc.filter", "unknown CF comparison " << nOperator);
+ }
+ }
+ break;
+
+ case EXC_CF_TYPE_FMLA:
+ eMode = ScConditionMode::Direct;
+ break;
+
+ default:
+ SAL_INFO("sc.filter", "unknown CF mode " << nType);
+ return;
+ }
+
+ // *** create style sheet ***
+
+ OUString aStyleName( XclTools::GetCondFormatStyleName( GetCurrScTab(), mnFormatIndex, mnCondIndex ) );
+ SfxItemSet& rStyleItemSet = ScfTools::MakeCellStyleSheet( GetStyleSheetPool(), aStyleName, true ).GetItemSet();
+
+ const XclImpPalette& rPalette = GetPalette();
+
+ // number format
+
+ if( get_flag( nFlags, EXC_CF_BLOCK_NUMFMT ) )
+ {
+ XclImpNumFmtBuffer& rNumFmtBuffer = GetRoot().GetNumFmtBuffer();
+ bool bIFmt = get_flag( nFlags, EXC_CF_IFMT_USER );
+ sal_uInt16 nFormat = rNumFmtBuffer.ReadCFFormat( rStrm, bIFmt );
+ rNumFmtBuffer.FillToItemSet( rStyleItemSet, nFormat );
+ }
+
+ // *** font block ***
+
+ if( ::get_flag( nFlags, EXC_CF_BLOCK_FONT ) )
+ {
+ XclImpFont aFont( GetRoot() );
+ aFont.ReadCFFontBlock( rStrm );
+ aFont.FillToItemSet( rStyleItemSet, XclFontItemType::Cell );
+ }
+
+ // alignment
+ if( get_flag( nFlags, EXC_CF_BLOCK_ALIGNMENT ) )
+ {
+ XclImpCellAlign aAlign;
+ sal_uInt16 nAlign(0);
+ sal_uInt16 nAlignMisc(0);
+ nAlign = rStrm.ReaduInt16();
+ nAlignMisc = rStrm.ReaduInt16();
+ aAlign.FillFromCF( nAlign, nAlignMisc );
+ aAlign.FillToItemSet( rStyleItemSet, nullptr );
+ rStrm.Ignore(4);
+ }
+
+ // *** border block ***
+
+ if( ::get_flag( nFlags, EXC_CF_BLOCK_BORDER ) )
+ {
+ sal_uInt16 nLineStyle(0);
+ sal_uInt32 nLineColor(0);
+ nLineStyle = rStrm.ReaduInt16();
+ nLineColor = rStrm.ReaduInt32();
+ rStrm.Ignore( 2 );
+
+ XclImpCellBorder aBorder;
+ aBorder.FillFromCF8( nLineStyle, nLineColor, nFlags );
+ aBorder.FillToItemSet( rStyleItemSet, rPalette );
+ }
+
+ // *** pattern block ***
+
+ if( ::get_flag( nFlags, EXC_CF_BLOCK_AREA ) )
+ {
+ sal_uInt16 nPattern(0), nColor(0);
+ nPattern = rStrm.ReaduInt16();
+ nColor = rStrm.ReaduInt16();
+
+ XclImpCellArea aArea;
+ aArea.FillFromCF8( nPattern, nColor, nFlags );
+ aArea.FillToItemSet( rStyleItemSet, rPalette );
+ }
+
+ if( get_flag( nFlags, EXC_CF_BLOCK_PROTECTION ) )
+ {
+ sal_uInt16 nCellProt;
+ nCellProt = rStrm.ReaduInt16();
+ XclImpCellProt aCellProt;
+ aCellProt.FillFromXF3(nCellProt);
+ aCellProt.FillToItemSet( rStyleItemSet );
+ }
+
+ // *** formulas ***
+
+ const ScAddress& rPos = maRanges.front().aStart; // assured above that maRanges is not empty
+ ExcelToSc& rFmlaConv = GetOldFmlaConverter();
+
+ ::std::unique_ptr< ScTokenArray > xTokArr1;
+ if( nFmlaSize1 > 0 )
+ {
+ std::unique_ptr<ScTokenArray> pTokArr;
+ rFmlaConv.Reset( rPos );
+ rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize1, false, FT_CondFormat );
+ // formula converter owns pTokArr -> create a copy of the token array
+ if( pTokArr )
+ {
+ xTokArr1 = std::move( pTokArr );
+ GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr1);
+ }
+ }
+
+ ::std::unique_ptr< ScTokenArray > xTokArr2;
+ if( nFmlaSize2 > 0 )
+ {
+ std::unique_ptr<ScTokenArray> pTokArr;
+ rFmlaConv.Reset( rPos );
+ rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize2, false, FT_CondFormat );
+ // formula converter owns pTokArr -> create a copy of the token array
+ if( pTokArr )
+ {
+ xTokArr2 = std::move( pTokArr );
+ GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr2);
+ }
+ }
+
+ // *** create the Calc conditional formatting ***
+
+ const ScAddress aPos(rPos); //in case maRanges.Join invalidates it
+
+ if( !mxScCondFmt )
+ {
+ mxScCondFmt.reset( new ScConditionalFormat( 0/*nKey*/, &GetDoc() ) );
+ if(maRanges.size() > 1)
+ maRanges.Join(maRanges[0], true);
+ mxScCondFmt->SetRange(maRanges);
+ }
+
+ ScCondFormatEntry* pEntry = new ScCondFormatEntry(eMode, xTokArr1.get(), xTokArr2.get(), GetDoc(), aPos, aStyleName);
+ mxScCondFmt->AddEntry( pEntry );
+ ++mnCondIndex;
+}
+
+void XclImpCondFormat::Apply()
+{
+ if( mxScCondFmt )
+ {
+ ScDocument& rDoc = GetDoc();
+
+ SCTAB nTab = maRanges.front().aStart.Tab();
+ sal_uLong nKey = rDoc.AddCondFormat( mxScCondFmt->Clone(), nTab );
+
+ rDoc.AddCondFormatData( maRanges, nTab, nKey );
+ }
+}
+
+XclImpCondFormatManager::XclImpCondFormatManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpCondFormatManager::ReadCondfmt( XclImpStream& rStrm )
+{
+ XclImpCondFormat* pFmt = new XclImpCondFormat( GetRoot(), maCondFmtList.size() );
+ pFmt->ReadCondfmt( rStrm );
+ maCondFmtList.push_back( std::unique_ptr<XclImpCondFormat>(pFmt) );
+}
+
+void XclImpCondFormatManager::ReadCF( XclImpStream& rStrm )
+{
+ OSL_ENSURE( !maCondFmtList.empty(), "XclImpCondFormatManager::ReadCF - CF without leading CONDFMT" );
+ if( !maCondFmtList.empty() )
+ maCondFmtList.back()->ReadCF( rStrm );
+}
+
+void XclImpCondFormatManager::Apply()
+{
+ for( auto& rxFmt : maCondFmtList )
+ rxFmt->Apply();
+ maCondFmtList.clear();
+}
+
+// Data Validation ============================================================
+
+XclImpValidationManager::DVItem::DVItem( const ScRangeList& rRanges, const ScValidationData& rValidData ) :
+ maRanges(rRanges), maValidData(rValidData) {}
+
+XclImpValidationManager::XclImpValidationManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpValidationManager::ReadDval( XclImpStream& rStrm )
+{
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
+
+ sal_uInt32 nObjId(0);
+ rStrm.Ignore( 10 );
+ nObjId = rStrm.ReaduInt32();
+ if( nObjId != EXC_DVAL_NOOBJ )
+ {
+ OSL_ENSURE( nObjId <= 0xFFFF, "XclImpValidation::ReadDval - invalid object ID" );
+ rRoot.GetCurrSheetDrawing().SetSkipObj( static_cast< sal_uInt16 >( nObjId ) );
+ }
+}
+
+void XclImpValidationManager::ReadDV( XclImpStream& rStrm )
+{
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
+
+ ScDocument& rDoc = rRoot.GetDoc();
+ SCTAB nScTab = rRoot.GetCurrScTab();
+ ExcelToSc& rFmlaConv = rRoot.GetOldFmlaConverter();
+
+ // flags
+ sal_uInt32 nFlags = rStrm.ReaduInt32();
+
+ // message strings
+ /* Empty strings are single NUL characters in Excel (string length is 1).
+ -> Do not let the stream replace them with '?' characters. */
+ rStrm.SetNulSubstChar( '\0' );
+ OUString aPromptTitle( rStrm.ReadUniString() );
+ OUString aErrorTitle( rStrm.ReadUniString() );
+ OUString aPromptMessage( rStrm.ReadUniString() );
+ OUString aErrorMessage( rStrm.ReadUniString() );
+ rStrm.SetNulSubstChar(); // back to default
+
+ // formula(s)
+ if ( rStrm.GetRecLeft() <= 8 )
+ // Not enough bytes left in the record. Bail out.
+ return;
+
+ // first formula
+ // string list is single tStr token with NUL separators -> replace them with LF
+ rStrm.SetNulSubstChar( '\n' );
+ ::std::unique_ptr< ScTokenArray > xTokArr1;
+
+ // We can't import the formula directly because we need the range
+ sal_uInt16 nLenFormula1 = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ XclImpStreamPos aPosFormula1;
+ rStrm.StorePosition(aPosFormula1);
+ rStrm.Ignore(nLenFormula1);
+
+ // second formula
+ ::std::unique_ptr< ScTokenArray > xTokArr2;
+
+ sal_uInt16 nLenFormula2 = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ XclImpStreamPos aPosFormula2;
+ rStrm.StorePosition(aPosFormula2);
+ rStrm.Ignore(nLenFormula2);
+
+ // read all cell ranges
+ XclRangeList aXclRanges;
+ rStrm >> aXclRanges;
+
+ // convert to Calc range list
+ ScRangeList aScRanges;
+ rRoot.GetAddressConverter().ConvertRangeList( aScRanges, aXclRanges, nScTab, true );
+
+ // only continue if there are valid ranges
+ if ( aScRanges.empty() )
+ return;
+
+ ScRange aCombinedRange = aScRanges.Combine();
+
+ XclImpStreamPos aCurrentPos;
+ rStrm.StorePosition(aCurrentPos);
+ rStrm.RestorePosition(aPosFormula1);
+ if( nLenFormula1 > 0 )
+ {
+ std::unique_ptr<ScTokenArray> pTokArr;
+ rFmlaConv.Reset(aCombinedRange.aStart);
+ rFmlaConv.Convert( pTokArr, rStrm, nLenFormula1, false, FT_CondFormat );
+ // formula converter owns pTokArr -> create a copy of the token array
+ if( pTokArr )
+ xTokArr1 = std::move( pTokArr );
+ }
+ rStrm.SetNulSubstChar(); // back to default
+ if (nLenFormula2 > 0)
+ {
+ rStrm.RestorePosition(aPosFormula2);
+ std::unique_ptr<ScTokenArray> pTokArr;
+ rFmlaConv.Reset(aCombinedRange.aStart);
+ rFmlaConv.Convert( pTokArr, rStrm, nLenFormula2, false, FT_CondFormat );
+ // formula converter owns pTokArr -> create a copy of the token array
+ if( pTokArr )
+ xTokArr2 = std::move( pTokArr );
+ }
+
+ rStrm.RestorePosition(aCurrentPos);
+
+ bool bIsValid = true; // valid settings in flags field
+
+ ScValidationMode eValMode = SC_VALID_ANY;
+ switch( nFlags & EXC_DV_MODE_MASK )
+ {
+ case EXC_DV_MODE_ANY: eValMode = SC_VALID_ANY; break;
+ case EXC_DV_MODE_WHOLE: eValMode = SC_VALID_WHOLE; break;
+ case EXC_DV_MODE_DECIMAL: eValMode = SC_VALID_DECIMAL; break;
+ case EXC_DV_MODE_LIST: eValMode = SC_VALID_LIST; break;
+ case EXC_DV_MODE_DATE: eValMode = SC_VALID_DATE; break;
+ case EXC_DV_MODE_TIME: eValMode = SC_VALID_TIME; break;
+ case EXC_DV_MODE_TEXTLEN: eValMode = SC_VALID_TEXTLEN; break;
+ case EXC_DV_MODE_CUSTOM: eValMode = SC_VALID_CUSTOM; break;
+ default: bIsValid = false;
+ }
+ rRoot.GetTracer().TraceDVType(eValMode == SC_VALID_CUSTOM);
+
+ ScConditionMode eCondMode = ScConditionMode::Between;
+ switch( nFlags & EXC_DV_COND_MASK )
+ {
+ case EXC_DV_COND_BETWEEN: eCondMode = ScConditionMode::Between; break;
+ case EXC_DV_COND_NOTBETWEEN:eCondMode = ScConditionMode::NotBetween; break;
+ case EXC_DV_COND_EQUAL: eCondMode = ScConditionMode::Equal; break;
+ case EXC_DV_COND_NOTEQUAL: eCondMode = ScConditionMode::NotEqual; break;
+ case EXC_DV_COND_GREATER: eCondMode = ScConditionMode::Greater; break;
+ case EXC_DV_COND_LESS: eCondMode = ScConditionMode::Less; break;
+ case EXC_DV_COND_EQGREATER: eCondMode = ScConditionMode::EqGreater; break;
+ case EXC_DV_COND_EQLESS: eCondMode = ScConditionMode::EqLess; break;
+ default: bIsValid = false;
+ }
+
+ if ( !bIsValid )
+ // No valid validation found. Bail out.
+ return;
+
+ // The default value for comparison is _BETWEEN. However, custom
+ // rules are a formula, and thus the comparator should be ignored
+ // and only a true or false from the formula is evaluated. In Calc,
+ // formulas use comparison SC_COND_DIRECT.
+ if( eValMode == SC_VALID_CUSTOM )
+ {
+ eCondMode = ScConditionMode::Direct;
+ }
+
+ // first range for base address for relative references
+ const ScRange& rScRange = aScRanges.front(); // aScRanges is not empty
+
+ // process string list of a list validity (convert to list of string tokens)
+ if( xTokArr1 && (eValMode == SC_VALID_LIST) && ::get_flag( nFlags, EXC_DV_STRINGLIST ) )
+ XclTokenArrayHelper::ConvertStringToList(*xTokArr1, rDoc.GetSharedStringPool(), '\n');
+
+ maDVItems.push_back(
+ std::make_unique<DVItem>(aScRanges, ScValidationData(eValMode, eCondMode, xTokArr1.get(), xTokArr2.get(), rDoc, rScRange.aStart)));
+ DVItem& rItem = *maDVItems.back();
+
+ rItem.maValidData.SetIgnoreBlank( ::get_flag( nFlags, EXC_DV_IGNOREBLANK ) );
+ rItem.maValidData.SetListType( ::get_flagvalue( nFlags, EXC_DV_SUPPRESSDROPDOWN, css::sheet::TableValidationVisibility::INVISIBLE, css::sheet::TableValidationVisibility::UNSORTED ) );
+
+ // *** prompt box ***
+ if( !aPromptTitle.isEmpty() || !aPromptMessage.isEmpty() )
+ {
+ // set any text stored in the record
+ rItem.maValidData.SetInput( aPromptTitle, aPromptMessage );
+ if( !::get_flag( nFlags, EXC_DV_SHOWPROMPT ) )
+ rItem.maValidData.ResetInput();
+ }
+
+ // *** error box ***
+ ScValidErrorStyle eErrStyle = SC_VALERR_STOP;
+ switch( nFlags & EXC_DV_ERROR_MASK )
+ {
+ case EXC_DV_ERROR_WARNING: eErrStyle = SC_VALERR_WARNING; break;
+ case EXC_DV_ERROR_INFO: eErrStyle = SC_VALERR_INFO; break;
+ }
+ // set texts and error style
+ rItem.maValidData.SetError( aErrorTitle, aErrorMessage, eErrStyle );
+ if( !::get_flag( nFlags, EXC_DV_SHOWERROR ) )
+ rItem.maValidData.ResetError();
+}
+
+void XclImpValidationManager::Apply()
+{
+ ScDocument& rDoc = GetRoot().GetDoc();
+ for (const auto& rxDVItem : maDVItems)
+ {
+ DVItem& rItem = *rxDVItem;
+ // set the handle ID
+ sal_uLong nHandle = rDoc.AddValidationEntry( rItem.maValidData );
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nHandle ) );
+
+ // apply all ranges
+ for ( size_t i = 0, nRanges = rItem.maRanges.size(); i < nRanges; ++i )
+ {
+ const ScRange & rScRange = rItem.maRanges[ i ];
+ rDoc.ApplyPatternAreaTab( rScRange.aStart.Col(), rScRange.aStart.Row(),
+ rScRange.aEnd.Col(), rScRange.aEnd.Row(), rScRange.aStart.Tab(), aPattern );
+ }
+ }
+ maDVItems.clear();
+}
+
+// Web queries ================================================================
+
+XclImpWebQuery::XclImpWebQuery( const ScRange& rDestRange ) :
+ maDestRange( rDestRange ),
+ meMode( xlWQUnknown ),
+ mnRefresh( 0 )
+{
+}
+
+void XclImpWebQuery::ReadParamqry( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags = rStrm.ReaduInt16();
+ sal_uInt16 nType = ::extract_value< sal_uInt16 >( nFlags, 0, 3 );
+ if( !((nType == EXC_PQRYTYPE_WEBQUERY) && ::get_flag( nFlags, EXC_PQRY_WEBQUERY )) )
+ return;
+
+ if( ::get_flag( nFlags, EXC_PQRY_TABLES ) )
+ {
+ meMode = xlWQAllTables;
+ maTables = ScfTools::GetHTMLTablesName();
+ }
+ else
+ {
+ meMode = xlWQDocument;
+ maTables = ScfTools::GetHTMLDocName();
+ }
+}
+
+void XclImpWebQuery::ReadWqstring( XclImpStream& rStrm )
+{
+ maURL = rStrm.ReadUniString();
+}
+
+void XclImpWebQuery::ReadWqsettings( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 10 );
+ sal_uInt16 nFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 10 );
+ mnRefresh = rStrm.ReaduInt16();
+
+ if( ::get_flag( nFlags, EXC_WQSETT_SPECTABLES ) && (meMode == xlWQAllTables) )
+ meMode = xlWQSpecTables;
+}
+
+void XclImpWebQuery::ReadWqtables( XclImpStream& rStrm )
+{
+ if( meMode != xlWQSpecTables )
+ return;
+
+ rStrm.Ignore( 4 );
+ OUString aTables( rStrm.ReadUniString() );
+
+ const sal_Unicode cSep = ';';
+ static const OUStringLiteral aQuotedPairs( u"\"\"" );
+ maTables.clear();
+ for ( sal_Int32 nStringIx {aTables.isEmpty() ? -1 : 0}; nStringIx>=0; )
+ {
+ OUString aToken( ScStringUtil::GetQuotedToken( aTables, 0, aQuotedPairs, ',', nStringIx ) );
+ sal_Int32 nTabNum = CharClass::isAsciiNumeric( aToken ) ? aToken.toInt32() : 0;
+ if( nTabNum > 0 )
+ maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLIndex( static_cast< sal_uInt32 >( nTabNum ) ), cSep );
+ else
+ {
+ ScGlobal::EraseQuotes( aToken, '"', false );
+ if( !aToken.isEmpty() )
+ maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLName( aToken ), cSep );
+ }
+ }
+}
+
+void XclImpWebQuery::Apply( ScDocument& rDoc, const OUString& rFilterName )
+{
+ if( !maURL.isEmpty() && (meMode != xlWQUnknown) && rDoc.GetDocumentShell() )
+ {
+ ScAreaLink* pLink = new ScAreaLink( rDoc.GetDocumentShell(),
+ maURL, rFilterName, OUString(), maTables, maDestRange, mnRefresh * 60UL );
+ rDoc.GetLinkManager()->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile,
+ maURL, &rFilterName, &maTables );
+ }
+}
+
+XclImpWebQueryBuffer::XclImpWebQueryBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpWebQueryBuffer::ReadQsi( XclImpStream& rStrm )
+{
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ rStrm.Ignore( 10 );
+ OUString aXclName( rStrm.ReadUniString() );
+
+ // #i64794# Excel replaces spaces with underscores
+ aXclName = aXclName.replaceAll( " ", "_" );
+
+ // find the defined name used in Calc
+ if( const XclImpName* pName = GetNameManager().FindName( aXclName, GetCurrScTab() ) )
+ {
+ if( const ScRangeData* pRangeData = pName->GetScRangeData() )
+ {
+ ScRange aRange;
+ if( pRangeData->IsReference( aRange ) )
+ maWQList.emplace_back( aRange );
+ }
+ }
+ }
+ else
+ {
+ DBG_ERROR_BIFF();
+ }
+}
+
+void XclImpWebQueryBuffer::ReadParamqry( XclImpStream& rStrm )
+{
+ if (!maWQList.empty())
+ maWQList.back().ReadParamqry( rStrm );
+}
+
+void XclImpWebQueryBuffer::ReadWqstring( XclImpStream& rStrm )
+{
+ if (!maWQList.empty())
+ maWQList.back().ReadWqstring( rStrm );
+}
+
+void XclImpWebQueryBuffer::ReadWqsettings( XclImpStream& rStrm )
+{
+ if (!maWQList.empty())
+ maWQList.back().ReadWqsettings( rStrm );
+}
+
+void XclImpWebQueryBuffer::ReadWqtables( XclImpStream& rStrm )
+{
+ if (!maWQList.empty())
+ maWQList.back().ReadWqtables( rStrm );
+}
+
+void XclImpWebQueryBuffer::Apply()
+{
+ ScDocument& rDoc = GetDoc();
+ for( auto& rQuery : maWQList )
+ rQuery.Apply( rDoc, EXC_WEBQRY_FILTER );
+}
+
+// Decryption =================================================================
+
+namespace {
+
+XclImpDecrypterRef lclReadFilepass5( XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xDecr;
+ OSL_ENSURE( rStrm.GetRecLeft() == 4, "lclReadFilepass5 - wrong record size" );
+ if( rStrm.GetRecLeft() == 4 )
+ {
+ sal_uInt16 nKey(0), nHash(0);
+ nKey = rStrm.ReaduInt16();
+ nHash = rStrm.ReaduInt16();
+ xDecr = std::make_shared<XclImpBiff5Decrypter>( nKey, nHash );
+ }
+ return xDecr;
+}
+
+XclImpDecrypterRef lclReadFilepass8_Standard( XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xDecr;
+ OSL_ENSURE( rStrm.GetRecLeft() == 48, "lclReadFilepass8 - wrong record size" );
+ if( rStrm.GetRecLeft() == 48 )
+ {
+ std::vector<sal_uInt8> aSalt(16);
+ std::vector<sal_uInt8> aVerifier(16);
+ std::vector<sal_uInt8> aVerifierHash(16);
+ rStrm.Read(aSalt.data(), 16);
+ rStrm.Read(aVerifier.data(), 16);
+ rStrm.Read(aVerifierHash.data(), 16);
+ xDecr = std::make_shared<XclImpBiff8StdDecrypter>(std::move(aSalt), std::move(aVerifier), std::move(aVerifierHash));
+ }
+ return xDecr;
+}
+
+XclImpDecrypterRef lclReadFilepass8_Strong(XclImpStream& rStream)
+{
+ //It is possible there are other variants in existence but these
+ //are the defaults I get with Excel 2013
+ XclImpDecrypterRef xDecr;
+
+ msfilter::RC4EncryptionInfo info;
+
+ info.header.flags = rStream.ReaduInt32();
+ if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
+ return xDecr;
+
+ sal_uInt32 nHeaderSize = rStream.ReaduInt32();
+ sal_uInt32 actualHeaderSize = sizeof(info.header);
+
+ if( nHeaderSize < actualHeaderSize )
+ return xDecr;
+
+ info.header.flags = rStream.ReaduInt32();
+ info.header.sizeExtra = rStream.ReaduInt32();
+ info.header.algId = rStream.ReaduInt32();
+ info.header.algIdHash = rStream.ReaduInt32();
+ info.header.keyBits = rStream.ReaduInt32();
+ info.header.providedType = rStream.ReaduInt32();
+ info.header.reserved1 = rStream.ReaduInt32();
+ info.header.reserved2 = rStream.ReaduInt32();
+
+ rStream.Ignore(nHeaderSize - actualHeaderSize);
+
+ info.verifier.saltSize = rStream.ReaduInt32();
+ if (info.verifier.saltSize != msfilter::SALT_LENGTH)
+ return xDecr;
+ rStream.Read(&info.verifier.salt, sizeof(info.verifier.salt));
+ rStream.Read(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
+
+ info.verifier.encryptedVerifierHashSize = rStream.ReaduInt32();
+ if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1)
+ return xDecr;
+ rStream.Read(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize);
+
+ // check flags and algorithm IDs, required are AES128 and SHA-1
+ if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
+ return xDecr;
+
+ if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
+ return xDecr;
+
+ if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4)
+ return xDecr;
+
+ // hash algorithm ID 0 defaults to SHA-1 too
+ if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
+ return xDecr;
+
+ xDecr = std::make_shared<XclImpBiff8CryptoAPIDecrypter>(
+ std::vector<sal_uInt8>(info.verifier.salt,
+ info.verifier.salt + SAL_N_ELEMENTS(info.verifier.salt)),
+ std::vector<sal_uInt8>(info.verifier.encryptedVerifier,
+ info.verifier.encryptedVerifier + SAL_N_ELEMENTS(info.verifier.encryptedVerifier)),
+ std::vector<sal_uInt8>(info.verifier.encryptedVerifierHash,
+ info.verifier.encryptedVerifierHash + SAL_N_ELEMENTS(info.verifier.encryptedVerifierHash)));
+
+ return xDecr;
+}
+
+XclImpDecrypterRef lclReadFilepass8( XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xDecr;
+
+ sal_uInt16 nMode = rStrm.ReaduInt16();
+ switch( nMode )
+ {
+ case EXC_FILEPASS_BIFF5:
+ xDecr = lclReadFilepass5( rStrm );
+ break;
+
+ case EXC_FILEPASS_BIFF8:
+ {
+ sal_uInt32 nVersion = rStrm.ReaduInt32();
+ if (nVersion == msfilter::VERSION_INFO_1997_FORMAT)
+ {
+ //A Version structure where Version.vMajor MUST be 0x0001,
+ //and Version.vMinor MUST be 0x0001.
+ xDecr = lclReadFilepass8_Standard(rStrm);
+ }
+ else if (nVersion == msfilter::VERSION_INFO_2007_FORMAT ||
+ nVersion == msfilter::VERSION_INFO_2007_FORMAT_SP2)
+ {
+ //Version.vMajor MUST be 0x0002, 0x0003 or 0x0004 and
+ //Version.vMinor MUST be 0x0002.
+ xDecr = lclReadFilepass8_Strong(rStrm);
+ }
+ else
+ OSL_FAIL("lclReadFilepass8 - unknown BIFF8 encryption sub mode");
+ }
+ break;
+
+ default:
+ OSL_FAIL( "lclReadFilepass8 - unknown encryption mode" );
+ }
+
+ return xDecr;
+}
+
+} // namespace
+
+const ErrCode& XclImpDecryptHelper::ReadFilepass( XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xDecr;
+ rStrm.DisableDecryption();
+
+ // read the FILEPASS record and create a new decrypter object
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5: xDecr = lclReadFilepass5( rStrm ); break;
+ case EXC_BIFF8: xDecr = lclReadFilepass8( rStrm ); break;
+ default: DBG_ERROR_BIFF();
+ };
+
+ // set decrypter at import stream
+ rStrm.SetDecrypter( xDecr );
+
+ // request and verify a password (decrypter implements IDocPasswordVerifier)
+ if( xDecr )
+ rStrm.GetRoot().RequestEncryptionData( *xDecr );
+
+ // return error code (success, wrong password, etc.)
+ return xDecr ? xDecr->GetError() : EXC_ENCR_ERROR_UNSUPP_CRYPT;
+}
+
+// Document protection ========================================================
+
+XclImpDocProtectBuffer::XclImpDocProtectBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mnPassHash(0x0000),
+ mbDocProtect(false),
+ mbWinProtect(false)
+{
+}
+
+void XclImpDocProtectBuffer::ReadDocProtect( XclImpStream& rStrm )
+{
+ mbDocProtect = rStrm.ReaduInt16() != 0;
+}
+
+void XclImpDocProtectBuffer::ReadWinProtect( XclImpStream& rStrm )
+{
+ mbWinProtect = rStrm.ReaduInt16() != 0;
+}
+
+void XclImpDocProtectBuffer::ReadPasswordHash( XclImpStream& rStrm )
+{
+ rStrm.EnableDecryption();
+ mnPassHash = rStrm.ReaduInt16();
+}
+
+void XclImpDocProtectBuffer::Apply() const
+{
+ if (!mbDocProtect && !mbWinProtect)
+ // Excel requires either the structure or windows protection is set.
+ // If neither is set then the document is not protected at all.
+ return;
+
+ unique_ptr<ScDocProtection> pProtect(new ScDocProtection);
+ pProtect->setProtected(true);
+
+ if (mnPassHash)
+ {
+ // 16-bit password hash.
+ Sequence<sal_Int8> aPass{sal_Int8(mnPassHash >> 8), sal_Int8(mnPassHash & 0xFF)};
+ pProtect->setPasswordHash(aPass, PASSHASH_XL);
+ }
+
+ // document protection options
+ pProtect->setOption(ScDocProtection::STRUCTURE, mbDocProtect);
+ pProtect->setOption(ScDocProtection::WINDOWS, mbWinProtect);
+
+ GetDoc().SetDocProtection(pProtect.get());
+}
+
+// Sheet Protection ===========================================================
+
+XclImpSheetProtectBuffer::Sheet::Sheet() :
+ mbProtected(false),
+ mnPasswordHash(0x0000),
+ mnOptions(0x4400)
+{
+}
+
+XclImpSheetProtectBuffer::Sheet::Sheet(const Sheet& r) :
+ mbProtected(r.mbProtected),
+ mnPasswordHash(r.mnPasswordHash),
+ mnOptions(r.mnOptions)
+{
+}
+
+XclImpSheetProtectBuffer::XclImpSheetProtectBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpSheetProtectBuffer::ReadProtect( XclImpStream& rStrm, SCTAB nTab )
+{
+ if ( rStrm.ReaduInt16() )
+ {
+ Sheet* pSheet = GetSheetItem(nTab);
+ if (pSheet)
+ pSheet->mbProtected = true;
+ }
+}
+
+void XclImpSheetProtectBuffer::ReadOptions( XclImpStream& rStrm, SCTAB nTab )
+{
+ // The flag size specifies the size of bytes that follows that stores
+ // feature data. If -1 it depends on the feature type imported earlier.
+ // For enhanced protection data, the size is always 4. For the most xls
+ // documents out there this value is almost always -1.
+ sal_Int32 nFlagSize = rStrm.ReadInt32();
+ if (nFlagSize != -1)
+ return;
+
+ // There are actually 4 bytes to read, but the upper 2 bytes currently
+ // don't store any bits.
+ sal_uInt16 nOptions = rStrm.ReaduInt16();
+
+ Sheet* pSheet = GetSheetItem(nTab);
+ if (pSheet)
+ pSheet->mnOptions = nOptions;
+}
+
+void XclImpSheetProtectBuffer::AppendEnhancedProtection( const ScEnhancedProtection & rProt, SCTAB nTab )
+{
+ Sheet* pSheet = GetSheetItem(nTab);
+ if (pSheet)
+ pSheet->maEnhancedProtections.push_back( rProt);
+}
+
+void XclImpSheetProtectBuffer::ReadPasswordHash( XclImpStream& rStrm, SCTAB nTab )
+{
+ sal_uInt16 nHash = rStrm.ReaduInt16();
+ Sheet* pSheet = GetSheetItem(nTab);
+ if (pSheet)
+ pSheet->mnPasswordHash = nHash;
+}
+
+void XclImpSheetProtectBuffer::Apply() const
+{
+ for (const auto& [rTab, rSheet] : maProtectedSheets)
+ {
+ if (!rSheet.mbProtected)
+ // This sheet is (for whatever reason) not protected.
+ continue;
+
+ unique_ptr<ScTableProtection> pProtect(new ScTableProtection);
+ pProtect->setProtected(true);
+
+ // 16-bit hash password
+ const sal_uInt16 nHash = rSheet.mnPasswordHash;
+ if (nHash)
+ {
+ Sequence<sal_Int8> aPass{sal_Int8(nHash >> 8), sal_Int8(nHash & 0xFF)};
+ pProtect->setPasswordHash(aPass, PASSHASH_XL);
+ }
+
+ // sheet protection options
+ const sal_uInt16 nOptions = rSheet.mnOptions;
+ pProtect->setOption( ScTableProtection::OBJECTS, (nOptions & 0x0001) );
+ pProtect->setOption( ScTableProtection::SCENARIOS, (nOptions & 0x0002) );
+ pProtect->setOption( ScTableProtection::FORMAT_CELLS, (nOptions & 0x0004) );
+ pProtect->setOption( ScTableProtection::FORMAT_COLUMNS, (nOptions & 0x0008) );
+ pProtect->setOption( ScTableProtection::FORMAT_ROWS, (nOptions & 0x0010) );
+ pProtect->setOption( ScTableProtection::INSERT_COLUMNS, (nOptions & 0x0020) );
+ pProtect->setOption( ScTableProtection::INSERT_ROWS, (nOptions & 0x0040) );
+ pProtect->setOption( ScTableProtection::INSERT_HYPERLINKS, (nOptions & 0x0080) );
+ pProtect->setOption( ScTableProtection::DELETE_COLUMNS, (nOptions & 0x0100) );
+ pProtect->setOption( ScTableProtection::DELETE_ROWS, (nOptions & 0x0200) );
+ pProtect->setOption( ScTableProtection::SELECT_LOCKED_CELLS, (nOptions & 0x0400) );
+ pProtect->setOption( ScTableProtection::SORT, (nOptions & 0x0800) );
+ pProtect->setOption( ScTableProtection::AUTOFILTER, (nOptions & 0x1000) );
+ pProtect->setOption( ScTableProtection::PIVOT_TABLES, (nOptions & 0x2000) );
+ pProtect->setOption( ScTableProtection::SELECT_UNLOCKED_CELLS, (nOptions & 0x4000) );
+
+ // Enhanced protection containing editable ranges and permissions.
+ pProtect->setEnhancedProtection( std::vector(rSheet.maEnhancedProtections) );
+
+ // all done. now commit.
+ GetDoc().SetTabProtection(rTab, pProtect.get());
+ }
+}
+
+XclImpSheetProtectBuffer::Sheet* XclImpSheetProtectBuffer::GetSheetItem( SCTAB nTab )
+{
+ ProtectedSheetMap::iterator itr = maProtectedSheets.find(nTab);
+ if (itr == maProtectedSheets.end())
+ {
+ // new sheet
+ if ( !maProtectedSheets.emplace( nTab, Sheet() ).second )
+ return nullptr;
+
+ itr = maProtectedSheets.find(nTab);
+ }
+
+ return &itr->second;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiescher.cxx b/sc/source/filter/excel/xiescher.cxx
new file mode 100644
index 000000000..1de9da95d
--- /dev/null
+++ b/sc/source/filter/excel/xiescher.cxx
@@ -0,0 +1,4453 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xiescher.hxx>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/awt/PushButtonType.hpp>
+#include <com/sun/star/awt/ScrollBarOrientation.hpp>
+#include <com/sun/star/awt/VisualEffect.hpp>
+#include <com/sun/star/style/VerticalAlignment.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/XFormsSupplier.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XValueBinding.hpp>
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/form/binding/XListEntrySource.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <sfx2/objsh.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/fltrcfg.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/wmf.hxx>
+#include <comphelper/classids.hxx>
+#include <comphelper/documentinfo.hxx>
+#include <o3tl/safeint.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <sal/log.hxx>
+
+#include <svx/svdopath.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdpage.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/svditer.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnedcit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstcit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/sdasitm.hxx>
+#include <svx/sdshcitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdsxyitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtditm.hxx>
+
+#include <editeng/eeitem.hxx>
+#include <svx/xflclit.hxx>
+#include <sal/macros.h>
+#include <editeng/adjustitem.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlinjoit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xbitmap.hxx>
+#include <svtools/embedhlp.hxx>
+#include <sot/storage.hxx>
+
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <userdat.hxx>
+#include <unonames.hxx>
+#include <convuno.hxx>
+#include <postit.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <fprogressbar.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xiformula.hxx>
+#include <xilink.hxx>
+#include <xistyle.hxx>
+#include <xipage.hxx>
+#include <xichart.hxx>
+#include <xicontent.hxx>
+#include <scextopt.hxx>
+
+#include <namebuff.hxx>
+#include <sfx2/docfile.hxx>
+#include <memory>
+#include <numeric>
+#include <string_view>
+#include <utility>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::beans::NamedValue;
+using ::com::sun::star::lang::XMultiServiceFactory;
+using ::com::sun::star::container::XIndexContainer;
+using ::com::sun::star::container::XNameContainer;
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::embed::XEmbeddedObject;
+using ::com::sun::star::embed::XEmbedPersist;
+using ::com::sun::star::drawing::XControlShape;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::form::XFormComponent;
+using ::com::sun::star::form::XFormsSupplier;
+using ::com::sun::star::form::binding::XBindableValue;
+using ::com::sun::star::form::binding::XValueBinding;
+using ::com::sun::star::form::binding::XListEntrySink;
+using ::com::sun::star::form::binding::XListEntrySource;
+using ::com::sun::star::script::ScriptEventDescriptor;
+using ::com::sun::star::script::XEventAttacherManager;
+using ::com::sun::star::table::CellAddress;
+using ::com::sun::star::table::CellRangeAddress;
+
+// Drawing objects ============================================================
+
+XclImpDrawObjBase::XclImpDrawObjBase( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mnObjId( EXC_OBJ_INVALID_ID ),
+ mnTab( 0 ),
+ mnObjType( EXC_OBJTYPE_UNKNOWN ),
+ mnDffShapeId( 0 ),
+ mnDffFlags( ShapeFlag::NONE ),
+ mbHasAnchor( false ),
+ mbHidden( false ),
+ mbVisible( true ),
+ mbPrintable( true ),
+ mbAreaObj( false ),
+ mbAutoMargin( true ),
+ mbSimpleMacro( true ),
+ mbProcessSdr( true ),
+ mbInsertSdr( true ),
+ mbCustomDff( false ),
+ mbNotifyMacroEventRead( false )
+{
+}
+
+XclImpDrawObjBase::~XclImpDrawObjBase()
+{
+}
+
+XclImpDrawObjRef XclImpDrawObjBase::ReadObj3( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ if( rStrm.GetRecLeft() >= 30 )
+ {
+ sal_uInt16 nObjType;
+ rStrm.Ignore( 4 );
+ nObjType = rStrm.ReaduInt16();
+ switch( nObjType )
+ {
+ case EXC_OBJTYPE_GROUP: xDrawObj= std::make_shared<XclImpGroupObj>( rRoot ); break;
+ case EXC_OBJTYPE_LINE: xDrawObj= std::make_shared<XclImpLineObj>( rRoot ); break;
+ case EXC_OBJTYPE_RECTANGLE: xDrawObj= std::make_shared<XclImpRectObj>( rRoot ); break;
+ case EXC_OBJTYPE_OVAL: xDrawObj= std::make_shared<XclImpOvalObj>( rRoot ); break;
+ case EXC_OBJTYPE_ARC: xDrawObj= std::make_shared<XclImpArcObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHART: xDrawObj= std::make_shared<XclImpChartObj>( rRoot ); break;
+ case EXC_OBJTYPE_TEXT: xDrawObj= std::make_shared<XclImpTextObj>( rRoot ); break;
+ case EXC_OBJTYPE_BUTTON: xDrawObj= std::make_shared<XclImpButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_PICTURE: xDrawObj= std::make_shared<XclImpPictureObj>( rRoot ); break;
+ default:
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj3 - unknown object type 0x" << std::hex << nObjType );
+ rRoot.GetTracer().TraceUnsupportedObjects();
+ }
+ }
+
+ if (!xDrawObj)
+ {
+ xDrawObj = std::make_shared<XclImpPhObj>(rRoot);
+ }
+
+ xDrawObj->mnTab = rRoot.GetCurrScTab();
+ xDrawObj->ImplReadObj3( rStrm );
+ return xDrawObj;
+}
+
+XclImpDrawObjRef XclImpDrawObjBase::ReadObj4( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ if( rStrm.GetRecLeft() >= 30 )
+ {
+ sal_uInt16 nObjType;
+ rStrm.Ignore( 4 );
+ nObjType = rStrm.ReaduInt16();
+ switch( nObjType )
+ {
+ case EXC_OBJTYPE_GROUP: xDrawObj = std::make_shared<XclImpGroupObj>( rRoot ); break;
+ case EXC_OBJTYPE_LINE: xDrawObj = std::make_shared<XclImpLineObj>( rRoot ); break;
+ case EXC_OBJTYPE_RECTANGLE: xDrawObj = std::make_shared<XclImpRectObj>( rRoot ); break;
+ case EXC_OBJTYPE_OVAL: xDrawObj = std::make_shared<XclImpOvalObj>( rRoot ); break;
+ case EXC_OBJTYPE_ARC: xDrawObj = std::make_shared<XclImpArcObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHART: xDrawObj = std::make_shared<XclImpChartObj>( rRoot ); break;
+ case EXC_OBJTYPE_TEXT: xDrawObj = std::make_shared<XclImpTextObj>( rRoot ); break;
+ case EXC_OBJTYPE_BUTTON: xDrawObj = std::make_shared<XclImpButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_PICTURE: xDrawObj = std::make_shared<XclImpPictureObj>( rRoot ); break;
+ case EXC_OBJTYPE_POLYGON: xDrawObj = std::make_shared<XclImpPolygonObj>( rRoot ); break;
+ default:
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj4 - unknown object type 0x" << std::hex << nObjType );
+ rRoot.GetTracer().TraceUnsupportedObjects();
+ }
+ }
+
+ if (!xDrawObj)
+ {
+ xDrawObj = std::make_shared<XclImpPhObj>(rRoot);
+ }
+
+ xDrawObj->mnTab = rRoot.GetCurrScTab();
+ xDrawObj->ImplReadObj4( rStrm );
+ return xDrawObj;
+}
+
+XclImpDrawObjRef XclImpDrawObjBase::ReadObj5( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ if( rStrm.GetRecLeft() >= 34 )
+ {
+ sal_uInt16 nObjType(EXC_OBJTYPE_UNKNOWN);
+ rStrm.Ignore( 4 );
+ nObjType = rStrm.ReaduInt16();
+ switch( nObjType )
+ {
+ case EXC_OBJTYPE_GROUP: xDrawObj = std::make_shared<XclImpGroupObj>( rRoot ); break;
+ case EXC_OBJTYPE_LINE: xDrawObj = std::make_shared<XclImpLineObj>( rRoot ); break;
+ case EXC_OBJTYPE_RECTANGLE: xDrawObj = std::make_shared<XclImpRectObj>( rRoot ); break;
+ case EXC_OBJTYPE_OVAL: xDrawObj = std::make_shared<XclImpOvalObj>( rRoot ); break;
+ case EXC_OBJTYPE_ARC: xDrawObj = std::make_shared<XclImpArcObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHART: xDrawObj = std::make_shared<XclImpChartObj>( rRoot ); break;
+ case EXC_OBJTYPE_TEXT: xDrawObj = std::make_shared<XclImpTextObj>( rRoot ); break;
+ case EXC_OBJTYPE_BUTTON: xDrawObj = std::make_shared<XclImpButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_PICTURE: xDrawObj = std::make_shared<XclImpPictureObj>( rRoot ); break;
+ case EXC_OBJTYPE_POLYGON: xDrawObj = std::make_shared<XclImpPolygonObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHECKBOX: xDrawObj = std::make_shared<XclImpCheckBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_OPTIONBUTTON: xDrawObj = std::make_shared<XclImpOptionButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_EDIT: xDrawObj = std::make_shared<XclImpEditObj>( rRoot ); break;
+ case EXC_OBJTYPE_LABEL: xDrawObj = std::make_shared<XclImpLabelObj>( rRoot ); break;
+ case EXC_OBJTYPE_DIALOG: xDrawObj = std::make_shared<XclImpDialogObj>( rRoot ); break;
+ case EXC_OBJTYPE_SPIN: xDrawObj = std::make_shared<XclImpSpinButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_SCROLLBAR: xDrawObj = std::make_shared<XclImpScrollBarObj>( rRoot ); break;
+ case EXC_OBJTYPE_LISTBOX: xDrawObj = std::make_shared<XclImpListBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_GROUPBOX: xDrawObj = std::make_shared<XclImpGroupBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_DROPDOWN: xDrawObj = std::make_shared<XclImpDropDownObj>( rRoot ); break;
+ default:
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj5 - unknown object type 0x" << std::hex << nObjType );
+ rRoot.GetTracer().TraceUnsupportedObjects();
+ xDrawObj = std::make_shared<XclImpPhObj>( rRoot );
+ }
+ }
+
+ OSL_ENSURE(xDrawObj, "object import failed");
+
+ if (xDrawObj)
+ {
+ xDrawObj->mnTab = rRoot.GetCurrScTab();
+ xDrawObj->ImplReadObj5( rStrm );
+ }
+ return xDrawObj;
+}
+
+XclImpDrawObjRef XclImpDrawObjBase::ReadObj8( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ if( rStrm.GetRecLeft() >= 10 )
+ {
+ sal_uInt16 nSubRecId(0), nSubRecSize(0), nObjType(0);
+ nSubRecId = rStrm.ReaduInt16();
+ nSubRecSize = rStrm.ReaduInt16();
+ nObjType = rStrm.ReaduInt16();
+ OSL_ENSURE( nSubRecId == EXC_ID_OBJCMO, "XclImpDrawObjBase::ReadObj8 - OBJCMO subrecord expected" );
+ if( (nSubRecId == EXC_ID_OBJCMO) && (nSubRecSize >= 6) )
+ {
+ switch( nObjType )
+ {
+ // in BIFF8, all simple objects support text
+ case EXC_OBJTYPE_LINE:
+ case EXC_OBJTYPE_ARC:
+ xDrawObj = std::make_shared<XclImpTextObj>( rRoot );
+ // lines and arcs may be 2-dimensional
+ xDrawObj->SetAreaObj( false );
+ break;
+
+ // in BIFF8, all simple objects support text
+ case EXC_OBJTYPE_RECTANGLE:
+ case EXC_OBJTYPE_OVAL:
+ case EXC_OBJTYPE_POLYGON:
+ case EXC_OBJTYPE_DRAWING:
+ case EXC_OBJTYPE_TEXT:
+ xDrawObj = std::make_shared<XclImpTextObj>( rRoot );
+ break;
+
+ case EXC_OBJTYPE_GROUP: xDrawObj = std::make_shared<XclImpGroupObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHART: xDrawObj = std::make_shared<XclImpChartObj>( rRoot ); break;
+ case EXC_OBJTYPE_BUTTON: xDrawObj = std::make_shared<XclImpButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_PICTURE: xDrawObj = std::make_shared<XclImpPictureObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHECKBOX: xDrawObj = std::make_shared<XclImpCheckBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_OPTIONBUTTON: xDrawObj = std::make_shared<XclImpOptionButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_EDIT: xDrawObj = std::make_shared<XclImpEditObj>( rRoot ); break;
+ case EXC_OBJTYPE_LABEL: xDrawObj = std::make_shared<XclImpLabelObj>( rRoot ); break;
+ case EXC_OBJTYPE_DIALOG: xDrawObj = std::make_shared<XclImpDialogObj>( rRoot ); break;
+ case EXC_OBJTYPE_SPIN: xDrawObj = std::make_shared<XclImpSpinButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_SCROLLBAR: xDrawObj = std::make_shared<XclImpScrollBarObj>( rRoot ); break;
+ case EXC_OBJTYPE_LISTBOX: xDrawObj = std::make_shared<XclImpListBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_GROUPBOX: xDrawObj = std::make_shared<XclImpGroupBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_DROPDOWN: xDrawObj = std::make_shared<XclImpDropDownObj>( rRoot ); break;
+ case EXC_OBJTYPE_NOTE: xDrawObj = std::make_shared<XclImpNoteObj>( rRoot ); break;
+
+ default:
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj8 - unknown object type 0x" << std::hex << nObjType );
+ rRoot.GetTracer().TraceUnsupportedObjects();
+ }
+ }
+ }
+
+ if (!xDrawObj) //ensure placeholder for unknown or broken records
+ {
+ SAL_WARN( "sc.filter", "XclImpDrawObjBase::ReadObj8 import failed, substituting placeholder");
+ xDrawObj = std::make_shared<XclImpPhObj>( rRoot );
+ }
+
+ xDrawObj->mnTab = rRoot.GetCurrScTab();
+ xDrawObj->ImplReadObj8( rStrm );
+ return xDrawObj;
+}
+
+void XclImpDrawObjBase::SetAnchor( const XclObjAnchor& rAnchor )
+{
+ maAnchor = rAnchor;
+ mbHasAnchor = true;
+}
+
+void XclImpDrawObjBase::SetDffData(
+ const DffObjData& rDffObjData, const OUString& rObjName, const OUString& rHyperlink,
+ bool bVisible, bool bAutoMargin )
+{
+ mnDffShapeId = rDffObjData.nShapeId;
+ mnDffFlags = rDffObjData.nSpFlags;
+ maObjName = rObjName;
+ maHyperlink = rHyperlink;
+ mbVisible = bVisible;
+ mbAutoMargin = bAutoMargin;
+}
+
+OUString XclImpDrawObjBase::GetObjName() const
+{
+ /* #i51348# Always return a non-empty name. Create English
+ default names depending on the object type. This is not implemented as
+ virtual functions in derived classes, as class type and object type may
+ not match. */
+ return maObjName.isEmpty() ? GetObjectManager().GetDefaultObjName(*this) : maObjName;
+}
+
+const XclObjAnchor* XclImpDrawObjBase::GetAnchor() const
+{
+ return mbHasAnchor ? &maAnchor : nullptr;
+}
+
+bool XclImpDrawObjBase::IsValidSize( const tools::Rectangle& rAnchorRect ) const
+{
+ // XclObjAnchor rounds up the width, width of 3 is the result of an Excel width of 0
+ return mbAreaObj ?
+ ((rAnchorRect.GetWidth() > 3) && (rAnchorRect.GetHeight() > 1)) :
+ ((rAnchorRect.GetWidth() > 3) || (rAnchorRect.GetHeight() > 1));
+}
+
+ScRange XclImpDrawObjBase::GetUsedArea( SCTAB nScTab ) const
+{
+ ScRange aScUsedArea( ScAddress::INITIALIZE_INVALID );
+ // #i44077# object inserted -> update used area for OLE object import
+ if( mbHasAnchor && GetAddressConverter().ConvertRange( aScUsedArea, maAnchor, nScTab, nScTab, false ) )
+ {
+ // reduce range, if object ends directly on borders between two columns or rows
+ if( (maAnchor.mnRX == 0) && (aScUsedArea.aStart.Col() < aScUsedArea.aEnd.Col()) )
+ aScUsedArea.aEnd.IncCol( -1 );
+ if( (maAnchor.mnBY == 0) && (aScUsedArea.aStart.Row() < aScUsedArea.aEnd.Row()) )
+ aScUsedArea.aEnd.IncRow( -1 );
+ }
+ return aScUsedArea;
+}
+
+std::size_t XclImpDrawObjBase::GetProgressSize() const
+{
+ return DoGetProgressSize();
+}
+
+SdrObjectUniquePtr XclImpDrawObjBase::CreateSdrObject( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect, bool bIsDff ) const
+{
+ SdrObjectUniquePtr xSdrObj;
+ if( bIsDff && !mbCustomDff )
+ {
+ rDffConv.Progress( GetProgressSize() );
+ }
+ else
+ {
+ xSdrObj = DoCreateSdrObj( rDffConv, rAnchorRect );
+
+ //added for exporting OCX control
+ /* mnObjType value set should be as below table:
+ 0x0000 Group 0x0001 Line
+ 0x0002 Rectangle 0x0003 Oval
+ 0x0004 Arc 0x0005 Chart
+ 0x0006 Text 0x0009 Polygon
+ +-----------------------------------------------------+
+ OCX ==>| 0x0008 Picture |
+ +-----------------------------------------------------+
+ | 0x0007 Button |
+ | 0x000B Checkbox 0x000C Radio button |
+ | 0x000D Edit box 0x000E Label |
+ TBX ==> | 0x000F Dialog box 0x0010 Spin control |
+ | 0x0011 Scrollbar 0x0012 List |
+ | 0x0013 Group box 0x0014 Dropdown list |
+ +-----------------------------------------------------+
+ 0x0019 Note 0x001E OfficeArt object
+ */
+ if( xSdrObj && xSdrObj->IsUnoObj() &&
+ ( (mnObjType < 25 && mnObjType > 10) || mnObjType == 7 || mnObjType == 8 ) )
+ {
+ SdrUnoObj* pSdrUnoObj = dynamic_cast< SdrUnoObj* >( xSdrObj.get() );
+ if( pSdrUnoObj != nullptr )
+ {
+ const Reference< XControlModel >& xCtrlModel = pSdrUnoObj->GetUnoControlModel();
+ Reference< XPropertySet > xPropSet(xCtrlModel,UNO_QUERY);
+ static constexpr OUStringLiteral sPropertyName(u"ControlTypeinMSO");
+
+ enum { eCreateFromOffice = 0, eCreateFromMSTBXControl, eCreateFromMSOCXControl };
+
+ if( mnObjType == 7 || (mnObjType < 25 && mnObjType > 10) )//TBX
+ {
+ try
+ {
+ //Need summary type for export. Detail type(checkbox, button ...) has been contained by mnObjType
+ const sal_Int16 nTBXControlType = eCreateFromMSTBXControl ;
+ xPropSet->setPropertyValue(sPropertyName, Any(nTBXControlType));
+ }
+ catch(const Exception&)
+ {
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::CreateSdrObject, this control can't be set the property ControlTypeinMSO!");
+ }
+ }
+ if( mnObjType == 8 )//OCX
+ {
+ //Need summary type for export
+ static constexpr OUStringLiteral sObjIdPropertyName(u"ObjIDinMSO");
+ const XclImpPictureObj* const pObj = dynamic_cast< const XclImpPictureObj* const >(this);
+ if( pObj != nullptr && pObj->IsOcxControl() )
+ {
+ try
+ {
+ const sal_Int16 nOCXControlType = eCreateFromMSOCXControl;
+ xPropSet->setPropertyValue(sPropertyName, Any(nOCXControlType));
+ //Detail type(checkbox, button ...)
+ xPropSet->setPropertyValue(sObjIdPropertyName, Any(sal_uInt16(mnObjId)));
+ }
+ catch(const Exception&)
+ {
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::CreateSdrObject, this control can't be set the property ObjIDinMSO!");
+ }
+ }
+ }
+
+ }
+ }
+ }
+ return xSdrObj;
+}
+
+void XclImpDrawObjBase::NotifyMacroEventRead()
+{
+ if (mbNotifyMacroEventRead)
+ return;
+ SfxObjectShell* pDocShell = GetDocShell();
+ if (!pDocShell)
+ return;
+ comphelper::DocumentInfo::notifyMacroEventRead(pDocShell->GetModel());
+ mbNotifyMacroEventRead = true;
+}
+
+void XclImpDrawObjBase::PreProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj )
+{
+ // default: front layer, derived classes may have to set other layer in DoPreProcessSdrObj()
+ rSdrObj.NbcSetLayer( SC_LAYER_FRONT );
+
+ // set object name (GetObjName() will always return a non-empty name)
+ rSdrObj.SetName( GetObjName() );
+
+ // #i39167# full width for all objects regardless of horizontal alignment
+ rSdrObj.SetMergedItem( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_BLOCK ) );
+
+ // automatic text margin
+ if( mbAutoMargin )
+ {
+ sal_Int32 nMargin = rDffConv.GetDefaultTextMargin();
+ rSdrObj.SetMergedItem( makeSdrTextLeftDistItem( nMargin ) );
+ rSdrObj.SetMergedItem( makeSdrTextRightDistItem( nMargin ) );
+ rSdrObj.SetMergedItem( makeSdrTextUpperDistItem( nMargin ) );
+ rSdrObj.SetMergedItem( makeSdrTextLowerDistItem( nMargin ) );
+ }
+
+ // macro and hyperlink
+ // removed oracle/sun check for mbSimpleMacro ( no idea what its for )
+ if (!maMacroName.isEmpty())
+ {
+ if( ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( &rSdrObj, true ) )
+ {
+ OUString sMacro = XclTools::GetSbMacroUrl(maMacroName, GetDocShell());
+ if (!sMacro.isEmpty())
+ NotifyMacroEventRead();
+ pInfo->SetMacro(sMacro);
+ }
+ }
+ if (!maHyperlink.isEmpty())
+ rSdrObj.setHyperlink(maHyperlink);
+
+ // call virtual function for object type specific processing
+ DoPreProcessSdrObj( rDffConv, rSdrObj );
+}
+
+void XclImpDrawObjBase::PostProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ // call virtual function for object type specific processing
+ DoPostProcessSdrObj( rDffConv, rSdrObj );
+}
+
+// protected ------------------------------------------------------------------
+
+void XclImpDrawObjBase::ReadName5( XclImpStream& rStrm, sal_uInt16 nNameLen )
+{
+ maObjName.clear();
+ if( nNameLen > 0 )
+ {
+ // name length field is repeated before the name
+ maObjName = rStrm.ReadByteString( false );
+ // skip padding byte for word boundaries
+ if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 );
+ }
+}
+
+void XclImpDrawObjBase::ReadMacro3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ maMacroName.clear();
+ rStrm.Ignore( nMacroSize );
+ // skip padding byte for word boundaries, not contained in nMacroSize
+ if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 );
+}
+
+void XclImpDrawObjBase::ReadMacro4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ maMacroName.clear();
+ rStrm.Ignore( nMacroSize );
+}
+
+void XclImpDrawObjBase::ReadMacro5( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ maMacroName.clear();
+ rStrm.Ignore( nMacroSize );
+}
+
+void XclImpDrawObjBase::ReadMacro8( XclImpStream& rStrm )
+{
+ maMacroName.clear();
+ if( rStrm.GetRecLeft() <= 6 )
+ return;
+
+ // macro is stored in a tNameXR token containing a link to a defined name
+ sal_uInt16 nFmlaSize;
+ nFmlaSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ OSL_ENSURE( nFmlaSize == 7, "XclImpDrawObjBase::ReadMacro - unexpected formula size" );
+ if( nFmlaSize == 7 )
+ {
+ sal_uInt8 nTokenId;
+ sal_uInt16 nExtSheet, nExtName;
+ nTokenId = rStrm.ReaduInt8();
+ nExtSheet = rStrm.ReaduInt16();
+ nExtName = rStrm.ReaduInt16();
+ OSL_ENSURE( nTokenId == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ),
+ "XclImpDrawObjBase::ReadMacro - tNameXR token expected" );
+ if( nTokenId == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ) )
+ maMacroName = GetLinkManager().GetMacroName( nExtSheet, nExtName );
+ }
+}
+
+void XclImpDrawObjBase::ConvertLineStyle( SdrObject& rSdrObj, const XclObjLineData& rLineData ) const
+{
+ if( rLineData.IsAuto() )
+ {
+ XclObjLineData aAutoData;
+ aAutoData.mnAuto = 0;
+ ConvertLineStyle( rSdrObj, aAutoData );
+ }
+ else
+ {
+ tools::Long nLineWidth = 35 * ::std::min( rLineData.mnWidth, EXC_OBJ_LINE_THICK );
+ rSdrObj.SetMergedItem( XLineWidthItem( nLineWidth ) );
+ rSdrObj.SetMergedItem( XLineColorItem( OUString(), GetPalette().GetColor( rLineData.mnColorIdx ) ) );
+ rSdrObj.SetMergedItem( XLineJointItem( css::drawing::LineJoint_MITER ) );
+
+ sal_uLong nDotLen = ::std::max< sal_uLong >( 70 * rLineData.mnWidth, 35 );
+ sal_uLong nDashLen = 3 * nDotLen;
+ sal_uLong nDist = 2 * nDotLen;
+
+ switch( rLineData.mnStyle )
+ {
+ default:
+ case EXC_OBJ_LINE_SOLID:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ break;
+ case EXC_OBJ_LINE_DASH:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) );
+ rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 0, nDotLen, 1, nDashLen, nDist ) ) );
+ break;
+ case EXC_OBJ_LINE_DOT:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) );
+ rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 1, nDotLen, 0, nDashLen, nDist ) ) );
+ break;
+ case EXC_OBJ_LINE_DASHDOT:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) );
+ rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 1, nDotLen, 1, nDashLen, nDist ) ) );
+ break;
+ case EXC_OBJ_LINE_DASHDOTDOT:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) );
+ rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 2, nDotLen, 1, nDashLen, nDist ) ) );
+ break;
+ case EXC_OBJ_LINE_MEDTRANS:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ rSdrObj.SetMergedItem( XLineTransparenceItem( 50 ) );
+ break;
+ case EXC_OBJ_LINE_DARKTRANS:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ rSdrObj.SetMergedItem( XLineTransparenceItem( 25 ) );
+ break;
+ case EXC_OBJ_LINE_LIGHTTRANS:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ rSdrObj.SetMergedItem( XLineTransparenceItem( 75 ) );
+ break;
+ case EXC_OBJ_LINE_NONE:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_NONE ) );
+ break;
+ }
+ }
+}
+
+void XclImpDrawObjBase::ConvertFillStyle( SdrObject& rSdrObj, const XclObjFillData& rFillData ) const
+{
+ if( rFillData.IsAuto() )
+ {
+ XclObjFillData aAutoData;
+ aAutoData.mnAuto = 0;
+ ConvertFillStyle( rSdrObj, aAutoData );
+ }
+ else if( rFillData.mnPattern == EXC_PATT_NONE )
+ {
+ rSdrObj.SetMergedItem( XFillStyleItem( drawing::FillStyle_NONE ) );
+ }
+ else
+ {
+ Color aPattColor = GetPalette().GetColor( rFillData.mnPattColorIdx );
+ Color aBackColor = GetPalette().GetColor( rFillData.mnBackColorIdx );
+ if( (rFillData.mnPattern == EXC_PATT_SOLID) || (aPattColor == aBackColor) )
+ {
+ rSdrObj.SetMergedItem( XFillStyleItem( drawing::FillStyle_SOLID ) );
+ rSdrObj.SetMergedItem( XFillColorItem( OUString(), aPattColor ) );
+ }
+ else
+ {
+ static const sal_uInt8 sppnPatterns[][ 8 ] =
+ {
+ { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 },
+ { 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD },
+ { 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22 },
+ { 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00 },
+ { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC },
+ { 0x33, 0x66, 0xCC, 0x99, 0x33, 0x66, 0xCC, 0x99 },
+ { 0xCC, 0x66, 0x33, 0x99, 0xCC, 0x66, 0x33, 0x99 },
+ { 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33 },
+ { 0xCC, 0xFF, 0x33, 0xFF, 0xCC, 0xFF, 0x33, 0xFF },
+ { 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 },
+ { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 },
+ { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 },
+ { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 },
+ { 0xFF, 0x11, 0x11, 0x11, 0xFF, 0x11, 0x11, 0x11 },
+ { 0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11 },
+ { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 },
+ { 0x80, 0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x00 }
+ };
+ const sal_uInt8* const pnPattern = sppnPatterns[std::min<size_t>(rFillData.mnPattern - 2, SAL_N_ELEMENTS(sppnPatterns) - 1)];
+ // create 2-colored 8x8 DIB
+ SvMemoryStream aMemStrm;
+ aMemStrm.WriteUInt32( 12 ).WriteInt16( 8 ).WriteInt16( 8 ).WriteUInt16( 1 ).WriteUInt16( 1 );
+ aMemStrm.WriteUChar( 0xFF ).WriteUChar( 0xFF ).WriteUChar( 0xFF );
+ aMemStrm.WriteUChar( 0x00 ).WriteUChar( 0x00 ).WriteUChar( 0x00 );
+ for( size_t nIdx = 0; nIdx < 8; ++nIdx )
+ aMemStrm.WriteUInt32( pnPattern[ nIdx ] ); // 32-bit little-endian
+ aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ Bitmap aBitmap;
+ (void)ReadDIB(aBitmap, aMemStrm, false);
+
+ XOBitmap aXOBitmap(( BitmapEx(aBitmap) ));
+ aXOBitmap.Bitmap2Array();
+ if( aXOBitmap.GetBackgroundColor() == COL_BLACK )
+ ::std::swap( aPattColor, aBackColor );
+ aXOBitmap.SetPixelColor( aPattColor );
+ aXOBitmap.SetBackgroundColor( aBackColor );
+ aXOBitmap.Array2Bitmap();
+ aBitmap = aXOBitmap.GetBitmap().GetBitmap();
+
+ rSdrObj.SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP));
+ rSdrObj.SetMergedItem(XFillBitmapItem(OUString(), Graphic(BitmapEx(aBitmap))));
+ }
+ }
+}
+
+void XclImpDrawObjBase::ConvertFrameStyle( SdrObject& rSdrObj, sal_uInt16 nFrameFlags ) const
+{
+ if( ::get_flag( nFrameFlags, EXC_OBJ_FRAME_SHADOW ) )
+ {
+ rSdrObj.SetMergedItem( makeSdrShadowItem( true ) );
+ rSdrObj.SetMergedItem( makeSdrShadowXDistItem( 35 ) );
+ rSdrObj.SetMergedItem( makeSdrShadowYDistItem( 35 ) );
+ rSdrObj.SetMergedItem( makeSdrShadowColorItem( GetPalette().GetColor( EXC_COLOR_WINDOWTEXT ) ) );
+ }
+}
+
+Color XclImpDrawObjBase::GetSolidLineColor( const XclObjLineData& rLineData ) const
+{
+ Color aColor( COL_TRANSPARENT );
+ if( rLineData.IsAuto() )
+ {
+ XclObjLineData aAutoData;
+ aAutoData.mnAuto = 0;
+ aColor = GetSolidLineColor( aAutoData );
+ }
+ else if( rLineData.mnStyle != EXC_OBJ_LINE_NONE )
+ {
+ aColor = GetPalette().GetColor( rLineData.mnColorIdx );
+ }
+ return aColor;
+}
+
+Color XclImpDrawObjBase::GetSolidFillColor( const XclObjFillData& rFillData ) const
+{
+ Color aColor( COL_TRANSPARENT );
+ if( rFillData.IsAuto() )
+ {
+ XclObjFillData aAutoData;
+ aAutoData.mnAuto = 0;
+ aColor = GetSolidFillColor( aAutoData );
+ }
+ else if( rFillData.mnPattern != EXC_PATT_NONE )
+ {
+ Color aPattColor = GetPalette().GetColor( rFillData.mnPattColorIdx );
+ Color aBackColor = GetPalette().GetColor( rFillData.mnBackColorIdx );
+ aColor = XclTools::GetPatternColor( aPattColor, aBackColor, rFillData.mnPattern );
+ }
+ return aColor;
+}
+
+void XclImpDrawObjBase::DoReadObj3( XclImpStream&, sal_uInt16 )
+{
+}
+
+void XclImpDrawObjBase::DoReadObj4( XclImpStream&, sal_uInt16 )
+{
+}
+
+void XclImpDrawObjBase::DoReadObj5( XclImpStream&, sal_uInt16, sal_uInt16 )
+{
+}
+
+void XclImpDrawObjBase::DoReadObj8SubRec( XclImpStream&, sal_uInt16, sal_uInt16 )
+{
+}
+
+std::size_t XclImpDrawObjBase::DoGetProgressSize() const
+{
+ return 1;
+}
+
+SdrObjectUniquePtr XclImpDrawObjBase::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& ) const
+{
+ rDffConv.Progress( GetProgressSize() );
+ return nullptr;
+}
+
+void XclImpDrawObjBase::DoPreProcessSdrObj( XclImpDffConverter&, SdrObject& ) const
+{
+ // trace if object is not printable
+ if( !IsPrintable() )
+ GetTracer().TraceObjectNotPrintable();
+}
+
+void XclImpDrawObjBase::DoPostProcessSdrObj( XclImpDffConverter&, SdrObject& ) const
+{
+}
+
+void XclImpDrawObjBase::ImplReadObj3( XclImpStream& rStrm )
+{
+ // back to offset 4 (ignore object count field)
+ rStrm.Seek( 4 );
+
+ sal_uInt16 nObjFlags, nMacroSize;
+ mnObjType = rStrm.ReaduInt16();
+ mnObjId = rStrm.ReaduInt16();
+ nObjFlags = rStrm.ReaduInt16();
+ rStrm >> maAnchor;
+ nMacroSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+
+ mbHasAnchor = true;
+ mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN );
+ mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE );
+ DoReadObj3( rStrm, nMacroSize );
+}
+
+void XclImpDrawObjBase::ImplReadObj4( XclImpStream& rStrm )
+{
+ // back to offset 4 (ignore object count field)
+ rStrm.Seek( 4 );
+
+ sal_uInt16 nObjFlags, nMacroSize;
+ mnObjType = rStrm.ReaduInt16();
+ mnObjId = rStrm.ReaduInt16();
+ nObjFlags = rStrm.ReaduInt16();
+ rStrm >> maAnchor;
+ nMacroSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+
+ mbHasAnchor = true;
+ mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN );
+ mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE );
+ mbPrintable = ::get_flag( nObjFlags, EXC_OBJ_PRINTABLE );
+ DoReadObj4( rStrm, nMacroSize );
+}
+
+void XclImpDrawObjBase::ImplReadObj5( XclImpStream& rStrm )
+{
+ // back to offset 4 (ignore object count field)
+ rStrm.Seek( 4 );
+
+ sal_uInt16 nObjFlags, nMacroSize, nNameLen;
+ mnObjType = rStrm.ReaduInt16();
+ mnObjId = rStrm.ReaduInt16();
+ nObjFlags = rStrm.ReaduInt16();
+ rStrm >> maAnchor;
+ nMacroSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ nNameLen = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+
+ mbHasAnchor = true;
+ mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN );
+ mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE );
+ mbPrintable = ::get_flag( nObjFlags, EXC_OBJ_PRINTABLE );
+ DoReadObj5( rStrm, nNameLen, nMacroSize );
+}
+
+void XclImpDrawObjBase::ImplReadObj8( XclImpStream& rStrm )
+{
+ // back to beginning
+ rStrm.Seek( EXC_REC_SEEK_TO_BEGIN );
+
+ bool bLoop = true;
+ while (bLoop)
+ {
+ if (rStrm.GetRecLeft() < 4)
+ break;
+
+ sal_uInt16 nSubRecId = rStrm.ReaduInt16();
+ sal_uInt16 nSubRecSize = rStrm.ReaduInt16();
+ rStrm.PushPosition();
+ // sometimes the last subrecord has an invalid length (OBJLBSDATA) -> min()
+ nSubRecSize = static_cast< sal_uInt16 >( ::std::min< std::size_t >( nSubRecSize, rStrm.GetRecLeft() ) );
+
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJCMO:
+ OSL_ENSURE( rStrm.GetRecPos() == 4, "XclImpDrawObjBase::ImplReadObj8 - unexpected OBJCMO subrecord" );
+ if( (rStrm.GetRecPos() == 4) && (nSubRecSize >= 6) )
+ {
+ sal_uInt16 nObjFlags;
+ mnObjType = rStrm.ReaduInt16();
+ mnObjId = rStrm.ReaduInt16( );
+ nObjFlags = rStrm.ReaduInt16( );
+ mbPrintable = ::get_flag( nObjFlags, EXC_OBJCMO_PRINTABLE );
+ }
+ break;
+ case EXC_ID_OBJMACRO:
+ ReadMacro8( rStrm );
+ break;
+ case EXC_ID_OBJEND:
+ bLoop = false;
+ break;
+ default:
+ DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+
+ rStrm.PopPosition();
+ rStrm.Ignore( nSubRecSize );
+ }
+
+ /* Call DoReadObj8SubRec() with EXC_ID_OBJEND for further stream
+ processing (e.g. charts), even if the OBJEND subrecord is missing. */
+ DoReadObj8SubRec( rStrm, EXC_ID_OBJEND, 0 );
+
+ /* Pictures that Excel reads from BIFF5 and writes to BIFF8 still have the
+ IMGDATA record following the OBJ record (but they use the image data
+ stored in DFF). The IMGDATA record may be continued by several CONTINUE
+ records. But the last CONTINUE record may be in fact an MSODRAWING
+ record that contains the DFF data of the next drawing object! So we
+ have to skip just enough CONTINUE records to look at the next
+ MSODRAWING/CONTINUE record. */
+ if( !((rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord()) )
+ return;
+
+ rStrm.Ignore( 4 );
+ sal_uInt32 nDataSize = rStrm.ReaduInt32();
+ nDataSize -= rStrm.GetRecLeft();
+ // skip following CONTINUE records until IMGDATA ends
+ while (true)
+ {
+ if (!nDataSize)
+ break;
+ if (rStrm.GetNextRecId() != EXC_ID_CONT)
+ break;
+ if (!rStrm.StartNextRecord())
+ break;
+ OSL_ENSURE( nDataSize >= rStrm.GetRecLeft(), "XclImpDrawObjBase::ImplReadObj8 - CONTINUE too long" );
+ nDataSize -= ::std::min< sal_uInt32 >( rStrm.GetRecLeft(), nDataSize );
+ }
+ OSL_ENSURE( nDataSize == 0, "XclImpDrawObjBase::ImplReadObj8 - missing CONTINUE records" );
+ // next record may be MSODRAWING or CONTINUE or anything else
+}
+
+void XclImpDrawObjVector::InsertGrouped( XclImpDrawObjRef const & xDrawObj )
+{
+ if( !mObjs.empty() )
+ if( XclImpGroupObj* pGroupObj = dynamic_cast< XclImpGroupObj* >( mObjs.back().get() ) )
+ if( pGroupObj->TryInsert( xDrawObj ) )
+ return;
+ mObjs.push_back( xDrawObj );
+}
+
+std::size_t XclImpDrawObjVector::GetProgressSize() const
+{
+ return std::accumulate(mObjs.begin(), mObjs.end(), std::size_t(0),
+ [](const std::size_t& rSum, const XclImpDrawObjRef& rxObj) { return rSum + rxObj->GetProgressSize(); });
+}
+
+XclImpPhObj::XclImpPhObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot )
+{
+ SetProcessSdrObj( false );
+}
+
+XclImpGroupObj::XclImpGroupObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot ),
+ mnFirstUngrouped( 0 )
+{
+}
+
+bool XclImpGroupObj::TryInsert( XclImpDrawObjRef const & xDrawObj )
+{
+ if( xDrawObj->GetObjId() == mnFirstUngrouped )
+ return false;
+ // insert into own list or into nested group
+ maChildren.InsertGrouped( xDrawObj );
+ return true;
+}
+
+void XclImpGroupObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm.Ignore( 4 );
+ mnFirstUngrouped = rStrm.ReaduInt16();
+ rStrm.Ignore( 16 );
+ ReadMacro3( rStrm, nMacroSize );
+}
+
+void XclImpGroupObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm.Ignore( 4 );
+ mnFirstUngrouped = rStrm.ReaduInt16();
+ rStrm.Ignore( 16 );
+ ReadMacro4( rStrm, nMacroSize );
+}
+
+void XclImpGroupObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ rStrm.Ignore( 4 );
+ mnFirstUngrouped = rStrm.ReaduInt16();
+ rStrm.Ignore( 16 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+}
+
+std::size_t XclImpGroupObj::DoGetProgressSize() const
+{
+ return XclImpDrawObjBase::DoGetProgressSize() + maChildren.GetProgressSize();
+}
+
+SdrObjectUniquePtr XclImpGroupObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& /*rAnchorRect*/ ) const
+{
+ std::unique_ptr<SdrObjGroup, SdrObjectFreeOp> xSdrObj(
+ new SdrObjGroup(
+ *GetDoc().GetDrawLayer()));
+ // child objects in BIFF2-BIFF5 have absolute size, not needed to pass own anchor rectangle
+ SdrObjList& rObjList = *xSdrObj->GetSubList(); // SdrObjGroup always returns existing sublist
+ for( const auto& rxChild : maChildren )
+ rDffConv.ProcessObject( rObjList, *rxChild );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpLineObj::XclImpLineObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot ),
+ mnArrows( 0 ),
+ mnStartPoint( EXC_OBJ_LINE_TL )
+{
+ SetAreaObj( false );
+}
+
+void XclImpLineObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm >> maLineData;
+ mnArrows = rStrm.ReaduInt16();
+ mnStartPoint = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadMacro3( rStrm, nMacroSize );
+}
+
+void XclImpLineObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm >> maLineData;
+ mnArrows = rStrm.ReaduInt16();
+ mnStartPoint = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadMacro4( rStrm, nMacroSize );
+}
+
+void XclImpLineObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ rStrm >> maLineData;
+ mnArrows = rStrm.ReaduInt16();
+ mnStartPoint = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+}
+
+SdrObjectUniquePtr XclImpLineObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ ::basegfx::B2DPolygon aB2DPolygon;
+ switch( mnStartPoint )
+ {
+ default:
+ case EXC_OBJ_LINE_TL:
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Top() ) );
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Bottom() ) );
+ break;
+ case EXC_OBJ_LINE_TR:
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Top() ) );
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Bottom() ) );
+ break;
+ case EXC_OBJ_LINE_BR:
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Bottom() ) );
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Top() ) );
+ break;
+ case EXC_OBJ_LINE_BL:
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Bottom() ) );
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Top() ) );
+ break;
+ }
+ SdrObjectUniquePtr xSdrObj(
+ new SdrPathObj(
+ *GetDoc().GetDrawLayer(),
+ SdrObjKind::Line,
+ ::basegfx::B2DPolyPolygon(aB2DPolygon)));
+ ConvertLineStyle( *xSdrObj, maLineData );
+
+ // line ends
+ sal_uInt8 nArrowType = ::extract_value< sal_uInt8 >( mnArrows, 0, 4 );
+ bool bLineStart = false;
+ bool bLineEnd = false;
+ bool bFilled = false;
+ switch( nArrowType )
+ {
+ case EXC_OBJ_ARROW_OPEN: bLineStart = false; bLineEnd = true; bFilled = false; break;
+ case EXC_OBJ_ARROW_OPENBOTH: bLineStart = true; bLineEnd = true; bFilled = false; break;
+ case EXC_OBJ_ARROW_FILLED: bLineStart = false; bLineEnd = true; bFilled = true; break;
+ case EXC_OBJ_ARROW_FILLEDBOTH: bLineStart = true; bLineEnd = true; bFilled = true; break;
+ }
+ if( bLineStart || bLineEnd )
+ {
+ sal_uInt8 nArrowWidth = ::extract_value< sal_uInt8 >( mnArrows, 4, 4 );
+ double fArrowWidth = 3.0;
+ switch( nArrowWidth )
+ {
+ case EXC_OBJ_ARROW_NARROW: fArrowWidth = 2.0; break;
+ case EXC_OBJ_ARROW_MEDIUM: fArrowWidth = 3.0; break;
+ case EXC_OBJ_ARROW_WIDE: fArrowWidth = 5.0; break;
+ }
+
+ sal_uInt8 nArrowLength = ::extract_value< sal_uInt8 >( mnArrows, 8, 4 );
+ double fArrowLength = 3.0;
+ switch( nArrowLength )
+ {
+ case EXC_OBJ_ARROW_NARROW: fArrowLength = 2.5; break;
+ case EXC_OBJ_ARROW_MEDIUM: fArrowLength = 3.5; break;
+ case EXC_OBJ_ARROW_WIDE: fArrowLength = 6.0; break;
+ }
+
+ ::basegfx::B2DPolygon aArrowPoly;
+#define EXC_ARROW_POINT( x, y ) ::basegfx::B2DPoint( fArrowWidth * (x), fArrowLength * (y) )
+ if( bFilled )
+ {
+ aArrowPoly.append( EXC_ARROW_POINT( 0, 100 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 50, 0 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 100, 100 ) );
+ }
+ else
+ {
+ sal_uInt8 nLineWidth = ::limit_cast< sal_uInt8 >( maLineData.mnWidth, EXC_OBJ_LINE_THIN, EXC_OBJ_LINE_THICK );
+ aArrowPoly.append( EXC_ARROW_POINT( 50, 0 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 100, 100 - 3 * nLineWidth ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 100 - 5 * nLineWidth, 100 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 50, 12 * nLineWidth ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 5 * nLineWidth, 100 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 0, 100 - 3 * nLineWidth ) );
+ }
+#undef EXC_ARROW_POINT
+
+ ::basegfx::B2DPolyPolygon aArrowPolyPoly( aArrowPoly );
+ tools::Long nWidth = static_cast< tools::Long >( 125 * fArrowWidth );
+ if( bLineStart )
+ {
+ xSdrObj->SetMergedItem( XLineStartItem( OUString(), aArrowPolyPoly ) );
+ xSdrObj->SetMergedItem( XLineStartWidthItem( nWidth ) );
+ xSdrObj->SetMergedItem( XLineStartCenterItem( false ) );
+ }
+ if( bLineEnd )
+ {
+ xSdrObj->SetMergedItem( XLineEndItem( OUString(), aArrowPolyPoly ) );
+ xSdrObj->SetMergedItem( XLineEndWidthItem( nWidth ) );
+ xSdrObj->SetMergedItem( XLineEndCenterItem( false ) );
+ }
+ }
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpRectObj::XclImpRectObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot ),
+ mnFrameFlags( 0 )
+{
+ SetAreaObj( true );
+}
+
+void XclImpRectObj::ReadFrameData( XclImpStream& rStrm )
+{
+ rStrm >> maFillData >> maLineData;
+ mnFrameFlags = rStrm.ReaduInt16();
+}
+
+void XclImpRectObj::ConvertRectStyle( SdrObject& rSdrObj ) const
+{
+ ConvertLineStyle( rSdrObj, maLineData );
+ ConvertFillStyle( rSdrObj, maFillData );
+ ConvertFrameStyle( rSdrObj, mnFrameFlags );
+}
+
+void XclImpRectObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ ReadMacro3( rStrm, nMacroSize );
+}
+
+void XclImpRectObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ ReadMacro4( rStrm, nMacroSize );
+}
+
+void XclImpRectObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+}
+
+SdrObjectUniquePtr XclImpRectObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ SdrObjectUniquePtr xSdrObj(
+ new SdrRectObj(
+ *GetDoc().GetDrawLayer(),
+ rAnchorRect));
+ ConvertRectStyle( *xSdrObj );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpOvalObj::XclImpOvalObj( const XclImpRoot& rRoot ) :
+ XclImpRectObj( rRoot )
+{
+}
+
+SdrObjectUniquePtr XclImpOvalObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ SdrObjectUniquePtr xSdrObj(
+ new SdrCircObj(
+ *GetDoc().GetDrawLayer(),
+ SdrCircKind::Full,
+ rAnchorRect));
+ ConvertRectStyle( *xSdrObj );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpArcObj::XclImpArcObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot ),
+ mnQuadrant( EXC_OBJ_ARC_TR )
+{
+ SetAreaObj( false ); // arc may be 2-dimensional
+}
+
+void XclImpArcObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm >> maFillData >> maLineData;
+ mnQuadrant = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadMacro3( rStrm, nMacroSize );
+}
+
+void XclImpArcObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm >> maFillData >> maLineData;
+ mnQuadrant = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadMacro4( rStrm, nMacroSize );
+}
+
+void XclImpArcObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ rStrm >> maFillData >> maLineData;
+ mnQuadrant = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+}
+
+SdrObjectUniquePtr XclImpArcObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ tools::Rectangle aNewRect = rAnchorRect;
+ Degree100 nStartAngle;
+ Degree100 nEndAngle;
+ switch( mnQuadrant )
+ {
+ default:
+ case EXC_OBJ_ARC_TR:
+ nStartAngle = 0_deg100;
+ nEndAngle = 9000_deg100;
+ aNewRect.AdjustLeft( -(rAnchorRect.GetWidth()) );
+ aNewRect.AdjustBottom(rAnchorRect.GetHeight() );
+ break;
+ case EXC_OBJ_ARC_TL:
+ nStartAngle = 9000_deg100;
+ nEndAngle = 18000_deg100;
+ aNewRect.AdjustRight(rAnchorRect.GetWidth() );
+ aNewRect.AdjustBottom(rAnchorRect.GetHeight() );
+ break;
+ case EXC_OBJ_ARC_BL:
+ nStartAngle = 18000_deg100;
+ nEndAngle = 27000_deg100;
+ aNewRect.AdjustRight(rAnchorRect.GetWidth() );
+ aNewRect.AdjustTop( -(rAnchorRect.GetHeight()) );
+ break;
+ case EXC_OBJ_ARC_BR:
+ nStartAngle = 27000_deg100;
+ nEndAngle = 0_deg100;
+ aNewRect.AdjustLeft( -(rAnchorRect.GetWidth()) );
+ aNewRect.AdjustTop( -(rAnchorRect.GetHeight()) );
+ break;
+ }
+ SdrCircKind eObjKind = maFillData.IsFilled() ? SdrCircKind::Section : SdrCircKind::Arc;
+ SdrObjectUniquePtr xSdrObj(
+ new SdrCircObj(
+ *GetDoc().GetDrawLayer(),
+ eObjKind,
+ aNewRect,
+ nStartAngle,
+ nEndAngle));
+ ConvertFillStyle( *xSdrObj, maFillData );
+ ConvertLineStyle( *xSdrObj, maLineData );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpPolygonObj::XclImpPolygonObj( const XclImpRoot& rRoot ) :
+ XclImpRectObj( rRoot ),
+ mnPolyFlags( 0 ),
+ mnPointCount( 0 )
+{
+ SetAreaObj( false ); // polygon may be 2-dimensional
+}
+
+void XclImpPolygonObj::ReadCoordList( XclImpStream& rStrm )
+{
+ if( (rStrm.GetNextRecId() == EXC_ID_COORDLIST) && rStrm.StartNextRecord() )
+ {
+ OSL_ENSURE( rStrm.GetRecLeft() / 4 == mnPointCount, "XclImpPolygonObj::ReadCoordList - wrong polygon point count" );
+ while (true)
+ {
+ if (rStrm.GetRecLeft() < 4)
+ break;
+ sal_uInt16 nX = rStrm.ReaduInt16();
+ sal_uInt16 nY = rStrm.ReaduInt16();
+ maCoords.emplace_back( nX, nY );
+ }
+ }
+}
+
+void XclImpPolygonObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ mnPolyFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 10 );
+ mnPointCount = rStrm.ReaduInt16();
+ rStrm.Ignore( 8 );
+ ReadMacro4( rStrm, nMacroSize );
+ ReadCoordList( rStrm );
+}
+
+void XclImpPolygonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ mnPolyFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 10 );
+ mnPointCount = rStrm.ReaduInt16();
+ rStrm.Ignore( 8 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+ ReadCoordList( rStrm );
+}
+
+namespace {
+
+::basegfx::B2DPoint lclGetPolyPoint( const tools::Rectangle& rAnchorRect, const Point& rPoint )
+{
+ return ::basegfx::B2DPoint(
+ rAnchorRect.Left() + static_cast< sal_Int32 >( ::std::min< double >( rPoint.X(), 16384.0 ) / 16384.0 * rAnchorRect.GetWidth() + 0.5 ),
+ rAnchorRect.Top() + static_cast< sal_Int32 >( ::std::min< double >( rPoint.Y(), 16384.0 ) / 16384.0 * rAnchorRect.GetHeight() + 0.5 ) );
+}
+
+} // namespace
+
+SdrObjectUniquePtr XclImpPolygonObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ SdrObjectUniquePtr xSdrObj;
+ if( maCoords.size() >= 2 )
+ {
+ // create the polygon
+ ::basegfx::B2DPolygon aB2DPolygon;
+ for( const auto& rCoord : maCoords )
+ aB2DPolygon.append( lclGetPolyPoint( rAnchorRect, rCoord ) );
+ // close polygon if specified
+ if( ::get_flag( mnPolyFlags, EXC_OBJ_POLY_CLOSED ) && (maCoords.front() != maCoords.back()) )
+ aB2DPolygon.append( lclGetPolyPoint( rAnchorRect, maCoords.front() ) );
+ // create the SdrObject
+ SdrObjKind eObjKind = maFillData.IsFilled() ? SdrObjKind::PathPoly : SdrObjKind::PathPolyLine;
+ xSdrObj.reset(
+ new SdrPathObj(
+ *GetDoc().GetDrawLayer(),
+ eObjKind,
+ ::basegfx::B2DPolyPolygon(aB2DPolygon)));
+ ConvertRectStyle( *xSdrObj );
+ }
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+void XclImpObjTextData::ReadByteString( XclImpStream& rStrm )
+{
+ mxString.reset();
+ if( maData.mnTextLen > 0 )
+ {
+ mxString = std::make_shared<XclImpString>( rStrm.ReadRawByteString( maData.mnTextLen ) );
+ // skip padding byte for word boundaries
+ if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 );
+ }
+}
+
+void XclImpObjTextData::ReadFormats( XclImpStream& rStrm )
+{
+ if( mxString )
+ mxString->ReadObjFormats( rStrm, maData.mnFormatSize );
+ else
+ rStrm.Ignore( maData.mnFormatSize );
+}
+
+XclImpTextObj::XclImpTextObj( const XclImpRoot& rRoot ) :
+ XclImpRectObj( rRoot )
+{
+}
+
+void XclImpTextObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ maTextData.maData.ReadObj3( rStrm );
+ ReadMacro3( rStrm, nMacroSize );
+ maTextData.ReadByteString( rStrm );
+ maTextData.ReadFormats( rStrm );
+}
+
+void XclImpTextObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ maTextData.maData.ReadObj3( rStrm );
+ ReadMacro4( rStrm, nMacroSize );
+ maTextData.ReadByteString( rStrm );
+ maTextData.ReadFormats( rStrm );
+}
+
+void XclImpTextObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ maTextData.maData.ReadObj5( rStrm );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+ maTextData.ReadByteString( rStrm );
+ rStrm.Ignore( maTextData.maData.mnLinkSize ); // ignore text link formula
+ maTextData.ReadFormats( rStrm );
+}
+
+SdrObjectUniquePtr XclImpTextObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ std::unique_ptr<SdrObjCustomShape, SdrObjectFreeOp> xSdrObj(
+ new SdrObjCustomShape(
+ *GetDoc().GetDrawLayer()));
+ xSdrObj->NbcSetSnapRect( rAnchorRect );
+ OUString aRectType = "rectangle";
+ xSdrObj->MergeDefaultAttributes( &aRectType );
+ ConvertRectStyle( *xSdrObj );
+ bool bAutoSize = ::get_flag( maTextData.maData.mnFlags, EXC_OBJ_TEXT_AUTOSIZE );
+ xSdrObj->SetMergedItem( makeSdrTextAutoGrowWidthItem( bAutoSize ) );
+ xSdrObj->SetMergedItem( makeSdrTextAutoGrowHeightItem( bAutoSize ) );
+ xSdrObj->SetMergedItem( makeSdrTextWordWrapItem( true ) );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+void XclImpTextObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ // set text data
+ if( SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( &rSdrObj ) )
+ {
+ if( maTextData.mxString )
+ {
+ if( maTextData.mxString->IsRich() )
+ {
+ if (maTextData.mxString->GetText().getLength() > 1024 && utl::ConfigManager::IsFuzzing())
+ {
+ SAL_WARN("sc.filter", "truncating slow long rich text for fuzzing performance");
+ maTextData.mxString->SetText(maTextData.mxString->GetText().copy(0, 1024));
+ }
+
+ // rich text
+ std::unique_ptr< EditTextObject > xEditObj(
+ XclImpStringHelper::CreateTextObject( GetRoot(), *maTextData.mxString ) );
+ OutlinerParaObject aOutlineObj(std::move(xEditObj));
+ aOutlineObj.SetOutlinerMode( OutlinerMode::TextObject );
+ pTextObj->NbcSetOutlinerParaObject( std::move(aOutlineObj) );
+ }
+ else
+ {
+ // plain text
+ pTextObj->NbcSetText( maTextData.mxString->GetText() );
+ }
+
+ /* #i96858# Do not apply any formatting if there is no text.
+ SdrObjCustomShape::SetVerticalWriting (initiated from
+ SetMergedItem) calls SdrTextObj::ForceOutlinerParaObject which
+ ensures that we can erroneously write a ClientTextbox record
+ (with no content) while exporting to XLS, which can cause a
+ corrupted exported document. */
+
+ SvxAdjust eHorAlign = SvxAdjust::Left;
+ SdrTextVertAdjust eVerAlign = SDRTEXTVERTADJUST_TOP;
+
+ // orientation (this is only a fake, drawing does not support real text orientation)
+ namespace csst = ::com::sun::star::text;
+ csst::WritingMode eWriteMode = csst::WritingMode_LR_TB;
+ switch( maTextData.maData.mnOrient )
+ {
+ default:
+ case EXC_OBJ_ORIENT_NONE:
+ {
+ eWriteMode = csst::WritingMode_LR_TB;
+ switch( maTextData.maData.GetHorAlign() )
+ {
+ case EXC_OBJ_HOR_LEFT: eHorAlign = SvxAdjust::Left; break;
+ case EXC_OBJ_HOR_CENTER: eHorAlign = SvxAdjust::Center; break;
+ case EXC_OBJ_HOR_RIGHT: eHorAlign = SvxAdjust::Right; break;
+ case EXC_OBJ_HOR_JUSTIFY: eHorAlign = SvxAdjust::Block; break;
+ }
+ switch( maTextData.maData.GetVerAlign() )
+ {
+ case EXC_OBJ_VER_TOP: eVerAlign = SDRTEXTVERTADJUST_TOP; break;
+ case EXC_OBJ_VER_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break;
+ case EXC_OBJ_VER_BOTTOM: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break;
+ case EXC_OBJ_VER_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break;
+ }
+ }
+ break;
+
+ case EXC_OBJ_ORIENT_90CCW:
+ {
+ if( SdrObjCustomShape* pObjCustomShape = dynamic_cast< SdrObjCustomShape* >( &rSdrObj ) )
+ {
+ css::beans::PropertyValue aTextRotateAngle;
+ aTextRotateAngle.Name = "TextRotateAngle";
+ aTextRotateAngle.Value <<= 180.0;
+ SdrCustomShapeGeometryItem aGeometryItem(pObjCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
+ aGeometryItem.SetPropertyValue( aTextRotateAngle );
+ pObjCustomShape->SetMergedItem( aGeometryItem );
+ }
+ eWriteMode = csst::WritingMode_TB_RL;
+ switch( maTextData.maData.GetHorAlign() )
+ {
+ case EXC_OBJ_HOR_LEFT: eVerAlign = SDRTEXTVERTADJUST_TOP; break;
+ case EXC_OBJ_HOR_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break;
+ case EXC_OBJ_HOR_RIGHT: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break;
+ case EXC_OBJ_HOR_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break;
+ }
+ MSO_Anchor eTextAnchor = static_cast<MSO_Anchor>(rDffConv.GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop ));
+ switch( eTextAnchor )
+ {
+ case mso_anchorTopCentered :
+ case mso_anchorMiddleCentered :
+ case mso_anchorBottomCentered :
+ {
+ eHorAlign = SvxAdjust::Center;
+ }
+ break;
+
+ default:
+ {
+ switch( maTextData.maData.GetVerAlign() )
+ {
+ case EXC_OBJ_VER_TOP: eHorAlign = SvxAdjust::Right; break;
+ case EXC_OBJ_VER_CENTER: eHorAlign = SvxAdjust::Center; break;
+ case EXC_OBJ_VER_BOTTOM: eHorAlign = SvxAdjust::Left; break;
+ case EXC_OBJ_VER_JUSTIFY: eHorAlign = SvxAdjust::Block; break;
+ }
+ }
+ }
+ }
+ break;
+
+ case EXC_OBJ_ORIENT_STACKED:
+ {
+ // sj: STACKED is not supported, maybe it can be optimized here a bit
+ [[fallthrough]];
+ }
+ case EXC_OBJ_ORIENT_90CW:
+ {
+ eWriteMode = csst::WritingMode_TB_RL;
+ switch( maTextData.maData.GetHorAlign() )
+ {
+ case EXC_OBJ_HOR_LEFT: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break;
+ case EXC_OBJ_HOR_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break;
+ case EXC_OBJ_HOR_RIGHT: eVerAlign = SDRTEXTVERTADJUST_TOP; break;
+ case EXC_OBJ_HOR_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break;
+ }
+ MSO_Anchor eTextAnchor = static_cast<MSO_Anchor>(rDffConv.GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop ));
+ switch ( eTextAnchor )
+ {
+ case mso_anchorTopCentered :
+ case mso_anchorMiddleCentered :
+ case mso_anchorBottomCentered :
+ {
+ eHorAlign = SvxAdjust::Center;
+ }
+ break;
+
+ default:
+ {
+ switch( maTextData.maData.GetVerAlign() )
+ {
+ case EXC_OBJ_VER_TOP: eHorAlign = SvxAdjust::Left; break;
+ case EXC_OBJ_VER_CENTER: eHorAlign = SvxAdjust::Center; break;
+ case EXC_OBJ_VER_BOTTOM: eHorAlign = SvxAdjust::Right; break;
+ case EXC_OBJ_VER_JUSTIFY: eHorAlign = SvxAdjust::Block; break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ rSdrObj.SetMergedItem( SvxAdjustItem( eHorAlign, EE_PARA_JUST ) );
+ rSdrObj.SetMergedItem( SdrTextVertAdjustItem( eVerAlign ) );
+ rSdrObj.SetMergedItem( SvxWritingModeItem( eWriteMode, SDRATTR_TEXTDIRECTION ) );
+ }
+ }
+ // base class processing
+ XclImpRectObj::DoPreProcessSdrObj( rDffConv, rSdrObj );
+}
+
+XclImpChartObj::XclImpChartObj( const XclImpRoot& rRoot, bool bOwnTab ) :
+ XclImpRectObj( rRoot ),
+ mbOwnTab( bOwnTab )
+{
+ SetSimpleMacro( false );
+ SetCustomDffObj( true );
+}
+
+void XclImpChartObj::ReadChartSubStream( XclImpStream& rStrm )
+{
+ /* If chart is read from a chartsheet (mbOwnTab == true), the BOF record
+ has already been read. If chart is embedded as object, the next record
+ has to be the BOF record. */
+ if( mbOwnTab )
+ {
+ /* #i109800# The input stream may point somewhere inside the chart
+ substream and not exactly to the leading BOF record. To read this
+ record correctly in the following, the stream has to rewind it, so
+ that the next call to StartNextRecord() will find it correctly. */
+ if( rStrm.GetRecId() != EXC_ID5_BOF )
+ rStrm.RewindRecord();
+ }
+ else
+ {
+ if( (rStrm.GetNextRecId() == EXC_ID5_BOF) && rStrm.StartNextRecord() )
+ {
+ sal_uInt16 nBofType;
+ rStrm.Seek( 2 );
+ nBofType = rStrm.ReaduInt16();
+ SAL_WARN_IF( nBofType != EXC_BOF_CHART, "sc.filter", "XclImpChartObj::ReadChartSubStream - no chart BOF record" );
+ }
+ else
+ {
+ SAL_INFO("sc.filter", "XclImpChartObj::ReadChartSubStream - missing chart substream");
+ return;
+ }
+ }
+
+ // read chart, even if BOF record contains wrong substream identifier
+ mxChart = std::make_shared<XclImpChart>( GetRoot(), mbOwnTab );
+ mxChart->ReadChartSubStream( rStrm );
+ if( mbOwnTab )
+ FinalizeTabChart();
+}
+
+void XclImpChartObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ // read OBJ record and the following chart substream
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 18 );
+ ReadMacro3( rStrm, nMacroSize );
+ // set frame format from OBJ record, it is used if chart itself is transparent
+ if( mxChart )
+ mxChart->UpdateObjFrame( maLineData, maFillData );
+}
+
+void XclImpChartObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ // read OBJ record and the following chart substream
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 18 );
+ ReadMacro4( rStrm, nMacroSize );
+ // set frame format from OBJ record, it is used if chart itself is transparent
+ if( mxChart )
+ mxChart->UpdateObjFrame( maLineData, maFillData );
+}
+
+void XclImpChartObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ // read OBJ record and the following chart substream
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 18 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+ ReadChartSubStream( rStrm );
+ // set frame format from OBJ record, it is used if chart itself is transparent
+ if( mxChart )
+ mxChart->UpdateObjFrame( maLineData, maFillData );
+}
+
+void XclImpChartObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 /*nSubRecSize*/ )
+{
+ // read the following chart substream
+ if( nSubRecId == EXC_ID_OBJEND )
+ {
+ // enable CONTINUE handling for the entire chart substream
+ rStrm.ResetRecord( true );
+ ReadChartSubStream( rStrm );
+ /* disable CONTINUE handling again to be able to read
+ following CONTINUE records as MSODRAWING records. */
+ rStrm.ResetRecord( false );
+ }
+}
+
+std::size_t XclImpChartObj::DoGetProgressSize() const
+{
+ return mxChart ? mxChart->GetProgressSize() : 1;
+}
+
+SdrObjectUniquePtr XclImpChartObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ SdrObjectUniquePtr xSdrObj;
+ SfxObjectShell* pDocShell = GetDocShell();
+ if( rDffConv.SupportsOleObjects() && SvtModuleOptions().IsChart() && pDocShell && mxChart && !mxChart->IsPivotChart() )
+ {
+ // create embedded chart object
+ OUString aEmbObjName;
+ OUString sBaseURL(GetRoot().GetMedium().GetBaseURL());
+ Reference< XEmbeddedObject > xEmbObj = pDocShell->GetEmbeddedObjectContainer().
+ CreateEmbeddedObject( SvGlobalName( SO3_SCH_CLASSID ).GetByteSequence(), aEmbObjName, &sBaseURL );
+
+ if (!xEmbObj)
+ return xSdrObj;
+
+ /* Set the size to the embedded object, this prevents that font sizes
+ of text objects are changed in the chart when the object is
+ inserted into the draw page. */
+ sal_Int64 nAspect = css::embed::Aspects::MSOLE_CONTENT;
+ MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xEmbObj->getMapUnit( nAspect ) );
+ Size aSize( OutputDevice::LogicToLogic( rAnchorRect.GetSize(), MapMode( MapUnit::Map100thMM ), MapMode( aUnit ) ) );
+ css::awt::Size aAwtSize( aSize.Width(), aSize.Height() );
+ xEmbObj->setVisualAreaSize( nAspect, aAwtSize );
+
+ // #i121334# This call will change the chart's default background fill from white to transparent.
+ // Add here again if this is wanted (see task description for details)
+ // ChartHelper::AdaptDefaultsForChart( xEmbObj );
+
+ // create the container OLE object
+ xSdrObj.reset(
+ new SdrOle2Obj(
+ *GetDoc().GetDrawLayer(),
+ svt::EmbeddedObjectRef(xEmbObj, nAspect),
+ aEmbObjName,
+ rAnchorRect));
+ }
+
+ return xSdrObj;
+}
+
+void XclImpChartObj::DoPostProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ const SdrOle2Obj* pSdrOleObj = dynamic_cast< const SdrOle2Obj* >( &rSdrObj );
+ if( !(mxChart && pSdrOleObj) )
+ return;
+
+ const Reference< XEmbeddedObject >& xEmbObj = pSdrOleObj->GetObjRef();
+ if( xEmbObj.is() && ::svt::EmbeddedObjectRef::TryRunningState( xEmbObj ) ) try
+ {
+ Reference< XEmbedPersist > xPersist( xEmbObj, UNO_QUERY_THROW );
+ Reference< XModel > xModel( xEmbObj->getComponent(), UNO_QUERY_THROW );
+ mxChart->Convert( xModel, rDffConv, xPersist->getEntryName(), rSdrObj.GetLogicRect() );
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+void XclImpChartObj::FinalizeTabChart()
+{
+ /* #i44077# Calculate and store DFF anchor for sheet charts.
+ Needed to get used area if this chart is inserted as OLE object. */
+ OSL_ENSURE( mbOwnTab, "XclImpChartObj::FinalizeTabChart - not allowed for embedded chart objects" );
+
+ // set uninitialized page to landscape
+ if( !GetPageSettings().GetPageData().mbValid )
+ GetPageSettings().SetPaperSize( EXC_PAPERSIZE_DEFAULT, false );
+
+ // calculate size of the chart object
+ const XclPageData& rPageData = GetPageSettings().GetPageData();
+ Size aPaperSize = rPageData.GetScPaperSize();
+
+ tools::Long nWidth = XclTools::GetHmmFromTwips( aPaperSize.Width() );
+ tools::Long nHeight = XclTools::GetHmmFromTwips( aPaperSize.Height() );
+
+ // subtract page margins, give some more extra space
+ nWidth -= o3tl::saturating_add(XclTools::GetHmmFromInch(rPageData.mfLeftMargin + rPageData.mfRightMargin), static_cast<sal_Int32>(2000));
+ nHeight -= o3tl::saturating_add(XclTools::GetHmmFromInch(rPageData.mfTopMargin + rPageData.mfBottomMargin), static_cast<sal_Int32>(1000));
+
+ // print column/row headers?
+ if( rPageData.mbPrintHeadings )
+ {
+ nWidth -= 2000;
+ nHeight -= 1000;
+ }
+
+ // create the object anchor
+ XclObjAnchor aAnchor;
+ aAnchor.SetRect( GetRoot(), GetCurrScTab(), tools::Rectangle( 1000, 500, nWidth, nHeight ), MapUnit::Map100thMM );
+ SetAnchor( aAnchor );
+}
+
+XclImpNoteObj::XclImpNoteObj( const XclImpRoot& rRoot ) :
+ XclImpTextObj( rRoot ),
+ maScPos( ScAddress::INITIALIZE_INVALID ),
+ mnNoteFlags( 0 )
+{
+ SetSimpleMacro( false );
+ // caption object will be created manually
+ SetInsertSdrObj( false );
+}
+
+void XclImpNoteObj::SetNoteData( const ScAddress& rScPos, sal_uInt16 nNoteFlags )
+{
+ maScPos = rScPos;
+ mnNoteFlags = nNoteFlags;
+}
+
+void XclImpNoteObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ // create formatted text
+ XclImpTextObj::DoPreProcessSdrObj( rDffConv, rSdrObj );
+ OutlinerParaObject* pOutlinerObj = rSdrObj.GetOutlinerParaObject();
+ if( maScPos.IsValid() && pOutlinerObj )
+ {
+ // create cell note with all data from drawing object
+ ScNoteUtil::CreateNoteFromObjectData(
+ GetDoc(), maScPos,
+ rSdrObj.GetMergedItemSet().CloneAsValue(), // new object on heap expected
+ *pOutlinerObj,
+ rSdrObj.GetLogicRect(),
+ ::get_flag( mnNoteFlags, EXC_NOTE_VISIBLE ) );
+ }
+}
+
+XclImpControlHelper::XclImpControlHelper( const XclImpRoot& rRoot, XclCtrlBindMode eBindMode ) :
+ mrRoot( rRoot ),
+ meBindMode( eBindMode )
+{
+}
+
+XclImpControlHelper::~XclImpControlHelper()
+{
+}
+
+SdrObjectUniquePtr XclImpControlHelper::CreateSdrObjectFromShape(
+ const Reference< XShape >& rxShape, const tools::Rectangle& rAnchorRect ) const
+{
+ mxShape = rxShape;
+ SdrObjectUniquePtr xSdrObj( SdrObject::getSdrObjectFromXShape( rxShape ) );
+ if( xSdrObj )
+ {
+ xSdrObj->NbcSetSnapRect( rAnchorRect );
+ // #i30543# insert into control layer
+ xSdrObj->NbcSetLayer( SC_LAYER_CONTROLS );
+ }
+ return xSdrObj;
+}
+
+void XclImpControlHelper::ApplySheetLinkProps() const
+{
+
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( mxShape );
+ if( !xCtrlModel.is() )
+ return;
+
+ // sheet links
+ SfxObjectShell* pDocShell = mrRoot.GetDocShell();
+ if(!pDocShell)
+ return;
+
+ Reference< XMultiServiceFactory > xFactory( pDocShell->GetModel(), UNO_QUERY );
+ if( !xFactory.is() )
+ return;
+
+ // cell link
+ if( mxCellLink ) try
+ {
+ Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY_THROW );
+
+ // create argument sequence for createInstanceWithArguments()
+ CellAddress aApiAddress;
+ ScUnoConversion::FillApiAddress( aApiAddress, *mxCellLink );
+
+ NamedValue aValue;
+ aValue.Name = SC_UNONAME_BOUNDCELL;
+ aValue.Value <<= aApiAddress;
+
+ Sequence< Any > aArgs{ Any(aValue) };
+
+ // create the CellValueBinding instance and set at the control model
+ OUString aServiceName;
+ switch( meBindMode )
+ {
+ case EXC_CTRL_BINDCONTENT: aServiceName = SC_SERVICENAME_VALBIND; break;
+ case EXC_CTRL_BINDPOSITION: aServiceName = SC_SERVICENAME_LISTCELLBIND; break;
+ }
+ Reference< XValueBinding > xBinding(
+ xFactory->createInstanceWithArguments( aServiceName, aArgs ), UNO_QUERY_THROW );
+ xBindable->setValueBinding( xBinding );
+ }
+ catch( const Exception& )
+ {
+ }
+
+ // source range
+ if( !mxSrcRange )
+ return;
+
+ try
+ {
+ Reference< XListEntrySink > xEntrySink( xCtrlModel, UNO_QUERY_THROW );
+
+ // create argument sequence for createInstanceWithArguments()
+ CellRangeAddress aApiRange;
+ ScUnoConversion::FillApiRange( aApiRange, *mxSrcRange );
+
+ NamedValue aValue;
+ aValue.Name = SC_UNONAME_CELLRANGE;
+ aValue.Value <<= aApiRange;
+
+ Sequence< Any > aArgs{ Any(aValue) };
+
+ // create the EntrySource instance and set at the control model
+ Reference< XListEntrySource > xEntrySource( xFactory->createInstanceWithArguments(
+ SC_SERVICENAME_LISTSOURCE, aArgs ), UNO_QUERY_THROW );
+ xEntrySink->setListEntrySource( xEntrySource );
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+void XclImpControlHelper::ProcessControl( const XclImpDrawObjBase& rDrawObj ) const
+{
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( mxShape );
+ if( !xCtrlModel.is() )
+ return;
+
+ ApplySheetLinkProps();
+
+ ScfPropertySet aPropSet( xCtrlModel );
+
+ // #i51348# set object name at control model
+ aPropSet.SetStringProperty( "Name", rDrawObj.GetObjName() );
+
+ // control visible and printable?
+ aPropSet.SetBoolProperty( "EnableVisible", rDrawObj.IsVisible() );
+ aPropSet.SetBoolProperty( "Printable", rDrawObj.IsPrintable() );
+
+ // virtual call for type specific processing
+ DoProcessControl( aPropSet );
+}
+
+void XclImpControlHelper::ReadCellLinkFormula( XclImpStream& rStrm, bool bWithBoundSize )
+{
+ ScRangeList aScRanges;
+ ReadRangeList( aScRanges, rStrm, bWithBoundSize );
+ // Use first cell of first range
+ if ( !aScRanges.empty() )
+ {
+ const ScRange & rScRange = aScRanges.front();
+ mxCellLink = std::make_shared<ScAddress>( rScRange.aStart );
+ }
+}
+
+void XclImpControlHelper::ReadSourceRangeFormula( XclImpStream& rStrm, bool bWithBoundSize )
+{
+ ScRangeList aScRanges;
+ ReadRangeList( aScRanges, rStrm, bWithBoundSize );
+ // Use first range
+ if ( !aScRanges.empty() )
+ {
+ const ScRange & rScRange = aScRanges.front();
+ mxSrcRange = std::make_shared<ScRange>( rScRange );
+ }
+}
+
+void XclImpControlHelper::DoProcessControl( ScfPropertySet& ) const
+{
+}
+
+void XclImpControlHelper::ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm )
+{
+ XclTokenArray aXclTokArr;
+ sal_uInt16 nSize = XclTokenArray::ReadSize(rStrm);
+ rStrm.Ignore( 4 );
+ aXclTokArr.ReadArray(nSize, rStrm);
+ mrRoot.GetFormulaCompiler().CreateRangeList( rScRanges, EXC_FMLATYPE_CONTROL, aXclTokArr, rStrm );
+}
+
+void XclImpControlHelper::ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm, bool bWithBoundSize )
+{
+ if( bWithBoundSize )
+ {
+ sal_uInt16 nSize;
+ nSize = rStrm.ReaduInt16();
+ if( nSize > 0 )
+ {
+ rStrm.PushPosition();
+ ReadRangeList( rScRanges, rStrm );
+ rStrm.PopPosition();
+ rStrm.Ignore( nSize );
+ }
+ }
+ else
+ {
+ ReadRangeList( rScRanges, rStrm );
+ }
+}
+
+XclImpTbxObjBase::XclImpTbxObjBase( const XclImpRoot& rRoot ) :
+ XclImpTextObj( rRoot ),
+ XclImpControlHelper( rRoot, EXC_CTRL_BINDPOSITION )
+{
+ SetSimpleMacro( false );
+ SetCustomDffObj( true );
+}
+
+namespace {
+
+void lclExtractColor( sal_uInt8& rnColorIdx, const DffPropSet& rDffPropSet, sal_uInt32 nPropId )
+{
+ if( rDffPropSet.IsProperty( nPropId ) )
+ {
+ sal_uInt32 nColor = rDffPropSet.GetPropertyValue( nPropId, 0 );
+ if( (nColor & 0xFF000000) == 0x08000000 )
+ rnColorIdx = ::extract_value< sal_uInt8 >( nColor, 0, 8 );
+ }
+}
+
+} // namespace
+
+void XclImpTbxObjBase::SetDffProperties( const DffPropSet& rDffPropSet )
+{
+ maFillData.mnPattern = rDffPropSet.GetPropertyBool( DFF_Prop_fFilled ) ? EXC_PATT_SOLID : EXC_PATT_NONE;
+ lclExtractColor( maFillData.mnBackColorIdx, rDffPropSet, DFF_Prop_fillBackColor );
+ lclExtractColor( maFillData.mnPattColorIdx, rDffPropSet, DFF_Prop_fillColor );
+ ::set_flag( maFillData.mnAuto, EXC_OBJ_LINE_AUTO, false );
+
+ maLineData.mnStyle = rDffPropSet.GetPropertyBool( DFF_Prop_fLine ) ? EXC_OBJ_LINE_SOLID : EXC_OBJ_LINE_NONE;
+ lclExtractColor( maLineData.mnColorIdx, rDffPropSet, DFF_Prop_lineColor );
+ ::set_flag( maLineData.mnAuto, EXC_OBJ_FILL_AUTO, false );
+}
+
+bool XclImpTbxObjBase::FillMacroDescriptor( ScriptEventDescriptor& rDescriptor ) const
+{
+ return XclControlHelper::FillMacroDescriptor( rDescriptor, DoGetEventType(), GetMacroName(), GetDocShell() );
+}
+
+void XclImpTbxObjBase::ConvertFont( ScfPropertySet& rPropSet ) const
+{
+ if( maTextData.mxString )
+ {
+ const XclFormatRunVec& rFormatRuns = maTextData.mxString->GetFormats();
+ if( rFormatRuns.empty() )
+ GetFontBuffer().WriteDefaultCtrlFontProperties( rPropSet );
+ else
+ GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL, rFormatRuns.front().mnFontIdx );
+ }
+}
+
+void XclImpTbxObjBase::ConvertLabel( ScfPropertySet& rPropSet ) const
+{
+ if( maTextData.mxString )
+ {
+ OUString aLabel = maTextData.mxString->GetText();
+ if( maTextData.maData.mnShortcut > 0 )
+ {
+ sal_Int32 nPos = aLabel.indexOf( static_cast< sal_Unicode >( maTextData.maData.mnShortcut ) );
+ if( nPos != -1 )
+ aLabel = aLabel.replaceAt( nPos, 0, u"~" );
+ }
+ rPropSet.SetStringProperty( "Label", aLabel );
+
+ //Excel Alt text <==> Aoo description
+ //For TBX control, if user does not operate alt text, alt text will be set label text as default value in Excel.
+ //In this case, DFF_Prop_wzDescription will not be set in excel file.
+ //So In the end of SvxMSDffManager::ImportShape, description will not be set. But actually in excel,
+ //the alt text is the label value. So here set description as label text first which is called before ImportShape.
+ Reference< css::beans::XPropertySet > xPropset( mxShape, UNO_QUERY );
+ try{
+ if(xPropset.is())
+ xPropset->setPropertyValue( "Description", Any(aLabel) );
+ }catch( ... )
+ {
+ SAL_WARN("sc.filter", "Can't set a default text for TBX Control ");
+ }
+ }
+ ConvertFont( rPropSet );
+}
+
+SdrObjectUniquePtr XclImpTbxObjBase::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ SdrObjectUniquePtr xSdrObj( rDffConv.CreateSdrObject( *this, rAnchorRect ) );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+void XclImpTbxObjBase::DoPreProcessSdrObj( XclImpDffConverter& /*rDffConv*/, SdrObject& /*rSdrObj*/ ) const
+{
+ // do not call DoPreProcessSdrObj() from base class (to skip text processing)
+ ProcessControl( *this );
+}
+
+XclImpButtonObj::XclImpButtonObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot )
+{
+}
+
+void XclImpButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+
+ /* Horizontal text alignment. For unknown reason, the property type is a
+ simple sal_Int16 and not a com.sun.star.style.HorizontalAlignment. */
+ sal_Int16 nHorAlign = 1;
+ switch( maTextData.maData.GetHorAlign() )
+ {
+ case EXC_OBJ_HOR_LEFT: nHorAlign = 0; break;
+ case EXC_OBJ_HOR_CENTER: nHorAlign = 1; break;
+ case EXC_OBJ_HOR_RIGHT: nHorAlign = 2; break;
+ }
+ rPropSet.SetProperty( "Align", nHorAlign );
+
+ // vertical text alignment
+ namespace csss = ::com::sun::star::style;
+ csss::VerticalAlignment eVerAlign = csss::VerticalAlignment_MIDDLE;
+ switch( maTextData.maData.GetVerAlign() )
+ {
+ case EXC_OBJ_VER_TOP: eVerAlign = csss::VerticalAlignment_TOP; break;
+ case EXC_OBJ_VER_CENTER: eVerAlign = csss::VerticalAlignment_MIDDLE; break;
+ case EXC_OBJ_VER_BOTTOM: eVerAlign = csss::VerticalAlignment_BOTTOM; break;
+ }
+ rPropSet.SetProperty( "VerticalAlign", eVerAlign );
+
+ // always wrap text automatically
+ rPropSet.SetBoolProperty( "MultiLine", true );
+
+ // default button
+ bool bDefButton = ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_DEFAULT );
+ rPropSet.SetBoolProperty( "DefaultButton", bDefButton );
+
+ // button type (flags cannot be combined in OOo)
+ namespace cssa = ::com::sun::star::awt;
+ cssa::PushButtonType eButtonType = cssa::PushButtonType_STANDARD;
+ if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_CLOSE ) )
+ eButtonType = cssa::PushButtonType_OK;
+ else if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_CANCEL ) )
+ eButtonType = cssa::PushButtonType_CANCEL;
+ else if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_HELP ) )
+ eButtonType = cssa::PushButtonType_HELP;
+ // property type is short, not enum
+ rPropSet.SetProperty( "PushButtonType", sal_Int16( eButtonType ) );
+}
+
+OUString XclImpButtonObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.CommandButton";
+}
+
+XclTbxEventType XclImpButtonObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_ACTION;
+}
+
+XclImpCheckBoxObj::XclImpCheckBoxObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot ),
+ mnState( EXC_OBJ_CHECKBOX_UNCHECKED ),
+ mnCheckBoxFlags( 0 )
+{
+}
+
+void XclImpCheckBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 10 );
+ maTextData.maData.mnFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 20 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ mnState = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16();
+ mnCheckBoxFlags = rStrm.ReaduInt16();
+}
+
+void XclImpCheckBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJCBLS:
+ // do not read EXC_ID_OBJCBLSDATA, not written by OOo Excel export
+ mnState = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16();
+ mnCheckBoxFlags = rStrm.ReaduInt16();
+ break;
+ case EXC_ID_OBJCBLSFMLA:
+ ReadCellLinkFormula( rStrm, false );
+ break;
+ default:
+ XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpCheckBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+
+ // state
+ bool bSupportsTristate = GetObjType() == EXC_OBJTYPE_CHECKBOX;
+ sal_Int16 nApiState = 0;
+ switch( mnState )
+ {
+ case EXC_OBJ_CHECKBOX_UNCHECKED: nApiState = 0; break;
+ case EXC_OBJ_CHECKBOX_CHECKED: nApiState = 1; break;
+ case EXC_OBJ_CHECKBOX_TRISTATE: nApiState = bSupportsTristate ? 2 : 1; break;
+ }
+ if( bSupportsTristate )
+ rPropSet.SetBoolProperty( "TriState", nApiState == 2 );
+ rPropSet.SetProperty( "DefaultState", nApiState );
+
+ // box style
+ namespace AwtVisualEffect = ::com::sun::star::awt::VisualEffect;
+ sal_Int16 nEffect = ::get_flagvalue( mnCheckBoxFlags, EXC_OBJ_CHECKBOX_FLAT, AwtVisualEffect::FLAT, AwtVisualEffect::LOOK3D );
+ rPropSet.SetProperty( "VisualEffect", nEffect );
+
+ // do not wrap text automatically
+ rPropSet.SetBoolProperty( "MultiLine", false );
+
+ // #i40279# always centered vertically
+ namespace csss = ::com::sun::star::style;
+ rPropSet.SetProperty( "VerticalAlign", csss::VerticalAlignment_MIDDLE );
+
+ // background color
+ if( maFillData.IsFilled() )
+ {
+ sal_Int32 nColor = static_cast< sal_Int32 >( GetSolidFillColor( maFillData ) );
+ rPropSet.SetProperty( "BackgroundColor", nColor );
+ }
+}
+
+OUString XclImpCheckBoxObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.CheckBox";
+}
+
+XclTbxEventType XclImpCheckBoxObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_ACTION;
+}
+
+XclImpOptionButtonObj::XclImpOptionButtonObj( const XclImpRoot& rRoot ) :
+ XclImpCheckBoxObj( rRoot ),
+ mnNextInGroup( 0 ),
+ mnFirstInGroup( 1 )
+{
+}
+
+void XclImpOptionButtonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 10 );
+ maTextData.maData.mnFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 32 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ mnState = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16();
+ mnCheckBoxFlags = rStrm.ReaduInt16();
+ mnNextInGroup = rStrm.ReaduInt16();
+ mnFirstInGroup = rStrm.ReaduInt16();
+}
+
+void XclImpOptionButtonObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJRBODATA:
+ mnNextInGroup = rStrm.ReaduInt16();
+ mnFirstInGroup = rStrm.ReaduInt16();
+ break;
+ default:
+ XclImpCheckBoxObj::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpOptionButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ XclImpCheckBoxObj::DoProcessControl( rPropSet );
+ // TODO: grouping
+ XclImpOptionButtonObj* pTbxObj = dynamic_cast< XclImpOptionButtonObj* >( GetObjectManager().GetSheetDrawing( GetTab() ).FindDrawObj( mnNextInGroup ).get() );
+ if ( pTbxObj && pTbxObj->mnFirstInGroup )
+ {
+ // Group has terminated
+ // traverse each RadioButton in group and
+ // a) apply the groupname
+ // b) propagate the linked cell from the lead radiobutton
+ // c) apply the correct Ref value
+ XclImpOptionButtonObj* pLeader = pTbxObj;
+
+ sal_Int32 nRefVal = 1;
+ do
+ {
+
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( pTbxObj->mxShape );
+ if ( xCtrlModel.is() )
+ {
+ ScfPropertySet aProps( xCtrlModel );
+ OUString sGroupName = OUString::number( pLeader->GetDffShapeId() );
+
+ aProps.SetStringProperty( "GroupName", sGroupName );
+ aProps.SetStringProperty( "RefValue", OUString::number( nRefVal++ ) );
+ if ( pLeader->HasCellLink() && !pTbxObj->HasCellLink() )
+ {
+ // propagate cell link info
+ pTbxObj->mxCellLink = std::make_shared<ScAddress>( *pLeader->mxCellLink );
+ pTbxObj->ApplySheetLinkProps();
+ }
+ pTbxObj = dynamic_cast< XclImpOptionButtonObj* >( GetObjectManager().GetSheetDrawing( GetTab() ).FindDrawObj( pTbxObj->mnNextInGroup ).get() );
+ }
+ else
+ pTbxObj = nullptr;
+ } while ( pTbxObj && ( pTbxObj->mnFirstInGroup != 1 ) );
+ }
+ else
+ {
+ // not the leader? try and find it
+ }
+}
+
+OUString XclImpOptionButtonObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.RadioButton";
+}
+
+XclTbxEventType XclImpOptionButtonObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_ACTION;
+}
+
+XclImpLabelObj::XclImpLabelObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot )
+{
+}
+
+void XclImpLabelObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+
+ // text alignment (always top/left aligned)
+ rPropSet.SetProperty( "Align", sal_Int16( 0 ) );
+ namespace csss = ::com::sun::star::style;
+ rPropSet.SetProperty( "VerticalAlign", csss::VerticalAlignment_TOP );
+
+ // always wrap text automatically
+ rPropSet.SetBoolProperty( "MultiLine", true );
+}
+
+OUString XclImpLabelObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.FixedText";
+}
+
+XclTbxEventType XclImpLabelObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_MOUSE;
+}
+
+XclImpGroupBoxObj::XclImpGroupBoxObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot ),
+ mnGroupBoxFlags( 0 )
+{
+}
+
+void XclImpGroupBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 10 );
+ maTextData.maData.mnFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 26 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16( );
+ mnGroupBoxFlags = rStrm.ReaduInt16();
+}
+
+void XclImpGroupBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJGBODATA:
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16();
+ mnGroupBoxFlags = rStrm.ReaduInt16();
+ break;
+ default:
+ XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpGroupBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+}
+
+OUString XclImpGroupBoxObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.GroupBox";
+}
+
+XclTbxEventType XclImpGroupBoxObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_MOUSE;
+}
+
+XclImpDialogObj::XclImpDialogObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot )
+{
+}
+
+void XclImpDialogObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+}
+
+OUString XclImpDialogObj::DoGetServiceName() const
+{
+ // dialog frame faked by a groupbox
+ return "com.sun.star.form.component.GroupBox";
+}
+
+XclTbxEventType XclImpDialogObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_MOUSE;
+}
+
+XclImpEditObj::XclImpEditObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot ),
+ mnContentType( EXC_OBJ_EDIT_TEXT ),
+ mnMultiLine( 0 ),
+ mnScrollBar( 0 ),
+ mnListBoxObjId( 0 )
+{
+}
+
+bool XclImpEditObj::IsNumeric() const
+{
+ return (mnContentType == EXC_OBJ_EDIT_INTEGER) || (mnContentType == EXC_OBJ_EDIT_DOUBLE);
+}
+
+void XclImpEditObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 10 );
+ maTextData.maData.mnFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 14 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ mnContentType = rStrm.ReaduInt16();
+ mnMultiLine = rStrm.ReaduInt16();
+ mnScrollBar = rStrm.ReaduInt16();
+ mnListBoxObjId = rStrm.ReaduInt16();
+}
+
+void XclImpEditObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJEDODATA:
+ mnContentType = rStrm.ReaduInt16();
+ mnMultiLine = rStrm.ReaduInt16();
+ mnScrollBar = rStrm.ReaduInt16();
+ mnListBoxObjId = rStrm.ReaduInt16();
+ break;
+ default:
+ XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpEditObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ if( maTextData.mxString )
+ {
+ OUString aText = maTextData.mxString->GetText();
+ if( IsNumeric() )
+ {
+ // TODO: OUString::toDouble() does not handle local decimal separator
+ rPropSet.SetProperty( "DefaultValue", aText.toDouble() );
+ rPropSet.SetBoolProperty( "Spin", mnScrollBar != 0 );
+ }
+ else
+ {
+ rPropSet.SetProperty( "DefaultText", aText );
+ rPropSet.SetBoolProperty( "MultiLine", mnMultiLine != 0 );
+ rPropSet.SetBoolProperty( "VScroll", mnScrollBar != 0 );
+ }
+ }
+ ConvertFont( rPropSet );
+}
+
+OUString XclImpEditObj::DoGetServiceName() const
+{
+ return IsNumeric() ?
+ OUString( "com.sun.star.form.component.NumericField" ) :
+ OUString( "com.sun.star.form.component.TextField" );
+}
+
+XclTbxEventType XclImpEditObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_TEXT;
+}
+
+XclImpTbxObjScrollableBase::XclImpTbxObjScrollableBase( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot ),
+ mnValue( 0 ),
+ mnMin( 0 ),
+ mnMax( 100 ),
+ mnStep( 1 ),
+ mnPageStep( 10 ),
+ mnOrient( 0 ),
+ mnThumbWidth( 1 ),
+ mnScrollFlags( 0 )
+{
+}
+
+void XclImpTbxObjScrollableBase::ReadSbs( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 4 );
+ mnValue = rStrm.ReaduInt16();
+ mnMin = rStrm.ReaduInt16();
+ mnMax = rStrm.ReaduInt16();
+ mnStep = rStrm.ReaduInt16();
+ mnPageStep = rStrm.ReaduInt16();
+ mnOrient = rStrm.ReaduInt16();
+ mnThumbWidth = rStrm.ReaduInt16();
+ mnScrollFlags = rStrm.ReaduInt16();
+}
+
+void XclImpTbxObjScrollableBase::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJSBS:
+ ReadSbs( rStrm );
+ break;
+ case EXC_ID_OBJSBSFMLA:
+ ReadCellLinkFormula( rStrm, false );
+ break;
+ default:
+ XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+XclImpSpinButtonObj::XclImpSpinButtonObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjScrollableBase( rRoot )
+{
+}
+
+void XclImpSpinButtonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ ReadSbs( rStrm );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+}
+
+void XclImpSpinButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // Calc's "Border" property is not the 3D/flat style effect in Excel (#i34712#)
+ rPropSet.SetProperty( "Border", css::awt::VisualEffect::NONE );
+ rPropSet.SetProperty< sal_Int32 >( "DefaultSpinValue", mnValue );
+ rPropSet.SetProperty< sal_Int32 >( "SpinValueMin", mnMin );
+ rPropSet.SetProperty< sal_Int32 >( "SpinValueMax", mnMax );
+ rPropSet.SetProperty< sal_Int32 >( "SpinIncrement", mnStep );
+
+ // Excel spin buttons always vertical
+ rPropSet.SetProperty( "Orientation", css::awt::ScrollBarOrientation::VERTICAL );
+}
+
+OUString XclImpSpinButtonObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.SpinButton";
+}
+
+XclTbxEventType XclImpSpinButtonObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_VALUE;
+}
+
+XclImpScrollBarObj::XclImpScrollBarObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjScrollableBase( rRoot )
+{
+}
+
+void XclImpScrollBarObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ ReadSbs( rStrm );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+}
+
+void XclImpScrollBarObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // Calc's "Border" property is not the 3D/flat style effect in Excel (#i34712#)
+ rPropSet.SetProperty( "Border", css::awt::VisualEffect::NONE );
+ rPropSet.SetProperty< sal_Int32 >( "DefaultScrollValue", mnValue );
+ rPropSet.SetProperty< sal_Int32 >( "ScrollValueMin", mnMin );
+ rPropSet.SetProperty< sal_Int32 >( "ScrollValueMax", mnMax );
+ rPropSet.SetProperty< sal_Int32 >( "LineIncrement", mnStep );
+ rPropSet.SetProperty< sal_Int32 >( "BlockIncrement", mnPageStep );
+ rPropSet.SetProperty( "VisibleSize", ::std::min< sal_Int32 >( mnPageStep, 1 ) );
+
+ namespace AwtScrollOrient = ::com::sun::star::awt::ScrollBarOrientation;
+ sal_Int32 nApiOrient = ::get_flagvalue( mnOrient, EXC_OBJ_SCROLLBAR_HOR, AwtScrollOrient::HORIZONTAL, AwtScrollOrient::VERTICAL );
+ rPropSet.SetProperty( "Orientation", nApiOrient );
+}
+
+OUString XclImpScrollBarObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.ScrollBar";
+}
+
+XclTbxEventType XclImpScrollBarObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_VALUE;
+}
+
+XclImpTbxObjListBase::XclImpTbxObjListBase( const XclImpRoot& rRoot ) :
+ XclImpTbxObjScrollableBase( rRoot ),
+ mnEntryCount( 0 ),
+ mnSelEntry( 0 ),
+ mnListFlags( 0 ),
+ mnEditObjId( 0 ),
+ mbHasDefFontIdx( false )
+{
+}
+
+void XclImpTbxObjListBase::ReadLbsData( XclImpStream& rStrm )
+{
+ ReadSourceRangeFormula( rStrm, true );
+ mnEntryCount = rStrm.ReaduInt16();
+ mnSelEntry = rStrm.ReaduInt16();
+ mnListFlags = rStrm.ReaduInt16();
+ mnEditObjId = rStrm.ReaduInt16();
+}
+
+void XclImpTbxObjListBase::SetBoxFormatting( ScfPropertySet& rPropSet ) const
+{
+ // border style
+ namespace AwtVisualEffect = ::com::sun::star::awt::VisualEffect;
+ sal_Int16 nApiBorder = ::get_flagvalue( mnListFlags, EXC_OBJ_LISTBOX_FLAT, AwtVisualEffect::FLAT, AwtVisualEffect::LOOK3D );
+ rPropSet.SetProperty( "Border", nApiBorder );
+
+ // font formatting
+ if( mbHasDefFontIdx )
+ GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL, maTextData.maData.mnDefFontIdx );
+ else
+ GetFontBuffer().WriteDefaultCtrlFontProperties( rPropSet );
+}
+
+XclImpListBoxObj::XclImpListBoxObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjListBase( rRoot )
+{
+}
+
+void XclImpListBoxObj::ReadFullLbsData( XclImpStream& rStrm, std::size_t nRecLeft )
+{
+ std::size_t nRecEnd = rStrm.GetRecPos() + nRecLeft;
+ ReadLbsData( rStrm );
+ OSL_ENSURE( (rStrm.GetRecPos() == nRecEnd) || (rStrm.GetRecPos() + mnEntryCount == nRecEnd),
+ "XclImpListBoxObj::ReadFullLbsData - invalid size of OBJLBSDATA record" );
+ while (rStrm.IsValid())
+ {
+ if (rStrm.GetRecPos() >= nRecEnd)
+ break;
+ maSelection.push_back( rStrm.ReaduInt8() );
+ }
+}
+
+void XclImpListBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ ReadSbs( rStrm );
+ rStrm.Ignore( 18 );
+ maTextData.maData.mnDefFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+ ReadFullLbsData( rStrm, rStrm.GetRecLeft() );
+ mbHasDefFontIdx = true;
+}
+
+void XclImpListBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJLBSDATA:
+ ReadFullLbsData( rStrm, nSubRecSize );
+ break;
+ default:
+ XclImpTbxObjListBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpListBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // listbox formatting
+ SetBoxFormatting( rPropSet );
+
+ // selection type
+ sal_uInt8 nSelType = ::extract_value< sal_uInt8 >( mnListFlags, 4, 2 );
+ bool bMultiSel = nSelType != EXC_OBJ_LISTBOX_SINGLE;
+ rPropSet.SetBoolProperty( "MultiSelection", bMultiSel );
+
+ // selection (do not set, if listbox is linked to a cell)
+ if( HasCellLink() )
+ return;
+
+ ScfInt16Vec aSelVec;
+
+ // multi selection: API expects sequence of list entry indexes
+ if( bMultiSel )
+ {
+ sal_Int16 nIndex = 0;
+ for( const auto& rItem : maSelection )
+ {
+ if( rItem != 0 )
+ aSelVec.push_back( nIndex );
+ ++nIndex;
+ }
+ }
+ // single selection: mnSelEntry is one-based, API expects zero-based
+ else if( mnSelEntry > 0 )
+ aSelVec.push_back( static_cast< sal_Int16 >( mnSelEntry - 1 ) );
+
+ if( !aSelVec.empty() )
+ {
+ Sequence<sal_Int16> aSelSeq(aSelVec.data(), static_cast<sal_Int32>(aSelVec.size()));
+ rPropSet.SetProperty( "DefaultSelection", aSelSeq );
+ }
+}
+
+OUString XclImpListBoxObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.ListBox";
+}
+
+XclTbxEventType XclImpListBoxObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_CHANGE;
+}
+
+XclImpDropDownObj::XclImpDropDownObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjListBase( rRoot ),
+ mnLeft( 0 ),
+ mnTop( 0 ),
+ mnRight( 0 ),
+ mnBottom( 0 ),
+ mnDropDownFlags( 0 ),
+ mnLineCount( 0 ),
+ mnMinWidth( 0 )
+{
+}
+
+sal_uInt16 XclImpDropDownObj::GetDropDownType() const
+{
+ return ::extract_value< sal_uInt8 >( mnDropDownFlags, 0, 2 );
+}
+
+void XclImpDropDownObj::ReadFullLbsData( XclImpStream& rStrm )
+{
+ ReadLbsData( rStrm );
+ mnDropDownFlags = rStrm.ReaduInt16();
+ mnLineCount = rStrm.ReaduInt16();
+ mnMinWidth = rStrm.ReaduInt16();
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ // dropdowns of auto-filters have 'simple' style, they don't have a text area
+ if( GetDropDownType() == EXC_OBJ_DROPDOWN_SIMPLE )
+ SetProcessSdrObj( false );
+}
+
+void XclImpDropDownObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ ReadSbs( rStrm );
+ rStrm.Ignore( 18 );
+ maTextData.maData.mnDefFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 14 );
+ mnLeft = rStrm.ReaduInt16();
+ mnTop = rStrm.ReaduInt16();
+ mnRight = rStrm.ReaduInt16();
+ mnBottom = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+ ReadFullLbsData( rStrm );
+ mbHasDefFontIdx = true;
+}
+
+void XclImpDropDownObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJLBSDATA:
+ ReadFullLbsData( rStrm );
+ break;
+ default:
+ XclImpTbxObjListBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpDropDownObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // dropdown listbox formatting
+ SetBoxFormatting( rPropSet );
+ // enable dropdown button
+ rPropSet.SetBoolProperty( "Dropdown", true );
+ // dropdown line count
+ rPropSet.SetProperty( "LineCount", mnLineCount );
+
+ if( GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX )
+ {
+ // text of editable combobox
+ if( maTextData.mxString )
+ rPropSet.SetStringProperty( "DefaultText", maTextData.mxString->GetText() );
+ }
+ else
+ {
+ // selection (do not set, if dropdown is linked to a cell)
+ if( !HasCellLink() && (mnSelEntry > 0) )
+ {
+ Sequence< sal_Int16 > aSelSeq{ o3tl::narrowing<sal_Int16>(mnSelEntry - 1) };
+ rPropSet.SetProperty( "DefaultSelection", aSelSeq );
+ }
+ }
+}
+
+OUString XclImpDropDownObj::DoGetServiceName() const
+{
+ return (GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX) ?
+ OUString( "com.sun.star.form.component.ComboBox" ) :
+ OUString( "com.sun.star.form.component.ListBox" );
+}
+
+XclTbxEventType XclImpDropDownObj::DoGetEventType() const
+{
+ return (GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX) ? EXC_TBX_EVENT_TEXT : EXC_TBX_EVENT_CHANGE;
+}
+
+XclImpPictureObj::XclImpPictureObj( const XclImpRoot& rRoot ) :
+ XclImpRectObj( rRoot ),
+ XclImpControlHelper( rRoot, EXC_CTRL_BINDCONTENT ),
+ mnStorageId( 0 ),
+ mnCtlsStrmPos( 0 ),
+ mnCtlsStrmSize( 0 ),
+ mbEmbedded( false ),
+ mbLinked( false ),
+ mbSymbol( false ),
+ mbControl( false ),
+ mbUseCtlsStrm( false )
+{
+ SetAreaObj( true );
+ SetSimpleMacro( true );
+ SetCustomDffObj( true );
+}
+
+OUString XclImpPictureObj::GetOleStorageName() const
+{
+ OUStringBuffer aStrgName;
+ if( (mbEmbedded || mbLinked) && !mbControl && (mnStorageId > 0) )
+ {
+ aStrgName = mbEmbedded ? std::u16string_view(u"" EXC_STORAGE_OLE_EMBEDDED) : std::u16string_view(u"" EXC_STORAGE_OLE_LINKED);
+ static const char spcHexChars[] = "0123456789ABCDEF";
+ for( sal_uInt8 nIndex = 32; nIndex > 0; nIndex -= 4 )
+ aStrgName.append(OUStringChar( spcHexChars[ ::extract_value< sal_uInt8 >( mnStorageId, nIndex - 4, 4 ) ] ));
+ }
+ return aStrgName.makeStringAndClear();
+}
+
+void XclImpPictureObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ sal_uInt16 nLinkSize;
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 6 );
+ nLinkSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ ReadFlags3( rStrm );
+ ReadMacro3( rStrm, nMacroSize );
+ ReadPictFmla( rStrm, nLinkSize );
+
+ if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() )
+ maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm );
+}
+
+void XclImpPictureObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ sal_uInt16 nLinkSize;
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 6 );
+ nLinkSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ ReadFlags3( rStrm );
+ ReadMacro4( rStrm, nMacroSize );
+ ReadPictFmla( rStrm, nLinkSize );
+
+ if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() )
+ maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm );
+}
+
+void XclImpPictureObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ sal_uInt16 nLinkSize;
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 6 );
+ nLinkSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ ReadFlags3( rStrm );
+ rStrm.Ignore( 4 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+ ReadPictFmla( rStrm, nLinkSize );
+
+ if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() )
+ {
+ // page background is stored as hidden picture with name "__BkgndObj"
+ if ( IsHidden() && (GetObjName() == "__BkgndObj") )
+ GetPageSettings().ReadImgData( rStrm );
+ else
+ maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm );
+ }
+}
+
+void XclImpPictureObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJFLAGS:
+ ReadFlags8( rStrm );
+ break;
+ case EXC_ID_OBJPICTFMLA:
+ ReadPictFmla( rStrm, rStrm.ReaduInt16() );
+ break;
+ default:
+ XclImpDrawObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+SdrObjectUniquePtr XclImpPictureObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ // try to create an OLE object or form control
+ SdrObjectUniquePtr xSdrObj( rDffConv.CreateSdrObject( *this, rAnchorRect ) );
+
+ // insert a graphic replacement for unsupported ole object ( if none already
+ // exists ) Hmm ok, it's possibly that there has been some imported
+ // graphic at a base level but unlikely, normally controls have a valid
+ // preview in the IMGDATA record ( see below )
+ // It might be possible to push such an imported graphic up to this
+ // XclImpPictureObj instance but there are so many layers of indirection I
+ // don't see an easy way. This way at least ensures that we can
+ // avoid a 'blank' shape that can result from a failed control import
+ if ( !xSdrObj && IsOcxControl() && maGraphic.GetType() == GraphicType::NONE )
+ {
+ const_cast< XclImpPictureObj* >( this )->maGraphic =
+ SdrOle2Obj::GetEmptyOLEReplacementGraphic();
+ }
+ // no OLE - create a plain picture from IMGDATA record data
+ if( !xSdrObj && (maGraphic.GetType() != GraphicType::NONE) )
+ {
+ xSdrObj.reset(
+ new SdrGrafObj(
+ *GetDoc().GetDrawLayer(),
+ maGraphic,
+ rAnchorRect));
+ ConvertRectStyle( *xSdrObj );
+ }
+
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+OUString XclImpPictureObj::GetObjName() const
+{
+ if( IsOcxControl() )
+ {
+ OUString sName( GetObjectManager().GetOleNameOverride( GetTab(), GetObjId() ) );
+ if (!sName.isEmpty())
+ return sName;
+ }
+ return XclImpDrawObjBase::GetObjName();
+}
+
+void XclImpPictureObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ if( IsOcxControl() )
+ {
+ // do not call XclImpRectObj::DoPreProcessSdrObj(), it would trace missing "printable" feature
+ ProcessControl( *this );
+ }
+ else if( mbEmbedded || mbLinked )
+ {
+ // trace missing "printable" feature
+ XclImpRectObj::DoPreProcessSdrObj( rDffConv, rSdrObj );
+
+ SfxObjectShell* pDocShell = GetDocShell();
+ SdrOle2Obj* pOleSdrObj = dynamic_cast< SdrOle2Obj* >( &rSdrObj );
+ if( pOleSdrObj && pDocShell )
+ {
+ comphelper::EmbeddedObjectContainer& rEmbObjCont = pDocShell->GetEmbeddedObjectContainer();
+ Reference< XEmbeddedObject > xEmbObj = pOleSdrObj->GetObjRef();
+ OUString aOldName( pOleSdrObj->GetPersistName() );
+
+ /* The object persistence should be already in the storage, but
+ the object still might not be inserted into the container. */
+ if( rEmbObjCont.HasEmbeddedObject( aOldName ) )
+ {
+ if( !rEmbObjCont.HasEmbeddedObject( xEmbObj ) )
+ // filter code is allowed to call the following method
+ rEmbObjCont.AddEmbeddedObject( xEmbObj, aOldName );
+ }
+ else
+ {
+ /* If the object is still not in container it must be inserted
+ there, the name must be generated in this case. */
+ OUString aNewName;
+ rEmbObjCont.InsertEmbeddedObject( xEmbObj, aNewName );
+ if( aOldName != aNewName )
+ // SetPersistName, not SetName
+ pOleSdrObj->SetPersistName( aNewName );
+ }
+ }
+ }
+}
+
+void XclImpPictureObj::ReadFlags3( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.ReaduInt16();
+ mbSymbol = ::get_flag( nFlags, EXC_OBJ_PIC_SYMBOL );
+}
+
+void XclImpPictureObj::ReadFlags8( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.ReaduInt16();
+ mbSymbol = ::get_flag( nFlags, EXC_OBJ_PIC_SYMBOL );
+ mbControl = ::get_flag( nFlags, EXC_OBJ_PIC_CONTROL );
+ mbUseCtlsStrm = ::get_flag( nFlags, EXC_OBJ_PIC_CTLSSTREAM );
+ OSL_ENSURE( mbControl || !mbUseCtlsStrm, "XclImpPictureObj::ReadFlags8 - CTLS stream for controls only" );
+ SetProcessSdrObj( mbControl || !mbUseCtlsStrm );
+}
+
+void XclImpPictureObj::ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nLinkSize )
+{
+ std::size_t nLinkEnd = rStrm.GetRecPos() + nLinkSize;
+ if( nLinkSize >= 6 )
+ {
+ sal_uInt16 nFmlaSize;
+ nFmlaSize = rStrm.ReaduInt16();
+ OSL_ENSURE( nFmlaSize > 0, "XclImpPictureObj::ReadPictFmla - missing link formula" );
+ // BIFF3/BIFF4 do not support storages, nothing to do here
+ if( (nFmlaSize > 0) && (GetBiff() >= EXC_BIFF5) )
+ {
+ rStrm.Ignore( 4 );
+ sal_uInt8 nToken;
+ nToken = rStrm.ReaduInt8();
+
+ // different processing for linked vs. embedded OLE objects
+ if( nToken == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ) )
+ {
+ mbLinked = true;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5:
+ {
+ sal_Int16 nRefIdx;
+ sal_uInt16 nNameIdx;
+ nRefIdx = rStrm.ReadInt16();
+ rStrm.Ignore( 8 );
+ nNameIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 12 );
+ const ExtName* pExtName = GetOldRoot().pExtNameBuff->GetNameByIndex( nRefIdx, nNameIdx );
+ if( pExtName && pExtName->IsOLE() )
+ mnStorageId = pExtName->nStorageId;
+ }
+ break;
+ case EXC_BIFF8:
+ {
+ sal_uInt16 nXti, nExtName;
+ nXti = rStrm.ReaduInt16();
+ nExtName = rStrm.ReaduInt16();
+ const XclImpExtName* pExtName = GetLinkManager().GetExternName( nXti, nExtName );
+ if( pExtName && (pExtName->GetType() == xlExtOLE) )
+ mnStorageId = pExtName->GetStorageId();
+ }
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+ }
+ else if( nToken == XclTokenArrayHelper::GetTokenId( EXC_TOKID_TBL, EXC_TOKCLASS_NONE ) )
+ {
+ mbEmbedded = true;
+ OSL_ENSURE( nFmlaSize == 5, "XclImpPictureObj::ReadPictFmla - unexpected formula size" );
+ rStrm.Ignore( nFmlaSize - 1 ); // token ID already read
+ if( nFmlaSize & 1 )
+ rStrm.Ignore( 1 ); // padding byte
+
+ // a class name may follow inside the picture link
+ if( rStrm.GetRecPos() + 2 <= nLinkEnd )
+ {
+ sal_uInt16 nLen = rStrm.ReaduInt16();
+ if( nLen > 0 )
+ maClassName = (GetBiff() == EXC_BIFF8) ? rStrm.ReadUniString( nLen ) : rStrm.ReadRawByteString( nLen );
+ }
+ }
+ // else: ignore other formulas, e.g. pictures linked to cell ranges
+ }
+ }
+
+ // seek behind picture link data
+ rStrm.Seek( nLinkEnd );
+
+ // read additional data for embedded OLE objects following the picture link
+ if( IsOcxControl() )
+ {
+ // #i26521# form controls to be ignored
+ if( maClassName == "Forms.HTML:Hidden.1" )
+ {
+ SetProcessSdrObj( false );
+ return;
+ }
+
+ if( rStrm.GetRecLeft() <= 8 ) return;
+
+ // position and size of control data in 'Ctls' stream
+ mnCtlsStrmPos = static_cast< std::size_t >( rStrm.ReaduInt32() );
+ mnCtlsStrmSize = static_cast< std::size_t >( rStrm.ReaduInt32() );
+
+ if( rStrm.GetRecLeft() <= 8 ) return;
+
+ // additional string (16-bit characters), e.g. for progress bar control
+ sal_uInt32 nAddStrSize;
+ nAddStrSize = rStrm.ReaduInt32();
+ OSL_ENSURE( rStrm.GetRecLeft() >= nAddStrSize + 4, "XclImpPictureObj::ReadPictFmla - missing data" );
+ if( rStrm.GetRecLeft() >= nAddStrSize + 4 )
+ {
+ rStrm.Ignore( nAddStrSize );
+ // cell link and source range
+ ReadCellLinkFormula( rStrm, true );
+ ReadSourceRangeFormula( rStrm, true );
+ }
+ }
+ else if( mbEmbedded && (rStrm.GetRecLeft() >= 4) )
+ {
+ mnStorageId = rStrm.ReaduInt32();
+ }
+}
+
+// DFF stream conversion ======================================================
+
+void XclImpSolverContainer::InsertSdrObjectInfo( SdrObject& rSdrObj, sal_uInt32 nDffShapeId, ShapeFlag nDffFlags )
+{
+ if( nDffShapeId > 0 )
+ {
+ maSdrInfoMap[ nDffShapeId ].Set( &rSdrObj, nDffFlags );
+ maSdrObjMap[ &rSdrObj ] = nDffShapeId;
+ }
+}
+
+void XclImpSolverContainer::RemoveSdrObjectInfo( SdrObject& rSdrObj )
+{
+ // remove info of passed object from the maps
+ XclImpSdrObjMap::iterator aIt = maSdrObjMap.find( &rSdrObj );
+ if( aIt != maSdrObjMap.end() )
+ {
+ maSdrInfoMap.erase( aIt->second );
+ maSdrObjMap.erase( aIt );
+ }
+
+ // remove info of all child objects of a group object
+ if( SdrObjGroup* pGroupObj = dynamic_cast< SdrObjGroup* >( &rSdrObj ) )
+ {
+ if( SdrObjList* pSubList = pGroupObj->GetSubList() )
+ {
+ // iterate flat over the list because this function already works recursively
+ SdrObjListIter aObjIt( pSubList, SdrIterMode::Flat );
+ for( SdrObject* pChildObj = aObjIt.Next(); pChildObj; pChildObj = aObjIt.Next() )
+ RemoveSdrObjectInfo( *pChildObj );
+ }
+ }
+}
+
+void XclImpSolverContainer::UpdateConnectorRules()
+{
+ for (auto const & pRule : aCList)
+ {
+ UpdateConnection( pRule->nShapeA, pRule->pAObj, &pRule->nSpFlagsA );
+ UpdateConnection( pRule->nShapeB, pRule->pBObj, &pRule->nSpFlagsB );
+ UpdateConnection( pRule->nShapeC, pRule->pCObj );
+ }
+}
+
+void XclImpSolverContainer::RemoveConnectorRules()
+{
+ aCList.clear();
+ maSdrInfoMap.clear();
+ maSdrObjMap.clear();
+}
+
+void XclImpSolverContainer::UpdateConnection( sal_uInt32 nDffShapeId, SdrObject*& rpSdrObj, ShapeFlag* pnDffFlags )
+{
+ XclImpSdrInfoMap::const_iterator aIt = maSdrInfoMap.find( nDffShapeId );
+ if( aIt != maSdrInfoMap.end() )
+ {
+ rpSdrObj = aIt->second.mpSdrObj;
+ if( pnDffFlags )
+ *pnDffFlags = aIt->second.mnDffFlags;
+ }
+}
+
+XclImpSimpleDffConverter::XclImpSimpleDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm ) :
+ SvxMSDffManager( rDffStrm, rRoot.GetBasePath(), 0, nullptr, rRoot.GetDoc().GetDrawLayer(), 1440, COL_DEFAULT, nullptr ),
+ XclImpRoot( rRoot )
+{
+ SetSvxMSDffSettings( SVXMSDFF_SETTINGS_CROP_BITMAPS | SVXMSDFF_SETTINGS_IMPORT_EXCEL );
+}
+
+XclImpSimpleDffConverter::~XclImpSimpleDffConverter()
+{
+}
+
+bool XclImpSimpleDffConverter::GetColorFromPalette( sal_uInt16 nIndex, Color& rColor ) const
+{
+ Color nColor = GetPalette().GetColor( nIndex );
+
+ if( nColor == COL_AUTO )
+ return false;
+
+ rColor = nColor;
+ return true;
+}
+
+XclImpDffConverter::XclImpDffConvData::XclImpDffConvData(
+ XclImpDrawing& rDrawing, SdrModel& rSdrModel, SdrPage& rSdrPage ) :
+ mrDrawing( rDrawing ),
+ mrSdrModel( rSdrModel ),
+ mrSdrPage( rSdrPage ),
+ mnLastCtrlIndex( -1 ),
+ mbHasCtrlForm( false )
+{
+}
+
+constexpr OUStringLiteral gaStdFormName( u"Standard" ); /// Standard name of control forms.
+
+XclImpDffConverter::XclImpDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm ) :
+ XclImpSimpleDffConverter( rRoot, rDffStrm ),
+ oox::ole::MSConvertOCXControls( rRoot.GetDocShell()->GetModel() ),
+ mnOleImpFlags( 0 ),
+ mbNotifyMacroEventRead(false)
+{
+ const SvtFilterOptions& rFilterOpt = SvtFilterOptions::Get();
+ if( rFilterOpt.IsMathType2Math() )
+ mnOleImpFlags |= OLE_MATHTYPE_2_STARMATH;
+ if( rFilterOpt.IsWinWord2Writer() )
+ mnOleImpFlags |= OLE_WINWORD_2_STARWRITER;
+ if( rFilterOpt.IsPowerPoint2Impress() )
+ mnOleImpFlags |= OLE_POWERPOINT_2_STARIMPRESS;
+
+ // try to open the 'Ctls' storage stream containing OCX control properties
+ mxCtlsStrm = OpenStream( EXC_STREAM_CTLS );
+
+ // default text margin (convert EMU to drawing layer units)
+ mnDefTextMargin = EXC_OBJ_TEXT_MARGIN;
+ ScaleEmu( mnDefTextMargin );
+}
+
+XclImpDffConverter::~XclImpDffConverter()
+{
+}
+
+OUString XclImpObjectManager::GetOleNameOverride( SCTAB nTab, sal_uInt16 nObjId )
+{
+ OUString sOleName;
+ OUString sCodeName = GetExtDocOptions().GetCodeName( nTab );
+
+ if (mxOleCtrlNameOverride.is() && mxOleCtrlNameOverride->hasByName(sCodeName))
+ {
+ Reference< XIndexContainer > xIdToOleName;
+ mxOleCtrlNameOverride->getByName( sCodeName ) >>= xIdToOleName;
+ xIdToOleName->getByIndex( nObjId ) >>= sOleName;
+ }
+
+ return sOleName;
+}
+
+void XclImpDffConverter::StartProgressBar( std::size_t nProgressSize )
+{
+ mxProgress = std::make_shared<ScfProgressBar>( GetDocShell(), STR_PROGRESS_CALCULATING );
+ mxProgress->AddSegment( nProgressSize );
+ mxProgress->Activate();
+}
+
+void XclImpDffConverter::Progress( std::size_t nDelta )
+{
+ OSL_ENSURE( mxProgress, "XclImpDffConverter::Progress - invalid call, no progress bar" );
+ mxProgress->Progress( nDelta );
+}
+
+void XclImpDffConverter::InitializeDrawing( XclImpDrawing& rDrawing, SdrModel& rSdrModel, SdrPage& rSdrPage )
+{
+ XclImpDffConvDataRef xConvData = std::make_shared<XclImpDffConvData>( rDrawing, rSdrModel, rSdrPage );
+ maDataStack.push_back( xConvData );
+ SetModel( &xConvData->mrSdrModel, 1440 );
+}
+
+void XclImpDffConverter::ProcessObject( SdrObjList& rObjList, XclImpDrawObjBase& rDrawObj )
+{
+ if( !rDrawObj.IsProcessSdrObj() )
+ return;
+
+ const XclObjAnchor* pAnchor = rDrawObj.GetAnchor();
+ if(!pAnchor)
+ return;
+
+ tools::Rectangle aAnchorRect = GetConvData().mrDrawing.CalcAnchorRect( *pAnchor, false );
+ if( rDrawObj.IsValidSize( aAnchorRect ) )
+ {
+ // CreateSdrObject() recursively creates embedded child objects
+ SdrObjectUniquePtr xSdrObj( rDrawObj.CreateSdrObject( *this, aAnchorRect, false ) );
+ if( xSdrObj )
+ rDrawObj.PreProcessSdrObject( *this, *xSdrObj );
+ // call InsertSdrObject() also, if SdrObject is missing
+ InsertSdrObject( rObjList, rDrawObj, xSdrObj.release() );
+ }
+}
+
+void XclImpDffConverter::ProcessDrawing( const XclImpDrawObjVector& rDrawObjs )
+{
+ SdrPage& rSdrPage = GetConvData().mrSdrPage;
+ for( const auto& rxDrawObj : rDrawObjs )
+ ProcessObject( rSdrPage, *rxDrawObj );
+}
+
+void XclImpDffConverter::ProcessDrawing( SvStream& rDffStrm )
+{
+ if( rDffStrm.TellEnd() > 0 )
+ {
+ rDffStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ DffRecordHeader aHeader;
+ ReadDffRecordHeader( rDffStrm, aHeader );
+ OSL_ENSURE( aHeader.nRecType == DFF_msofbtDgContainer, "XclImpDffConverter::ProcessDrawing - unexpected record" );
+ if( aHeader.nRecType == DFF_msofbtDgContainer )
+ ProcessDgContainer( rDffStrm, aHeader );
+ }
+}
+
+void XclImpDffConverter::FinalizeDrawing()
+{
+ OSL_ENSURE( !maDataStack.empty(), "XclImpDffConverter::FinalizeDrawing - no drawing manager on stack" );
+ maDataStack.pop_back();
+ // restore previous model at core DFF converter
+ if( !maDataStack.empty() )
+ SetModel( &maDataStack.back()->mrSdrModel, 1440 );
+}
+
+void XclImpDffConverter::NotifyMacroEventRead()
+{
+ if (mbNotifyMacroEventRead)
+ return;
+ comphelper::DocumentInfo::notifyMacroEventRead(mxModel);
+ mbNotifyMacroEventRead = true;
+}
+
+SdrObjectUniquePtr XclImpDffConverter::CreateSdrObject( const XclImpTbxObjBase& rTbxObj, const tools::Rectangle& rAnchorRect )
+{
+ SdrObjectUniquePtr xSdrObj;
+
+ OUString aServiceName = rTbxObj.GetServiceName();
+ if( SupportsOleObjects() && !aServiceName.isEmpty() ) try
+ {
+ // create the form control from scratch
+ Reference< XFormComponent > xFormComp( ScfApiHelper::CreateInstance( GetDocShell(), aServiceName ), UNO_QUERY_THROW );
+ // set controls form, needed in virtual function InsertControl()
+ InitControlForm();
+ // try to insert the control into the form
+ css::awt::Size aDummySize;
+ Reference< XShape > xShape;
+ XclImpDffConvData& rConvData = GetConvData();
+ if( rConvData.mxCtrlForm.is() && InsertControl( xFormComp, aDummySize, &xShape, true ) )
+ {
+ xSdrObj = rTbxObj.CreateSdrObjectFromShape( xShape, rAnchorRect );
+ // try to attach a macro to the control
+ ScriptEventDescriptor aDescriptor;
+ if( (rConvData.mnLastCtrlIndex >= 0) && rTbxObj.FillMacroDescriptor( aDescriptor ) )
+ {
+ NotifyMacroEventRead();
+ Reference< XEventAttacherManager > xEventMgr( rConvData.mxCtrlForm, UNO_QUERY_THROW );
+ xEventMgr->registerScriptEvent( rConvData.mnLastCtrlIndex, aDescriptor );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+
+ return xSdrObj;
+}
+
+SdrObjectUniquePtr XclImpDffConverter::CreateSdrObject( const XclImpPictureObj& rPicObj, const tools::Rectangle& rAnchorRect )
+{
+ SdrObjectUniquePtr xSdrObj;
+
+ if( SupportsOleObjects() )
+ {
+ if( rPicObj.IsOcxControl() )
+ {
+ if( mxCtlsStrm.is() ) try
+ {
+ /* set controls form, needed in virtual function InsertControl()
+ called from ReadOCXExcelKludgeStream() */
+ InitControlForm();
+
+ // read from mxCtlsStrm into xShape, insert the control model into the form
+ Reference< XShape > xShape;
+ if( GetConvData().mxCtrlForm.is() )
+ {
+ Reference< XFormComponent > xFComp;
+ ReadOCXCtlsStream( mxCtlsStrm, xFComp, rPicObj.GetCtlsStreamPos(), rPicObj.GetCtlsStreamSize() );
+ // recreate the method formerly known as ReadOCXExcelKludgeStream()
+ if ( xFComp.is() )
+ {
+ css::awt::Size aSz; // not used in import
+ ScfPropertySet aPropSet( xFComp );
+ aPropSet.SetStringProperty( "Name", rPicObj.GetObjName() );
+ InsertControl( xFComp, aSz,&xShape,true);
+ xSdrObj = rPicObj.CreateSdrObjectFromShape( xShape, rAnchorRect );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+ else
+ {
+ SfxObjectShell* pDocShell = GetDocShell();
+ tools::SvRef<SotStorage> xSrcStrg = GetRootStorage();
+ OUString aStrgName = rPicObj.GetOleStorageName();
+ if( pDocShell && xSrcStrg.is() && (!aStrgName.isEmpty()) )
+ {
+ // first try to resolve graphic from DFF storage
+ Graphic aGraphic;
+ tools::Rectangle aVisArea;
+ if( !GetBLIP( GetPropertyValue( DFF_Prop_pib, 0 ), aGraphic, &aVisArea ) )
+ {
+ // if not found, use graphic from object (imported from IMGDATA record)
+ aGraphic = rPicObj.GetGraphic();
+ }
+ if( aGraphic.GetType() != GraphicType::NONE )
+ {
+ ErrCode nError = ERRCODE_NONE;
+ namespace cssea = ::com::sun::star::embed::Aspects;
+ sal_Int64 nAspects = rPicObj.IsSymbol() ? cssea::MSOLE_ICON : cssea::MSOLE_CONTENT;
+ xSdrObj.reset(
+ CreateSdrOLEFromStorage(
+ GetConvData().mrSdrModel,
+ aStrgName,
+ xSrcStrg,
+ pDocShell->GetStorage(),
+ aGraphic,
+ rAnchorRect,
+ aVisArea,
+ nullptr,
+ nError,
+ mnOleImpFlags,
+ nAspects,
+ GetRoot().GetMedium().GetBaseURL()));
+ }
+ }
+ }
+ }
+
+ return xSdrObj;
+}
+
+bool XclImpDffConverter::SupportsOleObjects() const
+{
+ return GetConvData().mrDrawing.SupportsOleObjects();
+}
+
+// virtual functions ----------------------------------------------------------
+
+void XclImpDffConverter::ProcessClientAnchor2( SvStream& rDffStrm,
+ DffRecordHeader& rHeader, DffObjData& rObjData )
+{
+ // find the OBJ record data related to the processed shape
+ XclImpDffConvData& rConvData = GetConvData();
+ XclImpDrawObjBase* pDrawObj = rConvData.mrDrawing.FindDrawObj( rObjData.rSpHd ).get();
+ if(!pDrawObj)
+ return;
+
+ OSL_ENSURE( rHeader.nRecType == DFF_msofbtClientAnchor, "XclImpDffConverter::ProcessClientAnchor2 - no client anchor record" );
+ XclObjAnchor aAnchor;
+ rHeader.SeekToContent( rDffStrm );
+ sal_uInt8 nFlags(0);
+ rDffStrm.ReadUChar( nFlags );
+ rDffStrm.SeekRel( 1 ); // flags
+ rDffStrm >> aAnchor; // anchor format equal to BIFF5 OBJ records
+
+ if (!rDffStrm.good())
+ {
+ SAL_WARN("sc.filter", "ProcessClientAnchor2 short read");
+ return;
+ }
+
+ pDrawObj->SetAnchor( aAnchor );
+ rObjData.aChildAnchor = rConvData.mrDrawing.CalcAnchorRect( aAnchor, true );
+ rObjData.bChildAnchor = true;
+ // page anchoring is the best approximation we have if mbMove
+ // is set
+ rObjData.bPageAnchor = ( nFlags & 0x1 );
+}
+
+namespace {
+
+struct XclImpDrawObjClientData : public SvxMSDffClientData
+{
+ const XclImpDrawObjBase* m_pTopLevelObj;
+
+ XclImpDrawObjClientData()
+ : m_pTopLevelObj(nullptr)
+ {
+ }
+ virtual void NotifyFreeObj(SdrObject*) override {}
+};
+
+}
+
+SdrObject* XclImpDffConverter::ProcessObj( SvStream& rDffStrm, DffObjData& rDffObjData,
+ SvxMSDffClientData& rClientData, tools::Rectangle& /*rTextRect*/, SdrObject* pOldSdrObj )
+{
+ XclImpDffConvData& rConvData = GetConvData();
+
+ /* pOldSdrObj passes a generated SdrObject. This function owns this object
+ and can modify it. The function has either to return it back to caller
+ or to delete it by itself. */
+ SdrObjectUniquePtr xSdrObj( pOldSdrObj );
+
+ // find the OBJ record data related to the processed shape
+ XclImpDrawObjRef xDrawObj = rConvData.mrDrawing.FindDrawObj( rDffObjData.rSpHd );
+ const tools::Rectangle& rAnchorRect = rDffObjData.aChildAnchor;
+
+ // Do not process the global page group shape
+ bool bGlobalPageGroup( rDffObjData.nSpFlags & ShapeFlag::Patriarch );
+ if( !xDrawObj || !xDrawObj->IsProcessSdrObj() || bGlobalPageGroup )
+ return nullptr; // simply return, xSdrObj will be destroyed
+
+ /* Pass pointer to top-level object back to caller. If the processed
+ object is embedded in a group, the pointer is already set to the
+ top-level parent object. */
+ XclImpDrawObjClientData& rDrawObjClientData = static_cast<XclImpDrawObjClientData&>(rClientData);
+ const bool bIsTopLevel = !rDrawObjClientData.m_pTopLevelObj;
+ if (bIsTopLevel )
+ rDrawObjClientData.m_pTopLevelObj = xDrawObj.get();
+
+ // connectors don't have to be area objects
+ if( dynamic_cast< SdrEdgeObj* >( xSdrObj.get() ) )
+ xDrawObj->SetAreaObj( false );
+
+ /* Check for valid size for all objects. Needed to ignore lots of invisible
+ phantom objects from deleted rows or columns (for performance reasons).
+ #i30816# Include objects embedded in groups.
+ #i58780# Ignore group shapes, size is not initialized. */
+ bool bEmbeddedGroup = !bIsTopLevel && dynamic_cast< SdrObjGroup* >( xSdrObj.get() );
+ if( !bEmbeddedGroup && !xDrawObj->IsValidSize( rAnchorRect ) )
+ return nullptr; // simply return, xSdrObj will be destroyed
+
+ // set shape information from DFF stream
+ OUString aObjName = GetPropertyString( DFF_Prop_wzName, rDffStrm );
+ OUString aHyperlink = ReadHlinkProperty( rDffStrm );
+ bool bVisible = !GetPropertyBool( DFF_Prop_fHidden );
+ bool bAutoMargin = GetPropertyBool( DFF_Prop_AutoTextMargin );
+ xDrawObj->SetDffData( rDffObjData, aObjName, aHyperlink, bVisible, bAutoMargin );
+
+ /* Connect textbox data (string, alignment, text orientation) to object.
+ don't ask for a text-ID, DFF export doesn't set one. */
+ if( XclImpTextObj* pTextObj = dynamic_cast< XclImpTextObj* >( xDrawObj.get() ) )
+ if( const XclImpObjTextData* pTextData = rConvData.mrDrawing.FindTextData( rDffObjData.rSpHd ) )
+ pTextObj->SetTextData( *pTextData );
+
+ // copy line and fill formatting of TBX form controls from DFF properties
+ if( XclImpTbxObjBase* pTbxObj = dynamic_cast< XclImpTbxObjBase* >( xDrawObj.get() ) )
+ pTbxObj->SetDffProperties( *this );
+
+ // try to create a custom SdrObject that overwrites the passed object
+ SdrObjectUniquePtr xNewSdrObj( xDrawObj->CreateSdrObject( *this, rAnchorRect, true ) );
+ if( xNewSdrObj )
+ xSdrObj = std::move( xNewSdrObj );
+
+ // process the SdrObject
+ if( xSdrObj )
+ {
+ // filled without color -> set system window color
+ if( GetPropertyBool( DFF_Prop_fFilled ) && !IsProperty( DFF_Prop_fillColor ) )
+ xSdrObj->SetMergedItem( XFillColorItem( OUString(), GetPalette().GetColor( EXC_COLOR_WINDOWBACK ) ) );
+
+ // additional processing on the SdrObject
+ xDrawObj->PreProcessSdrObject( *this, *xSdrObj );
+
+ /* If the SdrObject will not be inserted into the draw page, delete it
+ here. Happens e.g. for notes: The PreProcessSdrObject() call above
+ has inserted the note into the document, and the SdrObject is not
+ needed anymore. */
+ if( !xDrawObj->IsInsertSdrObj() )
+ xSdrObj.reset();
+ }
+
+ if( xSdrObj )
+ {
+ /* Store the relation between shape ID and SdrObject for connectors.
+ Must be done here (and not in InsertSdrObject() function),
+ otherwise all SdrObjects embedded in groups would be lost. */
+ rConvData.maSolverCont.InsertSdrObjectInfo( *xSdrObj, xDrawObj->GetDffShapeId(), xDrawObj->GetDffFlags() );
+
+ /* If the drawing object is embedded in a group object, call
+ PostProcessSdrObject() here. For top-level objects this will be
+ done automatically in InsertSdrObject() but grouped shapes are
+ inserted into their groups somewhere in the SvxMSDffManager base
+ class without chance of notification. Unfortunately, now this is
+ called before the object is really inserted into its group object,
+ but that should not have any effect for grouped objects. */
+ if( !bIsTopLevel )
+ xDrawObj->PostProcessSdrObject( *this, *xSdrObj );
+ }
+
+ return xSdrObj.release();
+}
+
+SdrObject* XclImpDffConverter::FinalizeObj(DffObjData& rDffObjData, SdrObject* pOldSdrObj )
+{
+ XclImpDffConvData& rConvData = GetConvData();
+
+ /* pOldSdrObj passes a generated SdrObject. This function owns this object
+ and can modify it. The function has either to return it back to caller
+ or to delete it by itself. */
+ SdrObjectUniquePtr xSdrObj( pOldSdrObj );
+
+ // find the OBJ record data related to the processed shape
+ XclImpDrawObjRef xDrawObj = rConvData.mrDrawing.FindDrawObj( rDffObjData.rSpHd );
+
+ if( xSdrObj && xDrawObj )
+ {
+ // cell anchoring
+ if ( !rDffObjData.bPageAnchor )
+ ScDrawLayer::SetCellAnchoredFromPosition( *xSdrObj, GetDoc(), xDrawObj->GetTab(), false );
+ }
+
+ return xSdrObj.release();
+}
+
+bool XclImpDffConverter::InsertControl( const Reference< XFormComponent >& rxFormComp,
+ const css::awt::Size& /*rSize*/, Reference< XShape >* pxShape,
+ bool /*bFloatingCtrl*/ )
+{
+ if( GetDocShell() ) try
+ {
+ XclImpDffConvData& rConvData = GetConvData();
+ Reference< XIndexContainer > xFormIC( rConvData.mxCtrlForm, UNO_QUERY_THROW );
+ Reference< XControlModel > xCtrlModel( rxFormComp, UNO_QUERY_THROW );
+
+ // create the control shape
+ Reference< XShape > xShape( ScfApiHelper::CreateInstance( GetDocShell(), "com.sun.star.drawing.ControlShape" ), UNO_QUERY_THROW );
+ Reference< XControlShape > xCtrlShape( xShape, UNO_QUERY_THROW );
+
+ // insert the new control into the form
+ sal_Int32 nNewIndex = xFormIC->getCount();
+ xFormIC->insertByIndex( nNewIndex, Any( rxFormComp ) );
+ // on success: store new index of the control for later use (macro events)
+ rConvData.mnLastCtrlIndex = nNewIndex;
+
+ // set control model at control shape and pass back shape to caller
+ xCtrlShape->setControl( xCtrlModel );
+ if( pxShape ) *pxShape = xShape;
+ return true;
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "XclImpDffConverter::InsertControl - cannot create form control" );
+ }
+
+ return false;
+}
+
+// private --------------------------------------------------------------------
+
+XclImpDffConverter::XclImpDffConvData& XclImpDffConverter::GetConvData()
+{
+ OSL_ENSURE( !maDataStack.empty(), "XclImpDffConverter::GetConvData - no drawing manager on stack" );
+ return *maDataStack.back();
+}
+
+const XclImpDffConverter::XclImpDffConvData& XclImpDffConverter::GetConvData() const
+{
+ OSL_ENSURE( !maDataStack.empty(), "XclImpDffConverter::GetConvData - no drawing manager on stack" );
+ return *maDataStack.back();
+}
+
+OUString XclImpDffConverter::ReadHlinkProperty( SvStream& rDffStrm ) const
+{
+ /* Reads hyperlink data from a complex DFF property. Contents of this
+ property are equal to the HLINK record, import of this record is
+ implemented in class XclImpHyperlink. This function has to create an
+ instance of the XclImpStream class to be able to reuse the
+ functionality of XclImpHyperlink. */
+ OUString aString;
+ sal_uInt32 nBufferSize = GetPropertyValue( DFF_Prop_pihlShape, 0 );
+ if( (0 < nBufferSize) && (nBufferSize <= 0xFFFF) && SeekToContent( DFF_Prop_pihlShape, rDffStrm ) )
+ {
+ // create a faked BIFF record that can be read by XclImpStream class
+ SvMemoryStream aMemStream;
+ aMemStream.WriteUInt16( 0 ).WriteUInt16( nBufferSize );
+
+ // copy from DFF stream to memory stream
+ ::std::vector< sal_uInt8 > aBuffer( nBufferSize );
+ sal_uInt8* pnData = aBuffer.data();
+ if (rDffStrm.ReadBytes(pnData, nBufferSize) == nBufferSize)
+ {
+ aMemStream.WriteBytes(pnData, nBufferSize);
+
+ // create BIFF import stream to be able to use XclImpHyperlink class
+ XclImpStream aXclStrm( aMemStream, GetRoot() );
+ if( aXclStrm.StartNextRecord() )
+ aString = XclImpHyperlink::ReadEmbeddedData( aXclStrm );
+ }
+ }
+ return aString;
+}
+
+bool XclImpDffConverter::ProcessDgContainer( SvStream& rDffStrm, const DffRecordHeader& rDgHeader )
+{
+ std::size_t nEndPos = rDgHeader.GetRecEndFilePos();
+ bool isBreak(false);
+ while (!isBreak && rDffStrm.good() && rDffStrm.Tell() < nEndPos)
+ {
+ DffRecordHeader aHeader;
+ ReadDffRecordHeader( rDffStrm, aHeader );
+ switch( aHeader.nRecType )
+ {
+ case DFF_msofbtSolverContainer:
+ isBreak = !ProcessSolverContainer( rDffStrm, aHeader );
+ break;
+ case DFF_msofbtSpgrContainer:
+ isBreak = !ProcessShGrContainer( rDffStrm, aHeader );
+ break;
+ default:
+ isBreak = !aHeader.SeekToEndOfRecord( rDffStrm );
+ }
+ }
+ // seek to end of drawing page container
+ isBreak = !rDgHeader.SeekToEndOfRecord( rDffStrm );
+
+ // #i12638# #i37900# connector rules
+ XclImpSolverContainer& rSolverCont = GetConvData().maSolverCont;
+ rSolverCont.UpdateConnectorRules();
+ SolveSolver( rSolverCont );
+ rSolverCont.RemoveConnectorRules();
+ return !isBreak;
+}
+
+bool XclImpDffConverter::ProcessShGrContainer( SvStream& rDffStrm, const DffRecordHeader& rShGrHeader )
+{
+ std::size_t nEndPos = rShGrHeader.GetRecEndFilePos();
+ bool isBreak(false);
+ while (!isBreak && rDffStrm.good() && rDffStrm.Tell() < nEndPos)
+ {
+ DffRecordHeader aHeader;
+ ReadDffRecordHeader( rDffStrm, aHeader );
+ switch( aHeader.nRecType )
+ {
+ case DFF_msofbtSpgrContainer:
+ case DFF_msofbtSpContainer:
+ isBreak = !ProcessShContainer( rDffStrm, aHeader );
+ break;
+ default:
+ isBreak = !aHeader.SeekToEndOfRecord( rDffStrm );
+ }
+ }
+ // seek to end of shape group container
+ return rShGrHeader.SeekToEndOfRecord( rDffStrm ) && !isBreak;
+}
+
+bool XclImpDffConverter::ProcessSolverContainer( SvStream& rDffStrm, const DffRecordHeader& rSolverHeader )
+{
+ // solver container wants to read the solver container header again
+ rSolverHeader.SeekToBegOfRecord( rDffStrm );
+ // read the entire solver container
+ ReadSvxMSDffSolverContainer( rDffStrm, GetConvData().maSolverCont );
+ // seek to end of solver container
+ return rSolverHeader.SeekToEndOfRecord( rDffStrm );
+}
+
+bool XclImpDffConverter::ProcessShContainer( SvStream& rDffStrm, const DffRecordHeader& rShHeader )
+{
+ rShHeader.SeekToBegOfRecord( rDffStrm );
+ tools::Rectangle aDummy;
+ XclImpDrawObjClientData aDrawObjClientData;
+ /* The call to ImportObj() creates and returns a new SdrObject for the
+ processed shape. We take ownership of the returned object here. If the
+ shape is a group object, all embedded objects are created recursively,
+ and the returned group object contains them all. ImportObj() calls the
+ virtual functions ProcessClientAnchor2() and ProcessObj() and writes
+ the pointer to the related draw object data (OBJ record) into aDrawObjClientData. */
+ SdrObjectUniquePtr xSdrObj( ImportObj( rDffStrm, aDrawObjClientData, aDummy, aDummy, /*nCalledByGroup*/0, /*pShapeId*/nullptr ) );
+ if (aDrawObjClientData.m_pTopLevelObj && xSdrObj )
+ InsertSdrObject( GetConvData().mrSdrPage, *aDrawObjClientData.m_pTopLevelObj, xSdrObj.release() );
+ return rShHeader.SeekToEndOfRecord( rDffStrm );
+}
+
+void XclImpDffConverter::InsertSdrObject( SdrObjList& rObjList, const XclImpDrawObjBase& rDrawObj, SdrObject* pSdrObj )
+{
+ XclImpDffConvData& rConvData = GetConvData();
+ /* Take ownership of the passed object. If insertion fails (e.g. rDrawObj
+ states to skip insertion), the object is automatically deleted. */
+ SdrObjectUniquePtr xSdrObj( pSdrObj );
+ if( xSdrObj && rDrawObj.IsInsertSdrObj() )
+ {
+ rObjList.NbcInsertObject( xSdrObj.release() );
+ // callback to drawing manager for e.g. tracking of used sheet area
+ rConvData.mrDrawing.OnObjectInserted( rDrawObj );
+ // callback to drawing object for post processing (use pSdrObj, xSdrObj already released)
+ rDrawObj.PostProcessSdrObject( *this, *pSdrObj );
+ }
+ /* SdrObject still here? Insertion failed, remove data from shape ID map.
+ The SdrObject will be destructed then. */
+ if( xSdrObj )
+ rConvData.maSolverCont.RemoveSdrObjectInfo( *xSdrObj );
+}
+
+void XclImpDffConverter::InitControlForm()
+{
+ XclImpDffConvData& rConvData = GetConvData();
+ if( rConvData.mbHasCtrlForm )
+ return;
+
+ rConvData.mbHasCtrlForm = true;
+ if( !SupportsOleObjects() )
+ return;
+
+ try
+ {
+ Reference< XFormsSupplier > xFormsSupplier( rConvData.mrSdrPage.getUnoPage(), UNO_QUERY_THROW );
+ Reference< XNameContainer > xFormsNC( xFormsSupplier->getForms(), UNO_SET_THROW );
+ // find or create the Standard form used to insert the imported controls
+ if( xFormsNC->hasByName( gaStdFormName ) )
+ {
+ xFormsNC->getByName( gaStdFormName ) >>= rConvData.mxCtrlForm;
+ }
+ else if( SfxObjectShell* pDocShell = GetDocShell() )
+ {
+ rConvData.mxCtrlForm.set( ScfApiHelper::CreateInstance( pDocShell, "com.sun.star.form.component.Form" ), UNO_QUERY_THROW );
+ xFormsNC->insertByName( gaStdFormName, Any( rConvData.mxCtrlForm ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+// Drawing manager ============================================================
+
+XclImpDrawing::XclImpDrawing( const XclImpRoot& rRoot, bool bOleObjects ) :
+ XclImpRoot( rRoot ),
+ mbOleObjs( bOleObjects )
+{
+}
+
+XclImpDrawing::~XclImpDrawing()
+{
+}
+
+Graphic XclImpDrawing::ReadImgData( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ Graphic aGraphic;
+ sal_uInt16 nFormat = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );//nEnv
+ sal_uInt32 nDataSize = rStrm.ReaduInt32();
+ if( nDataSize <= rStrm.GetRecLeft() )
+ {
+ switch( nFormat )
+ {
+ case EXC_IMGDATA_WMF: ReadWmf( aGraphic, rStrm ); break;
+ case EXC_IMGDATA_BMP: ReadBmp( aGraphic, rRoot, rStrm ); break;
+ default: OSL_FAIL( "XclImpDrawing::ReadImgData - unknown image format" );
+ }
+ }
+ return aGraphic;
+}
+
+void XclImpDrawing::ReadObj( XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ /* #i61786# In BIFF8 streams, OBJ records may occur without MSODRAWING
+ records. In this case, the OBJ records are in BIFF5 format. Do a sanity
+ check here that there is no DFF data loaded before. */
+ OSL_ENSURE( maDffStrm.Tell() == 0, "XclImpDrawing::ReadObj - unexpected DFF stream data, OBJ will be ignored" );
+ if( maDffStrm.Tell() == 0 ) switch( GetBiff() )
+ {
+ case EXC_BIFF3:
+ xDrawObj = XclImpDrawObjBase::ReadObj3( GetRoot(), rStrm );
+ break;
+ case EXC_BIFF4:
+ xDrawObj = XclImpDrawObjBase::ReadObj4( GetRoot(), rStrm );
+ break;
+ case EXC_BIFF5:
+ case EXC_BIFF8:
+ xDrawObj = XclImpDrawObjBase::ReadObj5( GetRoot(), rStrm );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+
+ if( xDrawObj )
+ {
+ // insert into maRawObjs or into the last open group object
+ maRawObjs.InsertGrouped( xDrawObj );
+ // to be able to find objects by ID
+ maObjMapId[ xDrawObj->GetObjId() ] = xDrawObj;
+ }
+}
+
+void XclImpDrawing::ReadMsoDrawing( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
+ // disable internal CONTINUE handling
+ rStrm.ResetRecord( false );
+ // read leading MSODRAWING record
+ ReadDffRecord( rStrm );
+
+ // read following drawing records, but do not start following unrelated record
+ bool bLoop = true;
+ while( bLoop ) switch( rStrm.GetNextRecId() )
+ {
+ case EXC_ID_MSODRAWING:
+ case EXC_ID_MSODRAWINGSEL:
+ case EXC_ID_CONT:
+ rStrm.StartNextRecord();
+ ReadDffRecord( rStrm );
+ break;
+ case EXC_ID_OBJ:
+ rStrm.StartNextRecord();
+ ReadObj8( rStrm );
+ break;
+ case EXC_ID_TXO:
+ rStrm.StartNextRecord();
+ ReadTxo( rStrm );
+ break;
+ default:
+ bLoop = false;
+ }
+
+ // re-enable internal CONTINUE handling
+ rStrm.ResetRecord( true );
+}
+
+XclImpDrawObjRef XclImpDrawing::FindDrawObj( const DffRecordHeader& rHeader ) const
+{
+ /* maObjMap stores objects by position of the client data (OBJ record) in
+ the DFF stream, which is always behind shape start position of the
+ passed header. The function upper_bound() finds the first element in
+ the map whose key is greater than the start position of the header. Its
+ end position is used to test whether the found object is really related
+ to the shape. */
+ XclImpDrawObjRef xDrawObj;
+ XclImpObjMap::const_iterator aIt = maObjMap.upper_bound( rHeader.GetRecBegFilePos() );
+ if( (aIt != maObjMap.end()) && (aIt->first <= rHeader.GetRecEndFilePos()) )
+ xDrawObj = aIt->second;
+ return xDrawObj;
+}
+
+XclImpDrawObjRef XclImpDrawing::FindDrawObj( sal_uInt16 nObjId ) const
+{
+ XclImpDrawObjRef xDrawObj;
+ XclImpObjMapById::const_iterator aIt = maObjMapId.find( nObjId );
+ if( aIt != maObjMapId.end() )
+ xDrawObj = aIt->second;
+ return xDrawObj;
+}
+
+const XclImpObjTextData* XclImpDrawing::FindTextData( const DffRecordHeader& rHeader ) const
+{
+ /* maTextMap stores textbox data by position of the client data (TXO
+ record) in the DFF stream, which is always behind shape start position
+ of the passed header. The function upper_bound() finds the first
+ element in the map whose key is greater than the start position of the
+ header. Its end position is used to test whether the found object is
+ really related to the shape. */
+ XclImpObjTextMap::const_iterator aIt = maTextMap.upper_bound( rHeader.GetRecBegFilePos() );
+ if( (aIt != maTextMap.end()) && (aIt->first <= rHeader.GetRecEndFilePos()) )
+ return aIt->second.get();
+ return nullptr;
+}
+
+void XclImpDrawing::SetSkipObj( sal_uInt16 nObjId )
+{
+ maSkipObjs.push_back( nObjId );
+}
+
+std::size_t XclImpDrawing::GetProgressSize() const
+{
+ return std::accumulate(maObjMap.begin(), maObjMap.end(), maRawObjs.GetProgressSize(),
+ [](const std::size_t& rSum, const XclImpObjMap::value_type& rEntry) { return rSum + rEntry.second->GetProgressSize(); });
+}
+
+void XclImpDrawing::ImplConvertObjects( XclImpDffConverter& rDffConv, SdrModel& rSdrModel, SdrPage& rSdrPage )
+{
+ //rhbz#636521, disable undo during conversion. faster, smaller and stops
+ //temp objects being inserted into the undo list
+ bool bOrigUndoStatus = rSdrModel.IsUndoEnabled();
+ rSdrModel.EnableUndo(false);
+ // register this drawing manager at the passed (global) DFF manager
+ rDffConv.InitializeDrawing( *this, rSdrModel, rSdrPage );
+ // process list of objects to be skipped
+ for( const auto& rSkipObj : maSkipObjs )
+ if( XclImpDrawObjBase* pDrawObj = FindDrawObj( rSkipObj ).get() )
+ pDrawObj->SetProcessSdrObj( false );
+ // process drawing objects without DFF data
+ rDffConv.ProcessDrawing( maRawObjs );
+ // process all objects in the DFF stream
+ rDffConv.ProcessDrawing( maDffStrm );
+ // unregister this drawing manager at the passed (global) DFF manager
+ rDffConv.FinalizeDrawing();
+ rSdrModel.EnableUndo(bOrigUndoStatus);
+}
+
+// protected ------------------------------------------------------------------
+
+void XclImpDrawing::AppendRawObject( const XclImpDrawObjRef& rxDrawObj )
+{
+ OSL_ENSURE( rxDrawObj, "XclImpDrawing::AppendRawObject - unexpected empty reference" );
+ maRawObjs.push_back( rxDrawObj );
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpDrawing::ReadWmf( Graphic& rGraphic, XclImpStream& rStrm ) // static helper
+{
+ // extract graphic data from IMGDATA and following CONTINUE records
+ rStrm.Ignore( 8 );
+ SvMemoryStream aMemStrm;
+ rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() );
+ aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ // import the graphic from memory stream
+ GDIMetaFile aGDIMetaFile;
+ if( ::ReadWindowMetafile( aMemStrm, aGDIMetaFile ) )
+ rGraphic = aGDIMetaFile;
+}
+
+void XclImpDrawing::ReadBmp( Graphic& rGraphic, const XclImpRoot& rRoot, XclImpStream& rStrm ) // static helper
+{
+ // extract graphic data from IMGDATA and following CONTINUE records
+ SvMemoryStream aMemStrm;
+
+ /* Excel 3 and 4 seem to write broken BMP data. Usually they write a
+ DIBCOREHEADER (12 bytes) containing width, height, planes = 1, and
+ pixel depth = 32 bit. After that, 3 unused bytes are added before the
+ actual pixel data. This does even confuse Excel 5 and later, which
+ cannot read the image data correctly. */
+ if( rRoot.GetBiff() <= EXC_BIFF4 )
+ {
+ rStrm.PushPosition();
+ sal_uInt32 nHdrSize;
+ sal_uInt16 nWidth, nHeight, nPlanes, nDepth;
+ nHdrSize = rStrm.ReaduInt32();
+ nWidth = rStrm.ReaduInt16();
+ nHeight = rStrm.ReaduInt16();
+ nPlanes = rStrm.ReaduInt16();
+ nDepth = rStrm.ReaduInt16();
+ if( (nHdrSize == 12) && (nPlanes == 1) && (nDepth == 32) )
+ {
+ rStrm.Ignore( 3 );
+ aMemStrm.SetEndian( SvStreamEndian::LITTLE );
+ aMemStrm.WriteUInt32( nHdrSize ).WriteUInt16( nWidth ).WriteUInt16( nHeight ).WriteUInt16( nPlanes ).WriteUInt16( nDepth );
+ rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() );
+ }
+ rStrm.PopPosition();
+ }
+
+ // no special handling above -> just copy the remaining record data
+ if( aMemStrm.Tell() == 0 )
+ rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() );
+
+ // import the graphic from memory stream
+ aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ Bitmap aBitmap;
+ if( ReadDIB(aBitmap, aMemStrm, false) ) // read DIB without file header
+ rGraphic = BitmapEx(aBitmap);
+}
+
+void XclImpDrawing::ReadDffRecord( XclImpStream& rStrm )
+{
+ maDffStrm.Seek( STREAM_SEEK_TO_END );
+ rStrm.CopyRecordToStream( maDffStrm );
+}
+
+void XclImpDrawing::ReadObj8( XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj = XclImpDrawObjBase::ReadObj8( GetRoot(), rStrm );
+ // store the new object in the internal containers
+ maObjMap[ maDffStrm.Tell() ] = xDrawObj;
+ maObjMapId[ xDrawObj->GetObjId() ] = xDrawObj;
+}
+
+void XclImpDrawing::ReadTxo( XclImpStream& rStrm )
+{
+ XclImpObjTextRef xTextData = std::make_shared<XclImpObjTextData>();
+ maTextMap[ maDffStrm.Tell() ] = xTextData;
+
+ // 1) read the TXO record
+ xTextData->maData.ReadTxo8( rStrm );
+
+ // 2) first CONTINUE with string
+ xTextData->mxString.reset();
+ bool bValid = true;
+ if( xTextData->maData.mnTextLen > 0 )
+ {
+ bValid = (rStrm.GetNextRecId() == EXC_ID_CONT) && rStrm.StartNextRecord();
+ OSL_ENSURE( bValid, "XclImpDrawing::ReadTxo - missing CONTINUE record" );
+ if( bValid )
+ xTextData->mxString = std::make_shared<XclImpString>( rStrm.ReadUniString( xTextData->maData.mnTextLen ) );
+ }
+
+ // 3) second CONTINUE with formatting runs
+ if( xTextData->maData.mnFormatSize > 0 )
+ {
+ bValid = (rStrm.GetNextRecId() == EXC_ID_CONT) && rStrm.StartNextRecord();
+ OSL_ENSURE( bValid, "XclImpDrawing::ReadTxo - missing CONTINUE record" );
+ if( bValid )
+ xTextData->ReadFormats( rStrm );
+ }
+}
+
+XclImpSheetDrawing::XclImpSheetDrawing( const XclImpRoot& rRoot, SCTAB nScTab ) :
+ XclImpDrawing( rRoot, true ),
+ maScUsedArea( ScAddress::INITIALIZE_INVALID )
+{
+ maScUsedArea.aStart.SetTab( nScTab );
+ maScUsedArea.aEnd.SetTab( nScTab );
+}
+
+void XclImpSheetDrawing::ReadNote( XclImpStream& rStrm )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ ReadNote3( rStrm );
+ break;
+ case EXC_BIFF8:
+ ReadNote8( rStrm );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+void XclImpSheetDrawing::ReadTabChart( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF5 );
+ auto xChartObj = std::make_shared<XclImpChartObj>( GetRoot(), true );
+ xChartObj->ReadChartSubStream( rStrm );
+ // insert the chart as raw object without connected DFF data
+ AppendRawObject( xChartObj );
+}
+
+void XclImpSheetDrawing::ConvertObjects( XclImpDffConverter& rDffConv )
+{
+ if( SdrModel* pSdrModel = GetDoc().GetDrawLayer() )
+ if( SdrPage* pSdrPage = GetSdrPage( maScUsedArea.aStart.Tab() ) )
+ ImplConvertObjects( rDffConv, *pSdrModel, *pSdrPage );
+}
+
+tools::Rectangle XclImpSheetDrawing::CalcAnchorRect( const XclObjAnchor& rAnchor, bool /*bDffAnchor*/ ) const
+{
+ return rAnchor.GetRect( GetRoot(), maScUsedArea.aStart.Tab(), MapUnit::Map100thMM );
+}
+
+void XclImpSheetDrawing::OnObjectInserted( const XclImpDrawObjBase& rDrawObj )
+{
+ ScRange aScObjArea = rDrawObj.GetUsedArea( maScUsedArea.aStart.Tab() );
+ if( aScObjArea.IsValid() )
+ maScUsedArea.ExtendTo( aScObjArea );
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpSheetDrawing::ReadNote3( XclImpStream& rStrm )
+{
+ XclAddress aXclPos;
+ rStrm >> aXclPos;
+ sal_uInt16 nTotalLen = rStrm.ReaduInt16();
+
+ ScAddress aScNotePos( ScAddress::UNINITIALIZED );
+ if( !GetAddressConverter().ConvertAddress( aScNotePos, aXclPos, maScUsedArea.aStart.Tab(), true ) )
+ return;
+
+ sal_uInt16 nPartLen = ::std::min( nTotalLen, static_cast< sal_uInt16 >( rStrm.GetRecLeft() ) );
+ OUStringBuffer aNoteText(rStrm.ReadRawByteString( nPartLen ));
+ nTotalLen = nTotalLen - nPartLen;
+ while (true)
+ {
+ if (!nTotalLen)
+ break;
+ if (rStrm.GetNextRecId() != EXC_ID_NOTE)
+ break;
+ if (!rStrm.StartNextRecord())
+ break;
+ rStrm >> aXclPos;
+ nPartLen = rStrm.ReaduInt16();
+ OSL_ENSURE( aXclPos.mnRow == 0xFFFF, "XclImpObjectManager::ReadNote3 - missing continuation NOTE record" );
+ if( aXclPos.mnRow == 0xFFFF )
+ {
+ OSL_ENSURE( nPartLen <= nTotalLen, "XclImpObjectManager::ReadNote3 - string too long" );
+ aNoteText.append(rStrm.ReadRawByteString( nPartLen ));
+ nTotalLen = nTotalLen - ::std::min( nTotalLen, nPartLen );
+ }
+ else
+ {
+ // seems to be a new note, record already started -> load the note
+ rStrm.Seek( EXC_REC_SEEK_TO_BEGIN );
+ ReadNote( rStrm );
+ nTotalLen = 0;
+ }
+ }
+ ScNoteUtil::CreateNoteFromString( GetDoc(), aScNotePos, aNoteText.makeStringAndClear(), false, false );
+}
+
+void XclImpSheetDrawing::ReadNote8( XclImpStream& rStrm )
+{
+ XclAddress aXclPos;
+ sal_uInt16 nFlags, nObjId;
+ rStrm >> aXclPos;
+ nFlags = rStrm.ReaduInt16();
+ nObjId = rStrm.ReaduInt16();
+
+ ScAddress aScNotePos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScNotePos, aXclPos, maScUsedArea.aStart.Tab(), true ) )
+ if( nObjId != EXC_OBJ_INVALID_ID )
+ if( XclImpNoteObj* pNoteObj = dynamic_cast< XclImpNoteObj* >( FindDrawObj( nObjId ).get() ) )
+ pNoteObj->SetNoteData( aScNotePos, nFlags );
+}
+
+// The object manager =========================================================
+
+XclImpObjectManager::XclImpObjectManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+ maDefObjNames[ EXC_OBJTYPE_GROUP ] = "Group";
+ maDefObjNames[ EXC_OBJTYPE_LINE ] = ScResId( STR_SHAPE_LINE );
+ maDefObjNames[ EXC_OBJTYPE_RECTANGLE ] = ScResId( STR_SHAPE_RECTANGLE );
+ maDefObjNames[ EXC_OBJTYPE_OVAL ] = ScResId( STR_SHAPE_OVAL );
+ maDefObjNames[ EXC_OBJTYPE_ARC ] = "Arc";
+ maDefObjNames[ EXC_OBJTYPE_CHART ] = "Chart";
+ maDefObjNames[ EXC_OBJTYPE_TEXT ] = "Text";
+ maDefObjNames[ EXC_OBJTYPE_BUTTON ] = ScResId( STR_FORM_BUTTON );
+ maDefObjNames[ EXC_OBJTYPE_PICTURE ] = "Picture";
+ maDefObjNames[ EXC_OBJTYPE_POLYGON ] = "Freeform";
+ maDefObjNames[ EXC_OBJTYPE_CHECKBOX ] = ScResId( STR_FORM_CHECKBOX );
+ maDefObjNames[ EXC_OBJTYPE_OPTIONBUTTON ] = ScResId( STR_FORM_OPTIONBUTTON );
+ maDefObjNames[ EXC_OBJTYPE_EDIT ] = "Edit Box";
+ maDefObjNames[ EXC_OBJTYPE_LABEL ] = ScResId( STR_FORM_LABEL );
+ maDefObjNames[ EXC_OBJTYPE_DIALOG ] = "Dialog Frame";
+ maDefObjNames[ EXC_OBJTYPE_SPIN ] = ScResId( STR_FORM_SPINNER );
+ maDefObjNames[ EXC_OBJTYPE_SCROLLBAR ] = ScResId( STR_FORM_SCROLLBAR );
+ maDefObjNames[ EXC_OBJTYPE_LISTBOX ] = ScResId( STR_FORM_LISTBOX );
+ maDefObjNames[ EXC_OBJTYPE_GROUPBOX ] = ScResId( STR_FORM_GROUPBOX );
+ maDefObjNames[ EXC_OBJTYPE_DROPDOWN ] = ScResId( STR_FORM_DROPDOWN );
+ maDefObjNames[ EXC_OBJTYPE_NOTE ] = "Comment";
+ maDefObjNames[ EXC_OBJTYPE_DRAWING ] = ScResId( STR_SHAPE_AUTOSHAPE );
+}
+
+XclImpObjectManager::~XclImpObjectManager()
+{
+}
+
+void XclImpObjectManager::ReadMsoDrawingGroup( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
+ // Excel continues this record with MSODRAWINGGROUP and CONTINUE records, hmm.
+ rStrm.ResetRecord( true, EXC_ID_MSODRAWINGGROUP );
+ maDggStrm.Seek( STREAM_SEEK_TO_END );
+ rStrm.CopyRecordToStream( maDggStrm );
+}
+
+XclImpSheetDrawing& XclImpObjectManager::GetSheetDrawing( SCTAB nScTab )
+{
+ XclImpSheetDrawingRef& rxDrawing = maSheetDrawings[ nScTab ];
+ if( !rxDrawing )
+ rxDrawing = std::make_shared<XclImpSheetDrawing>( GetRoot(), nScTab );
+ return *rxDrawing;
+}
+
+void XclImpObjectManager::ConvertObjects()
+{
+ // do nothing if the document does not contain a drawing layer
+ if( !GetDoc().GetDrawLayer() )
+ return;
+
+ // get total progress bar size for all sheet drawing managers
+ std::size_t nProgressSize = std::accumulate(maSheetDrawings.begin(), maSheetDrawings.end(), std::size_t(0),
+ [](const std::size_t& rSum, const XclImpSheetDrawingMap::value_type& rEntry) { return rSum + rEntry.second->GetProgressSize(); });
+ // nothing to do if progress bar is zero (no objects present)
+ if( nProgressSize == 0 )
+ return;
+
+ XclImpDffConverter aDffConv( GetRoot(), maDggStrm );
+ aDffConv.StartProgressBar( nProgressSize );
+ for( auto& rEntry : maSheetDrawings )
+ rEntry.second->ConvertObjects( aDffConv );
+
+ // #i112436# don't call ScChartListenerCollection::SetDirty here,
+ // instead use InterpretDirtyCells in ScDocument::CalcAfterLoad.
+}
+
+OUString XclImpObjectManager::GetDefaultObjName( const XclImpDrawObjBase& rDrawObj ) const
+{
+ OUStringBuffer aDefName;
+ DefObjNameMap::const_iterator aIt = maDefObjNames.find( rDrawObj.GetObjType() );
+ if( aIt != maDefObjNames.end() )
+ aDefName.append(aIt->second);
+ return aDefName.append(' ').append(static_cast<sal_Int32>(rDrawObj.GetObjId())).makeStringAndClear();
+}
+
+ScRange XclImpObjectManager::GetUsedArea( SCTAB nScTab ) const
+{
+ XclImpSheetDrawingMap::const_iterator aIt = maSheetDrawings.find( nScTab );
+ if( aIt != maSheetDrawings.end() )
+ return aIt->second->GetUsedArea();
+ return ScRange( ScAddress::INITIALIZE_INVALID );
+}
+
+// DFF property set helper ====================================================
+
+XclImpDffPropSet::XclImpDffPropSet( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maDffConv( rRoot, maDummyStrm )
+{
+}
+
+void XclImpDffPropSet::Read( XclImpStream& rStrm )
+{
+ sal_uInt32 nPropSetSize;
+
+ rStrm.PushPosition();
+ rStrm.Ignore( 4 );
+ nPropSetSize = rStrm.ReaduInt32();
+ rStrm.PopPosition();
+
+ mxMemStrm.reset( new SvMemoryStream );
+ rStrm.CopyToStream( *mxMemStrm, 8 + nPropSetSize );
+ mxMemStrm->Seek( STREAM_SEEK_TO_BEGIN );
+ maDffConv.ReadPropSet( *mxMemStrm, nullptr );
+}
+
+sal_uInt32 XclImpDffPropSet::GetPropertyValue( sal_uInt16 nPropId ) const
+{
+ return maDffConv.GetPropertyValue( nPropId, 0 );
+}
+
+void XclImpDffPropSet::FillToItemSet( SfxItemSet& rItemSet ) const
+{
+ if( mxMemStrm )
+ maDffConv.ApplyAttributes( *mxMemStrm, rItemSet );
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclImpDffPropSet& rPropSet )
+{
+ rPropSet.Read( rStrm );
+ return rStrm;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiformula.cxx b/sc/source/filter/excel/xiformula.cxx
new file mode 100644
index 000000000..a5f4d7864
--- /dev/null
+++ b/sc/source/filter/excel/xiformula.cxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xiformula.hxx>
+#include <rangelst.hxx>
+#include <xistream.hxx>
+
+#include <excform.hxx>
+
+// Formula compiler ===========================================================
+
+/** Implementation class of the export formula compiler. */
+class XclImpFmlaCompImpl : protected XclImpRoot, protected XclTokenArrayHelper
+{
+public:
+ explicit XclImpFmlaCompImpl( const XclImpRoot& rRoot );
+
+ /** Creates a range list from the passed Excel token array. */
+ void CreateRangeList(
+ ScRangeList& rScRanges, XclFormulaType eType,
+ const XclTokenArray& rXclTokArr, XclImpStream& rStrm );
+
+ std::unique_ptr<ScTokenArray> CreateFormula( XclFormulaType eType, const XclTokenArray& rXclTokArr );
+
+};
+
+XclImpFmlaCompImpl::XclImpFmlaCompImpl( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpFmlaCompImpl::CreateRangeList(
+ ScRangeList& rScRanges, XclFormulaType /*eType*/,
+ const XclTokenArray& rXclTokArr, XclImpStream& /*rStrm*/ )
+{
+ rScRanges.RemoveAll();
+
+ //FIXME: evil hack, using old formula import :-)
+ if( !rXclTokArr.Empty() )
+ {
+ SvMemoryStream aMemStrm;
+ aMemStrm.WriteUInt16( EXC_ID_EOF ).WriteUInt16( rXclTokArr.GetSize() );
+ aMemStrm.WriteBytes(rXclTokArr.GetData(), rXclTokArr.GetSize());
+ XclImpStream aFmlaStrm( aMemStrm, GetRoot() );
+ aFmlaStrm.StartNextRecord();
+ GetOldFmlaConverter().GetAbsRefs( rScRanges, aFmlaStrm, aFmlaStrm.GetRecSize() );
+ }
+}
+
+std::unique_ptr<ScTokenArray> XclImpFmlaCompImpl::CreateFormula(
+ XclFormulaType /*eType*/, const XclTokenArray& rXclTokArr )
+{
+ if (rXclTokArr.Empty())
+ return nullptr;
+
+ // evil hack! are we trying to phase out the old style formula converter ?
+ SvMemoryStream aMemStrm;
+ aMemStrm.WriteUInt16( EXC_ID_EOF ).WriteUInt16( rXclTokArr.GetSize() );
+ aMemStrm.WriteBytes(rXclTokArr.GetData(), rXclTokArr.GetSize());
+ XclImpStream aFmlaStrm( aMemStrm, GetRoot() );
+ aFmlaStrm.StartNextRecord();
+ std::unique_ptr<ScTokenArray> pArray;
+ GetOldFmlaConverter().Reset();
+ GetOldFmlaConverter().Convert(pArray, aFmlaStrm, aFmlaStrm.GetRecSize(), true);
+ return pArray;
+}
+
+XclImpFormulaCompiler::XclImpFormulaCompiler( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mxImpl( std::make_shared<XclImpFmlaCompImpl>( rRoot ) )
+{
+}
+
+XclImpFormulaCompiler::~XclImpFormulaCompiler()
+{
+}
+
+void XclImpFormulaCompiler::CreateRangeList(
+ ScRangeList& rScRanges, XclFormulaType eType,
+ const XclTokenArray& rXclTokArr, XclImpStream& rStrm )
+{
+ mxImpl->CreateRangeList( rScRanges, eType, rXclTokArr, rStrm );
+}
+
+std::unique_ptr<ScTokenArray> XclImpFormulaCompiler::CreateFormula(
+ XclFormulaType eType, const XclTokenArray& rXclTokArr )
+{
+ return mxImpl->CreateFormula(eType, rXclTokArr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xihelper.cxx b/sc/source/filter/excel/xihelper.cxx
new file mode 100644
index 000000000..ef38c5b65
--- /dev/null
+++ b/sc/source/filter/excel/xihelper.cxx
@@ -0,0 +1,896 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <xihelper.hxx>
+#include <svl/itemset.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <editeng/editobj.hxx>
+#include <tools/urlobj.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/flditem.hxx>
+#include <document.hxx>
+#include <rangelst.hxx>
+#include <editutil.hxx>
+#include <attrib.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xistring.hxx>
+#include <xistyle.hxx>
+#include <excform.hxx>
+#include <scmatrix.hxx>
+#include <documentimport.hxx>
+#include <sal/log.hxx>
+
+// Excel->Calc cell address/range conversion ==================================
+
+namespace {
+
+/** Fills the passed Calc address with the passed Excel cell coordinates without checking any limits. */
+void lclFillAddress( ScAddress& rScPos, sal_uInt16 nXclCol, sal_uInt32 nXclRow, SCTAB nScTab )
+{
+ rScPos.SetCol( static_cast< SCCOL >( nXclCol ) );
+ rScPos.SetRow( static_cast< SCROW >( nXclRow ) );
+ rScPos.SetTab( nScTab );
+}
+
+} // namespace
+
+XclImpAddressConverter::XclImpAddressConverter( const XclImpRoot& rRoot ) :
+ XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetScMaxPos() )
+{
+}
+
+// cell address ---------------------------------------------------------------
+
+bool XclImpAddressConverter::CheckAddress( const XclAddress& rXclPos, bool bWarn )
+{
+ bool bValidCol = rXclPos.mnCol <= mnMaxCol;
+ bool bValidRow = rXclPos.mnRow <= mnMaxRow;
+ bool bValid = bValidCol && bValidRow;
+ if( !bValid && bWarn )
+ {
+ mbColTrunc |= !bValidCol;
+ mbRowTrunc |= !bValidRow;
+ mrTracer.TraceInvalidAddress( ScAddress(
+ static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), 0 ), maMaxPos );
+ }
+ return bValid;
+}
+
+bool XclImpAddressConverter::ConvertAddress( ScAddress& rScPos,
+ const XclAddress& rXclPos, SCTAB nScTab, bool bWarn )
+{
+ bool bValid = CheckAddress( rXclPos, bWarn );
+ if( bValid )
+ lclFillAddress( rScPos, rXclPos.mnCol, rXclPos.mnRow, nScTab );
+ return bValid;
+}
+
+ScAddress XclImpAddressConverter::CreateValidAddress(
+ const XclAddress& rXclPos, SCTAB nScTab, bool bWarn )
+{
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( !ConvertAddress( aScPos, rXclPos, nScTab, bWarn ) )
+ {
+ aScPos.SetCol( static_cast< SCCOL >( ::std::min( rXclPos.mnCol, mnMaxCol ) ) );
+ aScPos.SetRow( static_cast< SCROW >( ::std::min( rXclPos.mnRow, mnMaxRow ) ) );
+ aScPos.SetTab( limit_cast< SCTAB >( nScTab, 0, maMaxPos.Tab() ) );
+ }
+ return aScPos;
+}
+
+// cell range -----------------------------------------------------------------
+
+bool XclImpAddressConverter::ConvertRange( ScRange& rScRange,
+ const XclRange& rXclRange, SCTAB nScTab1, SCTAB nScTab2, bool bWarn )
+{
+ // check start position
+ bool bValidStart = CheckAddress( rXclRange.maFirst, bWarn );
+ if( bValidStart )
+ {
+ lclFillAddress( rScRange.aStart, rXclRange.maFirst.mnCol, rXclRange.maFirst.mnRow, nScTab1 );
+
+ // check & correct end position
+ sal_uInt16 nXclCol2 = rXclRange.maLast.mnCol;
+ sal_uInt32 nXclRow2 = rXclRange.maLast.mnRow;
+ if( !CheckAddress( rXclRange.maLast, bWarn ) )
+ {
+ nXclCol2 = ::std::min( nXclCol2, mnMaxCol );
+ nXclRow2 = ::std::min( nXclRow2, mnMaxRow );
+ }
+ lclFillAddress( rScRange.aEnd, nXclCol2, nXclRow2, nScTab2 );
+ }
+ return bValidStart;
+}
+
+// cell range list ------------------------------------------------------------
+
+void XclImpAddressConverter::ConvertRangeList( ScRangeList& rScRanges,
+ const XclRangeList& rXclRanges, SCTAB nScTab, bool bWarn )
+{
+ rScRanges.RemoveAll();
+ for( const auto& rXclRange : rXclRanges )
+ {
+ ScRange aScRange( ScAddress::UNINITIALIZED );
+ if( ConvertRange( aScRange, rXclRange, nScTab, nScTab, bWarn ) )
+ rScRanges.push_back( aScRange );
+ }
+}
+
+// String->EditEngine conversion ==============================================
+
+namespace {
+
+std::unique_ptr<EditTextObject> lclCreateTextObject( const XclImpRoot& rRoot,
+ const XclImpString& rString, XclFontItemType eType, sal_uInt16 nXFIndex )
+{
+ std::unique_ptr<EditTextObject> pTextObj;
+
+ const XclImpXFBuffer& rXFBuffer = rRoot.GetXFBuffer();
+ const XclImpFont* pFirstFont = rXFBuffer.GetFont( nXFIndex );
+ bool bFirstEscaped = pFirstFont && pFirstFont->HasEscapement();
+
+ if( rString.IsRich() || bFirstEscaped )
+ {
+ const XclImpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
+ const XclFormatRunVec& rFormats = rString.GetFormats();
+
+ ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
+ rEE.SetTextCurrentDefaults( rString.GetText() );
+
+ SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
+ if( bFirstEscaped )
+ rFontBuffer.FillToItemSet( aItemSet, eType, rXFBuffer.GetFontIndex( nXFIndex ) );
+ ESelection aSelection;
+
+ XclFormatRun aNextRun;
+ XclFormatRunVec::const_iterator aIt = rFormats.begin();
+ XclFormatRunVec::const_iterator aEnd = rFormats.end();
+
+ if( aIt != aEnd )
+ aNextRun = *aIt++;
+ else
+ aNextRun.mnChar = 0xFFFF;
+
+ sal_Int32 nLen = rString.GetText().getLength();
+ for( sal_Int32 nChar = 0; nChar < nLen; ++nChar )
+ {
+ // reached new different formatted text portion
+ if( nChar >= aNextRun.mnChar )
+ {
+ // send items to edit engine
+ rEE.QuickSetAttribs( aItemSet, aSelection );
+
+ // start new item set
+ aItemSet.ClearItem();
+ rFontBuffer.FillToItemSet( aItemSet, eType, aNextRun.mnFontIdx );
+
+ // read new formatting information
+ if( aIt != aEnd )
+ aNextRun = *aIt++;
+ else
+ aNextRun.mnChar = 0xFFFF;
+
+ // reset selection start to current position
+ aSelection.nStartPara = aSelection.nEndPara;
+ aSelection.nStartPos = aSelection.nEndPos;
+ }
+
+ // set end of selection to current position
+ if( rString.GetText()[ nChar ] == '\n' )
+ {
+ ++aSelection.nEndPara;
+ aSelection.nEndPos = 0;
+ }
+ else
+ ++aSelection.nEndPos;
+ }
+
+ // send items of last text portion to edit engine
+ rEE.QuickSetAttribs( aItemSet, aSelection );
+
+ pTextObj = rEE.CreateTextObject();
+ }
+
+ return pTextObj;
+}
+
+} // namespace
+
+std::unique_ptr<EditTextObject> XclImpStringHelper::CreateTextObject(
+ const XclImpRoot& rRoot, const XclImpString& rString )
+{
+ return lclCreateTextObject( rRoot, rString, XclFontItemType::Editeng, 0 );
+}
+
+void XclImpStringHelper::SetToDocument(
+ ScDocumentImport& rDoc, const ScAddress& rPos, const XclImpRoot& rRoot,
+ const XclImpString& rString, sal_uInt16 nXFIndex )
+{
+ if (rString.GetText().isEmpty())
+ return;
+
+ ::std::unique_ptr< EditTextObject > pTextObj( lclCreateTextObject( rRoot, rString, XclFontItemType::Editeng, nXFIndex ) );
+
+ if (pTextObj)
+ {
+ rDoc.setEditCell(rPos, std::move(pTextObj));
+ }
+ else
+ {
+ const OUString& aStr = rString.GetText();
+ if (aStr.indexOf('\n') != -1 || aStr.indexOf('\r') != -1)
+ {
+ // Multiline content.
+ ScFieldEditEngine& rEngine = rDoc.getDoc().GetEditEngine();
+ rEngine.SetTextCurrentDefaults(aStr);
+ rDoc.setEditCell(rPos, rEngine.CreateTextObject());
+ }
+ else
+ {
+ // Normal text cell.
+ rDoc.setStringCell(rPos, aStr);
+ }
+ }
+}
+
+// Header/footer conversion ===================================================
+
+XclImpHFConverter::XclImpHFPortionInfo::XclImpHFPortionInfo() :
+ mnHeight( 0 ),
+ mnMaxLineHt( 0 )
+{
+ maSel.nStartPara = maSel.nEndPara = 0;
+ maSel.nStartPos = maSel.nEndPos = 0;
+}
+
+XclImpHFConverter::XclImpHFConverter( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mrEE( rRoot.GetHFEditEngine() ),
+ mxFontData( new XclFontData ),
+ meCurrObj( EXC_HF_CENTER )
+{
+}
+
+XclImpHFConverter::~XclImpHFConverter()
+{
+}
+
+void XclImpHFConverter::ParseString( const OUString& rHFString )
+{
+ // edit engine objects
+ mrEE.SetText( OUString() );
+ maInfos.clear();
+ maInfos.resize( EXC_HF_PORTION_COUNT );
+ meCurrObj = EXC_HF_CENTER;
+
+ // parser temporaries
+ maCurrText.truncate();
+ OUStringBuffer aReadFont; // current font name
+ OUStringBuffer aReadStyle; // current font style
+ sal_uInt16 nReadHeight = 0; // current font height
+ ResetFontData();
+
+ /** State of the parser. */
+ enum XclHFParserState
+ {
+ xlPSText, /// Read text, search for functions.
+ xlPSFunc, /// Read function (token following a '&').
+ xlPSFont, /// Read font name ('&' is followed by '"', reads until next '"' or ',').
+ xlPSFontStyle, /// Read font style name (font part after ',', reads until next '"').
+ xlPSHeight /// Read font height ('&' is followed by num. digits, reads until non-digit).
+ } eState = xlPSText;
+
+ const sal_Unicode* pChar = rHFString.getStr();
+ const sal_Unicode* pNull = pChar + rHFString.getLength(); // pointer to terminating null char
+ while( *pChar )
+ {
+ switch( eState )
+ {
+
+// --- read text character ---
+
+ case xlPSText:
+ {
+ switch( *pChar )
+ {
+ case '&': // new command
+ InsertText();
+ eState = xlPSFunc;
+ break;
+ case '\n': // line break
+ InsertText();
+ InsertLineBreak();
+ break;
+ default:
+ maCurrText.append(OUStringChar(*pChar));
+ }
+ }
+ break;
+
+// --- read control sequence ---
+
+ case xlPSFunc:
+ {
+ eState = xlPSText;
+ switch( *pChar )
+ {
+ case '&': maCurrText.append("&"); break; // the '&' character
+
+ case 'L': SetNewPortion( EXC_HF_LEFT ); break; // Left portion
+ case 'C': SetNewPortion( EXC_HF_CENTER ); break; // Center portion
+ case 'R': SetNewPortion( EXC_HF_RIGHT ); break; // Right portion
+
+ case 'P': InsertField( SvxFieldItem( SvxPageField(), EE_FEATURE_FIELD ) ); break; // page
+ case 'N': InsertField( SvxFieldItem( SvxPagesField(), EE_FEATURE_FIELD ) ); break; // page count
+ case 'D': InsertField( SvxFieldItem( SvxDateField(), EE_FEATURE_FIELD ) ); break; // date
+ case 'T': InsertField( SvxFieldItem( SvxTimeField(), EE_FEATURE_FIELD ) ); break; // time
+ case 'A': InsertField( SvxFieldItem( SvxTableField(), EE_FEATURE_FIELD ) ); break; // table name
+
+ case 'Z': // file path
+ InsertField( SvxFieldItem( SvxExtFileField(), EE_FEATURE_FIELD ) ); // convert to full name
+ if( (pNull - pChar >= 2) && (*(pChar + 1) == '&') && (*(pChar + 2) == 'F') )
+ {
+ // &Z&F found - ignore the &F part
+ pChar += 2;
+ }
+ break;
+ case 'F': // file name
+ InsertField( SvxFieldItem( SvxExtFileField( OUString(), SvxFileType::Var, SvxFileFormat::NameAndExt ), EE_FEATURE_FIELD ) );
+ break;
+
+ case 'U': // underline
+ SetAttribs();
+ mxFontData->mnUnderline = (mxFontData->mnUnderline == EXC_FONTUNDERL_SINGLE) ?
+ EXC_FONTUNDERL_NONE : EXC_FONTUNDERL_SINGLE;
+ break;
+ case 'E': // double underline
+ SetAttribs();
+ mxFontData->mnUnderline = (mxFontData->mnUnderline == EXC_FONTUNDERL_DOUBLE) ?
+ EXC_FONTUNDERL_NONE : EXC_FONTUNDERL_DOUBLE;
+ break;
+ case 'S': // strikeout
+ SetAttribs();
+ mxFontData->mbStrikeout = !mxFontData->mbStrikeout;
+ break;
+ case 'X': // superscript
+ SetAttribs();
+ mxFontData->mnEscapem = (mxFontData->mnEscapem == EXC_FONTESC_SUPER) ?
+ EXC_FONTESC_NONE : EXC_FONTESC_SUPER;
+ break;
+ case 'Y': // subscript
+ SetAttribs();
+ mxFontData->mnEscapem = (mxFontData->mnEscapem == EXC_FONTESC_SUB) ?
+ EXC_FONTESC_NONE : EXC_FONTESC_SUB;
+ break;
+
+ case '\"': // font name
+ aReadFont.setLength(0);
+ aReadStyle.setLength(0);
+ eState = xlPSFont;
+ break;
+ default:
+ if( ('0' <= *pChar) && (*pChar <= '9') ) // font size
+ {
+ nReadHeight = *pChar - '0';
+ eState = xlPSHeight;
+ }
+ }
+ }
+ break;
+
+// --- read font name ---
+
+ case xlPSFont:
+ {
+ switch( *pChar )
+ {
+ case '\"':
+ --pChar;
+ [[fallthrough]];
+ case ',':
+ eState = xlPSFontStyle;
+ break;
+ default:
+ aReadFont.append(*pChar);
+ }
+ }
+ break;
+
+// --- read font style ---
+
+ case xlPSFontStyle:
+ {
+ switch( *pChar )
+ {
+ case '\"':
+ SetAttribs();
+ if( !aReadFont.isEmpty() )
+ mxFontData->maName = aReadFont.toString();
+ mxFontData->maStyle = aReadStyle.toString();
+ eState = xlPSText;
+ break;
+ default:
+ aReadStyle.append(*pChar);
+ }
+ }
+ break;
+
+// --- read font height ---
+
+ case xlPSHeight:
+ {
+ if( ('0' <= *pChar) && (*pChar <= '9') )
+ {
+ if( nReadHeight != 0xFFFF )
+ {
+ nReadHeight *= 10;
+ nReadHeight += (*pChar - '0');
+ if( nReadHeight > 1600 ) // max 1600pt = 32000twips
+ nReadHeight = 0xFFFF;
+ }
+ }
+ else
+ {
+ if( (nReadHeight != 0) && (nReadHeight != 0xFFFF) )
+ {
+ SetAttribs();
+ mxFontData->mnHeight = nReadHeight * 20;
+ }
+ --pChar;
+ eState = xlPSText;
+ }
+ }
+ break;
+ }
+ ++pChar;
+ }
+
+ // finalize
+ CreateCurrObject();
+ maInfos[ EXC_HF_LEFT ].mnHeight += GetMaxLineHeight( EXC_HF_LEFT );
+ maInfos[ EXC_HF_CENTER ].mnHeight += GetMaxLineHeight( EXC_HF_CENTER );
+ maInfos[ EXC_HF_RIGHT ].mnHeight += GetMaxLineHeight( EXC_HF_RIGHT );
+}
+
+void XclImpHFConverter::FillToItemSet( SfxItemSet& rItemSet, sal_uInt16 nWhichId ) const
+{
+ ScPageHFItem aHFItem( nWhichId );
+ if( maInfos[ EXC_HF_LEFT ].mxObj )
+ aHFItem.SetLeftArea( *maInfos[ EXC_HF_LEFT ].mxObj );
+ if( maInfos[ EXC_HF_CENTER ].mxObj )
+ aHFItem.SetCenterArea( *maInfos[ EXC_HF_CENTER ].mxObj );
+ if( maInfos[ EXC_HF_RIGHT ].mxObj )
+ aHFItem.SetRightArea( *maInfos[ EXC_HF_RIGHT ].mxObj );
+ rItemSet.Put( aHFItem );
+}
+
+sal_Int32 XclImpHFConverter::GetTotalHeight() const
+{
+ return ::std::max( maInfos[ EXC_HF_LEFT ].mnHeight,
+ ::std::max( maInfos[ EXC_HF_CENTER ].mnHeight, maInfos[ EXC_HF_RIGHT ].mnHeight ) );
+}
+
+// private --------------------------------------------------------------------
+
+sal_uInt16 XclImpHFConverter::GetMaxLineHeight( XclImpHFPortion ePortion ) const
+{
+ sal_uInt16 nMaxHt = maInfos[ ePortion ].mnMaxLineHt;
+ return (nMaxHt == 0) ? mxFontData->mnHeight : nMaxHt;
+}
+
+void XclImpHFConverter::UpdateMaxLineHeight( XclImpHFPortion ePortion )
+{
+ sal_uInt16& rnMaxHt = maInfos[ ePortion ].mnMaxLineHt;
+ rnMaxHt = ::std::max( rnMaxHt, mxFontData->mnHeight );
+}
+
+void XclImpHFConverter::UpdateCurrMaxLineHeight()
+{
+ UpdateMaxLineHeight( meCurrObj );
+}
+
+void XclImpHFConverter::SetAttribs()
+{
+ ESelection& rSel = GetCurrSel();
+ if( (rSel.nStartPara != rSel.nEndPara) || (rSel.nStartPos != rSel.nEndPos) )
+ {
+ SfxItemSet aItemSet( mrEE.GetEmptyItemSet() );
+ XclImpFont aFont( GetRoot(), *mxFontData );
+ aFont.FillToItemSet( aItemSet, XclFontItemType::HeaderFooter );
+ mrEE.QuickSetAttribs( aItemSet, rSel );
+ rSel.nStartPara = rSel.nEndPara;
+ rSel.nStartPos = rSel.nEndPos;
+ }
+}
+
+void XclImpHFConverter::ResetFontData()
+{
+ if( const XclImpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
+ *mxFontData = pFirstFont->GetFontData();
+ else
+ {
+ mxFontData->Clear();
+ mxFontData->mnHeight = 200;
+ }
+}
+
+void XclImpHFConverter::InsertText()
+{
+ if( !maCurrText.isEmpty() )
+ {
+ ESelection& rSel = GetCurrSel();
+ OUString sString(maCurrText.makeStringAndClear());
+ mrEE.QuickInsertText( sString, ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) );
+ rSel.nEndPos = rSel.nEndPos + sString.getLength();
+ UpdateCurrMaxLineHeight();
+ }
+}
+
+void XclImpHFConverter::InsertField( const SvxFieldItem& rFieldItem )
+{
+ ESelection& rSel = GetCurrSel();
+ mrEE.QuickInsertField( rFieldItem, ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) );
+ ++rSel.nEndPos;
+ UpdateCurrMaxLineHeight();
+}
+
+void XclImpHFConverter::InsertLineBreak()
+{
+ ESelection& rSel = GetCurrSel();
+ mrEE.QuickInsertText( OUString('\n'), ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) );
+ ++rSel.nEndPara;
+ rSel.nEndPos = 0;
+ GetCurrInfo().mnHeight += GetMaxLineHeight( meCurrObj );
+ GetCurrInfo().mnMaxLineHt = 0;
+}
+
+void XclImpHFConverter::CreateCurrObject()
+{
+ InsertText();
+ SetAttribs();
+ GetCurrObj() = mrEE.CreateTextObject();
+}
+
+void XclImpHFConverter::SetNewPortion( XclImpHFPortion eNew )
+{
+ if( eNew != meCurrObj )
+ {
+ CreateCurrObject();
+ meCurrObj = eNew;
+ if( GetCurrObj() )
+ mrEE.SetText( *GetCurrObj() );
+ else
+ mrEE.SetText( OUString() );
+ ResetFontData();
+ }
+}
+
+// URL conversion =============================================================
+
+namespace {
+
+void lclAppendUrlChar( OUString& rUrl, sal_Unicode cChar )
+{
+ // encode special characters
+ switch( cChar )
+ {
+ case '#': rUrl += "%23"; break;
+ case '%': rUrl += "%25"; break;
+ default: rUrl += OUStringChar( cChar );
+ }
+}
+
+} // namespace
+
+void XclImpUrlHelper::DecodeUrl(
+ OUString& rUrl, OUString& rTabName, bool& rbSameWb,
+ const XclImpRoot& rRoot, const OUString& rEncodedUrl )
+{
+ enum
+ {
+ xlUrlInit, /// Initial state, read string mode character.
+ xlUrlPath, /// Read URL path.
+ xlUrlFileName, /// Read file name.
+ xlUrlSheetName, /// Read sheet name.
+ xlUrlRaw /// Raw mode. No control characters will occur.
+ } eState = xlUrlInit;
+
+ bool bEncoded = true;
+ rbSameWb = false;
+
+ sal_Unicode cCurrDrive = 0;
+ OUString aDosBase( INetURLObject( rRoot.GetBasePath() ).getFSysPath( FSysStyle::Dos ) );
+ if (!aDosBase.isEmpty() && aDosBase.match(":\\", 1))
+ cCurrDrive = aDosBase[0];
+
+ const sal_Unicode* pChar = rEncodedUrl.getStr();
+ while( *pChar )
+ {
+ switch( eState )
+ {
+
+// --- first character ---
+
+ case xlUrlInit:
+ {
+ switch( *pChar )
+ {
+ case EXC_URLSTART_ENCODED:
+ eState = xlUrlPath;
+ break;
+ case EXC_URLSTART_SELF:
+ case EXC_URLSTART_SELFENCODED:
+ rbSameWb = true;
+ eState = xlUrlSheetName;
+ break;
+ case '[':
+ bEncoded = false;
+ eState = xlUrlFileName;
+ break;
+ default:
+ bEncoded = false;
+ lclAppendUrlChar( rUrl, *pChar );
+ eState = xlUrlPath;
+ }
+ }
+ break;
+
+// --- URL path ---
+
+ case xlUrlPath:
+ {
+ switch( *pChar )
+ {
+ case EXC_URL_DOSDRIVE:
+ {
+ if( *(pChar + 1) )
+ {
+ ++pChar;
+ if( *pChar == '@' )
+ rUrl += "\\\\";
+ else
+ {
+ lclAppendUrlChar( rUrl, *pChar );
+ rUrl += ":\\";
+ }
+ }
+ else
+ rUrl += "<NULL-DRIVE!>";
+ }
+ break;
+ case EXC_URL_DRIVEROOT:
+ if( cCurrDrive )
+ {
+ lclAppendUrlChar( rUrl, cCurrDrive );
+ rUrl += ":";
+ }
+ [[fallthrough]];
+ case EXC_URL_SUBDIR:
+ if( bEncoded )
+ rUrl += "\\";
+ else // control character in raw name -> DDE link
+ {
+ rUrl += OUStringChar(EXC_DDE_DELIM);
+ eState = xlUrlRaw;
+ }
+ break;
+ case EXC_URL_PARENTDIR:
+ rUrl += "..\\";
+ break;
+ case EXC_URL_RAW:
+ {
+ if( *(pChar + 1) )
+ {
+ sal_Int32 nLen = *++pChar;
+ for( sal_Int32 nChar = 0; (nChar < nLen) && *(pChar + 1); ++nChar )
+ lclAppendUrlChar( rUrl, *++pChar );
+// rUrl.Append( ':' );
+ }
+ }
+ break;
+ case '[':
+ eState = xlUrlFileName;
+ break;
+ default:
+ lclAppendUrlChar( rUrl, *pChar );
+ }
+ }
+ break;
+
+// --- file name ---
+
+ case xlUrlFileName:
+ {
+ switch( *pChar )
+ {
+ case ']': eState = xlUrlSheetName; break;
+ default: lclAppendUrlChar( rUrl, *pChar );
+ }
+ }
+ break;
+
+// --- sheet name ---
+
+ case xlUrlSheetName:
+ rTabName += OUStringChar( *pChar );
+ break;
+
+// --- raw read mode ---
+
+ case xlUrlRaw:
+ lclAppendUrlChar( rUrl, *pChar );
+ break;
+ }
+
+ ++pChar;
+ }
+}
+
+void XclImpUrlHelper::DecodeUrl(
+ OUString& rUrl, bool& rbSameWb, const XclImpRoot& rRoot, const OUString& rEncodedUrl )
+{
+ OUString aTabName;
+ OUString aUrl;
+ DecodeUrl( aUrl, aTabName, rbSameWb, rRoot, rEncodedUrl );
+ rUrl = aUrl;
+ OSL_ENSURE( aTabName.isEmpty(), "XclImpUrlHelper::DecodeUrl - sheet name ignored" );
+}
+
+bool XclImpUrlHelper::DecodeLink( OUString& rApplic, OUString& rTopic, const OUString& rEncUrl )
+{
+ sal_Int32 nPos = rEncUrl.indexOf( EXC_DDE_DELIM );
+ if( (nPos > 0) && (nPos + 1 < rEncUrl.getLength()) )
+ {
+ rApplic = rEncUrl.copy( 0, nPos );
+ rTopic = rEncUrl.copy( nPos + 1 );
+ return true;
+ }
+ return false;
+}
+
+// Cached Values ==============================================================
+
+XclImpCachedValue::XclImpCachedValue( XclImpStream& rStrm ) :
+ mfValue( 0.0 ),
+ mnBoolErr( 0 )
+{
+ mnType = rStrm.ReaduInt8();
+ switch( mnType )
+ {
+ case EXC_CACHEDVAL_EMPTY:
+ rStrm.Ignore( 8 );
+ break;
+ case EXC_CACHEDVAL_DOUBLE:
+ mfValue = rStrm.ReadDouble();
+ break;
+ case EXC_CACHEDVAL_STRING:
+ maStr = rStrm.ReadUniString();
+ break;
+ case EXC_CACHEDVAL_BOOL:
+ case EXC_CACHEDVAL_ERROR:
+ {
+ double fVal;
+ mnBoolErr = rStrm.ReaduInt8();
+ rStrm.Ignore( 7 );
+
+ std::unique_ptr<ScTokenArray> pScTokArr = rStrm.GetRoot().GetOldFmlaConverter().GetBoolErr(
+ XclTools::ErrorToEnum( fVal, mnType == EXC_CACHEDVAL_ERROR, mnBoolErr ) );
+ if( pScTokArr )
+ mxTokArr = std::move( pScTokArr );
+ }
+ break;
+ default:
+ OSL_FAIL( "XclImpCachedValue::XclImpCachedValue - unknown data type" );
+ }
+}
+
+XclImpCachedValue::~XclImpCachedValue()
+{
+}
+
+FormulaError XclImpCachedValue::GetScError() const
+{
+ return (mnType == EXC_CACHEDVAL_ERROR) ? XclTools::GetScErrorCode( mnBoolErr ) : FormulaError::NONE;
+}
+
+// Matrix Cached Values ==============================================================
+
+XclImpCachedMatrix::XclImpCachedMatrix( XclImpStream& rStrm ) :
+ mnScCols( 0 ),
+ mnScRows( 0 )
+{
+ mnScCols = rStrm.ReaduInt8();
+ mnScRows = rStrm.ReaduInt16();
+
+ if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
+ {
+ // in BIFF2-BIFF7: 256 columns represented by 0 columns
+ if( mnScCols == 0 )
+ mnScCols = 256;
+ }
+ else
+ {
+ // in BIFF8: columns and rows decreased by 1
+ ++mnScCols;
+ ++mnScRows;
+ }
+
+ //assuming worst case scenario of unknown types
+ const size_t nMinRecordSize = 1;
+ const size_t nMaxRows = rStrm.GetRecLeft() / (nMinRecordSize * mnScCols);
+ if (mnScRows > nMaxRows)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRows <<
+ " max possible rows, but " << mnScRows << " claimed, truncating");
+ mnScRows = nMaxRows;
+ }
+
+ for( SCSIZE nScRow = 0; nScRow < mnScRows; ++nScRow )
+ for( SCSIZE nScCol = 0; nScCol < mnScCols; ++nScCol )
+ maValueList.push_back( std::make_unique<XclImpCachedValue>( rStrm ) );
+}
+
+XclImpCachedMatrix::~XclImpCachedMatrix()
+{
+}
+
+ScMatrixRef XclImpCachedMatrix::CreateScMatrix( svl::SharedStringPool& rPool ) const
+{
+ ScMatrixRef xScMatrix;
+ OSL_ENSURE( mnScCols * mnScRows == maValueList.size(), "XclImpCachedMatrix::CreateScMatrix - element count mismatch" );
+ if( mnScCols && mnScRows && static_cast< sal_uLong >( mnScCols * mnScRows ) <= maValueList.size() )
+ {
+ xScMatrix = new ScMatrix(mnScCols, mnScRows, 0.0);
+ XclImpValueList::const_iterator itValue = maValueList.begin();
+ for( SCSIZE nScRow = 0; nScRow < mnScRows; ++nScRow )
+ {
+ for( SCSIZE nScCol = 0; nScCol < mnScCols; ++nScCol )
+ {
+ switch( (*itValue)->GetType() )
+ {
+ case EXC_CACHEDVAL_EMPTY:
+ // Excel shows 0.0 here, not an empty cell
+ xScMatrix->PutEmpty( nScCol, nScRow );
+ break;
+ case EXC_CACHEDVAL_DOUBLE:
+ xScMatrix->PutDouble( (*itValue)->GetValue(), nScCol, nScRow );
+ break;
+ case EXC_CACHEDVAL_STRING:
+ xScMatrix->PutString(rPool.intern((*itValue)->GetString()), nScCol, nScRow);
+ break;
+ case EXC_CACHEDVAL_BOOL:
+ xScMatrix->PutBoolean( (*itValue)->GetBool(), nScCol, nScRow );
+ break;
+ case EXC_CACHEDVAL_ERROR:
+ xScMatrix->PutError( (*itValue)->GetScError(), nScCol, nScRow );
+ break;
+ default:
+ OSL_FAIL( "XclImpCachedMatrix::CreateScMatrix - unknown value type" );
+ xScMatrix->PutEmpty( nScCol, nScRow );
+ }
+ ++itValue;
+ }
+ }
+ }
+ return xScMatrix;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xilink.cxx b/sc/source/filter/excel/xilink.cxx
new file mode 100644
index 000000000..e9fea951e
--- /dev/null
+++ b/sc/source/filter/excel/xilink.cxx
@@ -0,0 +1,962 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xilink.hxx>
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xiname.hxx>
+#include <xltools.hxx>
+#include <excform.hxx>
+#include <tokenarray.hxx>
+#include <externalrefmgr.hxx>
+#include <scmatrix.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+
+#include <vector>
+#include <memory>
+
+// *** Helper classes ***
+
+// Cached external cells ======================================================
+
+namespace {
+
+/**
+ * Contains the address and value of an external referenced cell.
+ * Note that this is non-copyable, so cannot be used in most stl/boost containers.
+ */
+class XclImpCrn : public XclImpCachedValue
+{
+public:
+ /** Reads a cached value and stores it with its cell address. */
+ explicit XclImpCrn( XclImpStream& rStrm, const XclAddress& rXclPos );
+
+ const XclAddress& GetAddress() const { return maXclPos;}
+
+private:
+ XclAddress maXclPos; /// Excel position of the cached cell.
+};
+
+// Sheet in an external document ==============================================
+
+/** Contains the name and sheet index of one sheet in an external document. */
+class XclImpSupbookTab
+{
+public:
+ /** Stores the sheet name and marks the sheet index as invalid.
+ The sheet index is set while creating the Calc sheet with CreateTable(). */
+ explicit XclImpSupbookTab( const OUString& rTabName );
+
+ const OUString& GetTabName() const { return maTabName; }
+
+ /** Reads a CRN record (external referenced cell) at the specified address. */
+ void ReadCrn( XclImpStream& rStrm, const XclAddress& rXclPos );
+
+ void LoadCachedValues( const ScExternalRefCache::TableTypeRef& pCacheTable,
+ svl::SharedStringPool& rPool );
+
+private:
+ typedef std::shared_ptr< XclImpCrn > XclImpCrnRef;
+
+ std::vector< XclImpCrnRef > maCrnList; /// List of CRN records (cached cell values).
+ OUString maTabName; /// Name of the external sheet.
+};
+
+}
+
+// External document (SUPBOOK) ================================================
+
+/** This class represents an external linked document (record SUPBOOK).
+ @descr Contains a list of all referenced sheets in the document. */
+class XclImpSupbook : protected XclImpRoot
+{
+public:
+ /** Reads the SUPBOOK record from stream. */
+ explicit XclImpSupbook( XclImpStream& rStrm );
+
+ /** Reads an XCT record (count of following CRNs and current sheet). */
+ void ReadXct( XclImpStream& rStrm );
+ /** Reads a CRN record (external referenced cell). */
+ void ReadCrn( XclImpStream& rStrm );
+ /** Reads an EXTERNNAME record. */
+ void ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv );
+
+ /** Returns the SUPBOOK record type. */
+ XclSupbookType GetType() const { return meType; }
+
+ /** Returns the URL of the external document. */
+ const OUString& GetXclUrl() const { return maXclUrl; }
+
+ /** Returns the external name specified by an index from the Excel document (one-based). */
+ const XclImpExtName* GetExternName( sal_uInt16 nXclIndex ) const;
+ /** Tries to decode the URL to OLE or DDE link components.
+ @descr For DDE links: Decodes to application name and topic.
+ For OLE object links: Decodes to class name and document URL.
+ @return true = decoding was successful, returned strings are valid (not empty). */
+ bool GetLinkData( OUString& rApplic, OUString& rDoc ) const;
+ /** Returns the specified macro name (1-based) or an empty string on error. */
+ OUString GetMacroName( sal_uInt16 nXclNameIdx ) const;
+
+ OUString GetTabName( sal_uInt16 nXtiTab ) const;
+
+ sal_uInt16 GetTabCount() const;
+
+ void LoadCachedValues();
+
+ svl::SharedStringPool& GetSharedStringPool();
+
+private:
+
+ std::vector< std::unique_ptr<XclImpSupbookTab> >
+ maSupbTabList; /// All sheet names of the document.
+ std::vector< std::unique_ptr<XclImpExtName> >
+ maExtNameList; /// All external names of the document.
+ OUString maXclUrl; /// URL of the external document (Excel mode).
+ XclSupbookType meType; /// Type of the supbook record.
+ sal_uInt16 mnSBTab; /// Current Excel sheet index from SUPBOOK for XCT/CRN records.
+};
+
+// Import link manager ========================================================
+
+namespace {
+
+/** Contains the SUPBOOK index and sheet indexes of an external link.
+ @descr It is possible to enter a formula like =SUM(Sheet1:Sheet3!A1),
+ therefore here occurs a sheet range. */
+struct XclImpXti
+{
+ sal_uInt16 mnSupbook; /// Index to SUPBOOK record.
+ sal_uInt16 mnSBTabFirst; /// Index to the first sheet of the range in the SUPBOOK.
+ sal_uInt16 mnSBTabLast; /// Index to the last sheet of the range in the SUPBOOK.
+ explicit XclImpXti() : mnSupbook( SAL_MAX_UINT16 ), mnSBTabFirst( SAL_MAX_UINT16 ), mnSBTabLast( SAL_MAX_UINT16 ) {}
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclImpXti& rXti )
+{
+ rXti.mnSupbook = rStrm.ReaduInt16();
+ rXti.mnSBTabFirst = rStrm.ReaduInt16();
+ rXti.mnSBTabLast = rStrm.ReaduInt16();
+ return rStrm;
+}
+
+}
+
+/** Implementation of the link manager. */
+class XclImpLinkManagerImpl : protected XclImpRoot
+{
+public:
+ explicit XclImpLinkManagerImpl( const XclImpRoot& rRoot );
+
+ /** Reads the EXTERNSHEET record. */
+ void ReadExternsheet( XclImpStream& rStrm );
+ /** Reads a SUPBOOK record. */
+ void ReadSupbook( XclImpStream& rStrm );
+ /** Reads an XCT record and appends it to the current SUPBOOK. */
+ void ReadXct( XclImpStream& rStrm );
+ /** Reads a CRN record and appends it to the current SUPBOOK. */
+ void ReadCrn( XclImpStream& rStrm );
+ /** Reads an EXTERNNAME record and appends it to the current SUPBOOK. */
+ void ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv );
+
+ /** Returns true, if the specified XTI entry contains an internal reference. */
+ bool IsSelfRef( sal_uInt16 nXtiIndex ) const;
+ /** Returns the Calc sheet index range of the specified XTI entry.
+ @return true = XTI data found, returned sheet index range is valid. */
+ bool GetScTabRange(
+ SCTAB& rnFirstScTab, SCTAB& rnLastScTab,
+ sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified external name or 0 on error. */
+ const XclImpExtName* GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const;
+
+ /** Returns the absolute file URL of a supporting workbook specified by
+ the index. */
+ const OUString* GetSupbookUrl( sal_uInt16 nXtiIndex ) const;
+
+ OUString GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const;
+
+ /** Tries to decode the URL of the specified XTI entry to OLE or DDE link components.
+ @descr For DDE links: Decodes to application name and topic.
+ For OLE object links: Decodes to class name and document URL.
+ @return true = decoding was successful, returned strings are valid (not empty). */
+ bool GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified macro name or an empty string on error. */
+ OUString GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const;
+
+private:
+ /** Returns the specified XTI (link entry from BIFF8 EXTERNSHEET record). */
+ const XclImpXti* GetXti( sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified SUPBOOK (external document). */
+ const XclImpSupbook* GetSupbook( sal_uInt16 nXtiIndex ) const;
+
+ void LoadCachedValues();
+
+private:
+ typedef std::vector< XclImpXti > XclImpXtiVector;
+
+ XclImpXtiVector maXtiList; /// List of all XTI structures.
+ std::vector< std::unique_ptr<XclImpSupbook> >
+ maSupbookList; /// List of external documents.
+};
+
+// *** Implementation ***
+
+// Excel sheet indexes ========================================================
+
+// original Excel sheet names -------------------------------------------------
+
+void XclImpTabInfo::AppendXclTabName( const OUString& rXclTabName, SCTAB nScTab )
+{
+ maTabNames[ rXclTabName ] = nScTab;
+}
+
+void XclImpTabInfo::InsertScTab( SCTAB nScTab )
+{
+ for( auto& rEntry : maTabNames )
+ if( rEntry.second >= nScTab )
+ ++rEntry.second;
+}
+
+SCTAB XclImpTabInfo::GetScTabFromXclName( const OUString& rXclTabName ) const
+{
+ XclTabNameMap::const_iterator aIt = maTabNames.find( rXclTabName );
+ return (aIt != maTabNames.end()) ? aIt->second : SCTAB_INVALID;
+}
+
+// record creation order - TABID record ---------------------------------------
+
+void XclImpTabInfo::ReadTabid( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() == EXC_BIFF8 );
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ {
+ rStrm.EnableDecryption();
+ std::size_t nReadCount = rStrm.GetRecLeft() / 2;
+ OSL_ENSURE( nReadCount <= 0xFFFF, "XclImpTabInfo::ReadTabid - record too long" );
+ maTabIdVec.clear();
+ maTabIdVec.reserve( nReadCount );
+ for( std::size_t nIndex = 0; rStrm.IsValid() && (nIndex < nReadCount); ++nIndex )
+ // zero index is not allowed in BIFF8, but it seems that it occurs in real life
+ maTabIdVec.push_back( rStrm.ReaduInt16() );
+ }
+}
+
+sal_uInt16 XclImpTabInfo::GetCurrentIndex( sal_uInt16 nCreatedId, sal_uInt16 nMaxTabId ) const
+{
+ sal_uInt16 nReturn = 0;
+ for( sal_uInt16 nValue : maTabIdVec )
+ {
+ if( nValue == nCreatedId )
+ return nReturn;
+ if( nValue <= nMaxTabId )
+ ++nReturn;
+ }
+ return 0;
+}
+
+// External names =============================================================
+
+XclImpExtName::MOper::MOper(svl::SharedStringPool& rPool, XclImpStream& rStrm) :
+ mxCached(new ScMatrix(0,0))
+{
+ SCSIZE nLastCol = rStrm.ReaduInt8();
+ SCSIZE nLastRow = rStrm.ReaduInt16();
+
+ //assuming worst case scenario of nOp + one byte unistring len
+ const size_t nMinRecordSize = 2;
+ const size_t nMaxRows = rStrm.GetRecLeft() / (nMinRecordSize * (nLastCol+1));
+ if (nLastRow >= nMaxRows)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRows <<
+ " max possible rows, but " << nLastRow << " index claimed, truncating");
+ if (nMaxRows > 0)
+ nLastRow = nMaxRows-1;
+ else
+ return;
+ }
+
+ mxCached->Resize(nLastCol+1, nLastRow+1);
+ for (SCSIZE nRow = 0; nRow <= nLastRow; ++nRow)
+ {
+ for (SCSIZE nCol = 0; nCol <= nLastCol; ++nCol)
+ {
+ sal_uInt8 nOp;
+ nOp = rStrm.ReaduInt8();
+ switch (nOp)
+ {
+ case 0x01:
+ {
+ double fVal = rStrm.ReadDouble();
+ mxCached->PutDouble(fVal, nCol, nRow);
+ }
+ break;
+ case 0x02:
+ {
+ OUString aStr = rStrm.ReadUniString();
+ mxCached->PutString(rPool.intern(aStr), nCol, nRow);
+ }
+ break;
+ case 0x04:
+ {
+ bool bVal = rStrm.ReaduInt8();
+ mxCached->PutBoolean(bVal, nCol, nRow);
+ rStrm.Ignore(7);
+ }
+ break;
+ case 0x10:
+ {
+ sal_uInt8 nErr = rStrm.ReaduInt8();
+ // Map the error code from xls to calc.
+ mxCached->PutError(XclTools::GetScErrorCode(nErr), nCol, nRow);
+ rStrm.Ignore(7);
+ }
+ break;
+ default:
+ rStrm.Ignore(8);
+ }
+ }
+ }
+}
+
+const ScMatrix& XclImpExtName::MOper::GetCache() const
+{
+ return *mxCached;
+}
+
+XclImpExtName::XclImpExtName( XclImpSupbook& rSupbook, XclImpStream& rStrm, XclSupbookType eSubType, ExcelToSc* pFormulaConv )
+ : mnStorageId(0)
+{
+ sal_uInt16 nFlags(0);
+ sal_uInt8 nLen(0);
+
+ nFlags = rStrm.ReaduInt16();
+ mnStorageId = rStrm.ReaduInt32();
+ nLen = rStrm.ReaduInt8();
+ maName = rStrm.ReadUniString( nLen );
+ if( ::get_flag( nFlags, EXC_EXTN_BUILTIN ) || !::get_flag( nFlags, EXC_EXTN_OLE_OR_DDE ) )
+ {
+ if( eSubType == XclSupbookType::Addin )
+ {
+ meType = xlExtAddIn;
+ maName = XclImpRoot::GetScAddInName( maName );
+ }
+ else if ( (eSubType == XclSupbookType::Eurotool) &&
+ maName.equalsIgnoreAsciiCase( "EUROCONVERT" ) )
+ meType = xlExtEuroConvert;
+ else
+ {
+ meType = xlExtName;
+ maName = ScfTools::ConvertToScDefinedName( maName );
+ }
+ }
+ else
+ {
+ meType = ::get_flagvalue( nFlags, EXC_EXTN_OLE, xlExtOLE, xlExtDDE );
+ }
+
+ switch (meType)
+ {
+ case xlExtDDE:
+ if (rStrm.GetRecLeft() > 1)
+ mxDdeMatrix.reset(new XclImpCachedMatrix(rStrm));
+ break;
+ case xlExtName:
+ // TODO: For now, only global external names are supported. In future
+ // we should extend this to supporting per-sheet external names.
+ if (mnStorageId == 0 && pFormulaConv)
+ {
+ std::unique_ptr<ScTokenArray> pArray;
+ sal_uInt16 nFmlaLen;
+ nFmlaLen = rStrm.ReaduInt16();
+ std::vector<OUString> aTabNames;
+ sal_uInt16 nCount = rSupbook.GetTabCount();
+ aTabNames.reserve(nCount);
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ aTabNames.push_back(rSupbook.GetTabName(i));
+
+ pFormulaConv->ConvertExternName(pArray, rStrm, nFmlaLen, rSupbook.GetXclUrl(), aTabNames);
+ if (pArray)
+ mxArray = std::move( pArray );
+ }
+ break;
+ case xlExtOLE:
+ mpMOper.reset( new MOper(rSupbook.GetSharedStringPool(), rStrm) );
+ break;
+ default:
+ ;
+ }
+}
+
+XclImpExtName::~XclImpExtName()
+{
+}
+
+void XclImpExtName::CreateDdeData( ScDocument& rDoc, const OUString& rApplic, const OUString& rTopic ) const
+{
+ ScMatrixRef xResults;
+ if( mxDdeMatrix )
+ xResults = mxDdeMatrix->CreateScMatrix(rDoc.GetSharedStringPool());
+ rDoc.CreateDdeLink( rApplic, rTopic, maName, SC_DDE_DEFAULT, xResults );
+}
+
+void XclImpExtName::CreateExtNameData( const ScDocument& rDoc, sal_uInt16 nFileId ) const
+{
+ if (!mxArray)
+ return;
+
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ pRefMgr->storeRangeNameTokens(nFileId, maName, *mxArray);
+}
+
+namespace {
+
+/**
+ * Decompose the name into sheet name and range name. An OLE link name is
+ * always formatted like this [ !Sheet1!R1C1:R5C2 ] and it always uses R1C1
+ * notation.
+ */
+bool extractSheetAndRange(const OUString& rName, OUString& rSheet, OUString& rRange)
+{
+ sal_Int32 n = rName.getLength();
+ const sal_Unicode* p = rName.getStr();
+ OUStringBuffer aBuf;
+ bool bInSheet = true;
+ for (sal_Int32 i = 0; i < n; ++i, ++p)
+ {
+ if (i == 0)
+ {
+ // first character must be '!'.
+ if (*p != '!')
+ return false;
+ continue;
+ }
+
+ if (*p == '!')
+ {
+ // sheet name to range separator.
+ if (!bInSheet)
+ return false;
+ rSheet = aBuf.makeStringAndClear();
+ bInSheet = false;
+ continue;
+ }
+
+ aBuf.append(*p);
+ }
+
+ rRange = aBuf.makeStringAndClear();
+ return true;
+}
+
+}
+
+bool XclImpExtName::CreateOleData(const ScDocument& rDoc, const OUString& rUrl,
+ sal_uInt16& rFileId, OUString& rTabName, ScRange& rRange) const
+{
+ if (!mpMOper)
+ return false;
+
+ OUString aSheet, aRangeStr;
+ if (!extractSheetAndRange(maName, aSheet, aRangeStr))
+ return false;
+
+ ScRange aRange;
+ ScRefFlags nRes = aRange.ParseAny(aRangeStr, rDoc, formula::FormulaGrammar::CONV_XL_R1C1);
+ if ((nRes & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ return false;
+
+ if (aRange.aStart.Tab() != aRange.aEnd.Tab())
+ // We don't support multi-sheet range for this.
+ return false;
+
+ const ScMatrix& rCache = mpMOper->GetCache();
+ SCSIZE nC, nR;
+ rCache.GetDimensions(nC, nR);
+ if (!nC || !nR)
+ // cache matrix is empty.
+ return false;
+
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(rUrl);
+ ScExternalRefCache::TableTypeRef xTab = pRefMgr->getCacheTable(nFileId, aSheet, true);
+ if (!xTab)
+ // cache table creation failed.
+ return false;
+
+ xTab->setWholeTableCached();
+ for (SCSIZE i = 0; i < nR; ++i)
+ {
+ for (SCSIZE j = 0; j < nC; ++j)
+ {
+ SCCOL nCol = aRange.aStart.Col() + j;
+ SCROW nRow = aRange.aStart.Row() + i;
+
+ ScMatrixValue aVal = rCache.Get(j, i);
+ switch (aVal.nType)
+ {
+ case ScMatValType::Boolean:
+ {
+ bool b = aVal.GetBoolean();
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(b ? 1.0 : 0.0));
+ xTab->setCell(nCol, nRow, pToken, 0, false);
+ }
+ break;
+ case ScMatValType::Value:
+ {
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(aVal.fVal));
+ xTab->setCell(nCol, nRow, pToken, 0, false);
+ }
+ break;
+ case ScMatValType::String:
+ {
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaStringToken(aVal.GetString()));
+ xTab->setCell(nCol, nRow, pToken, 0, false);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ rFileId = nFileId;
+ rTabName = aSheet;
+ rRange = aRange;
+ return true;
+}
+
+bool XclImpExtName::HasFormulaTokens() const
+{
+ return bool(mxArray);
+}
+
+// Cached external cells ======================================================
+
+XclImpCrn::XclImpCrn( XclImpStream& rStrm, const XclAddress& rXclPos ) :
+ XclImpCachedValue( rStrm ),
+ maXclPos( rXclPos )
+{
+}
+
+// Sheet in an external document ==============================================
+
+XclImpSupbookTab::XclImpSupbookTab( const OUString& rTabName ) :
+ maTabName( rTabName )
+{
+}
+
+void XclImpSupbookTab::ReadCrn( XclImpStream& rStrm, const XclAddress& rXclPos )
+{
+ XclImpCrnRef crnRef = std::make_shared<XclImpCrn>(rStrm, rXclPos);
+ maCrnList.push_back( crnRef );
+}
+
+void XclImpSupbookTab::LoadCachedValues( const ScExternalRefCache::TableTypeRef& pCacheTable,
+ svl::SharedStringPool& rPool )
+{
+ if (maCrnList.empty())
+ return;
+
+ for (const auto& rxCrn : maCrnList)
+ {
+ const XclImpCrn* const pCrn = rxCrn.get();
+ const XclAddress& rAddr = pCrn->GetAddress();
+ switch (pCrn->GetType())
+ {
+ case EXC_CACHEDVAL_BOOL:
+ {
+ bool b = pCrn->GetBool();
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(b ? 1.0 : 0.0));
+ pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
+ }
+ break;
+ case EXC_CACHEDVAL_DOUBLE:
+ {
+ double f = pCrn->GetValue();
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(f));
+ pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
+ }
+ break;
+ case EXC_CACHEDVAL_ERROR:
+ {
+ double fError = XclTools::ErrorToDouble( pCrn->GetXclError() );
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(fError));
+ pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
+ }
+ break;
+ case EXC_CACHEDVAL_STRING:
+ {
+ svl::SharedString aSS( rPool.intern( pCrn->GetString()));
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaStringToken( aSS));
+ pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+// External document (SUPBOOK) ================================================
+
+XclImpSupbook::XclImpSupbook( XclImpStream& rStrm ) :
+ XclImpRoot( rStrm.GetRoot() ),
+ meType( XclSupbookType::Unknown ),
+ mnSBTab( EXC_TAB_DELETED )
+{
+ sal_uInt16 nSBTabCnt;
+ nSBTabCnt = rStrm.ReaduInt16();
+
+ if( rStrm.GetRecLeft() == 2 )
+ {
+ switch( rStrm.ReaduInt16() )
+ {
+ case EXC_SUPB_SELF: meType = XclSupbookType::Self; break;
+ case EXC_SUPB_ADDIN: meType = XclSupbookType::Addin; break;
+ default: OSL_FAIL( "XclImpSupbook::XclImpSupbook - unknown special SUPBOOK type" );
+ }
+ return;
+ }
+
+ OUString aEncUrl( rStrm.ReadUniString() );
+ bool bSelf = false;
+ XclImpUrlHelper::DecodeUrl( maXclUrl, bSelf, GetRoot(), aEncUrl );
+
+ if( maXclUrl.equalsIgnoreAsciiCase( "\010EUROTOOL.XLA" ) )
+ {
+ meType = XclSupbookType::Eurotool;
+ maSupbTabList.push_back( std::make_unique<XclImpSupbookTab>( maXclUrl ) );
+ }
+ else if( nSBTabCnt )
+ {
+ meType = XclSupbookType::Extern;
+
+ //assuming all empty strings with just len header of 0
+ const size_t nMinRecordSize = sizeof(sal_Int16);
+ const size_t nMaxRecords = rStrm.GetRecLeft() / nMinRecordSize;
+ if (nSBTabCnt > nMaxRecords)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nSBTabCnt << " claimed, truncating");
+ nSBTabCnt = nMaxRecords;
+ }
+
+ for( sal_uInt16 nSBTab = 0; nSBTab < nSBTabCnt; ++nSBTab )
+ {
+ OUString aTabName( rStrm.ReadUniString() );
+ maSupbTabList.push_back( std::make_unique<XclImpSupbookTab>( aTabName ) );
+ }
+ }
+ else
+ {
+ meType = XclSupbookType::Special;
+ // create dummy list entry
+ maSupbTabList.push_back( std::make_unique<XclImpSupbookTab>( maXclUrl ) );
+ }
+}
+
+void XclImpSupbook::ReadXct( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 2 );
+ mnSBTab = rStrm.ReaduInt16();
+}
+
+void XclImpSupbook::ReadCrn( XclImpStream& rStrm )
+{
+ if (mnSBTab >= maSupbTabList.size())
+ return;
+ XclImpSupbookTab& rSbTab = *maSupbTabList[mnSBTab];
+ sal_uInt8 nXclColLast, nXclColFirst;
+ sal_uInt16 nXclRow;
+ nXclColLast = rStrm.ReaduInt8();
+ nXclColFirst = rStrm.ReaduInt8();
+ nXclRow = rStrm.ReaduInt16();
+
+ for( sal_uInt8 nXclCol = nXclColFirst; (nXclCol <= nXclColLast) && (rStrm.GetRecLeft() > 1); ++nXclCol )
+ rSbTab.ReadCrn( rStrm, XclAddress( nXclCol, nXclRow ) );
+}
+
+void XclImpSupbook::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv )
+{
+ maExtNameList.push_back( std::make_unique<XclImpExtName>( *this, rStrm, meType, pFormulaConv ) );
+}
+
+const XclImpExtName* XclImpSupbook::GetExternName( sal_uInt16 nXclIndex ) const
+{
+ if (nXclIndex == 0)
+ {
+ SAL_WARN("sc", "XclImpSupbook::GetExternName - index must be >0");
+ return nullptr;
+ }
+ if (meType == XclSupbookType::Self || nXclIndex > maExtNameList.size())
+ return nullptr;
+ return maExtNameList[nXclIndex-1].get();
+}
+
+bool XclImpSupbook::GetLinkData( OUString& rApplic, OUString& rTopic ) const
+{
+ return (meType == XclSupbookType::Special) && XclImpUrlHelper::DecodeLink( rApplic, rTopic, maXclUrl );
+}
+
+OUString XclImpSupbook::GetMacroName( sal_uInt16 nXclNameIdx ) const
+{
+ OSL_ENSURE( nXclNameIdx > 0, "XclImpSupbook::GetMacroName - index must be >0" );
+ const XclImpName* pName = (meType == XclSupbookType::Self) ? GetNameManager().GetName( nXclNameIdx ) : nullptr;
+ return (pName && pName->IsVBName()) ? pName->GetScName() : OUString();
+}
+
+OUString XclImpSupbook::GetTabName( sal_uInt16 nXtiTab ) const
+{
+ if (nXtiTab >= maSupbTabList.size())
+ return OUString();
+ return maSupbTabList[nXtiTab]->GetTabName();
+}
+
+sal_uInt16 XclImpSupbook::GetTabCount() const
+{
+ return ulimit_cast<sal_uInt16>(maSupbTabList.size());
+}
+
+void XclImpSupbook::LoadCachedValues()
+{
+ if (meType != XclSupbookType::Extern || GetExtDocOptions().GetDocSettings().mnLinkCnt > 0 || !GetDocShell())
+ return;
+
+ OUString aAbsUrl( ScGlobal::GetAbsDocName(maXclUrl, GetDocShell()) );
+
+ ScExternalRefManager* pRefMgr = GetRoot().GetDoc().GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(aAbsUrl);
+
+ for (auto& rxTab : maSupbTabList)
+ {
+ const OUString& rTabName = rxTab->GetTabName();
+ ScExternalRefCache::TableTypeRef pCacheTable = pRefMgr->getCacheTable(nFileId, rTabName, true);
+ rxTab->LoadCachedValues( pCacheTable, GetSharedStringPool());
+ pCacheTable->setWholeTableCached();
+ }
+}
+
+svl::SharedStringPool& XclImpSupbook::GetSharedStringPool()
+{
+ return GetDoc().GetSharedStringPool();
+}
+
+// Import link manager ========================================================
+
+XclImpLinkManagerImpl::XclImpLinkManagerImpl( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpLinkManagerImpl::ReadExternsheet( XclImpStream& rStrm )
+{
+ sal_uInt16 nXtiCount;
+ nXtiCount = rStrm.ReaduInt16();
+ OSL_ENSURE( static_cast< std::size_t >( nXtiCount * 6 ) == rStrm.GetRecLeft(), "XclImpLinkManagerImpl::ReadExternsheet - invalid count" );
+ nXtiCount = static_cast< sal_uInt16 >( ::std::min< std::size_t >( nXtiCount, rStrm.GetRecLeft() / 6 ) );
+
+ /* #i104057# A weird external XLS generator writes multiple EXTERNSHEET
+ records instead of only one as expected. Surprisingly, Excel seems to
+ insert the entries of the second record before the entries of the first
+ record. */
+ XclImpXtiVector aNewEntries( nXtiCount );
+ for( auto& rNewEntry : aNewEntries )
+ {
+ if (!rStrm.IsValid())
+ break;
+ rStrm >> rNewEntry;
+ }
+ maXtiList.insert( maXtiList.begin(), aNewEntries.begin(), aNewEntries.end() );
+
+ LoadCachedValues();
+}
+
+void XclImpLinkManagerImpl::ReadSupbook( XclImpStream& rStrm )
+{
+ maSupbookList.push_back( std::make_unique<XclImpSupbook>( rStrm ) );
+}
+
+void XclImpLinkManagerImpl::ReadXct( XclImpStream& rStrm )
+{
+ if( !maSupbookList.empty() )
+ maSupbookList.back()->ReadXct( rStrm );
+}
+
+void XclImpLinkManagerImpl::ReadCrn( XclImpStream& rStrm )
+{
+ if( !maSupbookList.empty() )
+ maSupbookList.back()->ReadCrn( rStrm );
+}
+
+void XclImpLinkManagerImpl::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv )
+{
+ if( !maSupbookList.empty() )
+ maSupbookList.back()->ReadExternname( rStrm, pFormulaConv );
+}
+
+bool XclImpLinkManagerImpl::IsSelfRef( sal_uInt16 nXtiIndex ) const
+{
+ const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex );
+ return pSupbook && (pSupbook->GetType() == XclSupbookType::Self);
+}
+
+bool XclImpLinkManagerImpl::GetScTabRange(
+ SCTAB& rnFirstScTab, SCTAB& rnLastScTab, sal_uInt16 nXtiIndex ) const
+{
+ if( const XclImpXti* pXti = GetXti( nXtiIndex ) )
+ {
+ if (!maSupbookList.empty() && (pXti->mnSupbook < maSupbookList.size()) )
+ {
+ rnFirstScTab = pXti->mnSBTabFirst;
+ rnLastScTab = pXti->mnSBTabLast;
+ return true;
+ }
+ }
+ return false;
+}
+
+const XclImpExtName* XclImpLinkManagerImpl::GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const
+{
+ const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex );
+ return pSupbook ? pSupbook->GetExternName( nExtName ) : nullptr;
+}
+
+const OUString* XclImpLinkManagerImpl::GetSupbookUrl( sal_uInt16 nXtiIndex ) const
+{
+ const XclImpSupbook* p = GetSupbook( nXtiIndex );
+ if (!p)
+ return nullptr;
+ return &p->GetXclUrl();
+}
+
+OUString XclImpLinkManagerImpl::GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const
+{
+ const XclImpSupbook* p = GetSupbook(nXti);
+ return p ? p->GetTabName(nXtiTab) : OUString();
+}
+
+bool XclImpLinkManagerImpl::GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const
+{
+ const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex );
+ return pSupbook && pSupbook->GetLinkData( rApplic, rTopic );
+}
+
+OUString XclImpLinkManagerImpl::GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const
+{
+ const XclImpSupbook* pSupbook = GetSupbook( nExtSheet );
+ return pSupbook ? pSupbook->GetMacroName( nExtName ) : OUString();
+}
+
+const XclImpXti* XclImpLinkManagerImpl::GetXti( sal_uInt16 nXtiIndex ) const
+{
+ return (nXtiIndex < maXtiList.size()) ? &maXtiList[ nXtiIndex ] : nullptr;
+}
+
+const XclImpSupbook* XclImpLinkManagerImpl::GetSupbook( sal_uInt16 nXtiIndex ) const
+{
+ if ( maSupbookList.empty() )
+ return nullptr;
+ const XclImpXti* pXti = GetXti( nXtiIndex );
+ if (!pXti || pXti->mnSupbook >= maSupbookList.size())
+ return nullptr;
+ return maSupbookList.at( pXti->mnSupbook ).get();
+}
+
+void XclImpLinkManagerImpl::LoadCachedValues()
+{
+ // Read all CRN records which can be accessed via XclImpSupbook, and store
+ // the cached values to the external reference manager.
+ for (auto& rxSupbook : maSupbookList)
+ rxSupbook->LoadCachedValues();
+}
+
+XclImpLinkManager::XclImpLinkManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mxImpl( new XclImpLinkManagerImpl( rRoot ) )
+{
+}
+
+XclImpLinkManager::~XclImpLinkManager()
+{
+}
+
+void XclImpLinkManager::ReadExternsheet( XclImpStream& rStrm )
+{
+ mxImpl->ReadExternsheet( rStrm );
+}
+
+void XclImpLinkManager::ReadSupbook( XclImpStream& rStrm )
+{
+ mxImpl->ReadSupbook( rStrm );
+}
+
+void XclImpLinkManager::ReadXct( XclImpStream& rStrm )
+{
+ mxImpl->ReadXct( rStrm );
+}
+
+void XclImpLinkManager::ReadCrn( XclImpStream& rStrm )
+{
+ mxImpl->ReadCrn( rStrm );
+}
+
+void XclImpLinkManager::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv )
+{
+ mxImpl->ReadExternname( rStrm, pFormulaConv );
+}
+
+bool XclImpLinkManager::IsSelfRef( sal_uInt16 nXtiIndex ) const
+{
+ return mxImpl->IsSelfRef( nXtiIndex );
+}
+
+bool XclImpLinkManager::GetScTabRange(
+ SCTAB& rnFirstScTab, SCTAB& rnLastScTab, sal_uInt16 nXtiIndex ) const
+{
+ return mxImpl->GetScTabRange( rnFirstScTab, rnLastScTab, nXtiIndex );
+}
+
+const XclImpExtName* XclImpLinkManager::GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const
+{
+ return mxImpl->GetExternName( nXtiIndex, nExtName );
+}
+
+const OUString* XclImpLinkManager::GetSupbookUrl( sal_uInt16 nXtiIndex ) const
+{
+ return mxImpl->GetSupbookUrl(nXtiIndex);
+}
+
+OUString XclImpLinkManager::GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const
+{
+ return mxImpl->GetSupbookTabName(nXti, nXtiTab);
+}
+
+bool XclImpLinkManager::GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const
+{
+ return mxImpl->GetLinkData( rApplic, rTopic, nXtiIndex );
+}
+
+OUString XclImpLinkManager::GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const
+{
+ return mxImpl->GetMacroName( nExtSheet, nExtName );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiname.cxx b/sc/source/filter/excel/xiname.cxx
new file mode 100644
index 000000000..d498dfba4
--- /dev/null
+++ b/sc/source/filter/excel/xiname.cxx
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xiname.hxx>
+#include <xlname.hxx>
+#include <rangenam.hxx>
+#include <xistream.hxx>
+#include <excform.hxx>
+#include <excimp8.hxx>
+#include <scextopt.hxx>
+#include <document.hxx>
+
+// *** Implementation ***
+
+XclImpName::TokenStrmData::TokenStrmData( XclImpStream& rStrm ) :
+ mrStrm(rStrm), mnStrmPos(0), mnStrmSize(0) {}
+
+XclImpName::XclImpName( XclImpStream& rStrm, sal_uInt16 nXclNameIdx ) :
+ XclImpRoot( rStrm.GetRoot() ),
+ mpScData( nullptr ),
+ mnScTab( SCTAB_MAX ),
+ meNameType( ScRangeData::Type::Name ),
+ mnXclTab( EXC_NAME_GLOBAL ),
+ mnNameIndex( nXclNameIdx ),
+ mbVBName( false ),
+ mbMacro( false )
+{
+ ExcelToSc& rFmlaConv = GetOldFmlaConverter();
+
+ // 1) *** read data from stream *** ---------------------------------------
+
+ sal_uInt16 nFlags = 0, nFmlaSize = 0, nExtSheet = EXC_NAME_GLOBAL;
+ sal_uInt8 nNameLen = 0;
+ sal_Unicode cBuiltIn(EXC_BUILTIN_UNKNOWN); /// Excel built-in name index.
+
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ {
+ sal_uInt8 nFlagsBiff2;
+ nFlagsBiff2 = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ rStrm.Ignore( 1 ); //nShortCut
+ nNameLen = rStrm.ReaduInt8();
+ nFmlaSize = rStrm.ReaduInt8();
+ ::set_flag( nFlags, EXC_NAME_FUNC, ::get_flag( nFlagsBiff2, EXC_NAME2_FUNC ) );
+ }
+ break;
+
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ {
+ nFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 1 ); //nShortCut
+ nNameLen = rStrm.ReaduInt8();
+ nFmlaSize = rStrm.ReaduInt16();
+ }
+ break;
+
+ case EXC_BIFF5:
+ case EXC_BIFF8:
+ {
+ nFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 1 ); //nShortCut
+ nNameLen = rStrm.ReaduInt8();
+ nFmlaSize = rStrm.ReaduInt16();
+ nExtSheet = rStrm.ReaduInt16();
+ mnXclTab = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ }
+ break;
+
+ default: DBG_ERROR_BIFF();
+ }
+
+ if( GetBiff() <= EXC_BIFF5 )
+ maXclName = rStrm.ReadRawByteString( nNameLen );
+ else
+ maXclName = rStrm.ReadUniString( nNameLen );
+
+ // 2) *** convert sheet index and name *** --------------------------------
+
+ // functions and VBA
+ bool bFunction = ::get_flag( nFlags, EXC_NAME_FUNC );
+ mbVBName = ::get_flag( nFlags, EXC_NAME_VB );
+ mbMacro = ::get_flag( nFlags, EXC_NAME_PROC );
+
+ // get built-in name, or convert characters invalid in Calc
+ bool bBuiltIn = ::get_flag( nFlags, EXC_NAME_BUILTIN );
+
+ // special case for BIFF5 filter range - name appears as plain text without built-in flag
+ if( (GetBiff() == EXC_BIFF5) && (maXclName == XclTools::GetXclBuiltInDefName(EXC_BUILTIN_FILTERDATABASE)) )
+ {
+ bBuiltIn = true;
+ maXclName = OUStringChar(EXC_BUILTIN_FILTERDATABASE);
+ }
+
+ // convert Excel name to Calc name
+ if( mbVBName )
+ {
+ // VB macro name
+ maScName = maXclName;
+ }
+ else if( bBuiltIn )
+ {
+ // built-in name
+ if( !maXclName.isEmpty() )
+ cBuiltIn = maXclName[0];
+ if( cBuiltIn == '?' ) // NUL character is imported as '?'
+ cBuiltIn = '\0';
+ maScName = XclTools::GetBuiltInDefName( cBuiltIn );
+ }
+ else
+ {
+ // any other name
+ maScName = ScfTools::ConvertToScDefinedName( maXclName );
+ }
+
+ // add index for local names
+ if( mnXclTab != EXC_NAME_GLOBAL )
+ {
+ sal_uInt16 nUsedTab = (GetBiff() == EXC_BIFF8) ? mnXclTab : nExtSheet;
+ // TODO: may not work for BIFF5, handle skipped sheets (all BIFF)
+ mnScTab = static_cast< SCTAB >( nUsedTab - 1 );
+ }
+
+ // 3) *** convert the name definition formula *** -------------------------
+
+ rFmlaConv.Reset();
+ std::unique_ptr<ScTokenArray> pTokArr;
+
+ if( ::get_flag( nFlags, EXC_NAME_BIG ) )
+ {
+ // special, unsupported name
+ pTokArr = rFmlaConv.GetDummy();
+ }
+ else if( bBuiltIn )
+ {
+ SCTAB const nLocalTab = (mnXclTab == EXC_NAME_GLOBAL) ? SCTAB_MAX : (mnXclTab - 1);
+
+ // --- print ranges or title ranges ---
+ rStrm.PushPosition();
+ switch( cBuiltIn )
+ {
+ case EXC_BUILTIN_PRINTAREA:
+ if( rFmlaConv.Convert( GetPrintAreaBuffer(), rStrm, nFmlaSize, nLocalTab, FT_RangeName ) == ConvErr::OK )
+ meNameType |= ScRangeData::Type::PrintArea;
+ break;
+ case EXC_BUILTIN_PRINTTITLES:
+ if( rFmlaConv.Convert( GetTitleAreaBuffer(), rStrm, nFmlaSize, nLocalTab, FT_RangeName ) == ConvErr::OK )
+ meNameType |= ScRangeData::Type::ColHeader | ScRangeData::Type::RowHeader;
+ break;
+ }
+ rStrm.PopPosition();
+
+ // --- name formula ---
+ // JEG : double check this. It is clearly false for normal names
+ // but some of the builtins (sheettitle?) might be able to handle arrays
+ rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize, false, FT_RangeName );
+
+ // --- auto or advanced filter ---
+ if ((GetBiff() == EXC_BIFF8) && pTokArr)
+ {
+ ScRange aRange;
+ if (pTokArr->IsReference(aRange, ScAddress()))
+ {
+ switch( cBuiltIn )
+ {
+ case EXC_BUILTIN_FILTERDATABASE:
+ GetFilterManager().Insert( &GetOldRoot(), aRange);
+ break;
+ case EXC_BUILTIN_CRITERIA:
+ GetFilterManager().AddAdvancedRange( aRange );
+ meNameType |= ScRangeData::Type::Criteria;
+ break;
+ case EXC_BUILTIN_EXTRACT:
+ if (pTokArr->IsValidReference(aRange, ScAddress()))
+ GetFilterManager().AddExtractPos( aRange );
+ break;
+ }
+ }
+ }
+ }
+ else if( nFmlaSize > 0 )
+ {
+ // Regular defined name. We need to convert the tokens after all the
+ // names have been registered (for cross-referenced names).
+ mpTokensData.reset(new TokenStrmData(rStrm));
+ mpTokensData->mnStrmPos = rStrm.GetSvStreamPos();
+ rStrm.StorePosition(mpTokensData->maStrmPos);
+ mpTokensData->mnStrmSize = nFmlaSize;
+ }
+
+ if (pTokArr && !bFunction && !mbVBName)
+ InsertName(pTokArr.get());
+}
+
+void XclImpName::ConvertTokens()
+{
+ if (!mpTokensData)
+ return;
+
+ ExcelToSc& rFmlaConv = GetOldFmlaConverter();
+ rFmlaConv.Reset();
+ std::unique_ptr<ScTokenArray> pArray;
+
+ XclImpStreamPos aOldPos;
+ XclImpStream& rStrm = mpTokensData->mrStrm;
+ rStrm.StorePosition(aOldPos);
+ rStrm.RestorePosition(mpTokensData->maStrmPos);
+ rFmlaConv.Convert(pArray, rStrm, mpTokensData->mnStrmSize, true, FT_RangeName);
+ rStrm.RestorePosition(aOldPos);
+
+ if (pArray)
+ InsertName(pArray.get());
+
+ mpTokensData.reset();
+}
+
+void XclImpName::InsertName(const ScTokenArray* pArray)
+{
+ // create the Calc name data
+ ScRangeData* pData = new ScRangeData(GetDoc(), maScName, *pArray, ScAddress(), meNameType);
+ pData->GuessPosition(); // calculate base position for relative refs
+ pData->SetIndex( mnNameIndex ); // used as unique identifier in formulas
+ if (mnXclTab == EXC_NAME_GLOBAL)
+ {
+ if (!GetDoc().GetRangeName()->insert(pData))
+ pData = nullptr;
+ }
+ else
+ {
+ ScRangeName* pLocalNames = GetDoc().GetRangeName(mnScTab);
+ if (pLocalNames)
+ {
+ if (!pLocalNames->insert(pData))
+ pData = nullptr;
+ }
+ else
+ {
+ delete pData;
+ pData = nullptr;
+ }
+
+ if (GetBiff() == EXC_BIFF8 && pData)
+ {
+ ScRange aRange;
+ // discard deleted ranges ( for the moment at least )
+ if ( pData->IsValidReference( aRange ) )
+ {
+ GetExtDocOptions().GetOrCreateTabSettings( mnXclTab );
+ }
+ }
+ }
+ if (pData)
+ {
+ GetDoc().CheckLinkFormulaNeedingCheck( *pData->GetCode());
+ mpScData = pData; // cache for later use
+ }
+}
+
+XclImpNameManager::XclImpNameManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpNameManager::ReadName( XclImpStream& rStrm )
+{
+ size_t nCount = maNameList.size();
+ if( nCount < 0xFFFF )
+ maNameList.push_back( std::make_unique<XclImpName>( rStrm, static_cast< sal_uInt16 >( nCount + 1 ) ) );
+}
+
+const XclImpName* XclImpNameManager::FindName( std::u16string_view rXclName, SCTAB nScTab ) const
+{
+ const XclImpName* pGlobalName = nullptr; // a found global name
+ const XclImpName* pLocalName = nullptr; // a found local name
+ for( const auto& rxName : maNameList )
+ {
+ if( rxName->GetXclName() == rXclName )
+ {
+ if( rxName->GetScTab() == nScTab )
+ pLocalName = rxName.get();
+ else if( rxName->IsGlobal() )
+ pGlobalName = rxName.get();
+ }
+
+ if (pLocalName)
+ break;
+ }
+ return pLocalName ? pLocalName : pGlobalName;
+}
+
+const XclImpName* XclImpNameManager::GetName( sal_uInt16 nXclNameIdx ) const
+{
+ OSL_ENSURE( nXclNameIdx > 0, "XclImpNameManager::GetName - index must be >0" );
+ return ( nXclNameIdx <= 0 || nXclNameIdx > maNameList.size() ) ? nullptr : maNameList.at( nXclNameIdx - 1 ).get();
+}
+
+void XclImpNameManager::ConvertAllTokens()
+{
+ for (auto& rxName : maNameList)
+ rxName->ConvertTokens();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xipage.cxx b/sc/source/filter/excel/xipage.cxx
new file mode 100644
index 000000000..c06308ba7
--- /dev/null
+++ b/sc/source/filter/excel/xipage.cxx
@@ -0,0 +1,401 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xipage.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/graph.hxx>
+#include <scitems.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svx/pageitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <unotools/configmgr.hxx>
+#include <document.hxx>
+#include <stlsheet.hxx>
+#include <attrib.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xiescher.hxx>
+
+// Page settings ==============================================================
+
+XclImpPageSettings::XclImpPageSettings( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+ Initialize();
+}
+
+void XclImpPageSettings::Initialize()
+{
+ maData.SetDefaults();
+ mbValidPaper = false;
+}
+
+void XclImpPageSettings::ReadSetup( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF4 );
+ if( GetBiff() < EXC_BIFF4 )
+ return;
+
+ // BIFF4 - BIFF8
+ sal_uInt16 nFlags;
+ maData.mnPaperSize = rStrm.ReaduInt16();
+ maData.mnScaling = rStrm.ReaduInt16();
+ maData.mnStartPage = rStrm.ReaduInt16();
+ maData.mnFitToWidth = rStrm.ReaduInt16();
+ maData.mnFitToHeight = rStrm.ReaduInt16();
+ nFlags = rStrm.ReaduInt16();
+
+ mbValidPaper = maData.mbValid = !::get_flag( nFlags, EXC_SETUP_INVALID );
+ maData.mbPrintInRows = ::get_flag( nFlags, EXC_SETUP_INROWS );
+ maData.mbPortrait = ::get_flag( nFlags, EXC_SETUP_PORTRAIT );
+ maData.mbBlackWhite = ::get_flag( nFlags, EXC_SETUP_BLACKWHITE );
+ maData.mbManualStart = true;
+
+ // new in BIFF5 - BIFF8
+ if( GetBiff() >= EXC_BIFF5 )
+ {
+ maData.mnHorPrintRes = rStrm.ReaduInt16();
+ maData.mnVerPrintRes = rStrm.ReaduInt16();
+ maData.mfHeaderMargin = rStrm.ReadDouble();
+ maData.mfFooterMargin = rStrm.ReadDouble();
+ maData.mnCopies = rStrm.ReaduInt16();
+
+ maData.mbDraftQuality = ::get_flag( nFlags, EXC_SETUP_DRAFT );
+ maData.mbPrintNotes = ::get_flag( nFlags, EXC_SETUP_PRINTNOTES );
+ maData.mbManualStart = ::get_flag( nFlags, EXC_SETUP_STARTPAGE );
+ }
+}
+
+void XclImpPageSettings::ReadMargin( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_LEFTMARGIN: maData.mfLeftMargin = rStrm.ReadDouble(); break;
+ case EXC_ID_RIGHTMARGIN: maData.mfRightMargin = rStrm.ReadDouble(); break;
+ case EXC_ID_TOPMARGIN: maData.mfTopMargin = rStrm.ReadDouble(); break;
+ case EXC_ID_BOTTOMMARGIN: maData.mfBottomMargin = rStrm.ReadDouble(); break;
+ default: OSL_FAIL( "XclImpPageSettings::ReadMargin - unknown record" );
+ }
+}
+
+void XclImpPageSettings::ReadCenter( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF3 ); // read it anyway
+ bool bCenter = (rStrm.ReaduInt16() != 0);
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_HCENTER: maData.mbHorCenter = bCenter; break;
+ case EXC_ID_VCENTER: maData.mbVerCenter = bCenter; break;
+ default: OSL_FAIL( "XclImpPageSettings::ReadCenter - unknown record" );
+ }
+}
+
+void XclImpPageSettings::ReadHeaderFooter( XclImpStream& rStrm )
+{
+ OUString aString;
+ if( rStrm.GetRecLeft() )
+ aString = (GetBiff() <= EXC_BIFF5) ? rStrm.ReadByteString( false ) : rStrm.ReadUniString();
+
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_HEADER: maData.maHeader = aString; break;
+ case EXC_ID_FOOTER: maData.maFooter = aString; break;
+ case EXC_ID_HEADER_EVEN: maData.maHeaderEven = aString; break;
+ case EXC_ID_FOOTER_EVEN: maData.maFooterEven = aString; break;
+ default: OSL_FAIL( "XclImpPageSettings::ReadHeaderFooter - unknown record" );
+ }
+
+ if (utl::ConfigManager::IsFuzzing())
+ {
+ if (maData.maHeader.getLength() > 10)
+ maData.maHeader = maData.maHeader.copy(0, 10);
+ if (maData.maFooter.getLength() > 10)
+ maData.maFooter = maData.maFooter.copy(0, 10);
+ if (maData.maHeaderEven.getLength() > 10)
+ maData.maHeaderEven = maData.maHeaderEven.copy(0, 10);
+ if (maData.maFooterEven.getLength() > 10)
+ maData.maFooterEven = maData.maFooterEven.copy(0, 10);
+ }
+}
+
+void XclImpPageSettings::ReadPageBreaks( XclImpStream& rStrm )
+{
+ ScfUInt16Vec* pVec = nullptr;
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_HORPAGEBREAKS: pVec = &maData.maHorPageBreaks; break;
+ case EXC_ID_VERPAGEBREAKS: pVec = &maData.maVerPageBreaks; break;
+ default: OSL_FAIL( "XclImpPageSettings::ReadPageBreaks - unknown record" );
+ }
+
+ if( !pVec )
+ return;
+
+ bool bIgnore = GetBiff() == EXC_BIFF8; // ignore start/end columns or rows in BIFF8
+
+ sal_uInt16 nCount, nBreak;
+ nCount = rStrm.ReaduInt16();
+ pVec->clear();
+ pVec->reserve( nCount );
+
+ while( nCount-- )
+ {
+ nBreak = rStrm.ReaduInt16();
+ if( nBreak )
+ pVec->push_back( nBreak );
+ if( bIgnore )
+ rStrm.Ignore( 4 );
+ }
+}
+
+void XclImpPageSettings::ReadPrintHeaders( XclImpStream& rStrm )
+{
+ maData.mbPrintHeadings = (rStrm.ReaduInt16() != 0);
+}
+
+void XclImpPageSettings::ReadPrintGridLines( XclImpStream& rStrm )
+{
+ maData.mbPrintGrid = (rStrm.ReaduInt16() != 0);
+}
+
+void XclImpPageSettings::ReadImgData( XclImpStream& rStrm )
+{
+ Graphic aGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm );
+ if( aGraphic.GetType() != GraphicType::NONE )
+ maData.mxBrushItem.reset( new SvxBrushItem( aGraphic, GPOS_TILED, ATTR_BACKGROUND ) );
+}
+
+void XclImpPageSettings::SetPaperSize( sal_uInt16 nXclPaperSize, bool bPortrait )
+{
+ maData.mnPaperSize = nXclPaperSize;
+ maData.mbPortrait = bPortrait;
+ mbValidPaper = true;
+}
+
+namespace {
+
+void lclPutMarginItem( SfxItemSet& rItemSet, sal_uInt16 nRecId, double fMarginInch )
+{
+ sal_uInt16 nMarginTwips = XclTools::GetTwipsFromInch( fMarginInch );
+ switch( nRecId )
+ {
+ case EXC_ID_TOPMARGIN:
+ case EXC_ID_BOTTOMMARGIN:
+ {
+ SvxULSpaceItem aItem( rItemSet.Get( ATTR_ULSPACE ) );
+ if( nRecId == EXC_ID_TOPMARGIN )
+ aItem.SetUpperValue( nMarginTwips );
+ else
+ aItem.SetLowerValue( nMarginTwips );
+ rItemSet.Put( aItem );
+ }
+ break;
+ case EXC_ID_LEFTMARGIN:
+ case EXC_ID_RIGHTMARGIN:
+ {
+ SvxLRSpaceItem aItem( rItemSet.Get( ATTR_LRSPACE ) );
+ if( nRecId == EXC_ID_LEFTMARGIN )
+ aItem.SetLeftValue( nMarginTwips );
+ else
+ aItem.SetRightValue( nMarginTwips );
+ rItemSet.Put( aItem );
+ }
+ break;
+ default:
+ OSL_FAIL( "XclImpPageSettings::SetMarginItem - unknown record id" );
+ }
+}
+
+} // namespace
+
+void XclImpPageSettings::Finalize()
+{
+ ScDocument& rDoc = GetDoc();
+ SCTAB nScTab = GetCurrScTab();
+
+ // *** create page style sheet ***
+
+ OUString aStyleName;
+ OUString aTableName;
+ if( GetDoc().GetName( nScTab, aTableName ) )
+ aStyleName = "PageStyle_" + aTableName;
+ else
+ aStyleName = "PageStyle_" + OUString::number(static_cast<sal_Int32>(nScTab+1));
+
+ ScStyleSheet& rStyleSheet = ScfTools::MakePageStyleSheet(
+ GetStyleSheetPool(), aStyleName, false);
+
+ SfxItemSet& rItemSet = rStyleSheet.GetItemSet();
+
+ // *** page settings ***
+
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_TOPDOWN, !maData.mbPrintInRows ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_HORCENTER, maData.mbHorCenter ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_VERCENTER, maData.mbVerCenter ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_HEADERS, maData.mbPrintHeadings ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_GRID, maData.mbPrintGrid ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_NOTES, maData.mbPrintNotes ), true );
+
+ sal_uInt16 nStartPage = maData.mbManualStart ? maData.mnStartPage : 0;
+ ScfTools::PutItem( rItemSet, SfxUInt16Item( ATTR_PAGE_FIRSTPAGENO, nStartPage ), true );
+
+ if( maData.mxBrushItem )
+ rItemSet.Put( *maData.mxBrushItem );
+
+ if( mbValidPaper )
+ {
+ SvxPageItem aPageItem( rItemSet.Get( ATTR_PAGE ) );
+ aPageItem.SetLandscape( !maData.mbPortrait );
+ rItemSet.Put( aPageItem );
+ ScfTools::PutItem( rItemSet, SvxSizeItem( ATTR_PAGE_SIZE, maData.GetScPaperSize() ), true );
+ }
+
+ if( maData.mbFitToPages )
+ rItemSet.Put( ScPageScaleToItem( maData.mnFitToWidth, maData.mnFitToHeight ) );
+ else if( maData.mbValid )
+ rItemSet.Put( SfxUInt16Item( ATTR_PAGE_SCALE, maData.mnScaling ) );
+
+ // *** margin preparations ***
+
+ double fLeftMargin = maData.mfLeftMargin;
+ double fRightMargin = maData.mfRightMargin;
+ double fTopMargin = maData.mfTopMargin;
+ double fBottomMargin = maData.mfBottomMargin;
+ // distances between header/footer and page area
+ double fHeaderHeight = 0.0;
+ double fHeaderDist = 0.0;
+ double fFooterHeight = 0.0;
+ double fFooterDist = 0.0;
+ // in Calc, "header/footer left/right margin" is X distance between header/footer and page margin
+ double fHdrLeftMargin = maData.mfHdrLeftMargin - maData.mfLeftMargin;
+ double fHdrRightMargin = maData.mfHdrRightMargin - maData.mfRightMargin;
+ double fFtrLeftMargin = maData.mfFtrLeftMargin - maData.mfLeftMargin;
+ double fFtrRightMargin = maData.mfFtrRightMargin - maData.mfRightMargin;
+
+ // *** header and footer ***
+
+ XclImpHFConverter aHFConv( GetRoot() );
+
+ // header
+ bool bHasHeader = !maData.maHeader.isEmpty();
+ SvxSetItem aHdrSetItem( rItemSet.Get( ATTR_PAGE_HEADERSET ) );
+ SfxItemSet& rHdrItemSet = aHdrSetItem.GetItemSet();
+ rHdrItemSet.Put( SfxBoolItem( ATTR_PAGE_ON, bHasHeader ) );
+ if( bHasHeader )
+ {
+ aHFConv.ParseString( maData.maHeader );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_HEADERLEFT );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_HEADERRIGHT );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_HEADERFIRST );
+ // #i23296# In Calc, "top margin" is distance to header
+ fTopMargin = maData.mfHeaderMargin;
+ // Calc uses distance between header and sheet data area
+ fHeaderHeight = XclTools::GetInchFromTwips( aHFConv.GetTotalHeight() );
+ fHeaderDist = maData.mfTopMargin - maData.mfHeaderMargin - fHeaderHeight;
+ }
+ if( fHeaderDist < 0.0 )
+ {
+ /* #i23296# Header overlays sheet data:
+ -> set fixed header height to get correct sheet data position. */
+ ScfTools::PutItem( rHdrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, false ), true );
+ // shrink header height
+ tools::Long nHdrHeight = XclTools::GetTwipsFromInch( fHeaderHeight + fHeaderDist );
+ ScfTools::PutItem( rHdrItemSet, SvxSizeItem( ATTR_PAGE_SIZE, Size( 0, nHdrHeight ) ), true );
+ lclPutMarginItem( rHdrItemSet, EXC_ID_BOTTOMMARGIN, 0.0 );
+ }
+ else
+ {
+ // use dynamic header height
+ ScfTools::PutItem( rHdrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, true ), true );
+ lclPutMarginItem( rHdrItemSet, EXC_ID_BOTTOMMARGIN, fHeaderDist );
+ }
+ lclPutMarginItem( rHdrItemSet, EXC_ID_LEFTMARGIN, fHdrLeftMargin );
+ lclPutMarginItem( rHdrItemSet, EXC_ID_RIGHTMARGIN, fHdrRightMargin );
+ rItemSet.Put( aHdrSetItem );
+
+ // footer
+ bool bHasFooter = !maData.maFooter.isEmpty();
+ SvxSetItem aFtrSetItem( rItemSet.Get( ATTR_PAGE_FOOTERSET ) );
+ SfxItemSet& rFtrItemSet = aFtrSetItem.GetItemSet();
+ rFtrItemSet.Put( SfxBoolItem( ATTR_PAGE_ON, bHasFooter ) );
+ if( bHasFooter )
+ {
+ aHFConv.ParseString( maData.maFooter );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_FOOTERLEFT );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_FOOTERRIGHT );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_FOOTERFIRST );
+ // #i23296# In Calc, "bottom margin" is distance to footer
+ fBottomMargin = maData.mfFooterMargin;
+ // Calc uses distance between footer and sheet data area
+ fFooterHeight = XclTools::GetInchFromTwips( aHFConv.GetTotalHeight() );
+ fFooterDist = maData.mfBottomMargin - maData.mfFooterMargin - fFooterHeight;
+ }
+ if( fFooterDist < 0.0 )
+ {
+ /* #i23296# Footer overlays sheet data:
+ -> set fixed footer height to get correct sheet data end position. */
+ ScfTools::PutItem( rFtrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, false ), true );
+ // shrink footer height
+ tools::Long nFtrHeight = XclTools::GetTwipsFromInch( fFooterHeight + fFooterDist );
+ ScfTools::PutItem( rFtrItemSet, SvxSizeItem( ATTR_PAGE_SIZE, Size( 0, nFtrHeight ) ), true );
+ lclPutMarginItem( rFtrItemSet, EXC_ID_TOPMARGIN, 0.0 );
+ }
+ else
+ {
+ // use dynamic footer height
+ ScfTools::PutItem( rFtrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, true ), true );
+ lclPutMarginItem( rFtrItemSet, EXC_ID_TOPMARGIN, fFooterDist );
+ }
+ lclPutMarginItem( rFtrItemSet, EXC_ID_LEFTMARGIN, fFtrLeftMargin );
+ lclPutMarginItem( rFtrItemSet, EXC_ID_RIGHTMARGIN, fFtrRightMargin );
+ rItemSet.Put( aFtrSetItem );
+
+ // *** set final margins ***
+
+ lclPutMarginItem( rItemSet, EXC_ID_LEFTMARGIN, fLeftMargin );
+ lclPutMarginItem( rItemSet, EXC_ID_RIGHTMARGIN, fRightMargin );
+ lclPutMarginItem( rItemSet, EXC_ID_TOPMARGIN, fTopMargin );
+ lclPutMarginItem( rItemSet, EXC_ID_BOTTOMMARGIN, fBottomMargin );
+
+ // *** put style sheet into document ***
+
+ rDoc.SetPageStyle( nScTab, rStyleSheet.GetName() );
+
+ // *** page breaks ***
+
+ for( const auto& rHorPageBreak : maData.maHorPageBreaks )
+ {
+ SCROW nScRow = static_cast< SCROW >( rHorPageBreak );
+ if( nScRow <= rDoc.MaxRow() )
+ rDoc.SetRowBreak(nScRow, nScTab, false, true);
+ }
+
+ for( const auto& rVerPageBreak : maData.maVerPageBreaks )
+ {
+ SCCOL nScCol = static_cast< SCCOL >( rVerPageBreak );
+ if( nScCol <= rDoc.MaxCol() )
+ rDoc.SetColBreak(nScCol, nScTab, false, true);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xipivot.cxx b/sc/source/filter/excel/xipivot.cxx
new file mode 100644
index 000000000..d8d4eaa63
--- /dev/null
+++ b/sc/source/filter/excel/xipivot.cxx
@@ -0,0 +1,1737 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xipivot.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+
+#include <tools/datetime.hxx>
+#include <svl/intitem.hxx>
+#include <svl/numformat.hxx>
+#include <sal/log.hxx>
+#include <sot/storage.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <dpobject.hxx>
+#include <dpshttab.hxx>
+#include <dpoutputgeometry.hxx>
+#include <scitems.hxx>
+#include <attrib.hxx>
+
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xilink.hxx>
+#include <xiescher.hxx>
+
+//TODO ExcelToSc usage
+#include <excform.hxx>
+#include <documentimport.hxx>
+
+#include <vector>
+
+using namespace com::sun::star;
+
+using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
+using ::com::sun::star::sheet::DataPilotFieldSortInfo;
+using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
+using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
+using ::com::sun::star::sheet::DataPilotFieldReference;
+using ::std::vector;
+
+// Pivot cache
+
+XclImpPCItem::XclImpPCItem( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_SXDOUBLE: ReadSxdouble( rStrm ); break;
+ case EXC_ID_SXBOOLEAN: ReadSxboolean( rStrm ); break;
+ case EXC_ID_SXERROR: ReadSxerror( rStrm ); break;
+ case EXC_ID_SXINTEGER: ReadSxinteger( rStrm ); break;
+ case EXC_ID_SXSTRING: ReadSxstring( rStrm ); break;
+ case EXC_ID_SXDATETIME: ReadSxdatetime( rStrm ); break;
+ case EXC_ID_SXEMPTY: ReadSxempty( rStrm ); break;
+ default: OSL_FAIL( "XclImpPCItem::XclImpPCItem - unknown record id" );
+ }
+}
+
+namespace {
+
+void lclSetValue( XclImpRoot& rRoot, const ScAddress& rScPos, double fValue, SvNumFormatType nFormatType )
+{
+ ScDocumentImport& rDoc = rRoot.GetDocImport();
+ rDoc.setNumericCell(rScPos, fValue);
+ sal_uInt32 nScNumFmt = rRoot.GetFormatter().GetStandardFormat( nFormatType, rRoot.GetDocLanguage() );
+ rDoc.getDoc().ApplyAttr(
+ rScPos.Col(), rScPos.Row(), rScPos.Tab(), SfxUInt32Item(ATTR_VALUE_FORMAT, nScNumFmt));
+}
+
+} // namespace
+
+void XclImpPCItem::WriteToSource( XclImpRoot& rRoot, const ScAddress& rScPos ) const
+{
+ ScDocumentImport& rDoc = rRoot.GetDocImport();
+ if( const OUString* pText = GetText() )
+ rDoc.setStringCell(rScPos, *pText);
+ else if( const double* pfValue = GetDouble() )
+ rDoc.setNumericCell(rScPos, *pfValue);
+ else if( const sal_Int16* pnValue = GetInteger() )
+ rDoc.setNumericCell(rScPos, *pnValue);
+ else if( const bool* pbValue = GetBool() )
+ lclSetValue( rRoot, rScPos, *pbValue ? 1.0 : 0.0, SvNumFormatType::LOGICAL );
+ else if( const DateTime* pDateTime = GetDateTime() )
+ {
+ // set number format date, time, or date/time, depending on the value
+ double fValue = rRoot.GetDoubleFromDateTime( *pDateTime );
+ double fInt = 0.0;
+ double fFrac = modf( fValue, &fInt );
+ SvNumFormatType nFormatType = ((fFrac == 0.0) && (fInt != 0.0)) ? SvNumFormatType::DATE :
+ ((fInt == 0.0) ? SvNumFormatType::TIME : SvNumFormatType::DATETIME);
+ lclSetValue( rRoot, rScPos, fValue, nFormatType );
+ }
+ else if( const sal_uInt16* pnError = GetError() )
+ {
+ double fValue;
+ sal_uInt8 nErrCode = static_cast< sal_uInt8 >( *pnError );
+ std::unique_ptr<ScTokenArray> pScTokArr = rRoot.GetOldFmlaConverter().GetBoolErr(
+ XclTools::ErrorToEnum( fValue, true, nErrCode ) );
+ ScFormulaCell* pCell = pScTokArr
+ ? new ScFormulaCell(rDoc.getDoc(), rScPos, std::move(pScTokArr))
+ : new ScFormulaCell(rDoc.getDoc(), rScPos);
+ pCell->SetHybridDouble( fValue );
+ rDoc.setFormulaCell(rScPos, pCell);
+ }
+}
+
+void XclImpPCItem::ReadSxdouble( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdouble - wrong record size" );
+ SetDouble( rStrm.ReadDouble() );
+}
+
+void XclImpPCItem::ReadSxboolean( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxboolean - wrong record size" );
+ SetBool( rStrm.ReaduInt16() != 0 );
+}
+
+void XclImpPCItem::ReadSxerror( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxerror - wrong record size" );
+ SetError( rStrm.ReaduInt16() );
+}
+
+void XclImpPCItem::ReadSxinteger( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxinteger - wrong record size" );
+ SetInteger( rStrm.ReadInt16() );
+}
+
+void XclImpPCItem::ReadSxstring( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() >= 3, "XclImpPCItem::ReadSxstring - wrong record size" );
+ SetText( rStrm.ReadUniString() );
+}
+
+void XclImpPCItem::ReadSxdatetime( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdatetime - wrong record size" );
+ sal_uInt16 nYear, nMonth;
+ sal_uInt8 nDay, nHour, nMin, nSec;
+ nYear = rStrm.ReaduInt16();
+ nMonth = rStrm.ReaduInt16();
+ nDay = rStrm.ReaduInt8();
+ nHour = rStrm.ReaduInt8();
+ nMin = rStrm.ReaduInt8();
+ nSec = rStrm.ReaduInt8();
+ SetDateTime( DateTime( Date( nDay, nMonth, nYear ), tools::Time( nHour, nMin, nSec ) ) );
+}
+
+void XclImpPCItem::ReadSxempty( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 0, "XclImpPCItem::ReadSxempty - wrong record size" );
+ SetEmpty();
+}
+
+XclImpPCField::XclImpPCField( const XclImpRoot& rRoot, XclImpPivotCache& rPCache, sal_uInt16 nFieldIdx ) :
+ XclPCField( EXC_PCFIELD_UNKNOWN, nFieldIdx ),
+ XclImpRoot( rRoot ),
+ mrPCache( rPCache ),
+ mnSourceScCol( -1 ),
+ mbNumGroupInfoRead( false )
+{
+}
+
+XclImpPCField::~XclImpPCField()
+{
+}
+
+// general field/item access --------------------------------------------------
+
+const OUString& XclImpPCField::GetFieldName( const ScfStringVec& rVisNames ) const
+{
+ if( IsGroupChildField() && (mnFieldIdx < rVisNames.size()) )
+ {
+ const OUString& rVisName = rVisNames[ mnFieldIdx ];
+ if (!rVisName.isEmpty())
+ return rVisName;
+ }
+ return maFieldInfo.maName;
+}
+
+const XclImpPCField* XclImpPCField::GetGroupBaseField() const
+{
+ OSL_ENSURE( IsGroupChildField(), "XclImpPCField::GetGroupBaseField - this field type does not have a base field" );
+ return IsGroupChildField() ? mrPCache.GetField( maFieldInfo.mnGroupBase ) : nullptr;
+}
+
+const XclImpPCItem* XclImpPCField::GetItem( sal_uInt16 nItemIdx ) const
+{
+ return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : nullptr;
+}
+
+const XclImpPCItem* XclImpPCField::GetLimitItem( sal_uInt16 nItemIdx ) const
+{
+ OSL_ENSURE( nItemIdx < 3, "XclImpPCField::GetLimitItem - invalid item index" );
+ OSL_ENSURE( nItemIdx < maNumGroupItems.size(), "XclImpPCField::GetLimitItem - no item found" );
+ return (nItemIdx < maNumGroupItems.size()) ? maNumGroupItems[ nItemIdx ].get() : nullptr;
+}
+
+void XclImpPCField::WriteFieldNameToSource( SCCOL nScCol, SCTAB nScTab )
+{
+ OSL_ENSURE( HasOrigItems(), "XclImpPCField::WriteFieldNameToSource - only for standard fields" );
+ GetDocImport().setStringCell(ScAddress(nScCol, 0, nScTab), maFieldInfo.maName);
+ mnSourceScCol = nScCol;
+}
+
+void XclImpPCField::WriteOrigItemToSource( SCROW nScRow, SCTAB nScTab, sal_uInt16 nItemIdx )
+{
+ if( nItemIdx < maOrigItems.size() )
+ maOrigItems[ nItemIdx ]->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) );
+}
+
+void XclImpPCField::WriteLastOrigItemToSource( SCROW nScRow, SCTAB nScTab )
+{
+ if( !maOrigItems.empty() )
+ maOrigItems.back()->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) );
+}
+
+// records --------------------------------------------------------------------
+
+void XclImpPCField::ReadSxfield( XclImpStream& rStrm )
+{
+ rStrm >> maFieldInfo;
+
+ /* Detect the type of this field. This is done very restrictive to detect
+ any unexpected state. */
+ meFieldType = EXC_PCFIELD_UNKNOWN;
+
+ bool bItems = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS );
+ bool bPostp = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_POSTPONE );
+ bool bCalced = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_CALCED );
+ bool bChild = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
+ bool bNum = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP );
+
+ sal_uInt16 nVisC = maFieldInfo.mnVisItems;
+ sal_uInt16 nGroupC = maFieldInfo.mnGroupItems;
+ sal_uInt16 nBaseC = maFieldInfo.mnBaseItems;
+ sal_uInt16 nOrigC = maFieldInfo.mnOrigItems;
+ OSL_ENSURE( nVisC > 0, "XclImpPCField::ReadSxfield - field without visible items" );
+
+ sal_uInt16 nType = maFieldInfo.mnFlags & EXC_SXFIELD_DATA_MASK;
+ bool bType =
+ (nType == EXC_SXFIELD_DATA_STR) ||
+ (nType == EXC_SXFIELD_DATA_INT) ||
+ (nType == EXC_SXFIELD_DATA_DBL) ||
+ (nType == EXC_SXFIELD_DATA_STR_INT) ||
+ (nType == EXC_SXFIELD_DATA_STR_DBL) ||
+ (nType == EXC_SXFIELD_DATA_DATE) ||
+ (nType == EXC_SXFIELD_DATA_DATE_EMP) ||
+ (nType == EXC_SXFIELD_DATA_DATE_NUM) ||
+ (nType == EXC_SXFIELD_DATA_DATE_STR);
+ bool bTypeNone =
+ (nType == EXC_SXFIELD_DATA_NONE);
+ // for now, ignore data type of calculated fields
+ OSL_ENSURE( bCalced || bType || bTypeNone, "XclImpPCField::ReadSxfield - unknown item data type" );
+
+ if( !(nVisC > 0 || bPostp) )
+ return;
+
+ if( bItems && !bPostp )
+ {
+ if( !bCalced )
+ {
+ // 1) standard fields and standard grouping fields
+ if( !bNum )
+ {
+ // 1a) standard field without grouping
+ if( bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == nVisC) )
+ meFieldType = EXC_PCFIELD_STANDARD;
+
+ // 1b) standard grouping field
+ else if( bTypeNone && (nGroupC == nVisC) && (nBaseC > 0) && (nOrigC == 0) )
+ meFieldType = EXC_PCFIELD_STDGROUP;
+ }
+ // 2) numerical grouping fields
+ else if( (nGroupC == nVisC) && (nBaseC == 0) )
+ {
+ // 2a) single num/date grouping field without child grouping field
+ if( !bChild && bType && (nOrigC > 0) )
+ {
+ switch( nType )
+ {
+ case EXC_SXFIELD_DATA_INT:
+ case EXC_SXFIELD_DATA_DBL: meFieldType = EXC_PCFIELD_NUMGROUP; break;
+ case EXC_SXFIELD_DATA_DATE: meFieldType = EXC_PCFIELD_DATEGROUP; break;
+ default: OSL_FAIL( "XclImpPCField::ReadSxfield - numeric group with wrong data type" );
+ }
+ }
+
+ // 2b) first date grouping field with child grouping field
+ else if( bChild && (nType == EXC_SXFIELD_DATA_DATE) && (nOrigC > 0) )
+ meFieldType = EXC_PCFIELD_DATEGROUP;
+
+ // 2c) additional date grouping field
+ else if( bTypeNone && (nOrigC == 0) )
+ meFieldType = EXC_PCFIELD_DATECHILD;
+ }
+ OSL_ENSURE( meFieldType != EXC_PCFIELD_UNKNOWN, "XclImpPCField::ReadSxfield - invalid standard or grouped field" );
+ }
+
+ // 3) calculated field
+ else
+ {
+ if( !bChild && !bNum && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) )
+ meFieldType = EXC_PCFIELD_CALCED;
+ OSL_ENSURE( meFieldType == EXC_PCFIELD_CALCED, "XclImpPCField::ReadSxfield - invalid calculated field" );
+ }
+ }
+
+ else if( !bItems && bPostp )
+ {
+ // 4) standard field with postponed items
+ if( !bCalced && !bChild && !bNum && bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) )
+ meFieldType = EXC_PCFIELD_STANDARD;
+ OSL_ENSURE( meFieldType == EXC_PCFIELD_STANDARD, "XclImpPCField::ReadSxfield - invalid postponed field" );
+ }
+}
+
+void XclImpPCField::ReadItem( XclImpStream& rStrm )
+{
+ OSL_ENSURE( HasInlineItems() || HasPostponedItems(), "XclImpPCField::ReadItem - field does not expect items" );
+
+ // read the item
+ XclImpPCItemRef xItem = std::make_shared<XclImpPCItem>( rStrm );
+
+ // try to insert into an item list
+ if( mbNumGroupInfoRead )
+ {
+ // there are 3 items after SXNUMGROUP that contain grouping limits and step count
+ if( maNumGroupItems.size() < 3 )
+ maNumGroupItems.push_back( xItem );
+ else
+ maOrigItems.push_back( xItem );
+ }
+ else if( HasInlineItems() || HasPostponedItems() )
+ {
+ maItems.push_back( xItem );
+ // visible item is original item in standard fields
+ if( IsStandardField() )
+ maOrigItems.push_back( xItem );
+ }
+}
+
+void XclImpPCField::ReadSxnumgroup( XclImpStream& rStrm )
+{
+ OSL_ENSURE( IsNumGroupField() || IsDateGroupField(), "XclImpPCField::ReadSxnumgroup - SXNUMGROUP outside numeric grouping field" );
+ OSL_ENSURE( !mbNumGroupInfoRead, "XclImpPCField::ReadSxnumgroup - multiple SXNUMGROUP records" );
+ OSL_ENSURE( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxnumgroup - SXNUMGROUP out of record order" );
+ rStrm >> maNumGroupInfo;
+ mbNumGroupInfoRead = IsNumGroupField() || IsDateGroupField();
+}
+
+void XclImpPCField::ReadSxgroupinfo( XclImpStream& rStrm )
+{
+ OSL_ENSURE( IsStdGroupField(), "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO outside grouping field" );
+ OSL_ENSURE( maGroupOrder.empty(), "XclImpPCField::ReadSxgroupinfo - multiple SXGROUPINFO records" );
+ OSL_ENSURE( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO out of record order" );
+ OSL_ENSURE( (rStrm.GetRecLeft() / 2) == maFieldInfo.mnBaseItems, "XclImpPCField::ReadSxgroupinfo - wrong SXGROUPINFO size" );
+ maGroupOrder.clear();
+ size_t nSize = rStrm.GetRecLeft() / 2;
+ maGroupOrder.resize( nSize, 0 );
+ for( size_t nIdx = 0; nIdx < nSize; ++nIdx )
+ maGroupOrder[ nIdx ] = rStrm.ReaduInt16();
+}
+
+// grouping -------------------------------------------------------------------
+
+void XclImpPCField::ConvertGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
+{
+ if (!GetFieldName(rVisNames).isEmpty())
+ {
+ if( IsStdGroupField() )
+ ConvertStdGroupField( rSaveData, rVisNames );
+ else if( IsNumGroupField() )
+ ConvertNumGroupField( rSaveData, rVisNames );
+ else if( IsDateGroupField() )
+ ConvertDateGroupField( rSaveData, rVisNames );
+ }
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpPCField::ConvertStdGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
+{
+ const XclImpPCField* pBaseField = GetGroupBaseField();
+ if(!pBaseField)
+ return;
+
+ const OUString& rBaseFieldName = pBaseField->GetFieldName( rVisNames );
+ if( rBaseFieldName.isEmpty() )
+ return;
+
+ // *** create a ScDPSaveGroupItem for each own item, they collect base item names ***
+ ScDPSaveGroupItemVec aGroupItems;
+ aGroupItems.reserve( maItems.size() );
+ // initialize with own item names
+ for( const auto& rxItem : maItems )
+ aGroupItems.emplace_back( rxItem->ConvertToText() );
+
+ // *** iterate over all base items, set their names at corresponding own items ***
+ for( sal_uInt16 nItemIdx = 0, nItemCount = static_cast< sal_uInt16 >( maGroupOrder.size() ); nItemIdx < nItemCount; ++nItemIdx )
+ if( maGroupOrder[ nItemIdx ] < aGroupItems.size() )
+ if( const XclImpPCItem* pBaseItem = pBaseField->GetItem( nItemIdx ) )
+ if( const XclImpPCItem* pGroupItem = GetItem( maGroupOrder[ nItemIdx ] ) )
+ if( *pBaseItem != *pGroupItem )
+ aGroupItems[ maGroupOrder[ nItemIdx ] ].AddElement( pBaseItem->ConvertToText() );
+
+ // *** create the ScDPSaveGroupDimension object, fill with grouping info ***
+ ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) );
+ for( const auto& rGroupItem : aGroupItems )
+ if( !rGroupItem.IsEmpty() )
+ aGroupDim.AddGroupItem( rGroupItem );
+ rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim );
+}
+
+void XclImpPCField::ConvertNumGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
+{
+ ScDPNumGroupInfo aNumInfo( GetScNumGroupInfo() );
+ ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aNumInfo );
+ rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
+}
+
+void XclImpPCField::ConvertDateGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
+{
+ ScDPNumGroupInfo aDateInfo( GetScDateGroupInfo() );
+ sal_Int32 nScDateType = maNumGroupInfo.GetScDateType();
+
+ switch( meFieldType )
+ {
+ case EXC_PCFIELD_DATEGROUP:
+ {
+ if( aDateInfo.mbDateValues )
+ {
+ // special case for days only with step value - create numeric grouping
+ ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aDateInfo );
+ rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
+ }
+ else
+ {
+ ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), ScDPNumGroupInfo() );
+ aNumGroupDim.SetDateInfo( aDateInfo, nScDateType );
+ rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
+ }
+ }
+ break;
+
+ case EXC_PCFIELD_DATECHILD:
+ {
+ if( const XclImpPCField* pBaseField = GetGroupBaseField() )
+ {
+ const OUString& rBaseFieldName = pBaseField->GetFieldName( rVisNames );
+ if( !rBaseFieldName.isEmpty() )
+ {
+ ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) );
+ aGroupDim.SetDateInfo( aDateInfo, nScDateType );
+ rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim );
+ }
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL( "XclImpPCField::ConvertDateGroupField - unknown date field type" );
+ }
+}
+
+ScDPNumGroupInfo XclImpPCField::GetScNumGroupInfo() const
+{
+ ScDPNumGroupInfo aNumInfo;
+ aNumInfo.mbEnable = true;
+ aNumInfo.mbDateValues = false;
+ aNumInfo.mbAutoStart = true;
+ aNumInfo.mbAutoEnd = true;
+
+ if( const double* pfMinValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MIN ) )
+ {
+ aNumInfo.mfStart = *pfMinValue;
+ aNumInfo.mbAutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN );
+ }
+ if( const double* pfMaxValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MAX ) )
+ {
+ aNumInfo.mfEnd = *pfMaxValue;
+ aNumInfo.mbAutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX );
+ }
+ if( const double* pfStepValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_STEP ) )
+ aNumInfo.mfStep = *pfStepValue;
+
+ return aNumInfo;
+}
+
+ScDPNumGroupInfo XclImpPCField::GetScDateGroupInfo() const
+{
+ ScDPNumGroupInfo aDateInfo;
+ aDateInfo.mbEnable = true;
+ aDateInfo.mbDateValues = false;
+ aDateInfo.mbAutoStart = true;
+ aDateInfo.mbAutoEnd = true;
+
+ if( const DateTime* pMinDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MIN ) )
+ {
+ aDateInfo.mfStart = GetDoubleFromDateTime( *pMinDate );
+ aDateInfo.mbAutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN );
+ }
+ if( const DateTime* pMaxDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MAX ) )
+ {
+ aDateInfo.mfEnd = GetDoubleFromDateTime( *pMaxDate );
+ aDateInfo.mbAutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX );
+ }
+ // GetDateGroupStep() returns a value for date type "day" in single date groups only
+ if( const sal_Int16* pnStepValue = GetDateGroupStep() )
+ {
+ aDateInfo.mfStep = *pnStepValue;
+ aDateInfo.mbDateValues = true;
+ }
+
+ return aDateInfo;
+}
+
+const double* XclImpPCField::GetNumGroupLimit( sal_uInt16 nLimitIdx ) const
+{
+ OSL_ENSURE( IsNumGroupField(), "XclImpPCField::GetNumGroupLimit - only for numeric grouping fields" );
+ if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) )
+ {
+ OSL_ENSURE( pItem->GetDouble(), "XclImpPCField::GetNumGroupLimit - SXDOUBLE item expected" );
+ return pItem->GetDouble();
+ }
+ return nullptr;
+}
+
+const DateTime* XclImpPCField::GetDateGroupLimit( sal_uInt16 nLimitIdx ) const
+{
+ OSL_ENSURE( IsDateGroupField(), "XclImpPCField::GetDateGroupLimit - only for date grouping fields" );
+ if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) )
+ {
+ OSL_ENSURE( pItem->GetDateTime(), "XclImpPCField::GetDateGroupLimit - SXDATETIME item expected" );
+ return pItem->GetDateTime();
+ }
+ return nullptr;
+}
+
+const sal_Int16* XclImpPCField::GetDateGroupStep() const
+{
+ // only for single date grouping fields, not for grouping chains
+ if( !IsGroupBaseField() && !IsGroupChildField() )
+ {
+ // only days may have a step value, return 0 for all other date types
+ if( maNumGroupInfo.GetXclDataType() == EXC_SXNUMGROUP_TYPE_DAY )
+ {
+ if( const XclImpPCItem* pItem = GetLimitItem( EXC_SXFIELD_INDEX_STEP ) )
+ {
+ OSL_ENSURE( pItem->GetInteger(), "XclImpPCField::GetDateGroupStep - SXINTEGER item expected" );
+ if( const sal_Int16* pnStep = pItem->GetInteger() )
+ {
+ OSL_ENSURE( *pnStep > 0, "XclImpPCField::GetDateGroupStep - invalid step count" );
+ // return nothing for step count 1 - this is also a standard date group in Excel
+ return (*pnStep > 1) ? pnStep : nullptr;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+XclImpPivotCache::XclImpPivotCache( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maSrcRange( ScAddress::INITIALIZE_INVALID ),
+ mnStrmId( 0 ),
+ mnSrcType( EXC_SXVS_UNKNOWN ),
+ mbSelfRef( false )
+{
+}
+
+XclImpPivotCache::~XclImpPivotCache()
+{
+}
+
+// data access ----------------------------------------------------------------
+
+const XclImpPCField* XclImpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
+{
+ return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr;
+}
+
+// records --------------------------------------------------------------------
+
+void XclImpPivotCache::ReadSxidstm( XclImpStream& rStrm )
+{
+ mnStrmId = rStrm.ReaduInt16();
+}
+
+void XclImpPivotCache::ReadSxvs( XclImpStream& rStrm )
+{
+ mnSrcType = rStrm.ReaduInt16();
+ GetTracer().TracePivotDataSource( mnSrcType != EXC_SXVS_SHEET );
+}
+
+void XclImpPivotCache::ReadDconref( XclImpStream& rStrm )
+{
+ /* Read DCONREF only once (by checking maTabName), there may be other
+ DCONREF records in another context. Read reference only if a leading
+ SXVS record is present (by checking mnSrcType). */
+ if( !maTabName.isEmpty() || (mnSrcType != EXC_SXVS_SHEET) )
+ return;
+
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ aXclRange.Read( rStrm, false );
+ OUString aEncUrl = rStrm.ReadUniString();
+
+ XclImpUrlHelper::DecodeUrl( maUrl, maTabName, mbSelfRef, GetRoot(), aEncUrl );
+
+ /* Do not convert maTabName to Calc sheet name -> original name is used to
+ find the sheet in the document. Sheet index of source range will be
+ found later in XclImpPivotCache::ReadPivotCacheStream(), because sheet
+ may not exist yet. */
+ if( mbSelfRef )
+ GetAddressConverter().ConvertRange( maSrcRange, aXclRange, 0, 0, true );
+}
+
+void XclImpPivotCache::ReadDConName( XclImpStream& rStrm )
+{
+ maSrcRangeName = rStrm.ReadUniString();
+
+ // This 2-byte value equals the length of string that follows, or if 0 it
+ // indicates that the name has a workbook scope. For now, we only support
+ // internal defined name with a workbook scope.
+ sal_uInt16 nFlag;
+ nFlag = rStrm.ReaduInt16();
+ mbSelfRef = (nFlag == 0);
+
+ if (!mbSelfRef)
+ // External name is not supported yet.
+ maSrcRangeName.clear();
+}
+
+void XclImpPivotCache::ReadPivotCacheStream( const XclImpStream& rStrm )
+{
+ if( (mnSrcType != EXC_SXVS_SHEET) && (mnSrcType != EXC_SXVS_EXTERN) )
+ return;
+
+ ScDocument& rDoc = GetDoc();
+ SCCOL nFieldScCol = 0; // column index of source data for next field
+ SCROW nItemScRow = 0; // row index of source data for current items
+ SCTAB nScTab = 0; // sheet index of source data
+ bool bGenerateSource = false; // true = write source data from cache to dummy table
+
+ if( mbSelfRef )
+ {
+ if (maSrcRangeName.isEmpty())
+ {
+ // try to find internal sheet containing the source data
+ nScTab = GetTabInfo().GetScTabFromXclName( maTabName );
+ if( rDoc.HasTable( nScTab ) )
+ {
+ // set sheet index to source range
+ maSrcRange.aStart.SetTab( nScTab );
+ maSrcRange.aEnd.SetTab( nScTab );
+ }
+ else
+ {
+ // create dummy sheet for deleted internal sheet
+ bGenerateSource = true;
+ }
+ }
+ }
+ else
+ {
+ // create dummy sheet for external sheet
+ bGenerateSource = true;
+ }
+
+ // create dummy sheet for source data from external or deleted sheet
+ if( bGenerateSource )
+ {
+ if( rDoc.GetTableCount() >= MAXTABCOUNT )
+ // cannot create more sheets -> exit
+ return;
+
+ nScTab = rDoc.GetTableCount();
+ rDoc.MakeTable( nScTab );
+ OUStringBuffer aDummyName("DPCache");
+ if( maTabName.getLength() > 0 )
+ aDummyName.append( '_' ).append( maTabName );
+ OUString aName = aDummyName.makeStringAndClear();
+ rDoc.CreateValidTabName( aName );
+ rDoc.RenameTab( nScTab, aName );
+ // set sheet index to source range
+ maSrcRange.aStart.SetTab( nScTab );
+ maSrcRange.aEnd.SetTab( nScTab );
+ }
+
+ // open pivot cache storage stream
+ tools::SvRef<SotStorage> xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
+ tools::SvRef<SotStorageStream> xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( mnStrmId ) );
+ if( !xSvStrm.is() )
+ return;
+
+ // create Excel record stream object
+ XclImpStream aPCStrm( *xSvStrm, GetRoot() );
+ aPCStrm.CopyDecrypterFrom( rStrm ); // pivot cache streams are encrypted
+
+ XclImpPCFieldRef xCurrField; // current field for new items
+ XclImpPCFieldVec aOrigFields; // all standard fields with inline original items
+ XclImpPCFieldVec aPostpFields; // all standard fields with postponed original items
+ size_t nPostpIdx = 0; // index to current field with postponed items
+ bool bLoop = true; // true = continue loop
+
+ while( bLoop && aPCStrm.StartNextRecord() )
+ {
+ switch( aPCStrm.GetRecId() )
+ {
+ case EXC_ID_EOF:
+ bLoop = false;
+ break;
+
+ case EXC_ID_SXDB:
+ aPCStrm >> maPCInfo;
+ break;
+
+ case EXC_ID_SXFIELD:
+ {
+ xCurrField.reset();
+ sal_uInt16 nNewFieldIdx = static_cast< sal_uInt16 >( maFields.size() );
+ if( nNewFieldIdx < EXC_PC_MAXFIELDCOUNT )
+ {
+ xCurrField = std::make_shared<XclImpPCField>( GetRoot(), *this, nNewFieldIdx );
+ maFields.push_back( xCurrField );
+ xCurrField->ReadSxfield( aPCStrm );
+ if( xCurrField->HasOrigItems() )
+ {
+ if( xCurrField->HasPostponedItems() )
+ aPostpFields.push_back( xCurrField );
+ else
+ aOrigFields.push_back( xCurrField );
+ // insert field name into generated source data, field remembers its column index
+ if( bGenerateSource && (nFieldScCol <= rDoc.MaxCol()) )
+ xCurrField->WriteFieldNameToSource( nFieldScCol++, nScTab );
+ }
+ // do not read items into invalid/postponed fields
+ if( !xCurrField->HasInlineItems() )
+ xCurrField.reset();
+ }
+ }
+ break;
+
+ case EXC_ID_SXINDEXLIST:
+ // read index list and insert all items into generated source data
+ if( bGenerateSource && (nItemScRow <= rDoc.MaxRow()) && (++nItemScRow <= rDoc.MaxRow()) )
+ {
+ for( const auto& rxOrigField : aOrigFields )
+ {
+ sal_uInt16 nItemIdx = rxOrigField->Has16BitIndexes() ? aPCStrm.ReaduInt16() : aPCStrm.ReaduInt8();
+ rxOrigField->WriteOrigItemToSource( nItemScRow, nScTab, nItemIdx );
+ }
+ }
+ xCurrField.reset();
+ break;
+
+ case EXC_ID_SXDOUBLE:
+ case EXC_ID_SXBOOLEAN:
+ case EXC_ID_SXERROR:
+ case EXC_ID_SXINTEGER:
+ case EXC_ID_SXSTRING:
+ case EXC_ID_SXDATETIME:
+ case EXC_ID_SXEMPTY:
+ if( xCurrField ) // inline items
+ {
+ xCurrField->ReadItem( aPCStrm );
+ }
+ else if( !aPostpFields.empty() ) // postponed items
+ {
+ // read postponed item
+ aPostpFields[ nPostpIdx ]->ReadItem( aPCStrm );
+ // write item to source
+ if( bGenerateSource && (nItemScRow <= rDoc.MaxRow()) )
+ {
+ // start new row, if there are only postponed fields
+ if( aOrigFields.empty() && (nPostpIdx == 0) )
+ ++nItemScRow;
+ if( nItemScRow <= rDoc.MaxRow() )
+ aPostpFields[ nPostpIdx ]->WriteLastOrigItemToSource( nItemScRow, nScTab );
+ }
+ // get index of next postponed field
+ ++nPostpIdx;
+ if( nPostpIdx >= aPostpFields.size() )
+ nPostpIdx = 0;
+ }
+ break;
+
+ case EXC_ID_SXNUMGROUP:
+ if( xCurrField )
+ xCurrField->ReadSxnumgroup( aPCStrm );
+ break;
+
+ case EXC_ID_SXGROUPINFO:
+ if( xCurrField )
+ xCurrField->ReadSxgroupinfo( aPCStrm );
+ break;
+
+ // known but ignored records
+ case EXC_ID_SXRULE:
+ case EXC_ID_SXFILT:
+ case EXC_ID_00F5:
+ case EXC_ID_SXNAME:
+ case EXC_ID_SXPAIR:
+ case EXC_ID_SXFMLA:
+ case EXC_ID_SXFORMULA:
+ case EXC_ID_SXDBEX:
+ case EXC_ID_SXFDBTYPE:
+ break;
+
+ default:
+ SAL_WARN("sc.filter", "XclImpPivotCache::ReadPivotCacheStream - unknown record 0x" << std::hex << aPCStrm.GetRecId() );
+ }
+ }
+
+ OSL_ENSURE( maPCInfo.mnTotalFields == maFields.size(),
+ "XclImpPivotCache::ReadPivotCacheStream - field count mismatch" );
+
+ if (static_cast<bool>(maPCInfo.mnFlags & EXC_SXDB_SAVEDATA))
+ {
+ SCROW nNewEnd = maSrcRange.aStart.Row() + maPCInfo.mnSrcRecs;
+ maSrcRange.aEnd.SetRow(nNewEnd);
+ }
+
+ // set source range for external source data
+ if( bGenerateSource && (nFieldScCol > 0) )
+ {
+ maSrcRange.aStart.SetCol( 0 );
+ maSrcRange.aStart.SetRow( 0 );
+ // nFieldScCol points to first unused column
+ maSrcRange.aEnd.SetCol( nFieldScCol - 1 );
+ // nItemScRow points to last used row
+ maSrcRange.aEnd.SetRow( nItemScRow );
+ }
+}
+
+bool XclImpPivotCache::IsRefreshOnLoad() const
+{
+ return static_cast<bool>(maPCInfo.mnFlags & EXC_SXDB_REFRESH_LOAD);
+}
+
+bool XclImpPivotCache::IsValid() const
+{
+ if (!maSrcRangeName.isEmpty())
+ return true;
+
+ return maSrcRange.IsValid();
+}
+
+// Pivot table
+
+XclImpPTItem::XclImpPTItem( const XclImpPCField* pCacheField ) :
+ mpCacheField( pCacheField )
+{
+}
+
+const OUString* XclImpPTItem::GetItemName() const
+{
+ if( mpCacheField )
+ if( const XclImpPCItem* pCacheItem = mpCacheField->GetItem( maItemInfo.mnCacheIdx ) )
+ //TODO: use XclImpPCItem::ConvertToText(), if all conversions are available
+ return pCacheItem->IsEmpty() ? nullptr : pCacheItem->GetText();
+ return nullptr;
+}
+
+std::pair<bool, OUString> XclImpPTItem::GetItemName(const ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const XclImpRoot& rRoot) const
+{
+ if(!mpCacheField)
+ return std::pair<bool, OUString>(false, OUString());
+
+ const XclImpPCItem* pCacheItem = mpCacheField->GetItem( maItemInfo.mnCacheIdx );
+ if(!pCacheItem)
+ return std::pair<bool, OUString>(false, OUString());
+
+ OUString sItemName;
+ if(pCacheItem->GetType() == EXC_PCITEM_TEXT || pCacheItem->GetType() == EXC_PCITEM_ERROR)
+ {
+ const OUString* pItemName = pCacheItem->GetText();
+ if(!pItemName)
+ return std::pair<bool, OUString>(false, OUString());
+ sItemName = *pItemName;
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_DOUBLE)
+ {
+ sItemName = pObj->GetFormattedString(rSaveDim.GetName(), *pCacheItem->GetDouble());
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_INTEGER)
+ {
+ sItemName = pObj->GetFormattedString(rSaveDim.GetName(), static_cast<double>(*pCacheItem->GetInteger()));
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_BOOL)
+ {
+ sItemName = pObj->GetFormattedString(rSaveDim.GetName(), static_cast<double>(*pCacheItem->GetBool()));
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_DATETIME)
+ {
+ sItemName = pObj->GetFormattedString(rSaveDim.GetName(), rRoot.GetDoubleFromDateTime(*pCacheItem->GetDateTime()));
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_EMPTY)
+ {
+ // sItemName is an empty string
+ }
+ else // EXC_PCITEM_INVALID
+ return std::pair<bool, OUString>(false, OUString());
+
+ return std::pair<bool, OUString>(true, sItemName);
+}
+
+void XclImpPTItem::ReadSxvi( XclImpStream& rStrm )
+{
+ rStrm >> maItemInfo;
+}
+
+void XclImpPTItem::ConvertItem( ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const XclImpRoot& rRoot ) const
+{
+ // Find member and set properties
+ std::pair<bool, OUString> aReturnedName = GetItemName(rSaveDim, pObj, rRoot);
+ if(aReturnedName.first)
+ {
+ ScDPSaveMember* pMember = rSaveDim.GetExistingMemberByName(aReturnedName.second);
+ if(pMember)
+ {
+ pMember->SetIsVisible( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN ) );
+ pMember->SetShowDetails( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL ) );
+ if (maItemInfo.HasVisName())
+ pMember->SetLayoutName(*maItemInfo.GetVisName());
+ }
+ }
+}
+
+XclImpPTField::XclImpPTField( const XclImpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
+ mrPTable( rPTable )
+{
+ maFieldInfo.mnCacheIdx = nCacheIdx;
+}
+
+// general field/item access --------------------------------------------------
+
+const XclImpPCField* XclImpPTField::GetCacheField() const
+{
+ XclImpPivotCacheRef xPCache = mrPTable.GetPivotCache();
+ return xPCache ? xPCache->GetField( maFieldInfo.mnCacheIdx ) : nullptr;
+}
+
+OUString XclImpPTField::GetFieldName() const
+{
+ const XclImpPCField* pField = GetCacheField();
+ return pField ? pField->GetFieldName( mrPTable.GetVisFieldNames() ) : OUString();
+}
+
+OUString XclImpPTField::GetVisFieldName() const
+{
+ const OUString* pVisName = maFieldInfo.GetVisName();
+ return pVisName ? *pVisName : OUString();
+}
+
+const XclImpPTItem* XclImpPTField::GetItem( sal_uInt16 nItemIdx ) const
+{
+ return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : nullptr;
+}
+
+const OUString* XclImpPTField::GetItemName( sal_uInt16 nItemIdx ) const
+{
+ const XclImpPTItem* pItem = GetItem( nItemIdx );
+ return pItem ? pItem->GetItemName() : nullptr;
+}
+
+// records --------------------------------------------------------------------
+
+void XclImpPTField::ReadSxvd( XclImpStream& rStrm )
+{
+ rStrm >> maFieldInfo;
+}
+
+void XclImpPTField::ReadSxvdex( XclImpStream& rStrm )
+{
+ rStrm >> maFieldExtInfo;
+}
+
+void XclImpPTField::ReadSxvi( XclImpStream& rStrm )
+{
+ XclImpPTItemRef xItem = std::make_shared<XclImpPTItem>( GetCacheField() );
+ maItems.push_back( xItem );
+ xItem->ReadSxvi( rStrm );
+}
+
+// row/column fields ----------------------------------------------------------
+
+void XclImpPTField::ConvertRowColField( ScDPSaveData& rSaveData ) const
+{
+ OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOL, "XclImpPTField::ConvertRowColField - no row/column field" );
+ // special data orientation field?
+ if( maFieldInfo.mnCacheIdx == EXC_SXIVD_DATA )
+ rSaveData.GetDataLayoutDimension()->SetOrientation( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOL ) );
+ else
+ ConvertRCPField( rSaveData );
+}
+
+// page fields ----------------------------------------------------------------
+
+void XclImpPTField::SetPageFieldInfo( const XclPTPageFieldInfo& rPageInfo )
+{
+ maPageInfo = rPageInfo;
+}
+
+void XclImpPTField::ConvertPageField( ScDPSaveData& rSaveData ) const
+{
+ OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_PAGE, "XclImpPTField::ConvertPageField - no page field" );
+ ConvertRCPField( rSaveData );
+}
+
+// hidden fields --------------------------------------------------------------
+
+void XclImpPTField::ConvertHiddenField( ScDPSaveData& rSaveData ) const
+{
+ OSL_ENSURE( (maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOLPAGE) == 0, "XclImpPTField::ConvertHiddenField - field not hidden" );
+ ConvertRCPField( rSaveData );
+}
+
+// data fields ----------------------------------------------------------------
+
+bool XclImpPTField::HasDataFieldInfo() const
+{
+ return !maDataInfoVector.empty();
+}
+
+void XclImpPTField::AddDataFieldInfo( const XclPTDataFieldInfo& rDataInfo )
+{
+ OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::AddDataFieldInfo - no data field" );
+ maDataInfoVector.push_back( rDataInfo );
+}
+
+void XclImpPTField::ConvertDataField( ScDPSaveData& rSaveData ) const
+{
+ OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::ConvertDataField - no data field" );
+ OSL_ENSURE( !maDataInfoVector.empty(), "XclImpPTField::ConvertDataField - no data field info" );
+ if (maDataInfoVector.empty())
+ return;
+
+ OUString aFieldName = GetFieldName();
+ if (aFieldName.isEmpty())
+ return;
+
+ ScDPSaveDimension* pSaveDim = rSaveData.GetNewDimensionByName(aFieldName);
+ if (!pSaveDim)
+ {
+ SAL_WARN("sc.filter","XclImpPTField::ConvertDataField - field name not found: " << aFieldName);
+ return;
+ }
+
+ auto aIt = maDataInfoVector.begin(), aEnd = maDataInfoVector.end();
+
+ ConvertDataField( *pSaveDim, *aIt );
+
+ // multiple data fields -> clone dimension
+ for( ++aIt; aIt != aEnd; ++aIt )
+ {
+ ScDPSaveDimension& rDupDim = rSaveData.DuplicateDimension( *pSaveDim );
+ ConvertDataFieldInfo( rDupDim, *aIt );
+ }
+}
+
+// private --------------------------------------------------------------------
+
+/**
+ * Convert Excel-encoded subtotal name to a Calc-encoded one.
+ */
+static OUString lcl_convertExcelSubtotalName(const OUString& rName)
+{
+ OUStringBuffer aBuf;
+ const sal_Unicode* p = rName.getStr();
+ sal_Int32 n = rName.getLength();
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ const sal_Unicode c = p[i];
+ if (c == '\\')
+ {
+ aBuf.append(c);
+ aBuf.append(c);
+ }
+ else
+ aBuf.append(c);
+ }
+ return aBuf.makeStringAndClear();
+}
+
+void XclImpPTField::ConvertRCPField( ScDPSaveData& rSaveData ) const
+{
+ const OUString& rFieldName = GetFieldName();
+ if( rFieldName.isEmpty() )
+ return;
+
+ const XclImpPCField* pCacheField = GetCacheField();
+ if( !pCacheField || !pCacheField->IsSupportedField() )
+ return;
+
+ ScDPSaveDimension* pTest = rSaveData.GetNewDimensionByName(rFieldName);
+ if (!pTest)
+ return;
+
+ ScDPSaveDimension& rSaveDim = *pTest;
+
+ // orientation
+ rSaveDim.SetOrientation( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOLPAGE ) );
+
+ // visible name
+ if (const OUString* pVisName = maFieldInfo.GetVisName())
+ if (!pVisName->isEmpty())
+ rSaveDim.SetLayoutName( *pVisName );
+
+ // subtotal function(s)
+ XclPTSubtotalVec aSubtotalVec;
+ maFieldInfo.GetSubtotals( aSubtotalVec );
+ if( !aSubtotalVec.empty() )
+ rSaveDim.SetSubTotals( std::move(aSubtotalVec) );
+
+ // sorting
+ DataPilotFieldSortInfo aSortInfo;
+ aSortInfo.Field = mrPTable.GetDataFieldName( maFieldExtInfo.mnSortField );
+ aSortInfo.IsAscending = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC );
+ aSortInfo.Mode = maFieldExtInfo.GetApiSortMode();
+ rSaveDim.SetSortInfo( &aSortInfo );
+
+ // auto show
+ DataPilotFieldAutoShowInfo aShowInfo;
+ aShowInfo.IsEnabled = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW );
+ aShowInfo.ShowItemsMode = maFieldExtInfo.GetApiAutoShowMode();
+ aShowInfo.ItemCount = maFieldExtInfo.GetApiAutoShowCount();
+ aShowInfo.DataField = mrPTable.GetDataFieldName( maFieldExtInfo.mnShowField );
+ rSaveDim.SetAutoShowInfo( &aShowInfo );
+
+ // layout
+ DataPilotFieldLayoutInfo aLayoutInfo;
+ aLayoutInfo.LayoutMode = maFieldExtInfo.GetApiLayoutMode();
+ aLayoutInfo.AddEmptyLines = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK );
+ rSaveDim.SetLayoutInfo( &aLayoutInfo );
+
+ // grouping info
+ pCacheField->ConvertGroupField( rSaveData, mrPTable.GetVisFieldNames() );
+
+ // custom subtotal name
+ if (maFieldExtInfo.mpFieldTotalName)
+ {
+ OUString aSubName = lcl_convertExcelSubtotalName(*maFieldExtInfo.mpFieldTotalName);
+ rSaveDim.SetSubtotalName(aSubName);
+ }
+}
+
+void XclImpPTField::ConvertFieldInfo( const ScDPSaveData& rSaveData, ScDPObject* pObj, const XclImpRoot& rRoot, bool bPageField ) const
+{
+ const OUString& rFieldName = GetFieldName();
+ if( rFieldName.isEmpty() )
+ return;
+
+ const XclImpPCField* pCacheField = GetCacheField();
+ if( !pCacheField || !pCacheField->IsSupportedField() )
+ return;
+
+ ScDPSaveDimension* pSaveDim = rSaveData.GetExistingDimensionByName(rFieldName);
+ if (!pSaveDim)
+ return;
+
+ pSaveDim->SetShowEmpty( ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL ) );
+ for( const auto& rxItem : maItems )
+ rxItem->ConvertItem( *pSaveDim, pObj, rRoot );
+
+ if(bPageField && maPageInfo.mnSelItem != EXC_SXPI_ALLITEMS)
+ {
+ const XclImpPTItem* pItem = GetItem( maPageInfo.mnSelItem );
+ if(pItem)
+ {
+ std::pair<bool, OUString> aReturnedName = pItem->GetItemName(*pSaveDim, pObj, rRoot);
+ if(aReturnedName.first)
+ pSaveDim->SetCurrentPage(&aReturnedName.second);
+ }
+ }
+}
+
+void XclImpPTField::ConvertDataField( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const
+{
+ // orientation
+ rSaveDim.SetOrientation( DataPilotFieldOrientation_DATA );
+ // extended data field info
+ ConvertDataFieldInfo( rSaveDim, rDataInfo );
+}
+
+void XclImpPTField::ConvertDataFieldInfo( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const
+{
+ // visible name
+ const OUString* pVisName = rDataInfo.GetVisName();
+ if (pVisName && !pVisName->isEmpty())
+ rSaveDim.SetLayoutName(*pVisName);
+
+ // aggregation function
+ rSaveDim.SetFunction( rDataInfo.GetApiAggFunc() );
+
+ // result field reference
+ sal_Int32 nRefType = rDataInfo.GetApiRefType();
+ DataPilotFieldReference aFieldRef;
+ aFieldRef.ReferenceType = nRefType;
+ const XclImpPTField* pRefField = mrPTable.GetField(rDataInfo.mnRefField);
+ if (pRefField)
+ {
+ aFieldRef.ReferenceField = pRefField->GetFieldName();
+ aFieldRef.ReferenceItemType = rDataInfo.GetApiRefItemType();
+ if (aFieldRef.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::NAMED)
+ {
+ const OUString* pRefItemName = pRefField->GetItemName(rDataInfo.mnRefItem);
+ if (pRefItemName)
+ aFieldRef.ReferenceItemName = *pRefItemName;
+ }
+ }
+
+ rSaveDim.SetReferenceValue(&aFieldRef);
+}
+
+XclImpPivotTable::XclImpPivotTable( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maDataOrientField( *this, EXC_SXIVD_DATA ),
+ mpDPObj(nullptr)
+{
+}
+
+XclImpPivotTable::~XclImpPivotTable()
+{
+}
+
+// cache/field access, misc. --------------------------------------------------
+
+sal_uInt16 XclImpPivotTable::GetFieldCount() const
+{
+ return static_cast< sal_uInt16 >( maFields.size() );
+}
+
+const XclImpPTField* XclImpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
+{
+ return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField :
+ ((nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr);
+}
+
+XclImpPTField* XclImpPivotTable::GetFieldAcc( sal_uInt16 nFieldIdx )
+{
+ // do not return maDataOrientField
+ return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr;
+}
+
+const XclImpPTField* XclImpPivotTable::GetDataField( sal_uInt16 nDataFieldIdx ) const
+{
+ if( nDataFieldIdx < maOrigDataFields.size() )
+ return GetField( maOrigDataFields[ nDataFieldIdx ] );
+ return nullptr;
+}
+
+OUString XclImpPivotTable::GetDataFieldName( sal_uInt16 nDataFieldIdx ) const
+{
+ if( const XclImpPTField* pField = GetDataField( nDataFieldIdx ) )
+ return pField->GetFieldName();
+ return OUString();
+}
+
+// records --------------------------------------------------------------------
+
+void XclImpPivotTable::ReadSxview( XclImpStream& rStrm )
+{
+ rStrm >> maPTInfo;
+
+ GetAddressConverter().ConvertRange(
+ maOutScRange, maPTInfo.maOutXclRange, GetCurrScTab(), GetCurrScTab(), true );
+
+ mxPCache = GetPivotTableManager().GetPivotCache( maPTInfo.mnCacheIdx );
+ mxCurrField.reset();
+}
+
+void XclImpPivotTable::ReadSxvd( XclImpStream& rStrm )
+{
+ sal_uInt16 nFieldCount = GetFieldCount();
+ if( nFieldCount < EXC_PT_MAXFIELDCOUNT )
+ {
+ // cache index for the field is equal to the SXVD record index
+ mxCurrField = std::make_shared<XclImpPTField>( *this, nFieldCount );
+ maFields.push_back( mxCurrField );
+ mxCurrField->ReadSxvd( rStrm );
+ // add visible name of new field to list of visible names
+ maVisFieldNames.push_back( mxCurrField->GetVisFieldName() );
+ OSL_ENSURE( maFields.size() == maVisFieldNames.size(),
+ "XclImpPivotTable::ReadSxvd - wrong size of visible name array" );
+ }
+ else
+ mxCurrField.reset();
+}
+
+void XclImpPivotTable::ReadSxvi( XclImpStream& rStrm )
+{
+ if( mxCurrField )
+ mxCurrField->ReadSxvi( rStrm );
+}
+
+void XclImpPivotTable::ReadSxvdex( XclImpStream& rStrm )
+{
+ if( mxCurrField )
+ mxCurrField->ReadSxvdex( rStrm );
+}
+
+void XclImpPivotTable::ReadSxivd( XclImpStream& rStrm )
+{
+ mxCurrField.reset();
+
+ // find the index vector to fill (row SXIVD doesn't exist without row fields)
+ ScfUInt16Vec* pFieldVec = nullptr;
+ if( maRowFields.empty() && (maPTInfo.mnRowFields > 0) )
+ pFieldVec = &maRowFields;
+ else if( maColFields.empty() && (maPTInfo.mnColFields > 0) )
+ pFieldVec = &maColFields;
+
+ // fill the vector from record data
+ if( !pFieldVec )
+ return;
+
+ sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 2, EXC_PT_MAXROWCOLCOUNT );
+ pFieldVec->reserve( nSize );
+ for( sal_uInt16 nIdx = 0; nIdx < nSize; ++nIdx )
+ {
+ sal_uInt16 nFieldIdx;
+ nFieldIdx = rStrm.ReaduInt16();
+ pFieldVec->push_back( nFieldIdx );
+
+ // set orientation at special data orientation field
+ if( nFieldIdx == EXC_SXIVD_DATA )
+ {
+ sal_uInt16 nAxis = (pFieldVec == &maRowFields) ? EXC_SXVD_AXIS_ROW : EXC_SXVD_AXIS_COL;
+ maDataOrientField.SetAxes( nAxis );
+ }
+ }
+}
+
+void XclImpPivotTable::ReadSxpi( XclImpStream& rStrm )
+{
+ mxCurrField.reset();
+
+ sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 6 );
+ for( sal_uInt16 nEntry = 0; nEntry < nSize; ++nEntry )
+ {
+ XclPTPageFieldInfo aPageInfo;
+ rStrm >> aPageInfo;
+ if( XclImpPTField* pField = GetFieldAcc( aPageInfo.mnField ) )
+ {
+ maPageFields.push_back( aPageInfo.mnField );
+ pField->SetPageFieldInfo( aPageInfo );
+ }
+ GetCurrSheetDrawing().SetSkipObj( aPageInfo.mnObjId );
+ }
+}
+
+void XclImpPivotTable::ReadSxdi( XclImpStream& rStrm )
+{
+ mxCurrField.reset();
+
+ XclPTDataFieldInfo aDataInfo;
+ rStrm >> aDataInfo;
+ if( XclImpPTField* pField = GetFieldAcc( aDataInfo.mnField ) )
+ {
+ maOrigDataFields.push_back( aDataInfo.mnField );
+ // DataPilot does not support double data fields -> add first appearance to index list only
+ if( !pField->HasDataFieldInfo() )
+ maFiltDataFields.push_back( aDataInfo.mnField );
+ pField->AddDataFieldInfo( aDataInfo );
+ }
+}
+
+void XclImpPivotTable::ReadSxex( XclImpStream& rStrm )
+{
+ rStrm >> maPTExtInfo;
+}
+
+void XclImpPivotTable::ReadSxViewEx9( XclImpStream& rStrm )
+{
+ rStrm >> maPTViewEx9Info;
+}
+
+void XclImpPivotTable::ReadSxAddl( XclImpStream& rStrm )
+{
+ rStrm >> maPTAddlInfo;
+}
+
+void XclImpPivotTable::Convert()
+{
+ if( !mxPCache || !mxPCache->IsValid() )
+ return;
+
+ if (utl::ConfigManager::IsFuzzing()) //just too slow
+ return;
+
+ ScDPSaveData aSaveData;
+
+ // *** global settings ***
+
+ aSaveData.SetRowGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND ) );
+ aSaveData.SetColumnGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND ) );
+ aSaveData.SetFilterButton( false );
+ aSaveData.SetDrillDown( ::get_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN ) );
+ aSaveData.SetIgnoreEmptyRows( false );
+ aSaveData.SetRepeatIfEmpty( false );
+
+ // *** fields ***
+
+ // row fields
+ for( const auto& rRowField : maRowFields )
+ if( const XclImpPTField* pField = GetField( rRowField ) )
+ pField->ConvertRowColField( aSaveData );
+
+ // column fields
+ for( const auto& rColField : maColFields )
+ if( const XclImpPTField* pField = GetField( rColField ) )
+ pField->ConvertRowColField( aSaveData );
+
+ // page fields
+ for( const auto& rPageField : maPageFields )
+ if( const XclImpPTField* pField = GetField( rPageField ) )
+ pField->ConvertPageField( aSaveData );
+
+ // We need to import hidden fields because hidden fields may contain
+ // special settings for subtotals (aggregation function, filters, custom
+ // name etc.) and members (hidden, custom name etc.).
+
+ // hidden fields
+ for( sal_uInt16 nField = 0, nCount = GetFieldCount(); nField < nCount; ++nField )
+ if( const XclImpPTField* pField = GetField( nField ) )
+ if (!pField->GetAxes())
+ pField->ConvertHiddenField( aSaveData );
+
+ // data fields
+ for( const auto& rFiltDataField : maFiltDataFields )
+ if( const XclImpPTField* pField = GetField( rFiltDataField ) )
+ pField->ConvertDataField( aSaveData );
+
+ // *** insert into Calc document ***
+
+ // create source descriptor
+ ScSheetSourceDesc aDesc(&GetDoc());
+ const OUString& rSrcName = mxPCache->GetSourceRangeName();
+ if (!rSrcName.isEmpty())
+ // Range name is the data source.
+ aDesc.SetRangeName(rSrcName);
+ else
+ // Normal cell range.
+ aDesc.SetSourceRange(mxPCache->GetSourceRange());
+
+ // adjust output range to include the page fields
+ ScRange aOutRange( maOutScRange );
+ if( !maPageFields.empty() )
+ {
+ SCROW nDecRows = ::std::min< SCROW >( aOutRange.aStart.Row(), maPageFields.size() + 1 );
+ aOutRange.aStart.IncRow( -nDecRows );
+ }
+
+ // create the DataPilot
+ std::unique_ptr<ScDPObject> pDPObj(new ScDPObject( &GetDoc() ));
+ pDPObj->SetName( maPTInfo.maTableName );
+ if (!maPTInfo.maDataName.isEmpty())
+ aSaveData.GetDataLayoutDimension()->SetLayoutName(maPTInfo.maDataName);
+
+ if (!maPTViewEx9Info.maGrandTotalName.isEmpty())
+ aSaveData.SetGrandTotalName(maPTViewEx9Info.maGrandTotalName);
+
+ pDPObj->SetSaveData( aSaveData );
+ pDPObj->SetSheetDesc( aDesc );
+ pDPObj->SetOutRange( aOutRange );
+ pDPObj->SetHeaderLayout( maPTViewEx9Info.mnGridLayout == 0 );
+
+ mpDPObj = GetDoc().GetDPCollection()->InsertNewTable(std::move(pDPObj));
+
+ ApplyFieldInfo();
+ ApplyMergeFlags(aOutRange, aSaveData);
+}
+
+void XclImpPivotTable::MaybeRefresh()
+{
+ if (mpDPObj && mxPCache->IsRefreshOnLoad())
+ {
+ // 'refresh table on load' flag is set. Refresh the table now. Some
+ // Excel files contain partial table output when this flag is set.
+ ScRange aOutRange = mpDPObj->GetOutRange();
+ mpDPObj->Output(aOutRange.aStart);
+ }
+}
+
+void XclImpPivotTable::ApplyMergeFlags(const ScRange& rOutRange, const ScDPSaveData& rSaveData)
+{
+ // Apply merge flags for various datapilot controls.
+
+ ScDPOutputGeometry aGeometry(rOutRange, false);
+ aGeometry.setColumnFieldCount(maPTInfo.mnColFields);
+ aGeometry.setPageFieldCount(maPTInfo.mnPageFields);
+ aGeometry.setDataFieldCount(maPTInfo.mnDataFields);
+ aGeometry.setRowFieldCount(maPTInfo.mnRowFields);
+
+ // Make sure we set headerlayout when input file has additional raw header
+ if(maPTInfo.mnColFields == 0)
+ {
+ mpDPObj->SetHeaderLayout( maPTInfo.mnFirstHeadRow - 2 == static_cast<sal_uInt16>(aGeometry.getRowFieldHeaderRow()) );
+ }
+ aGeometry.setHeaderLayout(mpDPObj->GetHeaderLayout());
+ aGeometry.setCompactMode(maPTAddlInfo.mbCompactMode);
+
+ ScDocument& rDoc = GetDoc();
+
+ vector<const ScDPSaveDimension*> aFieldDims;
+ vector<ScAddress> aFieldBtns;
+
+ aGeometry.getPageFieldPositions(aFieldBtns);
+ for (const auto& rFieldBtn : aFieldBtns)
+ {
+ rDoc.ApplyFlagsTab(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab(), ScMF::Button);
+
+ ScMF nMFlag = ScMF::ButtonPopup;
+ OUString aName = rDoc.GetString(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab());
+ if (rSaveData.HasInvisibleMember(aName))
+ nMFlag |= ScMF::HiddenMember;
+
+ rDoc.ApplyFlagsTab(rFieldBtn.Col()+1, rFieldBtn.Row(), rFieldBtn.Col()+1, rFieldBtn.Row(), rFieldBtn.Tab(), nMFlag);
+ }
+
+ aGeometry.getColumnFieldPositions(aFieldBtns);
+ rSaveData.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aFieldDims);
+ if (aFieldBtns.size() == aFieldDims.size())
+ {
+ vector<const ScDPSaveDimension*>::const_iterator itDim = aFieldDims.begin();
+ for (const auto& rFieldBtn : aFieldBtns)
+ {
+ ScMF nMFlag = ScMF::Button;
+ const ScDPSaveDimension* pDim = *itDim;
+ if (pDim->HasInvisibleMember())
+ nMFlag |= ScMF::HiddenMember;
+ if (!pDim->IsDataLayout())
+ nMFlag |= ScMF::ButtonPopup;
+ rDoc.ApplyFlagsTab(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab(), nMFlag);
+ ++itDim;
+ }
+ }
+
+ aGeometry.getRowFieldPositions(aFieldBtns);
+ rSaveData.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aFieldDims);
+ if (!((aFieldBtns.size() == aFieldDims.size()) || (maPTAddlInfo.mbCompactMode && aFieldBtns.size() == 1)))
+ return;
+
+ vector<const ScDPSaveDimension*>::const_iterator itDim = aFieldDims.begin();
+ for (const auto& rFieldBtn : aFieldBtns)
+ {
+ ScMF nMFlag = ScMF::Button;
+ const ScDPSaveDimension* pDim = itDim != aFieldDims.end() ? *itDim++ : nullptr;
+ if (pDim && pDim->HasInvisibleMember())
+ nMFlag |= ScMF::HiddenMember;
+ if (!pDim || !pDim->IsDataLayout())
+ nMFlag |= ScMF::ButtonPopup;
+ rDoc.ApplyFlagsTab(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab(), nMFlag);
+ }
+}
+
+
+void XclImpPivotTable::ApplyFieldInfo()
+{
+ mpDPObj->BuildAllDimensionMembers();
+ ScDPSaveData& rSaveData = *mpDPObj->GetSaveData();
+
+ // row fields
+ for( const auto& rRowField : maRowFields )
+ if( const XclImpPTField* pField = GetField( rRowField ) )
+ pField->ConvertFieldInfo( rSaveData, mpDPObj, *this );
+
+ // column fields
+ for( const auto& rColField : maColFields )
+ if( const XclImpPTField* pField = GetField( rColField ) )
+ pField->ConvertFieldInfo( rSaveData, mpDPObj, *this );
+
+ // page fields
+ for( const auto& rPageField : maPageFields )
+ if( const XclImpPTField* pField = GetField( rPageField ) )
+ pField->ConvertFieldInfo( rSaveData, mpDPObj, *this, true );
+
+ // hidden fields
+ for( sal_uInt16 nField = 0, nCount = GetFieldCount(); nField < nCount; ++nField )
+ if( const XclImpPTField* pField = GetField( nField ) )
+ if (!pField->GetAxes())
+ pField->ConvertFieldInfo( rSaveData, mpDPObj, *this );
+}
+
+XclImpPivotTableManager::XclImpPivotTableManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+XclImpPivotTableManager::~XclImpPivotTableManager()
+{
+}
+
+// pivot cache records --------------------------------------------------------
+
+XclImpPivotCacheRef XclImpPivotTableManager::GetPivotCache( sal_uInt16 nCacheIdx )
+{
+ XclImpPivotCacheRef xPCache;
+ if( nCacheIdx < maPCaches.size() )
+ xPCache = maPCaches[ nCacheIdx ];
+ return xPCache;
+}
+
+void XclImpPivotTableManager::ReadSxidstm( XclImpStream& rStrm )
+{
+ XclImpPivotCacheRef xPCache = std::make_shared<XclImpPivotCache>( GetRoot() );
+ maPCaches.push_back( xPCache );
+ xPCache->ReadSxidstm( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxvs( XclImpStream& rStrm )
+{
+ if( !maPCaches.empty() )
+ maPCaches.back()->ReadSxvs( rStrm );
+}
+
+void XclImpPivotTableManager::ReadDconref( XclImpStream& rStrm )
+{
+ if( !maPCaches.empty() )
+ maPCaches.back()->ReadDconref( rStrm );
+}
+
+void XclImpPivotTableManager::ReadDConName( XclImpStream& rStrm )
+{
+ if( !maPCaches.empty() )
+ maPCaches.back()->ReadDConName( rStrm );
+}
+
+// pivot table records --------------------------------------------------------
+
+void XclImpPivotTableManager::ReadSxview( XclImpStream& rStrm )
+{
+ XclImpPivotTableRef xPTable = std::make_shared<XclImpPivotTable>( GetRoot() );
+ maPTables.push_back( xPTable );
+ xPTable->ReadSxview( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxvd( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxvd( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxvdex( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxvdex( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxivd( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxivd( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxpi( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxpi( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxdi( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxdi( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxvi( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxvi( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxex( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxex( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxViewEx9( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxViewEx9( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxAddl( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxAddl( rStrm );
+}
+
+void XclImpPivotTableManager::ReadPivotCaches( const XclImpStream& rStrm )
+{
+ for( auto& rxPCache : maPCaches )
+ rxPCache->ReadPivotCacheStream( rStrm );
+}
+
+void XclImpPivotTableManager::ConvertPivotTables()
+{
+ for( auto& rxPTable : maPTables )
+ rxPTable->Convert();
+}
+
+void XclImpPivotTableManager::MaybeRefreshPivotTables()
+{
+ for( auto& rxPTable : maPTables )
+ rxPTable->MaybeRefresh();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiroot.cxx b/sc/source/filter/excel/xiroot.cxx
new file mode 100644
index 000000000..af04654de
--- /dev/null
+++ b/sc/source/filter/excel/xiroot.cxx
@@ -0,0 +1,298 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xiroot.hxx>
+#include <addincol.hxx>
+#include <colrowst.hxx>
+#include <document.hxx>
+#include <formel.hxx>
+#include <scextopt.hxx>
+#include <xihelper.hxx>
+#include <xiformula.hxx>
+#include <xilink.hxx>
+#include <xiname.hxx>
+#include <xistyle.hxx>
+#include <xicontent.hxx>
+#include <xiescher.hxx>
+#include <xipivot.hxx>
+#include <xipage.hxx>
+#include <xiview.hxx>
+
+#include <root.hxx>
+#include <excimp8.hxx>
+#include <documentimport.hxx>
+
+// Global data ================================================================
+
+XclImpRootData::XclImpRootData( XclBiff eBiff, SfxMedium& rMedium,
+ const tools::SvRef<SotStorage>& xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc ) :
+ XclRootData( eBiff, rMedium, xRootStrg, rDoc, eTextEnc, false ),
+ mxDocImport(std::make_shared<ScDocumentImport>(rDoc)),
+ mbHasCodePage( false ),
+ mbHasBasic( false )
+{
+}
+
+XclImpRootData::~XclImpRootData()
+{
+}
+
+XclImpRoot::XclImpRoot( XclImpRootData& rImpRootData ) :
+ XclRoot( rImpRootData ),
+ mrImpData( rImpRootData )
+{
+ mrImpData.mxAddrConv = std::make_shared<XclImpAddressConverter>( GetRoot() );
+ mrImpData.mxFmlaComp = std::make_shared<XclImpFormulaCompiler>( GetRoot() );
+ mrImpData.mxPalette = std::make_shared<XclImpPalette>( GetRoot() );
+ mrImpData.mxFontBfr = std::make_shared<XclImpFontBuffer>( GetRoot() );
+ mrImpData.mxNumFmtBfr = std::make_shared<XclImpNumFmtBuffer>( GetRoot() );
+ mrImpData.mpXFBfr = std::make_shared<XclImpXFBuffer>( GetRoot() );
+ mrImpData.mxXFRangeBfr = std::make_shared<XclImpXFRangeBuffer>( GetRoot() );
+ mrImpData.mxTabInfo = std::make_shared<XclImpTabInfo>();
+ mrImpData.mxNameMgr = std::make_shared<XclImpNameManager>( GetRoot() );
+ mrImpData.mxObjMgr = std::make_shared<XclImpObjectManager>( GetRoot() );
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ mrImpData.mxLinkMgr = std::make_shared<XclImpLinkManager>( GetRoot() );
+ mrImpData.mxSst = std::make_shared<XclImpSst>( GetRoot() );
+ mrImpData.mxCondFmtMgr = std::make_shared<XclImpCondFormatManager>( GetRoot() );
+ mrImpData.mxValidMgr = std::make_shared<XclImpValidationManager>( GetRoot() );
+ // TODO still in old RootData (deleted by RootData)
+ GetOldRoot().pAutoFilterBuffer.reset( new XclImpAutoFilterBuffer );
+ mrImpData.mxWebQueryBfr = std::make_shared<XclImpWebQueryBuffer>( GetRoot() );
+ mrImpData.mxPTableMgr = std::make_shared<XclImpPivotTableManager>( GetRoot() );
+ mrImpData.mxTabProtect = std::make_shared<XclImpSheetProtectBuffer>( GetRoot() );
+ mrImpData.mxDocProtect = std::make_shared<XclImpDocProtectBuffer>( GetRoot() );
+ }
+
+ mrImpData.mxPageSett = std::make_shared<XclImpPageSettings>( GetRoot() );
+ mrImpData.mxDocViewSett = std::make_shared<XclImpDocViewSettings>( GetRoot() );
+ mrImpData.mxTabViewSett = std::make_shared<XclImpTabViewSettings>( GetRoot() );
+ mrImpData.mpPrintRanges = std::make_unique<ScRangeListTabs>( GetRoot() );
+ mrImpData.mpPrintTitles = std::make_unique<ScRangeListTabs>( GetRoot() );
+}
+
+void XclImpRoot::SetCodePage( sal_uInt16 nCodePage )
+{
+ SetTextEncoding( XclTools::GetTextEncoding( nCodePage ) );
+ mrImpData.mbHasCodePage = true;
+}
+
+void XclImpRoot::InitializeTable( SCTAB nScTab )
+{
+ if( GetBiff() <= EXC_BIFF4 )
+ {
+ GetPalette().Initialize();
+ GetFontBuffer().Initialize();
+ GetNumFmtBuffer().Initialize();
+ GetXFBuffer().Initialize();
+ }
+ GetXFRangeBuffer().Initialize();
+ GetPageSettings().Initialize();
+ GetTabViewSettings().Initialize();
+ // delete the automatically generated codename
+ GetDoc().SetCodeName( nScTab, OUString() );
+}
+
+void XclImpRoot::FinalizeTable()
+{
+ GetXFRangeBuffer().Finalize();
+ GetOldRoot().pColRowBuff->Convert( GetCurrScTab() );
+ GetPageSettings().Finalize();
+ GetTabViewSettings().Finalize();
+}
+
+XclImpAddressConverter& XclImpRoot::GetAddressConverter() const
+{
+ return *mrImpData.mxAddrConv;
+}
+
+XclImpFormulaCompiler& XclImpRoot::GetFormulaCompiler() const
+{
+ return *mrImpData.mxFmlaComp;
+}
+
+ExcelToSc& XclImpRoot::GetOldFmlaConverter() const
+{
+ // TODO still in old RootData
+ return *GetOldRoot().pFmlaConverter;
+}
+
+XclImpSst& XclImpRoot::GetSst() const
+{
+ assert(mrImpData.mxSst && "XclImpRoot::GetSst - invalid call, wrong BIFF");
+ return *mrImpData.mxSst;
+}
+
+XclImpPalette& XclImpRoot::GetPalette() const
+{
+ return *mrImpData.mxPalette;
+}
+
+XclImpFontBuffer& XclImpRoot::GetFontBuffer() const
+{
+ return *mrImpData.mxFontBfr;
+}
+
+XclImpNumFmtBuffer& XclImpRoot::GetNumFmtBuffer() const
+{
+ return *mrImpData.mxNumFmtBfr;
+}
+
+XclImpXFBuffer& XclImpRoot::GetXFBuffer() const
+{
+ return *mrImpData.mpXFBfr;
+}
+
+XclImpXFRangeBuffer& XclImpRoot::GetXFRangeBuffer() const
+{
+ return *mrImpData.mxXFRangeBfr;
+}
+
+ScRangeListTabs& XclImpRoot::GetPrintAreaBuffer() const
+{
+ return *mrImpData.mpPrintRanges;
+}
+
+ScRangeListTabs& XclImpRoot::GetTitleAreaBuffer() const
+{
+ return *mrImpData.mpPrintTitles;
+}
+
+XclImpTabInfo& XclImpRoot::GetTabInfo() const
+{
+ return *mrImpData.mxTabInfo;
+}
+
+XclImpNameManager& XclImpRoot::GetNameManager() const
+{
+ return *mrImpData.mxNameMgr;
+}
+
+XclImpLinkManager& XclImpRoot::GetLinkManager() const
+{
+ assert(mrImpData.mxLinkMgr && "XclImpRoot::GetLinkManager - invalid call, wrong BIFF");
+ return *mrImpData.mxLinkMgr;
+}
+
+XclImpObjectManager& XclImpRoot::GetObjectManager() const
+{
+ return *mrImpData.mxObjMgr;
+}
+
+XclImpSheetDrawing& XclImpRoot::GetCurrSheetDrawing() const
+{
+ OSL_ENSURE( !IsInGlobals(), "XclImpRoot::GetCurrSheetDrawing - must not be called from workbook globals" );
+ return mrImpData.mxObjMgr->GetSheetDrawing( GetCurrScTab() );
+}
+
+XclImpCondFormatManager& XclImpRoot::GetCondFormatManager() const
+{
+ assert(mrImpData.mxCondFmtMgr && "XclImpRoot::GetCondFormatManager - invalid call, wrong BIFF");
+ return *mrImpData.mxCondFmtMgr;
+}
+
+XclImpValidationManager& XclImpRoot::GetValidationManager() const
+{
+ assert(mrImpData.mxValidMgr && "XclImpRoot::GetValidationManager - invalid call, wrong BIFF");
+ return *mrImpData.mxValidMgr;
+}
+
+XclImpAutoFilterBuffer& XclImpRoot::GetFilterManager() const
+{
+ // TODO still in old RootData
+ assert(GetOldRoot().pAutoFilterBuffer && "XclImpRoot::GetFilterManager - invalid call, wrong BIFF");
+ return *GetOldRoot().pAutoFilterBuffer;
+}
+
+XclImpWebQueryBuffer& XclImpRoot::GetWebQueryBuffer() const
+{
+ assert(mrImpData.mxWebQueryBfr && "XclImpRoot::GetWebQueryBuffer - invalid call, wrong BIFF");
+ return *mrImpData.mxWebQueryBfr;
+}
+
+XclImpPivotTableManager& XclImpRoot::GetPivotTableManager() const
+{
+ assert(mrImpData.mxPTableMgr && "XclImpRoot::GetPivotTableManager - invalid call, wrong BIFF");
+ return *mrImpData.mxPTableMgr;
+}
+
+XclImpSheetProtectBuffer& XclImpRoot::GetSheetProtectBuffer() const
+{
+ assert(mrImpData.mxTabProtect && "XclImpRoot::GetSheetProtectBuffer - invalid call, wrong BIFF");
+ return *mrImpData.mxTabProtect;
+}
+
+XclImpDocProtectBuffer& XclImpRoot::GetDocProtectBuffer() const
+{
+ assert(mrImpData.mxDocProtect && "XclImpRoot::GetDocProtectBuffer - invalid call, wrong BIFF");
+ return *mrImpData.mxDocProtect;
+}
+
+XclImpPageSettings& XclImpRoot::GetPageSettings() const
+{
+ return *mrImpData.mxPageSett;
+}
+
+XclImpDocViewSettings& XclImpRoot::GetDocViewSettings() const
+{
+ return *mrImpData.mxDocViewSett;
+}
+
+XclImpTabViewSettings& XclImpRoot::GetTabViewSettings() const
+{
+ return *mrImpData.mxTabViewSett;
+}
+
+OUString XclImpRoot::GetScAddInName( const OUString& rXclName )
+{
+ OUString aScName;
+ if( ScGlobal::GetAddInCollection()->GetCalcName( rXclName, aScName ) )
+ return aScName;
+ return rXclName;
+}
+
+void XclImpRoot::ReadCodeName( XclImpStream& rStrm, bool bGlobals )
+{
+ if( !(mrImpData.mbHasBasic && (GetBiff() == EXC_BIFF8)) )
+ return;
+
+ OUString aName = rStrm.ReadUniString();
+ if( aName.isEmpty() )
+ return;
+
+ if( bGlobals )
+ {
+ GetExtDocOptions().GetDocSettings().maGlobCodeName = aName;
+ GetDoc().SetCodeName( aName );
+ }
+ else
+ {
+ GetExtDocOptions().SetCodeName( GetCurrScTab(), aName );
+ GetDoc().SetCodeName( GetCurrScTab(), aName );
+ }
+}
+
+ScDocumentImport& XclImpRoot::GetDocImport()
+{
+ return *mrImpData.mxDocImport;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xistream.cxx b/sc/source/filter/excel/xistream.cxx
new file mode 100644
index 000000000..0a6c24aca
--- /dev/null
+++ b/sc/source/filter/excel/xistream.cxx
@@ -0,0 +1,1084 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/docpasswordhelper.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/thread.h>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <tools/solar.h>
+#include <ftools.hxx>
+#include <xistream.hxx>
+#include <xlstring.hxx>
+#include <xiroot.hxx>
+
+#include <vector>
+#include <memory>
+
+using namespace ::com::sun::star;
+
+// Decryption
+XclImpDecrypter::XclImpDecrypter() :
+ mnError( EXC_ENCR_ERROR_UNSUPP_CRYPT ),
+ mnOldPos( STREAM_SEEK_TO_END ),
+ mnRecSize( 0 )
+{
+}
+
+XclImpDecrypter::XclImpDecrypter( const XclImpDecrypter& rSrc ) :
+ ::comphelper::IDocPasswordVerifier(),
+ mnError( rSrc.mnError ),
+ mnOldPos( STREAM_SEEK_TO_END ),
+ mnRecSize( 0 )
+{
+}
+
+XclImpDecrypter::~XclImpDecrypter()
+{
+}
+
+XclImpDecrypterRef XclImpDecrypter::Clone() const
+{
+ XclImpDecrypterRef xNewDecr;
+ if( IsValid() )
+ xNewDecr.reset( OnClone() );
+ return xNewDecr;
+}
+
+::comphelper::DocPasswordVerifierResult XclImpDecrypter::verifyPassword( const OUString& rPassword, uno::Sequence< beans::NamedValue >& o_rEncryptionData )
+{
+ o_rEncryptionData = OnVerifyPassword( rPassword );
+ mnError = o_rEncryptionData.hasElements() ? ERRCODE_NONE : ERRCODE_ABORT;
+ return o_rEncryptionData.hasElements() ? ::comphelper::DocPasswordVerifierResult::OK : ::comphelper::DocPasswordVerifierResult::WrongPassword;
+}
+
+::comphelper::DocPasswordVerifierResult XclImpDecrypter::verifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ bool bValid = OnVerifyEncryptionData( rEncryptionData );
+ mnError = bValid ? ERRCODE_NONE : ERRCODE_ABORT;
+ return bValid ? ::comphelper::DocPasswordVerifierResult::OK : ::comphelper::DocPasswordVerifierResult::WrongPassword;
+}
+
+void XclImpDecrypter::Update( const SvStream& rStrm, sal_uInt16 nRecSize )
+{
+ if( IsValid() )
+ {
+ sal_uInt64 const nNewPos = rStrm.Tell();
+ if( (mnOldPos != nNewPos) || (mnRecSize != nRecSize) )
+ {
+ OnUpdate( mnOldPos, nNewPos, nRecSize );
+ mnOldPos = nNewPos;
+ mnRecSize = nRecSize;
+ }
+ }
+}
+
+sal_uInt16 XclImpDecrypter::Read( SvStream& rStrm, void* pData, sal_uInt16 nBytes )
+{
+ sal_uInt16 nRet = 0;
+ if( pData && nBytes )
+ {
+ if( IsValid() )
+ {
+ Update( rStrm, mnRecSize );
+ nRet = OnRead( rStrm, static_cast< sal_uInt8* >( pData ), nBytes );
+ mnOldPos = rStrm.Tell();
+ }
+ else
+ nRet = static_cast<sal_uInt16>(rStrm.ReadBytes(pData, nBytes));
+ }
+ return nRet;
+}
+
+XclImpBiff5Decrypter::XclImpBiff5Decrypter( sal_uInt16 nKey, sal_uInt16 nHash ) :
+ mnKey( nKey ),
+ mnHash( nHash )
+{
+}
+
+XclImpBiff5Decrypter::XclImpBiff5Decrypter( const XclImpBiff5Decrypter& rSrc ) :
+ XclImpDecrypter( rSrc ),
+ maEncryptionData( rSrc.maEncryptionData ),
+ mnKey( rSrc.mnKey ),
+ mnHash( rSrc.mnHash )
+{
+ if( IsValid() )
+ maCodec.InitCodec( maEncryptionData );
+}
+
+XclImpBiff5Decrypter* XclImpBiff5Decrypter::OnClone() const
+{
+ return new XclImpBiff5Decrypter( *this );
+}
+
+uno::Sequence< beans::NamedValue > XclImpBiff5Decrypter::OnVerifyPassword( const OUString& rPassword )
+{
+ maEncryptionData.realloc( 0 );
+
+ /* Convert password to a byte string. TODO: this needs some fine tuning
+ according to the spec... */
+ OString aBytePassword = OUStringToOString( rPassword, osl_getThreadTextEncoding() );
+ sal_Int32 nLen = aBytePassword.getLength();
+ if( (0 < nLen) && (nLen < 16) )
+ {
+ // init codec
+ maCodec.InitKey( reinterpret_cast<sal_uInt8 const *>(aBytePassword.getStr()) );
+
+ if ( maCodec.VerifyKey( mnKey, mnHash ) )
+ {
+ maEncryptionData = maCodec.GetEncryptionData();
+
+ // since the export uses Std97 encryption always we have to request it here
+ ::std::vector< sal_uInt16 > aPassVect( 16 );
+ sal_Int32 nInd = 0;
+ std::for_each(aPassVect.begin(), aPassVect.begin() + nLen,
+ [&rPassword, &nInd](sal_uInt16& rPass) {
+ rPass = static_cast< sal_uInt16 >( rPassword[nInd] );
+ ++nInd;
+ });
+
+ uno::Sequence< sal_Int8 > aDocId = ::comphelper::DocPasswordHelper::GenerateRandomByteSequence( 16 );
+ OSL_ENSURE( aDocId.getLength() == 16, "Unexpected length of the sequence!" );
+
+ ::msfilter::MSCodec_Std97 aCodec97;
+ aCodec97.InitKey(aPassVect.data(), reinterpret_cast<sal_uInt8 const *>(aDocId.getConstArray()));
+
+ // merge the EncryptionData, there should be no conflicts
+ ::comphelper::SequenceAsHashMap aEncryptionHash( maEncryptionData );
+ aEncryptionHash.update( ::comphelper::SequenceAsHashMap( aCodec97.GetEncryptionData() ) );
+ aEncryptionHash >> maEncryptionData;
+ }
+ }
+
+ return maEncryptionData;
+}
+
+bool XclImpBiff5Decrypter::OnVerifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ maEncryptionData.realloc( 0 );
+
+ if( rEncryptionData.hasElements() )
+ {
+ // init codec
+ maCodec.InitCodec( rEncryptionData );
+
+ if ( maCodec.VerifyKey( mnKey, mnHash ) )
+ maEncryptionData = rEncryptionData;
+ }
+
+ return maEncryptionData.hasElements();
+}
+
+void XclImpBiff5Decrypter::OnUpdate( std::size_t /*nOldStrmPos*/, std::size_t nNewStrmPos, sal_uInt16 nRecSize )
+{
+ maCodec.InitCipher();
+ maCodec.Skip( (nNewStrmPos + nRecSize) & 0x0F );
+}
+
+sal_uInt16 XclImpBiff5Decrypter::OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes )
+{
+ sal_uInt16 nRet = static_cast<sal_uInt16>(rStrm.ReadBytes(pnData, nBytes));
+ maCodec.Decode( pnData, nRet );
+ return nRet;
+}
+
+XclImpBiff8Decrypter::XclImpBiff8Decrypter( std::vector<sal_uInt8>&& rSalt,
+ std::vector<sal_uInt8>&& rVerifier,
+ std::vector<sal_uInt8>&& rVerifierHash)
+ : maSalt(std::move(rSalt))
+ , maVerifier(std::move(rVerifier))
+ , maVerifierHash(std::move(rVerifierHash))
+ , mpCodec(nullptr)
+{
+}
+
+XclImpBiff8Decrypter::XclImpBiff8Decrypter(const XclImpBiff8Decrypter& rSrc)
+ : XclImpDecrypter(rSrc)
+ , maEncryptionData(rSrc.maEncryptionData)
+ , maSalt(rSrc.maSalt)
+ , maVerifier(rSrc.maVerifier)
+ , maVerifierHash(rSrc.maVerifierHash)
+ , mpCodec(nullptr)
+{
+}
+
+XclImpBiff8StdDecrypter::XclImpBiff8StdDecrypter(const XclImpBiff8StdDecrypter& rSrc)
+ : XclImpBiff8Decrypter(rSrc)
+{
+ mpCodec = &maCodec;
+ if (IsValid())
+ maCodec.InitCodec(maEncryptionData);
+}
+
+XclImpBiff8StdDecrypter* XclImpBiff8StdDecrypter::OnClone() const
+{
+ return new XclImpBiff8StdDecrypter(*this);
+}
+
+XclImpBiff8CryptoAPIDecrypter::XclImpBiff8CryptoAPIDecrypter(const XclImpBiff8CryptoAPIDecrypter& rSrc)
+ : XclImpBiff8Decrypter(rSrc)
+{
+ mpCodec = &maCodec;
+ if (IsValid())
+ maCodec.InitCodec(maEncryptionData);
+}
+
+XclImpBiff8CryptoAPIDecrypter* XclImpBiff8CryptoAPIDecrypter::OnClone() const
+{
+ return new XclImpBiff8CryptoAPIDecrypter(*this);
+}
+
+uno::Sequence< beans::NamedValue > XclImpBiff8Decrypter::OnVerifyPassword( const OUString& rPassword )
+{
+ maEncryptionData.realloc( 0 );
+
+ sal_Int32 nLen = rPassword.getLength();
+ if( (0 < nLen) && (nLen < 16) )
+ {
+ // copy string to sal_uInt16 array
+ ::std::vector< sal_uInt16 > aPassVect( 16 );
+ const sal_Unicode* pcChar = rPassword.getStr();
+ std::for_each(aPassVect.begin(), aPassVect.begin() + nLen,
+ [&pcChar](sal_uInt16& rPass) {
+ rPass = static_cast< sal_uInt16 >( *pcChar );
+ ++pcChar;
+ });
+
+ // init codec
+ mpCodec->InitKey(aPassVect.data(), maSalt.data());
+ if (mpCodec->VerifyKey(maVerifier.data(), maVerifierHash.data()))
+ maEncryptionData = mpCodec->GetEncryptionData();
+ }
+
+ return maEncryptionData;
+}
+
+bool XclImpBiff8Decrypter::OnVerifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ maEncryptionData.realloc( 0 );
+
+ if( rEncryptionData.hasElements() )
+ {
+ // init codec
+ mpCodec->InitCodec( rEncryptionData );
+
+ if (mpCodec->VerifyKey(maVerifier.data(), maVerifierHash.data()))
+ maEncryptionData = rEncryptionData;
+ }
+
+ return maEncryptionData.hasElements();
+}
+
+void XclImpBiff8Decrypter::OnUpdate( std::size_t nOldStrmPos, std::size_t nNewStrmPos, sal_uInt16 /*nRecSize*/ )
+{
+ if( nNewStrmPos == nOldStrmPos )
+ return;
+
+ sal_uInt32 nOldBlock = GetBlock( nOldStrmPos );
+ sal_uInt16 nOldOffset = GetOffset( nOldStrmPos );
+
+ sal_uInt32 nNewBlock = GetBlock( nNewStrmPos );
+ sal_uInt16 nNewOffset = GetOffset( nNewStrmPos );
+
+ /* Rekey cipher, if block changed or if previous offset in same block. */
+ if( (nNewBlock != nOldBlock) || (nNewOffset < nOldOffset) )
+ {
+ mpCodec->InitCipher( nNewBlock );
+ nOldOffset = 0; // reset nOldOffset for next if() statement
+ }
+
+ /* Seek to correct offset. */
+ if( nNewOffset > nOldOffset )
+ mpCodec->Skip( nNewOffset - nOldOffset );
+}
+
+sal_uInt16 XclImpBiff8Decrypter::OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes )
+{
+ sal_uInt16 nRet = 0;
+
+ sal_uInt8* pnCurrData = pnData;
+ sal_uInt16 nBytesLeft = nBytes;
+ while( nBytesLeft )
+ {
+ sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - GetOffset( rStrm.Tell() );
+ sal_uInt16 nDecBytes = ::std::min< sal_uInt16 >( nBytesLeft, nBlockLeft );
+
+ // read the block from stream
+ nRet = nRet + static_cast<sal_uInt16>(rStrm.ReadBytes(pnCurrData, nDecBytes));
+ // decode the block inplace
+ mpCodec->Decode( pnCurrData, nDecBytes, pnCurrData, nDecBytes );
+ if( GetOffset( rStrm.Tell() ) == 0 )
+ mpCodec->InitCipher( GetBlock( rStrm.Tell() ) );
+
+ pnCurrData += nDecBytes;
+ nBytesLeft = nBytesLeft - nDecBytes;
+ }
+
+ return nRet;
+}
+
+sal_uInt32 XclImpBiff8Decrypter::GetBlock( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt32 >( nStrmPos / EXC_ENCR_BLOCKSIZE );
+}
+
+sal_uInt16 XclImpBiff8Decrypter::GetOffset( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt16 >( nStrmPos % EXC_ENCR_BLOCKSIZE );
+}
+
+// Stream
+XclImpStreamPos::XclImpStreamPos() :
+ mnPos( STREAM_SEEK_TO_BEGIN ),
+ mnNextPos( STREAM_SEEK_TO_BEGIN ),
+ mnCurrSize( 0 ),
+ mnRawRecId( EXC_ID_UNKNOWN ),
+ mnRawRecSize( 0 ),
+ mnRawRecLeft( 0 ),
+ mbValid( false )
+{
+}
+
+void XclImpStreamPos::Set(
+ const SvStream& rStrm, std::size_t nNextPos, std::size_t nCurrSize,
+ sal_uInt16 nRawRecId, sal_uInt16 nRawRecSize, sal_uInt16 nRawRecLeft,
+ bool bValid )
+{
+ mnPos = rStrm.Tell();
+ mnNextPos = nNextPos;
+ mnCurrSize = nCurrSize;
+ mnRawRecId = nRawRecId;
+ mnRawRecSize = nRawRecSize;
+ mnRawRecLeft = nRawRecLeft;
+ mbValid = bValid;
+}
+
+void XclImpStreamPos::Get(
+ SvStream& rStrm, std::size_t& rnNextPos, std::size_t& rnCurrSize,
+ sal_uInt16& rnRawRecId, sal_uInt16& rnRawRecSize, sal_uInt16& rnRawRecLeft,
+ bool& rbValid ) const
+{
+ rStrm.Seek( mnPos );
+ rnNextPos = mnNextPos;
+ rnCurrSize = mnCurrSize;
+ rnRawRecId = mnRawRecId;
+ rnRawRecSize = mnRawRecSize;
+ rnRawRecLeft = mnRawRecLeft;
+ rbValid = mbValid;
+}
+
+XclBiff XclImpStream::DetectBiffVersion( SvStream& rStrm )
+{
+ XclBiff eBiff = EXC_BIFF_UNKNOWN;
+
+ rStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ sal_uInt16 nBofId(0), nBofSize(0);
+ rStrm.ReadUInt16( nBofId ).ReadUInt16( nBofSize );
+
+ if (rStrm.good() && 4 <= nBofSize && nBofSize <= 16) switch( nBofId )
+ {
+ case EXC_ID2_BOF:
+ eBiff = EXC_BIFF2;
+ break;
+ case EXC_ID3_BOF:
+ eBiff = EXC_BIFF3;
+ break;
+ case EXC_ID4_BOF:
+ eBiff = EXC_BIFF4;
+ break;
+ case EXC_ID5_BOF:
+ {
+ sal_uInt16 nVersion(0);
+ rStrm.ReadUInt16( nVersion );
+ // #i23425# #i44031# #i62752# there are some *really* broken documents out there...
+ switch( nVersion & 0xFF00 )
+ {
+ case 0: eBiff = EXC_BIFF5; break; // #i44031# #i62752#
+ case EXC_BOF_BIFF2: eBiff = EXC_BIFF2; break;
+ case EXC_BOF_BIFF3: eBiff = EXC_BIFF3; break;
+ case EXC_BOF_BIFF4: eBiff = EXC_BIFF4; break;
+ case EXC_BOF_BIFF5: eBiff = EXC_BIFF5; break;
+ case EXC_BOF_BIFF8: eBiff = EXC_BIFF8; break;
+ default: SAL_WARN("sc", "XclImpStream::DetectBiffVersion - unknown BIFF version: 0x" << std::hex << nVersion );
+ }
+ }
+ break;
+ }
+ return eBiff;
+}
+
+XclImpStream::XclImpStream( SvStream& rInStrm, const XclImpRoot& rRoot ) :
+ mrStrm( rInStrm ),
+ mrRoot( rRoot ),
+ mnGlobRecId( EXC_ID_UNKNOWN ),
+ mbGlobValidRec( false ),
+ mbHasGlobPos( false ),
+ mnNextRecPos( STREAM_SEEK_TO_BEGIN ),
+ mnCurrRecSize( 0 ),
+ mnComplRecSize( 0 ),
+ mbHasComplRec( false ),
+ mnRecId( EXC_ID_UNKNOWN ),
+ mnAltContId( EXC_ID_UNKNOWN ),
+ mnRawRecId( EXC_ID_UNKNOWN ),
+ mnRawRecSize( 0 ),
+ mnRawRecLeft( 0 ),
+ mcNulSubst( '?' ),
+ mbCont( true ),
+ mbUseDecr( false ),
+ mbValidRec( false ),
+ mbValid( false )
+{
+ mnStreamSize = mrStrm.TellEnd();
+ mrStrm.Seek( STREAM_SEEK_TO_BEGIN );
+}
+
+XclImpStream::~XclImpStream()
+{
+}
+
+bool XclImpStream::StartNextRecord()
+{
+ maPosStack.clear();
+
+ /* #i4266# Counter to ignore zero records (id==len==0) (i.e. the application
+ "Crystal Report" writes zero records between other records) */
+ std::size_t nZeroRecCount = 5;
+ bool bIsZeroRec = false;
+
+ do
+ {
+ mbValidRec = ReadNextRawRecHeader();
+ bIsZeroRec = (mnRawRecId == 0) && (mnRawRecSize == 0);
+ if( bIsZeroRec ) --nZeroRecCount;
+ mnNextRecPos = mrStrm.Tell() + mnRawRecSize;
+ }
+ while( mbValidRec && ((mbCont && IsContinueId( mnRawRecId )) || (bIsZeroRec && nZeroRecCount)) );
+
+ mbValidRec = mbValidRec && !bIsZeroRec;
+ mbValid = mbValidRec;
+ SetupRecord();
+
+ return mbValidRec;
+}
+
+bool XclImpStream::StartNextRecord( std::size_t nNextRecPos )
+{
+ mnNextRecPos = nNextRecPos;
+ return StartNextRecord();
+}
+
+void XclImpStream::ResetRecord( bool bContLookup, sal_uInt16 nAltContId )
+{
+ if( mbValidRec )
+ {
+ maPosStack.clear();
+ RestorePosition( maFirstRec );
+ mnCurrRecSize = mnComplRecSize = mnRawRecSize;
+ mbHasComplRec = !bContLookup;
+ mbCont = bContLookup;
+ mnAltContId = nAltContId;
+ EnableDecryption();
+ }
+}
+
+void XclImpStream::RewindRecord()
+{
+ mnNextRecPos = maFirstRec.GetPos();
+ mbValid = mbValidRec = false;
+}
+
+void XclImpStream::SetDecrypter( XclImpDecrypterRef const & xDecrypter )
+{
+ mxDecrypter = xDecrypter;
+ EnableDecryption();
+ SetupDecrypter();
+}
+
+void XclImpStream::CopyDecrypterFrom( const XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xNewDecr;
+ if( rStrm.mxDecrypter )
+ xNewDecr = rStrm.mxDecrypter->Clone();
+ SetDecrypter( xNewDecr );
+}
+
+void XclImpStream::EnableDecryption( bool bEnable )
+{
+ mbUseDecr = bEnable && mxDecrypter && mxDecrypter->IsValid();
+}
+
+void XclImpStream::PushPosition()
+{
+ maPosStack.emplace_back( );
+ StorePosition( maPosStack.back() );
+}
+
+void XclImpStream::PopPosition()
+{
+ OSL_ENSURE( !maPosStack.empty(), "XclImpStream::PopPosition - stack empty" );
+ if( !maPosStack.empty() )
+ {
+ RestorePosition( maPosStack.back() );
+ maPosStack.pop_back();
+ }
+}
+
+void XclImpStream::StoreGlobalPosition()
+{
+ StorePosition( maGlobPos );
+ mnGlobRecId = mnRecId;
+ mbGlobValidRec = mbValidRec;
+ mbHasGlobPos = true;
+}
+
+void XclImpStream::SeekGlobalPosition()
+{
+ OSL_ENSURE( mbHasGlobPos, "XclImpStream::SeekGlobalPosition - no position stored" );
+ if( mbHasGlobPos )
+ {
+ RestorePosition( maGlobPos );
+ mnRecId = mnGlobRecId;
+ mnComplRecSize = mnCurrRecSize;
+ mbHasComplRec = !mbCont;
+ mbValidRec = mbGlobValidRec;
+ }
+}
+
+std::size_t XclImpStream::GetRecPos() const
+{
+ return mbValid ? (mnCurrRecSize - mnRawRecLeft) : EXC_REC_SEEK_TO_END;
+}
+
+std::size_t XclImpStream::GetRecSize()
+{
+ if( !mbHasComplRec )
+ {
+ PushPosition();
+ while( JumpToNextContinue() ) ; // JumpToNextContinue() adds up mnCurrRecSize
+ mnComplRecSize = mnCurrRecSize;
+ mbHasComplRec = true;
+ PopPosition();
+ }
+ return mnComplRecSize;
+}
+
+std::size_t XclImpStream::GetRecLeft()
+{
+ return mbValid ? (GetRecSize() - GetRecPos()) : 0;
+}
+
+sal_uInt16 XclImpStream::GetNextRecId()
+{
+ sal_uInt16 nRecId = EXC_ID_UNKNOWN;
+ if( mbValidRec )
+ {
+ PushPosition();
+ while( JumpToNextContinue() ) ; // skip following CONTINUE records
+ if( mnNextRecPos < mnStreamSize )
+ {
+ mrStrm.Seek( mnNextRecPos );
+ mrStrm.ReadUInt16( nRecId );
+ }
+ PopPosition();
+ }
+ return nRecId;
+}
+
+sal_uInt16 XclImpStream::PeekRecId( std::size_t nPos )
+{
+ sal_uInt16 nRecId = EXC_ID_UNKNOWN;
+ if (mbValidRec && nPos < mnStreamSize)
+ {
+ sal_uInt64 const nCurPos = mrStrm.Tell();
+ mrStrm.Seek(nPos);
+ mrStrm.ReadUInt16( nRecId );
+ mrStrm.Seek(nCurPos);
+ }
+ return nRecId;
+}
+
+sal_uInt8 XclImpStream::ReaduInt8()
+{
+ sal_uInt8 nValue = 0;
+ if( EnsureRawReadSize( 1 ) )
+ {
+ if( mbUseDecr )
+ mxDecrypter->Read( mrStrm, &nValue, 1 );
+ else
+ mrStrm.ReadUChar( nValue );
+ --mnRawRecLeft;
+ }
+ return nValue;
+}
+
+sal_Int16 XclImpStream::ReadInt16()
+{
+ sal_Int16 nValue = 0;
+ if( EnsureRawReadSize( 2 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT16 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 2 );
+ nValue = static_cast< sal_Int16 >( SVBT16ToUInt16( pnBuffer ) );
+ }
+ else
+ mrStrm.ReadInt16( nValue );
+ mnRawRecLeft -= 2;
+ }
+ return nValue;
+}
+
+sal_uInt16 XclImpStream::ReaduInt16()
+{
+ sal_uInt16 nValue = 0;
+ if( EnsureRawReadSize( 2 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT16 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 2 );
+ nValue = SVBT16ToUInt16( pnBuffer );
+ }
+ else
+ mrStrm.ReadUInt16( nValue );
+ mnRawRecLeft -= 2;
+ }
+ return nValue;
+}
+
+sal_Int32 XclImpStream::ReadInt32()
+{
+ sal_Int32 nValue = 0;
+ if( EnsureRawReadSize( 4 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT32 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 4 );
+ nValue = static_cast< sal_Int32 >( SVBT32ToUInt32( pnBuffer ) );
+ }
+ else
+ mrStrm.ReadInt32( nValue );
+ mnRawRecLeft -= 4;
+ }
+ return nValue;
+}
+
+sal_uInt32 XclImpStream::ReaduInt32()
+{
+ sal_uInt32 nValue = 0;
+ if( EnsureRawReadSize( 4 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT32 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 4 );
+ nValue = SVBT32ToUInt32( pnBuffer );
+ }
+ else
+ mrStrm.ReadUInt32( nValue );
+ mnRawRecLeft -= 4;
+ }
+ return nValue;
+}
+
+double XclImpStream::ReadDouble()
+{
+ double nValue = 0;
+ if( EnsureRawReadSize( 8 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT64 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 8 );
+ nValue = SVBT64ToDouble( pnBuffer );
+ }
+ else
+ mrStrm.ReadDouble( nValue );
+ mnRawRecLeft -= 8;
+ }
+ return nValue;
+}
+
+std::size_t XclImpStream::Read( void* pData, std::size_t nBytes )
+{
+ std::size_t nRet = 0;
+ if( mbValid && pData && (nBytes > 0) )
+ {
+ sal_uInt8* pnBuffer = static_cast< sal_uInt8* >( pData );
+ std::size_t nBytesLeft = nBytes;
+
+ while( mbValid && (nBytesLeft > 0) )
+ {
+ sal_uInt16 nReadSize = GetMaxRawReadSize( nBytesLeft );
+ sal_uInt16 nReadRet = ReadRawData( pnBuffer, nReadSize );
+ nRet += nReadRet;
+ mbValid = (nReadSize == nReadRet);
+ OSL_ENSURE( mbValid, "XclImpStream::Read - stream read error" );
+ pnBuffer += nReadRet;
+ nBytesLeft -= nReadRet;
+ if( mbValid && (nBytesLeft > 0) )
+ JumpToNextContinue();
+ OSL_ENSURE( mbValid, "XclImpStream::Read - record overread" );
+ }
+ }
+ return nRet;
+}
+
+std::size_t XclImpStream::CopyToStream( SvStream& rOutStrm, std::size_t nBytes )
+{
+ std::size_t nRet = 0;
+ if (mbValid && nBytes)
+ {
+ const std::size_t nMaxBuffer = 4096;
+ std::vector<sal_uInt8> aBuffer(o3tl::sanitizing_min(nBytes, nMaxBuffer));
+ std::size_t nBytesLeft = nBytes;
+
+ while (mbValid)
+ {
+ if (!nBytesLeft)
+ break;
+ std::size_t nReadSize = o3tl::sanitizing_min(nBytesLeft, nMaxBuffer);
+ nRet += Read(aBuffer.data(), nReadSize);
+ // writing more bytes than read results in invalid memory access
+ SAL_WARN_IF(nRet != nReadSize, "sc", "read less bytes than requested");
+ rOutStrm.WriteBytes(aBuffer.data(), nReadSize);
+ nBytesLeft -= nReadSize;
+ }
+ }
+ return nRet;
+}
+
+void XclImpStream::CopyRecordToStream( SvStream& rOutStrm )
+{
+ if( mbValidRec )
+ {
+ PushPosition();
+ RestorePosition( maFirstRec );
+ CopyToStream( rOutStrm, GetRecSize() );
+ PopPosition();
+ }
+}
+
+void XclImpStream::Seek( std::size_t nPos )
+{
+ if( !mbValidRec )
+ return;
+
+ std::size_t nCurrPos = GetRecPos();
+ if( !mbValid || (nPos < nCurrPos) ) // from invalid state or backward
+ {
+ RestorePosition( maFirstRec );
+ Ignore( nPos );
+ }
+ else if( nPos > nCurrPos ) // forward
+ {
+ Ignore( nPos - nCurrPos );
+ }
+}
+
+void XclImpStream::Ignore( std::size_t nBytes )
+{
+ // implementation similar to Read(), but without really reading anything
+ std::size_t nBytesLeft = nBytes;
+ while (mbValid)
+ {
+ if (!nBytesLeft)
+ break;
+ sal_uInt16 nReadSize = GetMaxRawReadSize( nBytesLeft );
+ mbValid = checkSeek(mrStrm, mrStrm.Tell() + nReadSize);
+ mnRawRecLeft = mnRawRecLeft - nReadSize;
+ nBytesLeft -= nReadSize;
+ if (mbValid && nBytesLeft > 0)
+ JumpToNextContinue();
+ OSL_ENSURE( mbValid, "XclImpStream::Ignore - record overread" );
+ }
+}
+
+std::size_t XclImpStream::ReadUniStringExtHeader(
+ bool& rb16Bit, bool& rbRich, bool& rbFareast,
+ sal_uInt16& rnFormatRuns, sal_uInt32& rnExtInf, sal_uInt8 nFlags )
+{
+ OSL_ENSURE( !::get_flag( nFlags, EXC_STRF_UNKNOWN ), "XclImpStream::ReadUniStringExt - unknown flags" );
+ rb16Bit = ::get_flag( nFlags, EXC_STRF_16BIT );
+ rbRich = ::get_flag( nFlags, EXC_STRF_RICH );
+ rbFareast = ::get_flag( nFlags, EXC_STRF_FAREAST );
+ rnFormatRuns = rbRich ? ReaduInt16() : 0;
+ rnExtInf = rbFareast ? ReaduInt32() : 0;
+ return rnExtInf + 4 * rnFormatRuns;
+}
+
+std::size_t XclImpStream::ReadUniStringExtHeader( bool& rb16Bit, sal_uInt8 nFlags )
+{
+ bool bRich, bFareast;
+ sal_uInt16 nCrun;
+ sal_uInt32 nExtInf;
+ return ReadUniStringExtHeader( rb16Bit, bRich, bFareast, nCrun, nExtInf, nFlags );
+}
+
+OUString XclImpStream::ReadRawUniString( sal_uInt16 nChars, bool b16Bit )
+{
+ OUStringBuffer aRet(o3tl::sanitizing_min<sal_uInt16>(nChars, mnRawRecLeft / (b16Bit ? 2 : 1)));
+ sal_uInt16 nCharsLeft = nChars;
+ sal_uInt16 nReadSize;
+
+ while (IsValid() && nCharsLeft)
+ {
+ if( b16Bit )
+ {
+ nReadSize = o3tl::sanitizing_min<sal_uInt16>(nCharsLeft, mnRawRecLeft / 2);
+ OSL_ENSURE( (nReadSize <= nCharsLeft) || !(mnRawRecLeft & 0x1),
+ "XclImpStream::ReadRawUniString - missing a byte" );
+ }
+ else
+ nReadSize = GetMaxRawReadSize( nCharsLeft );
+
+ std::unique_ptr<sal_Unicode[]> pcBuffer(new sal_Unicode[nReadSize + 1]);
+
+ sal_Unicode* pcUniChar = pcBuffer.get();
+ sal_Unicode* pcEndChar = pcBuffer.get() + nReadSize;
+
+ if( b16Bit )
+ {
+ sal_uInt16 nReadChar;
+ for( ; IsValid() && (pcUniChar < pcEndChar); ++pcUniChar )
+ {
+ nReadChar = ReaduInt16();
+ (*pcUniChar) = (nReadChar == EXC_NUL) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar );
+ }
+ }
+ else
+ {
+ sal_uInt8 nReadChar;
+ for( ; IsValid() && (pcUniChar < pcEndChar); ++pcUniChar )
+ {
+ nReadChar = ReaduInt8();
+ (*pcUniChar) = (nReadChar == EXC_NUL_C) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar );
+ }
+ }
+
+ *pcEndChar = '\0';
+ // this has the side-effect of only copying as far as the first null, which appears to be intentional. e.g.
+ // see tdf#124318
+ aRet.append( pcBuffer.get() );
+
+ nCharsLeft = nCharsLeft - nReadSize;
+ if( nCharsLeft > 0 )
+ JumpToNextStringContinue( b16Bit );
+ }
+
+ return aRet.makeStringAndClear();
+}
+
+OUString XclImpStream::ReadUniString( sal_uInt16 nChars, sal_uInt8 nFlags )
+{
+ bool b16Bit;
+ std::size_t nExtSize = ReadUniStringExtHeader( b16Bit, nFlags );
+ OUString aRet( ReadRawUniString( nChars, b16Bit ) );
+ Ignore( nExtSize );
+ return aRet;
+}
+
+OUString XclImpStream::ReadUniString( sal_uInt16 nChars )
+{
+ return ReadUniString( nChars, ReaduInt8() );
+}
+
+OUString XclImpStream::ReadUniString()
+{
+ return ReadUniString( ReaduInt16() );
+}
+
+void XclImpStream::IgnoreRawUniString( sal_uInt16 nChars, bool b16Bit )
+{
+ sal_uInt16 nCharsLeft = nChars;
+ sal_uInt16 nReadSize;
+
+ while( IsValid() && (nCharsLeft > 0) )
+ {
+ if( b16Bit )
+ {
+ nReadSize = o3tl::sanitizing_min<sal_uInt16>(nCharsLeft, mnRawRecLeft / 2);
+ OSL_ENSURE( (nReadSize <= nCharsLeft) || !(mnRawRecLeft & 0x1),
+ "XclImpStream::IgnoreRawUniString - missing a byte" );
+ Ignore( nReadSize * 2 );
+ }
+ else
+ {
+ nReadSize = GetMaxRawReadSize( nCharsLeft );
+ Ignore( nReadSize );
+ }
+
+ nCharsLeft = nCharsLeft - nReadSize;
+ if( nCharsLeft > 0 )
+ JumpToNextStringContinue( b16Bit );
+ }
+}
+
+void XclImpStream::IgnoreUniString( sal_uInt16 nChars, sal_uInt8 nFlags )
+{
+ bool b16Bit;
+ std::size_t nExtSize = ReadUniStringExtHeader( b16Bit, nFlags );
+ IgnoreRawUniString( nChars, b16Bit );
+ Ignore( nExtSize );
+}
+
+void XclImpStream::IgnoreUniString( sal_uInt16 nChars )
+{
+ IgnoreUniString( nChars, ReaduInt8() );
+}
+
+OUString XclImpStream::ReadRawByteString( sal_uInt16 nChars )
+{
+ nChars = GetMaxRawReadSize(nChars);
+ std::unique_ptr<char[]> pcBuffer(new char[ nChars + 1 ]);
+ sal_uInt16 nCharsRead = ReadRawData( pcBuffer.get(), nChars );
+ pcBuffer[ nCharsRead ] = '\0';
+ OUString aRet( pcBuffer.get(), strlen(pcBuffer.get()), mrRoot.GetTextEncoding() );
+ return aRet;
+}
+
+OUString XclImpStream::ReadByteString( bool b16BitLen )
+{
+ return ReadRawByteString( b16BitLen ? ReaduInt16() : ReaduInt8() );
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpStream::StorePosition( XclImpStreamPos& rPos )
+{
+ rPos.Set( mrStrm, mnNextRecPos, mnCurrRecSize, mnRawRecId, mnRawRecSize, mnRawRecLeft, mbValid );
+}
+
+void XclImpStream::RestorePosition( const XclImpStreamPos& rPos )
+{
+ rPos.Get( mrStrm, mnNextRecPos, mnCurrRecSize, mnRawRecId, mnRawRecSize, mnRawRecLeft, mbValid );
+ SetupDecrypter();
+}
+
+bool XclImpStream::ReadNextRawRecHeader()
+{
+ bool bRet = checkSeek(mrStrm, mnNextRecPos) && (mnNextRecPos + 4 <= mnStreamSize);
+ if (bRet)
+ {
+ mrStrm.ReadUInt16( mnRawRecId ).ReadUInt16( mnRawRecSize );
+ bRet = mrStrm.good();
+ }
+ return bRet;
+}
+
+void XclImpStream::SetupDecrypter()
+{
+ if( mxDecrypter )
+ mxDecrypter->Update( mrStrm, mnRawRecSize );
+}
+
+void XclImpStream::SetupRawRecord()
+{
+ // pre: mnRawRecSize contains current raw record size
+ // pre: mrStrm points to start of raw record data
+ mnNextRecPos = mrStrm.Tell() + mnRawRecSize;
+ mnRawRecLeft = mnRawRecSize;
+ mnCurrRecSize += mnRawRecSize;
+ SetupDecrypter(); // decrypter works on raw record level
+}
+
+void XclImpStream::SetupRecord()
+{
+ mnRecId = mnRawRecId;
+ mnAltContId = EXC_ID_UNKNOWN;
+ mnCurrRecSize = 0;
+ mnComplRecSize = mnRawRecSize;
+ mbHasComplRec = !mbCont;
+ SetupRawRecord();
+ SetNulSubstChar();
+ EnableDecryption();
+ StorePosition( maFirstRec );
+}
+
+bool XclImpStream::IsContinueId( sal_uInt16 nRecId ) const
+{
+ return (nRecId == EXC_ID_CONT) || (nRecId == mnAltContId);
+}
+
+bool XclImpStream::JumpToNextContinue()
+{
+ mbValid = mbValid && mbCont && ReadNextRawRecHeader() && IsContinueId( mnRawRecId );
+ if( mbValid ) // do not setup a following non-CONTINUE record
+ SetupRawRecord();
+ return mbValid;
+}
+
+bool XclImpStream::JumpToNextStringContinue( bool& rb16Bit )
+{
+ OSL_ENSURE( mnRawRecLeft == 0, "XclImpStream::JumpToNextStringContinue - unexpected garbage" );
+
+ if( mbCont && (GetRecLeft() > 0) )
+ {
+ JumpToNextContinue();
+ }
+ else if( mnRecId == EXC_ID_CONT )
+ {
+ // CONTINUE handling is off, but we have started reading in a CONTINUE record
+ // -> start next CONTINUE for TXO import
+ mbValidRec = ReadNextRawRecHeader() && ((mnRawRecId != 0) || (mnRawRecSize > 0));
+ mbValid = mbValidRec && (mnRawRecId == EXC_ID_CONT);
+ // we really start a new record here - no chance to return to string origin
+ if( mbValid )
+ SetupRecord();
+ }
+ else
+ mbValid = false;
+
+ if( mbValid )
+ rb16Bit = ::get_flag( ReaduInt8(), EXC_STRF_16BIT );
+ return mbValid;
+}
+
+bool XclImpStream::EnsureRawReadSize( sal_uInt16 nBytes )
+{
+ if( mbValid && nBytes )
+ {
+ while( mbValid && !mnRawRecLeft ) JumpToNextContinue();
+ mbValid = mbValid && (nBytes <= mnRawRecLeft);
+ OSL_ENSURE( mbValid, "XclImpStream::EnsureRawReadSize - record overread" );
+ }
+ return mbValid;
+}
+
+sal_uInt16 XclImpStream::GetMaxRawReadSize( std::size_t nBytes ) const
+{
+ return static_cast<sal_uInt16>(o3tl::sanitizing_min<std::size_t>(nBytes, mnRawRecLeft));
+}
+
+sal_uInt16 XclImpStream::ReadRawData( void* pData, sal_uInt16 nBytes )
+{
+ OSL_ENSURE( (nBytes <= mnRawRecLeft), "XclImpStream::ReadRawData - record overread" );
+ sal_uInt16 nRet = 0;
+ if( mbUseDecr )
+ nRet = mxDecrypter->Read( mrStrm, pData, nBytes );
+ else
+ nRet = static_cast<sal_uInt16>(mrStrm.ReadBytes(pData, nBytes));
+ mnRawRecLeft = mnRawRecLeft - nRet;
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xistring.cxx b/sc/source/filter/excel/xistring.cxx
new file mode 100644
index 000000000..f0878a617
--- /dev/null
+++ b/sc/source/filter/excel/xistring.cxx
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xistring.hxx>
+#include <xlstyle.hxx>
+#include <xistream.hxx>
+#include <xiroot.hxx>
+#include <xltools.hxx>
+#include <sal/log.hxx>
+
+// Byte/Unicode strings =======================================================
+
+/** All allowed flags for import. */
+const XclStrFlags nAllowedFlags = XclStrFlags::EightBitLength | XclStrFlags::SmartFlags | XclStrFlags::SeparateFormats;
+
+XclImpString::XclImpString()
+{
+}
+
+XclImpString::XclImpString( const OUString& rString ) :
+ maString( rString )
+{
+}
+
+void XclImpString::Read( XclImpStream& rStrm, XclStrFlags nFlags )
+{
+ if( !( nFlags & XclStrFlags::SeparateFormats ) )
+ maFormats.clear();
+
+ SAL_WARN_IF(
+ nFlags & ~nAllowedFlags, "sc.filter",
+ "XclImpString::Read - unknown flag");
+ bool b16BitLen = !( nFlags & XclStrFlags::EightBitLength );
+
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ // no integrated formatting in BIFF2-BIFF7
+ maString = rStrm.ReadByteString( b16BitLen );
+ break;
+
+ case EXC_BIFF8:
+ {
+ // --- string header ---
+ sal_uInt16 nChars = b16BitLen ? rStrm.ReaduInt16() : rStrm.ReaduInt8();
+ sal_uInt8 nFlagField = 0;
+ if( nChars || !( nFlags & XclStrFlags::SmartFlags ) )
+ nFlagField = rStrm.ReaduInt8();
+
+ bool b16Bit, bRich, bFarEast;
+ sal_uInt16 nRunCount;
+ sal_uInt32 nExtInf;
+ rStrm.ReadUniStringExtHeader( b16Bit, bRich, bFarEast, nRunCount, nExtInf, nFlagField );
+ // ignore the flags, they may be wrong
+
+ // --- character array ---
+ maString = rStrm.ReadRawUniString( nChars, b16Bit );
+
+ // --- formatting ---
+ if (nRunCount)
+ ReadFormats( rStrm, maFormats, nRunCount );
+
+ // --- extended (FarEast) information ---
+ rStrm.Ignore( nExtInf );
+ }
+ break;
+
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+void XclImpString::AppendFormat( XclFormatRunVec& rFormats, sal_uInt16 nChar, sal_uInt16 nFontIdx )
+{
+ // #i33341# real life -- same character index may occur several times
+ OSL_ENSURE( rFormats.empty() || (rFormats.back().mnChar <= nChar), "XclImpString::AppendFormat - wrong char order" );
+ if( rFormats.empty() || (rFormats.back().mnChar < nChar) )
+ rFormats.emplace_back( nChar, nFontIdx );
+ else
+ rFormats.back().mnFontIdx = nFontIdx;
+}
+
+void XclImpString::ReadFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats )
+{
+ bool bBiff8 = rStrm.GetRoot().GetBiff() == EXC_BIFF8;
+ sal_uInt16 nRunCount = bBiff8 ? rStrm.ReaduInt16() : rStrm.ReaduInt8();
+ ReadFormats( rStrm, rFormats, nRunCount );
+}
+
+void XclImpString::ReadFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats, sal_uInt16 nRunCount )
+{
+ rFormats.clear();
+
+ size_t nElementSize = rStrm.GetRoot().GetBiff() == EXC_BIFF8 ? 4 : 2;
+ size_t nAvailableBytes = rStrm.GetRecLeft();
+ size_t nMaxElements = nAvailableBytes / nElementSize;
+ if (nRunCount > nMaxElements)
+ {
+ SAL_WARN("sc.filter", "XclImpString::ReadFormats - more formats claimed than stream could contain");
+ rStrm.SetSvStreamError(SVSTREAM_FILEFORMAT_ERROR);
+ return;
+ }
+
+ rFormats.reserve( nRunCount );
+ /* #i33341# real life -- same character index may occur several times
+ -> use AppendFormat() to validate formats */
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ {
+ for( sal_uInt16 nIdx = 0; nIdx < nRunCount; ++nIdx )
+ {
+ sal_uInt16 nChar = rStrm.ReaduInt16();
+ sal_uInt16 nFontIdx = rStrm.ReaduInt16();
+ AppendFormat( rFormats, nChar, nFontIdx );
+ }
+ }
+ else
+ {
+ for( sal_uInt16 nIdx = 0; nIdx < nRunCount; ++nIdx )
+ {
+ sal_uInt8 nChar = rStrm.ReaduInt8();
+ sal_uInt8 nFontIdx = rStrm.ReaduInt8();
+ AppendFormat( rFormats, nChar, nFontIdx );
+ }
+ }
+}
+
+void XclImpString::ReadObjFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats, sal_uInt16 nFormatSize )
+{
+ // number of formatting runs, each takes 8 bytes
+ sal_uInt16 nRunCount = nFormatSize / 8;
+ rFormats.clear();
+ rFormats.reserve( nRunCount );
+ for( sal_uInt16 nIdx = 0; nIdx < nRunCount; ++nIdx )
+ {
+ sal_uInt16 nChar = rStrm.ReaduInt16();
+ sal_uInt16 nFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ AppendFormat( rFormats, nChar, nFontIdx );
+ }
+}
+
+// String iterator ============================================================
+
+XclImpStringIterator::XclImpStringIterator( const XclImpString& rString ) :
+ mrText( rString.GetText() ),
+ mrFormats( rString.GetFormats() ),
+ mnPortion( 0 ),
+ mnTextBeg( 0 ),
+ mnTextEnd( 0 ),
+ mnFormatsBeg( 0 ),
+ mnFormatsEnd( 0 )
+{
+ // first portion is formatted, adjust vector index to next portion
+ if( !mrFormats.empty() && (mrFormats.front().mnChar == 0) )
+ ++mnFormatsEnd;
+ // find end position of the first portion
+ mnTextEnd = (mnFormatsEnd < mrFormats.size() ?
+ mrFormats[ mnFormatsEnd ].mnChar : mrText.getLength() );
+}
+
+OUString XclImpStringIterator::GetPortionText() const
+{
+ return mrText.copy( mnTextBeg, mnTextEnd - mnTextBeg );
+}
+
+sal_uInt16 XclImpStringIterator::GetPortionFont() const
+{
+ return (mnFormatsBeg < mnFormatsEnd) ? mrFormats[ mnFormatsBeg ].mnFontIdx : EXC_FONT_NOTFOUND;
+}
+
+XclImpStringIterator& XclImpStringIterator::operator++()
+{
+ if( Is() )
+ {
+ ++mnPortion;
+ do
+ {
+ // indexes into vector of formatting runs
+ if( mnFormatsBeg < mnFormatsEnd )
+ ++mnFormatsBeg;
+ if( mnFormatsEnd < mrFormats.size() )
+ ++mnFormatsEnd;
+ // character positions of next portion
+ mnTextBeg = mnTextEnd;
+ mnTextEnd = (mnFormatsEnd < mrFormats.size()) ?
+ mrFormats[ mnFormatsEnd ].mnChar : mrText.getLength();
+ }
+ while( Is() && (mnTextBeg == mnTextEnd) );
+ }
+ return *this;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xistyle.cxx b/sc/source/filter/excel/xistyle.cxx
new file mode 100644
index 000000000..7361c7ee6
--- /dev/null
+++ b/sc/source/filter/excel/xistyle.cxx
@@ -0,0 +1,2084 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <xistyle.hxx>
+#include <sfx2/objsh.hxx>
+#include <svtools/ctrltool.hxx>
+#include <editeng/editobj.hxx>
+#include <scitems.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/editids.hrc>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <vcl/outdev.hxx>
+#include <document.hxx>
+#include <documentimport.hxx>
+#include <docpool.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <attarray.hxx>
+#include <xladdress.hxx>
+#include <xlcontent.hxx>
+#include <xltracer.hxx>
+#include <xltools.hxx>
+#include <xistream.hxx>
+#include <xicontent.hxx>
+
+#include <root.hxx>
+#include <colrowst.hxx>
+
+#include <string_view>
+#include <vector>
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <svl/numformat.hxx>
+#include <o3tl/string_view.hxx>
+
+using ::std::vector;
+using namespace ::com::sun::star;
+
+typedef ::cppu::WeakImplHelper< container::XIndexAccess > XIndexAccess_BASE;
+typedef ::std::vector< Color > ColorVec;
+
+namespace {
+
+class PaletteIndex : public XIndexAccess_BASE
+{
+public:
+ explicit PaletteIndex( ColorVec&& rColorTable ) : maColor( std::move(rColorTable) ) {}
+
+ // Methods XIndexAccess
+ virtual ::sal_Int32 SAL_CALL getCount() override
+ {
+ return maColor.size();
+ }
+
+ virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
+ {
+ //--Index; // apparently the palette is already 1 based
+ return uno::Any( sal_Int32( maColor[ Index ] ) );
+ }
+
+ // Methods XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override
+ {
+ return ::cppu::UnoType<sal_Int32>::get();
+ }
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ return (!maColor.empty());
+ }
+
+private:
+ ColorVec maColor;
+};
+
+}
+
+void
+XclImpPalette::ExportPalette()
+{
+ SfxObjectShell* pDocShell = mrRoot.GetDocShell();
+ if(!pDocShell)
+ return;
+
+ // copy values in color palette
+ sal_Int16 nColors = maColorTable.size();
+ ColorVec aColors;
+ aColors.resize( nColors );
+ for( sal_uInt16 nIndex = 0; nIndex < nColors; ++nIndex )
+ aColors[ nIndex ] = GetColor( nIndex );
+
+ uno::Reference< beans::XPropertySet > xProps( pDocShell->GetModel(), uno::UNO_QUERY );
+ if ( xProps.is() )
+ {
+ uno::Reference< container::XIndexAccess > xIndex( new PaletteIndex( std::move(aColors) ) );
+ xProps->setPropertyValue( "ColorPalette", uno::Any( xIndex ) );
+ }
+
+}
+// PALETTE record - color information =========================================
+
+XclImpPalette::XclImpPalette( const XclImpRoot& rRoot ) :
+ XclDefaultPalette( rRoot ), mrRoot( rRoot )
+{
+}
+
+void XclImpPalette::Initialize()
+{
+ maColorTable.clear();
+}
+
+Color XclImpPalette::GetColor( sal_uInt16 nXclIndex ) const
+{
+ if( nXclIndex >= EXC_COLOR_USEROFFSET )
+ {
+ sal_uInt32 nIx = nXclIndex - EXC_COLOR_USEROFFSET;
+ if( nIx < maColorTable.size() )
+ return maColorTable[ nIx ];
+ }
+ return GetDefColor( nXclIndex );
+}
+
+void XclImpPalette::ReadPalette( XclImpStream& rStrm )
+{
+ sal_uInt16 nCount;
+ nCount = rStrm.ReaduInt16();
+
+ const size_t nMinRecordSize = 4;
+ const size_t nMaxRecords = rStrm.GetRecLeft() / nMinRecordSize;
+ if (nCount > nMaxRecords)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nCount << " claimed, truncating");
+ nCount = nMaxRecords;
+ }
+
+ maColorTable.resize( nCount );
+ Color aColor;
+ for( sal_uInt16 nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ rStrm >> aColor;
+ maColorTable[ nIndex ] = aColor;
+ }
+ ExportPalette();
+}
+
+// FONT record - font information =============================================
+XclImpFont::XclImpFont( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mbHasCharSet( false ),
+ mbHasWstrn( true ),
+ mbHasAsian( false ),
+ mbHasCmplx( false )
+{
+ SetAllUsedFlags( false );
+}
+
+XclImpFont::XclImpFont( const XclImpRoot& rRoot, const XclFontData& rFontData ) :
+ XclImpRoot( rRoot )
+{
+ SetFontData( rFontData, false );
+}
+
+void XclImpFont::SetAllUsedFlags( bool bUsed )
+{
+ mbFontNameUsed = mbHeightUsed = mbColorUsed = mbWeightUsed = mbEscapemUsed =
+ mbUnderlUsed = mbItalicUsed = mbStrikeUsed = mbOutlineUsed = mbShadowUsed = bUsed;
+}
+
+void XclImpFont::SetFontData( const XclFontData& rFontData, bool bHasCharSet )
+{
+ maData = rFontData;
+ mbHasCharSet = bHasCharSet;
+ if( !maData.maStyle.isEmpty() )
+ {
+ if( SfxObjectShell* pDocShell = GetDocShell() )
+ {
+ if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
+ pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
+ {
+ if( const FontList* pFontList = pInfoItem->GetFontList() )
+ {
+ FontMetric aFontMetric( pFontList->Get( maData.maName, maData.maStyle ) );
+ maData.SetScWeight( aFontMetric.GetWeight() );
+ maData.SetScPosture( aFontMetric.GetItalic() );
+ }
+ }
+ }
+ maData.maStyle.clear();
+ }
+ GuessScriptType();
+ SetAllUsedFlags( true );
+}
+
+rtl_TextEncoding XclImpFont::GetFontEncoding() const
+{
+ // #i63105# use text encoding from FONT record
+ // #i67768# BIFF2-BIFF4 FONT records do not contain character set
+ rtl_TextEncoding eFontEnc = mbHasCharSet ? maData.GetFontEncoding() : GetTextEncoding();
+ return (eFontEnc == RTL_TEXTENCODING_DONTKNOW) ? GetTextEncoding() : eFontEnc;
+}
+
+void XclImpFont::ReadFont( XclImpStream& rStrm )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ ReadFontData2( rStrm );
+ ReadFontName2( rStrm );
+ break;
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ ReadFontData2( rStrm );
+ ReadFontColor( rStrm );
+ ReadFontName2( rStrm );
+ break;
+ case EXC_BIFF5:
+ ReadFontData5( rStrm );
+ ReadFontName2( rStrm );
+ break;
+ case EXC_BIFF8:
+ ReadFontData5( rStrm );
+ ReadFontName8( rStrm );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ return;
+ }
+ GuessScriptType();
+ SetAllUsedFlags( true );
+}
+
+void XclImpFont::ReadEfont( XclImpStream& rStrm )
+{
+ ReadFontColor( rStrm );
+}
+
+void XclImpFont::ReadCFFontBlock( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
+ if( GetBiff() != EXC_BIFF8 )
+ return;
+
+ rStrm.Ignore( 64 );
+ sal_uInt32 nHeight = rStrm.ReaduInt32();
+ sal_uInt32 nStyle = rStrm.ReaduInt32();
+ sal_uInt16 nWeight = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 ); //nEscapem
+ sal_uInt8 nUnderl = rStrm.ReaduInt8();
+ rStrm.Ignore( 3 );
+ sal_uInt32 nColor = rStrm.ReaduInt32();
+ rStrm.Ignore( 4 );
+ sal_uInt32 nFontFlags1 = rStrm.ReaduInt32();
+ rStrm.Ignore( 4 ); //nFontFlags2
+ sal_uInt32 nFontFlags3 = rStrm.ReaduInt32();
+ rStrm.Ignore( 18 );
+
+ if( (mbHeightUsed = (nHeight <= 0x7FFF)) )
+ maData.mnHeight = static_cast< sal_uInt16 >( nHeight );
+ if( (mbWeightUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STYLE ) && (nWeight < 0x7FFF)) )
+ maData.mnWeight = nWeight;
+ if( (mbItalicUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STYLE )) )
+ maData.mbItalic = ::get_flag( nStyle, EXC_CF_FONT_STYLE );
+ if( (mbUnderlUsed = !::get_flag( nFontFlags3, EXC_CF_FONT_UNDERL ) && (nUnderl <= 0x7F)) )
+ maData.mnUnderline = nUnderl;
+ if( (mbColorUsed = (nColor <= 0x7FFF)) )
+ maData.maColor = GetPalette().GetColor( static_cast< sal_uInt16 >( nColor ) );
+ if( (mbStrikeUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT )) )
+ maData.mbStrikeout = ::get_flag( nStyle, EXC_CF_FONT_STRIKEOUT );
+}
+
+void XclImpFont::FillToItemSet( SfxItemSet& rItemSet, XclFontItemType eType, bool bSkipPoolDefs ) const
+{
+ // true = edit engine Which-IDs (EE_CHAR_*); false = Calc Which-IDs (ATTR_*)
+ bool bEE = eType != XclFontItemType::Cell;
+
+// item = the item to put into the item set
+// sc_which = the Calc Which-ID of the item
+// ee_which = the edit engine Which-ID of the item
+#define PUTITEM( item, sc_which, ee_which ) \
+ ScfTools::PutItem( rItemSet, item, (bEE ? (static_cast<sal_uInt16>(ee_which)) : (sc_which)), bSkipPoolDefs )
+
+// Font item
+ if( mbFontNameUsed )
+ {
+ rtl_TextEncoding eFontEnc = maData.GetFontEncoding();
+ rtl_TextEncoding eTempTextEnc = (bEE && (eFontEnc == GetTextEncoding())) ?
+ ScfTools::GetSystemTextEncoding() : eFontEnc;
+
+ //add corresponding pitch for FontFamily
+ FontPitch ePitch = PITCH_DONTKNOW;
+ FontFamily eFtFamily = maData.GetScFamily( GetTextEncoding() );
+ switch( eFtFamily ) //refer http://msdn.microsoft.com/en-us/library/aa246306(v=VS.60).aspx
+ {
+ case FAMILY_ROMAN: ePitch = PITCH_VARIABLE; break;
+ case FAMILY_SWISS: ePitch = PITCH_VARIABLE; break;
+ case FAMILY_MODERN: ePitch = PITCH_FIXED; break;
+ default: break;
+ }
+ SvxFontItem aFontItem( eFtFamily , maData.maName, OUString(), ePitch, eTempTextEnc, ATTR_FONT );
+
+ // set only for valid script types
+ if( mbHasWstrn )
+ PUTITEM( aFontItem, ATTR_FONT, EE_CHAR_FONTINFO );
+ if( mbHasAsian )
+ PUTITEM( aFontItem, ATTR_CJK_FONT, EE_CHAR_FONTINFO_CJK );
+ if( mbHasCmplx )
+ PUTITEM( aFontItem, ATTR_CTL_FONT, EE_CHAR_FONTINFO_CTL );
+ }
+
+// Font height (for all script types)
+ if( mbHeightUsed )
+ {
+ sal_Int32 nHeight = maData.mnHeight;
+ if( bEE && (eType != XclFontItemType::HeaderFooter) ) // do not convert header/footer height
+ nHeight = convertTwipToMm100(nHeight);
+
+ SvxFontHeightItem aHeightItem( nHeight, 100, ATTR_FONT_HEIGHT );
+ PUTITEM( aHeightItem, ATTR_FONT_HEIGHT, EE_CHAR_FONTHEIGHT );
+ PUTITEM( aHeightItem, ATTR_CJK_FONT_HEIGHT, EE_CHAR_FONTHEIGHT_CJK );
+ PUTITEM( aHeightItem, ATTR_CTL_FONT_HEIGHT, EE_CHAR_FONTHEIGHT_CTL );
+ }
+
+// Font color - pass AUTO_COL to item
+ if( mbColorUsed )
+ PUTITEM( SvxColorItem( maData.maColor, ATTR_FONT_COLOR ), ATTR_FONT_COLOR, EE_CHAR_COLOR );
+
+// Font weight (for all script types)
+ if( mbWeightUsed )
+ {
+ SvxWeightItem aWeightItem( maData.GetScWeight(), ATTR_FONT_WEIGHT );
+ PUTITEM( aWeightItem, ATTR_FONT_WEIGHT, EE_CHAR_WEIGHT );
+ PUTITEM( aWeightItem, ATTR_CJK_FONT_WEIGHT, EE_CHAR_WEIGHT_CJK );
+ PUTITEM( aWeightItem, ATTR_CTL_FONT_WEIGHT, EE_CHAR_WEIGHT_CTL );
+ }
+
+// Font underline
+ if( mbUnderlUsed )
+ {
+ SvxUnderlineItem aUnderlItem( maData.GetScUnderline(), ATTR_FONT_UNDERLINE );
+ PUTITEM( aUnderlItem, ATTR_FONT_UNDERLINE, EE_CHAR_UNDERLINE );
+ }
+
+// Font posture (for all script types)
+ if( mbItalicUsed )
+ {
+ SvxPostureItem aPostItem( maData.GetScPosture(), ATTR_FONT_POSTURE );
+ PUTITEM( aPostItem, ATTR_FONT_POSTURE, EE_CHAR_ITALIC );
+ PUTITEM( aPostItem, ATTR_CJK_FONT_POSTURE, EE_CHAR_ITALIC_CJK );
+ PUTITEM( aPostItem, ATTR_CTL_FONT_POSTURE, EE_CHAR_ITALIC_CTL );
+ }
+
+// Boolean attributes crossed out, contoured, shadowed
+ if( mbStrikeUsed )
+ PUTITEM( SvxCrossedOutItem( maData.GetScStrikeout(), ATTR_FONT_CROSSEDOUT ), ATTR_FONT_CROSSEDOUT, EE_CHAR_STRIKEOUT );
+ if( mbOutlineUsed )
+ PUTITEM( SvxContourItem( maData.mbOutline, ATTR_FONT_CONTOUR ), ATTR_FONT_CONTOUR, EE_CHAR_OUTLINE );
+ if( mbShadowUsed )
+ PUTITEM( SvxShadowedItem( maData.mbShadow, ATTR_FONT_SHADOWED ), ATTR_FONT_SHADOWED, EE_CHAR_SHADOW );
+
+// Super-/subscript: only on edit engine objects
+ if( mbEscapemUsed && bEE )
+ rItemSet.Put( SvxEscapementItem( maData.GetScEscapement(), EE_CHAR_ESCAPEMENT ) );
+
+#undef PUTITEM
+}
+
+void XclImpFont::WriteFontProperties( ScfPropertySet& rPropSet,
+ XclFontPropSetType eType, const Color* pFontColor ) const
+{
+ GetFontPropSetHelper().WriteFontProperties(
+ rPropSet, eType, maData, mbHasWstrn, mbHasAsian, mbHasCmplx, pFontColor );
+}
+
+void XclImpFont::ReadFontData2( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ maData.mnHeight = rStrm.ReaduInt16();
+ nFlags = rStrm.ReaduInt16();
+
+ maData.mnWeight = ::get_flagvalue( nFlags, EXC_FONTATTR_BOLD, EXC_FONTWGHT_BOLD, EXC_FONTWGHT_NORMAL );
+ maData.mnUnderline = ::get_flagvalue( nFlags, EXC_FONTATTR_UNDERLINE, EXC_FONTUNDERL_SINGLE, EXC_FONTUNDERL_NONE );
+ maData.mbItalic = ::get_flag( nFlags, EXC_FONTATTR_ITALIC );
+ maData.mbStrikeout = ::get_flag( nFlags, EXC_FONTATTR_STRIKEOUT );
+ maData.mbOutline = ::get_flag( nFlags, EXC_FONTATTR_OUTLINE );
+ maData.mbShadow = ::get_flag( nFlags, EXC_FONTATTR_SHADOW );
+ mbHasCharSet = false;
+}
+
+void XclImpFont::ReadFontData5( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags;
+
+ maData.mnHeight = rStrm.ReaduInt16();
+ nFlags = rStrm.ReaduInt16();
+ ReadFontColor( rStrm );
+ maData.mnWeight = rStrm.ReaduInt16();
+ maData.mnEscapem = rStrm.ReaduInt16();
+ maData.mnUnderline = rStrm.ReaduInt8();
+ maData.mnFamily = rStrm.ReaduInt8();
+ maData.mnCharSet = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+
+ maData.mbItalic = ::get_flag( nFlags, EXC_FONTATTR_ITALIC );
+ maData.mbStrikeout = ::get_flag( nFlags, EXC_FONTATTR_STRIKEOUT );
+ maData.mbOutline = ::get_flag( nFlags, EXC_FONTATTR_OUTLINE );
+ maData.mbShadow = ::get_flag( nFlags, EXC_FONTATTR_SHADOW );
+ mbHasCharSet = maData.mnCharSet != 0;
+}
+
+void XclImpFont::ReadFontColor( XclImpStream& rStrm )
+{
+ maData.maColor = GetPalette().GetColor( rStrm.ReaduInt16() );
+}
+
+void XclImpFont::ReadFontName2( XclImpStream& rStrm )
+{
+ maData.maName = rStrm.ReadByteString( false );
+}
+
+void XclImpFont::ReadFontName8( XclImpStream& rStrm )
+{
+ maData.maName = rStrm.ReadUniString( rStrm.ReaduInt8() );
+}
+
+void XclImpFont::GuessScriptType()
+{
+ mbHasWstrn = true;
+ mbHasAsian = mbHasCmplx = false;
+
+ // find the script types for which the font contains characters
+ OutputDevice* pPrinter = GetPrinter();
+ if(!pPrinter)
+ return;
+
+ vcl::Font aFont( maData.maName, Size( 0, 10 ) );
+ FontCharMapRef xFontCharMap;
+
+ pPrinter->SetFont( aFont );
+ if( !pPrinter->GetFontCharMap( xFontCharMap ) )
+ return;
+
+ // CJK fonts
+ mbHasAsian =
+ xFontCharMap->HasChar( 0x3041 ) || // 3040-309F: Hiragana
+ xFontCharMap->HasChar( 0x30A1 ) || // 30A0-30FF: Katakana
+ xFontCharMap->HasChar( 0x3111 ) || // 3100-312F: Bopomofo
+ xFontCharMap->HasChar( 0x3131 ) || // 3130-318F: Hangul Compatibility Jamo
+ xFontCharMap->HasChar( 0x3301 ) || // 3300-33FF: CJK Compatibility
+ xFontCharMap->HasChar( 0x3401 ) || // 3400-4DBF: CJK Unified Ideographs Extension A
+ xFontCharMap->HasChar( 0x4E01 ) || // 4E00-9FFF: CJK Unified Ideographs
+ xFontCharMap->HasChar( 0x7E01 ) || // 4E00-9FFF: CJK Unified Ideographs
+ xFontCharMap->HasChar( 0xA001 ) || // A001-A48F: Yi Syllables
+ xFontCharMap->HasChar( 0xAC01 ) || // AC00-D7AF: Hangul Syllables
+ xFontCharMap->HasChar( 0xCC01 ) || // AC00-D7AF: Hangul Syllables
+ xFontCharMap->HasChar( 0xF901 ) || // F900-FAFF: CJK Compatibility Ideographs
+ xFontCharMap->HasChar( 0xFF71 ); // FF00-FFEF: Halfwidth/Fullwidth Forms
+ // CTL fonts
+ mbHasCmplx =
+ xFontCharMap->HasChar( 0x05D1 ) || // 0590-05FF: Hebrew
+ xFontCharMap->HasChar( 0x0631 ) || // 0600-06FF: Arabic
+ xFontCharMap->HasChar( 0x0721 ) || // 0700-074F: Syriac
+ xFontCharMap->HasChar( 0x0911 ) || // 0900-0DFF: Indic scripts
+ xFontCharMap->HasChar( 0x0E01 ) || // 0E00-0E7F: Thai
+ xFontCharMap->HasChar( 0xFB21 ) || // FB1D-FB4F: Hebrew Presentation Forms
+ xFontCharMap->HasChar( 0xFB51 ) || // FB50-FDFF: Arabic Presentation Forms-A
+ xFontCharMap->HasChar( 0xFE71 ); // FE70-FEFF: Arabic Presentation Forms-B
+ // Western fonts
+ mbHasWstrn = (!mbHasAsian && !mbHasCmplx) || xFontCharMap->HasChar( 'A' );
+}
+
+XclImpFontBuffer::XclImpFontBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maFont4( rRoot ),
+ maCtrlFont( rRoot )
+{
+ Initialize();
+
+ // default font for form controls without own font information
+ XclFontData aCtrlFontData;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ aCtrlFontData.maName = "Helv";
+ aCtrlFontData.mnHeight = 160;
+ aCtrlFontData.mnWeight = EXC_FONTWGHT_BOLD;
+ break;
+ case EXC_BIFF8:
+ aCtrlFontData.maName = "Tahoma";
+ aCtrlFontData.mnHeight = 160;
+ aCtrlFontData.mnWeight = EXC_FONTWGHT_NORMAL;
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+ maCtrlFont.SetFontData( aCtrlFontData, false );
+}
+
+void XclImpFontBuffer::Initialize()
+{
+ maFontList.clear();
+
+ // application font for column width calculation, later filled with first font from font list
+ XclFontData aAppFontData;
+ aAppFontData.maName = "Arial";
+ aAppFontData.mnHeight = 200;
+ aAppFontData.mnWeight = EXC_FONTWGHT_NORMAL;
+ UpdateAppFont( aAppFontData, false );
+}
+
+const XclImpFont* XclImpFontBuffer::GetFont( sal_uInt16 nFontIndex ) const
+{
+ /* Font with index 4 is not stored in an Excel file, but used e.g. by
+ BIFF5 form pushbutton objects. It is the bold default font.
+ This also means that entries above 4 are out by one in the list. */
+
+ if (nFontIndex == 4)
+ return &maFont4;
+
+ if (nFontIndex < 4)
+ {
+ // Font ID is zero-based when it's less than 4.
+ return nFontIndex >= maFontList.size() ? nullptr : &maFontList[nFontIndex];
+ }
+
+ // Font ID is greater than 4. It is now 1-based.
+ return nFontIndex > maFontList.size() ? nullptr : &maFontList[nFontIndex-1];
+}
+
+void XclImpFontBuffer::ReadFont( XclImpStream& rStrm )
+{
+ maFontList.emplace_back( GetRoot() );
+ XclImpFont& rFont = maFontList.back();
+ rFont.ReadFont( rStrm );
+
+ if( maFontList.size() == 1 )
+ {
+ UpdateAppFont( rFont.GetFontData(), rFont.HasCharSet() );
+ }
+}
+
+void XclImpFontBuffer::ReadEfont( XclImpStream& rStrm )
+{
+ if( !maFontList.empty() )
+ maFontList.back().ReadEfont( rStrm );
+}
+
+void XclImpFontBuffer::FillToItemSet(
+ SfxItemSet& rItemSet, XclFontItemType eType,
+ sal_uInt16 nFontIdx, bool bSkipPoolDefs ) const
+{
+ if( const XclImpFont* pFont = GetFont( nFontIdx ) )
+ pFont->FillToItemSet( rItemSet, eType, bSkipPoolDefs );
+}
+
+void XclImpFontBuffer::WriteFontProperties( ScfPropertySet& rPropSet,
+ XclFontPropSetType eType, sal_uInt16 nFontIdx, const Color* pFontColor ) const
+{
+ if( const XclImpFont* pFont = GetFont( nFontIdx ) )
+ pFont->WriteFontProperties( rPropSet, eType, pFontColor );
+}
+
+void XclImpFontBuffer::WriteDefaultCtrlFontProperties( ScfPropertySet& rPropSet ) const
+{
+ maCtrlFont.WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL );
+}
+
+void XclImpFontBuffer::UpdateAppFont( const XclFontData& rFontData, bool bHasCharSet )
+{
+ maAppFont = rFontData;
+ // #i3006# Calculate the width of '0' from first font and current printer.
+ SetCharWidth( maAppFont );
+
+ // font 4 is bold font 0
+ XclFontData aFont4Data( maAppFont );
+ aFont4Data.mnWeight = EXC_FONTWGHT_BOLD;
+ maFont4.SetFontData( aFont4Data, bHasCharSet );
+}
+
+// FORMAT record - number formats =============================================
+
+XclImpNumFmtBuffer::XclImpNumFmtBuffer( const XclImpRoot& rRoot ) :
+ XclNumFmtBuffer( rRoot ),
+ XclImpRoot( rRoot ),
+ mnNextXclIdx( 0 )
+{
+}
+
+void XclImpNumFmtBuffer::Initialize()
+{
+ maIndexMap.clear();
+ mnNextXclIdx = 0;
+ InitializeImport(); // base class
+}
+
+void XclImpNumFmtBuffer::ReadFormat( XclImpStream& rStrm )
+{
+ OUString aFormat;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ aFormat = rStrm.ReadByteString( false );
+ break;
+
+ case EXC_BIFF4:
+ rStrm.Ignore( 2 ); // in BIFF4 the index field exists, but is undefined
+ aFormat = rStrm.ReadByteString( false );
+ break;
+
+ case EXC_BIFF5:
+ mnNextXclIdx = rStrm.ReaduInt16();
+ aFormat = rStrm.ReadByteString( false );
+ break;
+
+ case EXC_BIFF8:
+ mnNextXclIdx = rStrm.ReaduInt16();
+ aFormat = rStrm.ReadUniString();
+ break;
+
+ default:
+ DBG_ERROR_BIFF();
+ return;
+ }
+
+ if( mnNextXclIdx < 0xFFFF )
+ {
+ InsertFormat( mnNextXclIdx, aFormat );
+ ++mnNextXclIdx;
+ }
+}
+
+sal_uInt16 XclImpNumFmtBuffer::ReadCFFormat( XclImpStream& rStrm, bool bIFmt )
+{
+ // internal number format ?
+ if(bIFmt)
+ {
+ rStrm.Ignore(1);
+ sal_uInt8 nIndex;
+ nIndex = rStrm.ReaduInt8();
+ return nIndex;
+ }
+ else
+ {
+ OUString aFormat = rStrm.ReadUniString();
+ InsertFormat( mnNextXclIdx, aFormat );
+ ++mnNextXclIdx;
+ return mnNextXclIdx - 1;
+ }
+}
+
+void XclImpNumFmtBuffer::CreateScFormats()
+{
+ OSL_ENSURE( maIndexMap.empty(), "XclImpNumFmtBuffer::CreateScFormats - already created" );
+
+ SvNumberFormatter& rFormatter = GetFormatter();
+ for( const auto& [rXclNumFmt, rNumFmt] : GetFormatMap() )
+ {
+ // insert/convert the Excel number format
+ sal_uInt32 nKey;
+ if( !rNumFmt.maFormat.isEmpty() )
+ {
+ OUString aFormat( rNumFmt.maFormat );
+ sal_Int32 nCheckPos;
+ SvNumFormatType nType = SvNumFormatType::DEFINED;
+ rFormatter.PutandConvertEntry( aFormat, nCheckPos,
+ nType, nKey, LANGUAGE_ENGLISH_US, rNumFmt.meLanguage, false);
+ }
+ else
+ nKey = rFormatter.GetFormatIndex( rNumFmt.meOffset, rNumFmt.meLanguage );
+
+ // insert the resulting format key into the Excel->Calc index map
+ maIndexMap[ rXclNumFmt ] = nKey;
+ }
+}
+
+sal_uInt32 XclImpNumFmtBuffer::GetScFormat( sal_uInt16 nXclNumFmt ) const
+{
+ XclImpIndexMap::const_iterator aIt = maIndexMap.find( nXclNumFmt );
+ return (aIt != maIndexMap.end()) ? aIt->second : NUMBERFORMAT_ENTRY_NOT_FOUND;
+}
+
+void XclImpNumFmtBuffer::FillToItemSet( SfxItemSet& rItemSet, sal_uInt16 nXclNumFmt, bool bSkipPoolDefs ) const
+{
+ sal_uInt32 nScNumFmt = GetScFormat( nXclNumFmt );
+ if( nScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND )
+ nScNumFmt = GetStdScNumFmt();
+ FillScFmtToItemSet( rItemSet, nScNumFmt, bSkipPoolDefs );
+}
+
+void XclImpNumFmtBuffer::FillScFmtToItemSet( SfxItemSet& rItemSet, sal_uInt32 nScNumFmt, bool bSkipPoolDefs ) const
+{
+ OSL_ENSURE( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND, "XclImpNumFmtBuffer::FillScFmtToItemSet - invalid number format" );
+ ScfTools::PutItem( rItemSet, SfxUInt32Item( ATTR_VALUE_FORMAT, nScNumFmt ), bSkipPoolDefs );
+ if( rItemSet.GetItemState( ATTR_VALUE_FORMAT, false ) == SfxItemState::SET )
+ ScGlobal::AddLanguage( rItemSet, GetFormatter() );
+}
+
+// XF, STYLE record - Cell formatting =========================================
+
+void XclImpCellProt::FillFromXF2( sal_uInt8 nNumFmt )
+{
+ mbLocked = ::get_flag( nNumFmt, EXC_XF2_LOCKED );
+ mbHidden = ::get_flag( nNumFmt, EXC_XF2_HIDDEN );
+}
+
+void XclImpCellProt::FillFromXF3( sal_uInt16 nProt )
+{
+ mbLocked = ::get_flag( nProt, EXC_XF_LOCKED );
+ mbHidden = ::get_flag( nProt, EXC_XF_HIDDEN );
+}
+
+void XclImpCellProt::FillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ ScfTools::PutItem( rItemSet, ScProtectionAttr( mbLocked, mbHidden ), bSkipPoolDefs );
+}
+
+void XclImpCellAlign::FillFromXF2( sal_uInt8 nFlags )
+{
+ mnHorAlign = ::extract_value< sal_uInt8 >( nFlags, 0, 3 );
+}
+
+void XclImpCellAlign::FillFromXF3( sal_uInt16 nAlign )
+{
+ mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 );
+ mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK ); // new in BIFF3
+}
+
+void XclImpCellAlign::FillFromXF4( sal_uInt16 nAlign )
+{
+ FillFromXF3( nAlign );
+ mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 2 ); // new in BIFF4
+ mnOrient = ::extract_value< sal_uInt8 >( nAlign, 6, 2 ); // new in BIFF4
+}
+
+void XclImpCellAlign::FillFromXF5( sal_uInt16 nAlign )
+{
+ mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 );
+ mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 );
+ mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK );
+ mnOrient = ::extract_value< sal_uInt8 >( nAlign, 8, 2 );
+}
+
+void XclImpCellAlign::FillFromXF8( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib )
+{
+ mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 );
+ mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 );
+ mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK );
+ mnRotation = ::extract_value< sal_uInt8 >( nAlign, 8, 8 ); // new in BIFF8
+ mnIndent = ::extract_value< sal_uInt8 >( nMiscAttrib, 0, 4 ); // new in BIFF8
+ mbShrink = ::get_flag( nMiscAttrib, EXC_XF8_SHRINK ); // new in BIFF8
+ mnTextDir = ::extract_value< sal_uInt8 >( nMiscAttrib, 6, 2 ); // new in BIFF8
+}
+
+void XclImpCellAlign::FillFromCF( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib )
+{
+ mnHorAlign = extract_value< sal_uInt8 >( nAlign, 0, 3 );
+ mbLineBreak = get_flag< sal_uInt8 >( nAlign, EXC_XF_LINEBREAK );
+ mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 );
+ mnRotation = ::extract_value< sal_uInt8 >( nAlign, 8, 8 );
+ mnIndent = ::extract_value< sal_uInt8 >( nMiscAttrib, 0, 4 );
+ mbShrink = ::get_flag( nMiscAttrib, EXC_XF8_SHRINK );
+ mnTextDir = ::extract_value< sal_uInt8 >( nMiscAttrib, 6, 2 );
+}
+
+void XclImpCellAlign::FillToItemSet( SfxItemSet& rItemSet, const XclImpFont* pFont, bool bSkipPoolDefs ) const
+{
+ // horizontal alignment
+ ScfTools::PutItem( rItemSet, SvxHorJustifyItem( GetScHorAlign(), ATTR_HOR_JUSTIFY ), bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, SvxJustifyMethodItem( GetScHorJustifyMethod(), ATTR_HOR_JUSTIFY_METHOD ), bSkipPoolDefs );
+
+ // text wrap (#i74508# always if vertical alignment is justified or distributed)
+ bool bLineBreak = mbLineBreak || (mnVerAlign == EXC_XF_VER_JUSTIFY) || (mnVerAlign == EXC_XF_VER_DISTRIB);
+ ScfTools::PutItem( rItemSet, ScLineBreakCell( bLineBreak ), bSkipPoolDefs );
+
+ // vertical alignment
+ ScfTools::PutItem( rItemSet, SvxVerJustifyItem( GetScVerAlign(), ATTR_VER_JUSTIFY ), bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, SvxJustifyMethodItem( GetScVerJustifyMethod(), ATTR_VER_JUSTIFY_METHOD ), bSkipPoolDefs );
+
+ // indent
+ sal_uInt16 nScIndent = mnIndent * 200; // 1 Excel unit == 10 pt == 200 twips
+ ScfTools::PutItem( rItemSet, ScIndentItem( nScIndent ), bSkipPoolDefs );
+
+ // shrink to fit
+ ScfTools::PutItem( rItemSet, ScShrinkToFitCell( mbShrink ), bSkipPoolDefs );
+
+ // text orientation/rotation (BIFF2-BIFF7 sets mnOrient)
+ sal_uInt8 nXclRot = (mnOrient == EXC_ORIENT_NONE) ? mnRotation : XclTools::GetXclRotFromOrient( mnOrient );
+ bool bStacked = (nXclRot == EXC_ROT_STACKED);
+ ScfTools::PutItem( rItemSet, ScVerticalStackCell( bStacked ), bSkipPoolDefs );
+ // set an angle in the range from -90 to 90 degrees
+ Degree100 nAngle = XclTools::GetScRotation( nXclRot, 0_deg100 );
+ ScfTools::PutItem( rItemSet, ScRotateValueItem( nAngle ), bSkipPoolDefs );
+ // set "Use asian vertical layout", if cell is stacked and font contains CKJ characters
+ bool bAsianVert = bStacked && pFont && pFont->HasAsianChars();
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_VERTICAL_ASIAN, bAsianVert ), bSkipPoolDefs );
+
+ // CTL text direction
+ ScfTools::PutItem( rItemSet, SvxFrameDirectionItem( GetScFrameDir(), ATTR_WRITINGDIR ), bSkipPoolDefs );
+}
+
+XclImpCellBorder::XclImpCellBorder()
+{
+ SetUsedFlags( false, false );
+}
+
+void XclImpCellBorder::SetUsedFlags( bool bOuterUsed, bool bDiagUsed )
+{
+ mbLeftUsed = mbRightUsed = mbTopUsed = mbBottomUsed = bOuterUsed;
+ mbDiagUsed = bDiagUsed;
+}
+
+void XclImpCellBorder::FillFromXF2( sal_uInt8 nFlags )
+{
+ mnLeftLine = ::get_flagvalue( nFlags, EXC_XF2_LEFTLINE, EXC_LINE_THIN, EXC_LINE_NONE );
+ mnRightLine = ::get_flagvalue( nFlags, EXC_XF2_RIGHTLINE, EXC_LINE_THIN, EXC_LINE_NONE );
+ mnTopLine = ::get_flagvalue( nFlags, EXC_XF2_TOPLINE, EXC_LINE_THIN, EXC_LINE_NONE );
+ mnBottomLine = ::get_flagvalue( nFlags, EXC_XF2_BOTTOMLINE, EXC_LINE_THIN, EXC_LINE_NONE );
+ mnLeftColor = mnRightColor = mnTopColor = mnBottomColor = EXC_COLOR_BIFF2_BLACK;
+ SetUsedFlags( true, false );
+}
+
+void XclImpCellBorder::FillFromXF3( sal_uInt32 nBorder )
+{
+ mnTopLine = ::extract_value< sal_uInt8 >( nBorder, 0, 3 );
+ mnLeftLine = ::extract_value< sal_uInt8 >( nBorder, 8, 3 );
+ mnBottomLine = ::extract_value< sal_uInt8 >( nBorder, 16, 3 );
+ mnRightLine = ::extract_value< sal_uInt8 >( nBorder, 24, 3 );
+ mnTopColor = ::extract_value< sal_uInt16 >( nBorder, 3, 5 );
+ mnLeftColor = ::extract_value< sal_uInt16 >( nBorder, 11, 5 );
+ mnBottomColor = ::extract_value< sal_uInt16 >( nBorder, 19, 5 );
+ mnRightColor = ::extract_value< sal_uInt16 >( nBorder, 27, 5 );
+ SetUsedFlags( true, false );
+}
+
+void XclImpCellBorder::FillFromXF5( sal_uInt32 nBorder, sal_uInt32 nArea )
+{
+ mnTopLine = ::extract_value< sal_uInt8 >( nBorder, 0, 3 );
+ mnLeftLine = ::extract_value< sal_uInt8 >( nBorder, 3, 3 );
+ mnBottomLine = ::extract_value< sal_uInt8 >( nArea, 22, 3 );
+ mnRightLine = ::extract_value< sal_uInt8 >( nBorder, 6, 3 );
+ mnTopColor = ::extract_value< sal_uInt16 >( nBorder, 9, 7 );
+ mnLeftColor = ::extract_value< sal_uInt16 >( nBorder, 16, 7 );
+ mnBottomColor = ::extract_value< sal_uInt16 >( nArea, 25, 7 );
+ mnRightColor = ::extract_value< sal_uInt16 >( nBorder, 23, 7 );
+ SetUsedFlags( true, false );
+}
+
+void XclImpCellBorder::FillFromXF8( sal_uInt32 nBorder1, sal_uInt32 nBorder2 )
+{
+ mnLeftLine = ::extract_value< sal_uInt8 >( nBorder1, 0, 4 );
+ mnRightLine = ::extract_value< sal_uInt8 >( nBorder1, 4, 4 );
+ mnTopLine = ::extract_value< sal_uInt8 >( nBorder1, 8, 4 );
+ mnBottomLine = ::extract_value< sal_uInt8 >( nBorder1, 12, 4 );
+ mnLeftColor = ::extract_value< sal_uInt16 >( nBorder1, 16, 7 );
+ mnRightColor = ::extract_value< sal_uInt16 >( nBorder1, 23, 7 );
+ mnTopColor = ::extract_value< sal_uInt16 >( nBorder2, 0, 7 );
+ mnBottomColor = ::extract_value< sal_uInt16 >( nBorder2, 7, 7 );
+ mbDiagTLtoBR = ::get_flag( nBorder1, EXC_XF_DIAGONAL_TL_TO_BR );
+ mbDiagBLtoTR = ::get_flag( nBorder1, EXC_XF_DIAGONAL_BL_TO_TR );
+ if( mbDiagTLtoBR || mbDiagBLtoTR )
+ {
+ mnDiagLine = ::extract_value< sal_uInt8 >( nBorder2, 21, 4 );
+ mnDiagColor = ::extract_value< sal_uInt16 >( nBorder2, 14, 7 );
+ }
+ SetUsedFlags( true, true );
+}
+
+void XclImpCellBorder::FillFromCF8( sal_uInt16 nLineStyle, sal_uInt32 nLineColor, sal_uInt32 nFlags )
+{
+ mnLeftLine = ::extract_value< sal_uInt8 >( nLineStyle, 0, 4 );
+ mnRightLine = ::extract_value< sal_uInt8 >( nLineStyle, 4, 4 );
+ mnTopLine = ::extract_value< sal_uInt8 >( nLineStyle, 8, 4 );
+ mnBottomLine = ::extract_value< sal_uInt8 >( nLineStyle, 12, 4 );
+ mnLeftColor = ::extract_value< sal_uInt16 >( nLineColor, 0, 7 );
+ mnRightColor = ::extract_value< sal_uInt16 >( nLineColor, 7, 7 );
+ mnTopColor = ::extract_value< sal_uInt16 >( nLineColor, 16, 7 );
+ mnBottomColor = ::extract_value< sal_uInt16 >( nLineColor, 23, 7 );
+ mbLeftUsed = !::get_flag( nFlags, EXC_CF_BORDER_LEFT );
+ mbRightUsed = !::get_flag( nFlags, EXC_CF_BORDER_RIGHT );
+ mbTopUsed = !::get_flag( nFlags, EXC_CF_BORDER_TOP );
+ mbBottomUsed = !::get_flag( nFlags, EXC_CF_BORDER_BOTTOM );
+ mbDiagUsed = false;
+}
+
+bool XclImpCellBorder::HasAnyOuterBorder() const
+{
+ return
+ (mbLeftUsed && (mnLeftLine != EXC_LINE_NONE)) ||
+ (mbRightUsed && (mnRightLine != EXC_LINE_NONE)) ||
+ (mbTopUsed && (mnTopLine != EXC_LINE_NONE)) ||
+ (mbBottomUsed && (mnBottomLine != EXC_LINE_NONE));
+}
+
+namespace {
+
+/** Converts the passed line style to a ::editeng::SvxBorderLine, or returns false, if style is "no line". */
+bool lclConvertBorderLine( ::editeng::SvxBorderLine& rLine, const XclImpPalette& rPalette, sal_uInt8 nXclLine, sal_uInt16 nXclColor )
+{
+ static const sal_uInt16 ppnLineParam[][ 4 ] =
+ {
+ // outer width, type
+ { 0, table::BorderLineStyle::SOLID }, // 0 = none
+ { EXC_BORDER_THIN, table::BorderLineStyle::SOLID }, // 1 = thin
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::SOLID }, // 2 = medium
+ { EXC_BORDER_THIN, table::BorderLineStyle::FINE_DASHED }, // 3 = dashed
+ { EXC_BORDER_THIN, table::BorderLineStyle::DOTTED }, // 4 = dotted
+ { EXC_BORDER_THICK, table::BorderLineStyle::SOLID }, // 5 = thick
+ { EXC_BORDER_THICK, table::BorderLineStyle::DOUBLE_THIN }, // 6 = double
+ { EXC_BORDER_HAIR, table::BorderLineStyle::SOLID }, // 7 = hair
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASHED }, // 8 = med dash
+ { EXC_BORDER_THIN, table::BorderLineStyle::DASH_DOT }, // 9 = thin dashdot
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASH_DOT }, // A = med dashdot
+ { EXC_BORDER_THIN, table::BorderLineStyle::DASH_DOT_DOT }, // B = thin dashdotdot
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASH_DOT_DOT }, // C = med dashdotdot
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASH_DOT } // D = med slant dashdot
+ };
+
+ if( nXclLine == EXC_LINE_NONE )
+ return false;
+ if( nXclLine >= SAL_N_ELEMENTS( ppnLineParam ) )
+ nXclLine = EXC_LINE_THIN;
+
+ rLine.SetColor( rPalette.GetColor( nXclColor ) );
+ rLine.SetWidth( ppnLineParam[ nXclLine ][ 0 ] );
+ rLine.SetBorderLineStyle( static_cast< SvxBorderLineStyle>(
+ ppnLineParam[ nXclLine ][ 1 ]) );
+ return true;
+}
+
+} // namespace
+
+void XclImpCellBorder::FillToItemSet( SfxItemSet& rItemSet, const XclImpPalette& rPalette, bool bSkipPoolDefs ) const
+{
+ if( mbLeftUsed || mbRightUsed || mbTopUsed || mbBottomUsed )
+ {
+ SvxBoxItem aBoxItem( ATTR_BORDER );
+ ::editeng::SvxBorderLine aLine;
+ if( mbLeftUsed && lclConvertBorderLine( aLine, rPalette, mnLeftLine, mnLeftColor ) )
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::LEFT );
+ if( mbRightUsed && lclConvertBorderLine( aLine, rPalette, mnRightLine, mnRightColor ) )
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::RIGHT );
+ if( mbTopUsed && lclConvertBorderLine( aLine, rPalette, mnTopLine, mnTopColor ) )
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::TOP );
+ if( mbBottomUsed && lclConvertBorderLine( aLine, rPalette, mnBottomLine, mnBottomColor ) )
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
+ ScfTools::PutItem( rItemSet, aBoxItem, bSkipPoolDefs );
+ }
+ if( !mbDiagUsed )
+ return;
+
+ SvxLineItem aTLBRItem( ATTR_BORDER_TLBR );
+ SvxLineItem aBLTRItem( ATTR_BORDER_BLTR );
+ ::editeng::SvxBorderLine aLine;
+ if( lclConvertBorderLine( aLine, rPalette, mnDiagLine, mnDiagColor ) )
+ {
+ if( mbDiagTLtoBR )
+ aTLBRItem.SetLine( &aLine );
+ if( mbDiagBLtoTR )
+ aBLTRItem.SetLine( &aLine );
+ }
+ ScfTools::PutItem( rItemSet, aTLBRItem, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aBLTRItem, bSkipPoolDefs );
+}
+
+XclImpCellArea::XclImpCellArea()
+{
+ SetUsedFlags( false );
+}
+
+void XclImpCellArea::SetUsedFlags( bool bUsed )
+{
+ mbForeUsed = mbBackUsed = mbPattUsed = bUsed;
+}
+
+void XclImpCellArea::FillFromXF2( sal_uInt8 nFlags )
+{
+ mnPattern = ::get_flagvalue( nFlags, EXC_XF2_BACKGROUND, EXC_PATT_12_5_PERC, EXC_PATT_NONE );
+ mnForeColor = EXC_COLOR_BIFF2_BLACK;
+ mnBackColor = EXC_COLOR_BIFF2_WHITE;
+ SetUsedFlags( true );
+}
+
+void XclImpCellArea::FillFromXF3( sal_uInt16 nArea )
+{
+ mnPattern = ::extract_value< sal_uInt8 >( nArea, 0, 6 );
+ mnForeColor = ::extract_value< sal_uInt16 >( nArea, 6, 5 );
+ mnBackColor = ::extract_value< sal_uInt16 >( nArea, 11, 5 );
+ SetUsedFlags( true );
+}
+
+void XclImpCellArea::FillFromXF5( sal_uInt32 nArea )
+{
+ mnPattern = ::extract_value< sal_uInt8 >( nArea, 16, 6 );
+ mnForeColor = ::extract_value< sal_uInt16 >( nArea, 0, 7 );
+ mnBackColor = ::extract_value< sal_uInt16 >( nArea, 7, 7 );
+ SetUsedFlags( true );
+}
+
+void XclImpCellArea::FillFromXF8( sal_uInt32 nBorder2, sal_uInt16 nArea )
+{
+ mnPattern = ::extract_value< sal_uInt8 >( nBorder2, 26, 6 );
+ mnForeColor = ::extract_value< sal_uInt16 >( nArea, 0, 7 );
+ mnBackColor = ::extract_value< sal_uInt16 >( nArea, 7, 7 );
+ SetUsedFlags( true );
+}
+
+void XclImpCellArea::FillFromCF8( sal_uInt16 nPattern, sal_uInt16 nColor, sal_uInt32 nFlags )
+{
+ mnForeColor = ::extract_value< sal_uInt16 >( nColor, 0, 7 );
+ mnBackColor = ::extract_value< sal_uInt16 >( nColor, 7, 7 );
+ mnPattern = ::extract_value< sal_uInt8 >( nPattern, 10, 6 );
+ mbForeUsed = !::get_flag( nFlags, EXC_CF_AREA_FGCOLOR );
+ mbBackUsed = !::get_flag( nFlags, EXC_CF_AREA_BGCOLOR );
+ mbPattUsed = !::get_flag( nFlags, EXC_CF_AREA_PATTERN );
+
+ if( mbBackUsed && (!mbPattUsed || (mnPattern == EXC_PATT_SOLID)) )
+ {
+ mnForeColor = mnBackColor;
+ mnPattern = EXC_PATT_SOLID;
+ mbForeUsed = mbPattUsed = true;
+ }
+ else if( !mbBackUsed && mbPattUsed && (mnPattern == EXC_PATT_SOLID) )
+ {
+ mbPattUsed = false;
+ }
+}
+
+void XclImpCellArea::FillToItemSet( SfxItemSet& rItemSet, const XclImpPalette& rPalette, bool bSkipPoolDefs ) const
+{
+ if( !mbPattUsed ) // colors may be both unused in cond. formats
+ return;
+
+ SvxBrushItem aBrushItem( ATTR_BACKGROUND );
+
+ // do not use IsTransparent() - old Calc filter writes transparency with different color indexes
+ if( mnPattern == EXC_PATT_NONE )
+ {
+ aBrushItem.SetColor( COL_TRANSPARENT );
+ }
+ else
+ {
+ Color aFore( rPalette.GetColor( mbForeUsed ? mnForeColor : EXC_COLOR_WINDOWTEXT ) );
+ Color aBack( rPalette.GetColor( mbBackUsed ? mnBackColor : EXC_COLOR_WINDOWBACK ) );
+ aBrushItem.SetColor( XclTools::GetPatternColor( aFore, aBack, mnPattern ) );
+ }
+
+ ScfTools::PutItem( rItemSet, aBrushItem, bSkipPoolDefs );
+}
+
+XclImpXF::XclImpXF( const XclImpRoot& rRoot ) :
+ XclXFBase( true ), // default is cell XF
+ XclImpRoot( rRoot ),
+ mpStyleSheet( nullptr ),
+ mnXclNumFmt( 0 ),
+ mnXclFont( 0 )
+{
+}
+
+XclImpXF::~XclImpXF()
+{
+}
+
+void XclImpXF::ReadXF2( XclImpStream& rStrm )
+{
+ sal_uInt8 nReadFont, nReadNumFmt, nFlags;
+ nReadFont = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ nReadNumFmt = rStrm.ReaduInt8();
+ nFlags = rStrm.ReaduInt8();
+
+ // XF type always cell, no parent, used flags always true
+ SetAllUsedFlags( true );
+
+ // attributes
+ maProtection.FillFromXF2( nReadNumFmt );
+ mnXclFont = nReadFont;
+ mnXclNumFmt = nReadNumFmt & EXC_XF2_VALFMT_MASK;
+ maAlignment.FillFromXF2( nFlags );
+ maBorder.FillFromXF2( nFlags );
+ maArea.FillFromXF2( nFlags );
+}
+
+void XclImpXF::ReadXF3( XclImpStream& rStrm )
+{
+ sal_uInt32 nBorder;
+ sal_uInt16 nTypeProt, nAlign, nArea;
+ sal_uInt8 nReadFont, nReadNumFmt;
+ nReadFont = rStrm.ReaduInt8();
+ nReadNumFmt = rStrm.ReaduInt8();
+ nTypeProt = rStrm.ReaduInt16();
+ nAlign = rStrm.ReaduInt16();
+ nArea = rStrm.ReaduInt16();
+ nBorder = rStrm.ReaduInt32();
+
+ // XF type/parent, attribute used flags
+ mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE ); // new in BIFF3
+ mnParent = ::extract_value< sal_uInt16 >( nAlign, 4, 12 ); // new in BIFF3
+ SetUsedFlags( ::extract_value< sal_uInt8 >( nTypeProt, 10, 6 ) );
+
+ // attributes
+ maProtection.FillFromXF3( nTypeProt );
+ mnXclFont = nReadFont;
+ mnXclNumFmt = nReadNumFmt;
+ maAlignment.FillFromXF3( nAlign );
+ maBorder.FillFromXF3( nBorder );
+ maArea.FillFromXF3( nArea ); // new in BIFF3
+}
+
+void XclImpXF::ReadXF4( XclImpStream& rStrm )
+{
+ sal_uInt32 nBorder;
+ sal_uInt16 nTypeProt, nAlign, nArea;
+ sal_uInt8 nReadFont, nReadNumFmt;
+ nReadFont = rStrm.ReaduInt8();
+ nReadNumFmt = rStrm.ReaduInt8();
+ nTypeProt = rStrm.ReaduInt16();
+ nAlign = rStrm.ReaduInt16();
+ nArea = rStrm.ReaduInt16();
+ nBorder = rStrm.ReaduInt32();
+
+ // XF type/parent, attribute used flags
+ mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE );
+ mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 );
+ SetUsedFlags( ::extract_value< sal_uInt8 >( nAlign, 10, 6 ) );
+
+ // attributes
+ maProtection.FillFromXF3( nTypeProt );
+ mnXclFont = nReadFont;
+ mnXclNumFmt = nReadNumFmt;
+ maAlignment.FillFromXF4( nAlign );
+ maBorder.FillFromXF3( nBorder );
+ maArea.FillFromXF3( nArea );
+}
+
+void XclImpXF::ReadXF5( XclImpStream& rStrm )
+{
+ sal_uInt32 nArea, nBorder;
+ sal_uInt16 nTypeProt, nAlign;
+ mnXclFont = rStrm.ReaduInt16();
+ mnXclNumFmt = rStrm.ReaduInt16();
+ nTypeProt = rStrm.ReaduInt16();
+ nAlign = rStrm.ReaduInt16();
+ nArea = rStrm.ReaduInt32();
+ nBorder = rStrm.ReaduInt32();
+
+ // XF type/parent, attribute used flags
+ mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE );
+ mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 );
+ SetUsedFlags( ::extract_value< sal_uInt8 >( nAlign, 10, 6 ) );
+
+ // attributes
+ maProtection.FillFromXF3( nTypeProt );
+ maAlignment.FillFromXF5( nAlign );
+ maBorder.FillFromXF5( nBorder, nArea );
+ maArea.FillFromXF5( nArea );
+}
+
+void XclImpXF::ReadXF8( XclImpStream& rStrm )
+{
+ sal_uInt32 nBorder1, nBorder2;
+ sal_uInt16 nTypeProt, nAlign, nMiscAttrib, nArea;
+ mnXclFont = rStrm.ReaduInt16();
+ mnXclNumFmt = rStrm.ReaduInt16();
+ nTypeProt = rStrm.ReaduInt16();
+ nAlign = rStrm.ReaduInt16();
+ nMiscAttrib = rStrm.ReaduInt16();
+ nBorder1 = rStrm.ReaduInt32();
+ nBorder2 = rStrm.ReaduInt32( );
+ nArea = rStrm.ReaduInt16();
+
+ // XF type/parent, attribute used flags
+ mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE );
+ mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 );
+ SetUsedFlags( ::extract_value< sal_uInt8 >( nMiscAttrib, 10, 6 ) );
+
+ // attributes
+ maProtection.FillFromXF3( nTypeProt );
+ maAlignment.FillFromXF8( nAlign, nMiscAttrib );
+ maBorder.FillFromXF8( nBorder1, nBorder2 );
+ maArea.FillFromXF8( nBorder2, nArea );
+}
+
+void XclImpXF::ReadXF( XclImpStream& rStrm )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2: ReadXF2( rStrm ); break;
+ case EXC_BIFF3: ReadXF3( rStrm ); break;
+ case EXC_BIFF4: ReadXF4( rStrm ); break;
+ case EXC_BIFF5: ReadXF5( rStrm ); break;
+ case EXC_BIFF8: ReadXF8( rStrm ); break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+const ScPatternAttr& XclImpXF::CreatePattern( bool bSkipPoolDefs )
+{
+ if( mpPattern )
+ return *mpPattern;
+
+ // create new pattern attribute set
+ mpPattern.reset( new ScPatternAttr( GetDoc().GetPool() ) );
+ SfxItemSet& rItemSet = mpPattern->GetItemSet();
+ XclImpXF* pParentXF = IsCellXF() ? GetXFBuffer().GetXF( mnParent ) : nullptr;
+
+ // parent cell style
+ if( IsCellXF() && !mpStyleSheet )
+ {
+ mpStyleSheet = GetXFBuffer().CreateStyleSheet( mnParent );
+
+ /* Enables mb***Used flags, if the formatting attributes differ from
+ the passed XF record. In cell XFs Excel uses the cell attributes,
+ if they differ from the parent style XF.
+ ...or if the respective flag is not set in parent style XF. */
+ if( pParentXF )
+ {
+ if( !mbProtUsed )
+ mbProtUsed = !pParentXF->mbProtUsed || !(maProtection == pParentXF->maProtection);
+ if( !mbFontUsed )
+ mbFontUsed = !pParentXF->mbFontUsed || (mnXclFont != pParentXF->mnXclFont);
+ if( !mbFmtUsed )
+ mbFmtUsed = !pParentXF->mbFmtUsed || (mnXclNumFmt != pParentXF->mnXclNumFmt);
+ if( !mbAlignUsed )
+ mbAlignUsed = !pParentXF->mbAlignUsed || !(maAlignment == pParentXF->maAlignment);
+ if( !mbBorderUsed )
+ mbBorderUsed = !pParentXF->mbBorderUsed || !(maBorder == pParentXF->maBorder);
+ if( !mbAreaUsed )
+ mbAreaUsed = !pParentXF->mbAreaUsed || !(maArea == pParentXF->maArea);
+ }
+ }
+
+ // cell protection
+ if( mbProtUsed )
+ maProtection.FillToItemSet( rItemSet, bSkipPoolDefs );
+
+ // font
+ if( mbFontUsed )
+ GetFontBuffer().FillToItemSet( rItemSet, XclFontItemType::Cell, mnXclFont, bSkipPoolDefs );
+
+ // value format
+ if( mbFmtUsed )
+ {
+ GetNumFmtBuffer().FillToItemSet( rItemSet, mnXclNumFmt, bSkipPoolDefs );
+ // Trace occurrences of Windows date formats
+ GetTracer().TraceDates( mnXclNumFmt );
+ }
+
+ // alignment
+ if( mbAlignUsed )
+ maAlignment.FillToItemSet( rItemSet, GetFontBuffer().GetFont( mnXclFont ), bSkipPoolDefs );
+
+ // border
+ if( mbBorderUsed )
+ {
+ maBorder.FillToItemSet( rItemSet, GetPalette(), bSkipPoolDefs );
+ GetTracer().TraceBorderLineStyle(maBorder.mnLeftLine > EXC_LINE_HAIR ||
+ maBorder.mnRightLine > EXC_LINE_HAIR || maBorder.mnTopLine > EXC_LINE_HAIR ||
+ maBorder.mnBottomLine > EXC_LINE_HAIR );
+ }
+
+ // area
+ if( mbAreaUsed )
+ {
+ maArea.FillToItemSet( rItemSet, GetPalette(), bSkipPoolDefs );
+ GetTracer().TraceFillPattern(maArea.mnPattern != EXC_PATT_NONE &&
+ maArea.mnPattern != EXC_PATT_SOLID);
+ }
+
+ /* #i38709# Decide which rotation reference mode to use. If any outer
+ border line of the cell is set (either explicitly or via cell style),
+ and the cell contents are rotated, set rotation reference to bottom of
+ cell. This causes the borders to be painted rotated with the text. */
+ if( mbAlignUsed || mbBorderUsed )
+ {
+ SvxRotateMode eRotateMode = SVX_ROTATE_MODE_STANDARD;
+ const XclImpCellAlign* pAlign = mbAlignUsed ? &maAlignment : (pParentXF ? &pParentXF->maAlignment : nullptr);
+ const XclImpCellBorder* pBorder = mbBorderUsed ? &maBorder : (pParentXF ? &pParentXF->maBorder : nullptr);
+ if( pAlign && pBorder && (0 < pAlign->mnRotation) && (pAlign->mnRotation <= 180) && pBorder->HasAnyOuterBorder() )
+ eRotateMode = SVX_ROTATE_MODE_BOTTOM;
+ ScfTools::PutItem( rItemSet, SvxRotateModeItem( eRotateMode, ATTR_ROTATE_MODE ), bSkipPoolDefs );
+ }
+
+ // Excel's cell margins are different from Calc's default margins.
+ SvxMarginItem aItem(40, 40, 40, 40, ATTR_MARGIN);
+ ScfTools::PutItem(rItemSet, aItem, bSkipPoolDefs);
+
+ return *mpPattern;
+}
+
+void XclImpXF::ApplyPatternToAttrVector(
+ std::vector<ScAttrEntry>& rAttrs, SCROW nRow1, SCROW nRow2, sal_uInt32 nForceScNumFmt)
+{
+ // force creation of cell style and hard formatting, do it here to have mpStyleSheet
+ CreatePattern();
+ ScPatternAttr& rPat = *mpPattern;
+
+ // insert into document
+ ScDocument& rDoc = GetDoc();
+
+ if (IsCellXF())
+ {
+ if (mpStyleSheet)
+ {
+ // Apply style sheet. Don't clear the direct formats.
+ rPat.SetStyleSheet(mpStyleSheet, false);
+ }
+ else
+ {
+ // When the cell format is not associated with any style, use the
+ // 'Default' style. Some buggy XLS docs generated by apps other
+ // than Excel (such as 1C) may not have any built-in styles at
+ // all.
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ if (pStylePool)
+ {
+ ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>(
+ pStylePool->Find(
+ ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Para));
+
+ if (pStyleSheet)
+ rPat.SetStyleSheet(pStyleSheet, false);
+ }
+
+ }
+ }
+
+ if (nForceScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ ScPatternAttr aNumPat(rDoc.GetPool());
+ GetNumFmtBuffer().FillScFmtToItemSet(aNumPat.GetItemSet(), nForceScNumFmt);
+ rPat.GetItemSet().Put(aNumPat.GetItemSet());
+ }
+
+ // Make sure we skip unnamed styles.
+ if (!rPat.GetStyleName())
+ return;
+
+ // Check for a gap between the last entry and this one.
+ bool bHasGap = false;
+ if (rAttrs.empty() && nRow1 > 0)
+ // First attribute range doesn't start at row 0.
+ bHasGap = true;
+
+ if (!rAttrs.empty() && rAttrs.back().nEndRow + 1 < nRow1)
+ bHasGap = true;
+
+ if (bHasGap)
+ {
+ // Fill this gap with the default pattern.
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = nRow1 - 1;
+ aEntry.pPattern = rDoc.GetDefPattern();
+ rAttrs.push_back(aEntry);
+ }
+
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = nRow2;
+ aEntry.pPattern = &rDoc.GetPool()->Put(rPat);
+ rAttrs.push_back(aEntry);
+}
+
+void XclImpXF::ApplyPattern(
+ SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2,
+ SCTAB nScTab )
+{
+ // force creation of cell style and hard formatting, do it here to have mpStyleSheet
+ const ScPatternAttr& rPattern = CreatePattern();
+
+ // insert into document
+ ScDocument& rDoc = GetDoc();
+ if( IsCellXF() && mpStyleSheet )
+ rDoc.ApplyStyleAreaTab( nScCol1, nScRow1, nScCol2, nScRow2, nScTab, *mpStyleSheet );
+ if( HasUsedFlags() )
+ rDoc.ApplyPatternAreaTab( nScCol1, nScRow1, nScCol2, nScRow2, nScTab, rPattern );
+
+}
+
+/*static*/ void XclImpXF::ApplyPatternForBiff2CellFormat( const XclImpRoot& rRoot,
+ const ScAddress& rScPos, sal_uInt8 nFlags1, sal_uInt8 nFlags2, sal_uInt8 nFlags3 )
+{
+ /* Create an XF object and let it do the work. We will have access to its
+ private members here. */
+ XclImpXF aXF( rRoot );
+
+ // no used flags available in BIFF2 (always true)
+ aXF.SetAllUsedFlags( true );
+
+ // set the attributes
+ aXF.maProtection.FillFromXF2( nFlags1 );
+ aXF.maAlignment.FillFromXF2( nFlags3 );
+ aXF.maBorder.FillFromXF2( nFlags3 );
+ aXF.maArea.FillFromXF2( nFlags3 );
+ aXF.mnXclNumFmt = ::extract_value< sal_uInt16 >( nFlags2, 0, 6 );
+ aXF.mnXclFont = ::extract_value< sal_uInt16 >( nFlags2, 6, 2 );
+
+ // write the attributes to the cell
+ aXF.ApplyPattern( rScPos.Col(), rScPos.Row(), rScPos.Col(), rScPos.Row(), rScPos.Tab() );
+}
+
+void XclImpXF::SetUsedFlags( sal_uInt8 nUsedFlags )
+{
+ /* Notes about finding the mb***Used flags:
+ - In cell XFs a *set* bit means a used attribute.
+ - In style XFs a *cleared* bit means a used attribute.
+ The mb***Used members always store true, if the attribute is used.
+ The "mbCellXF == ::get_flag(...)" construct evaluates to true in
+ both mentioned cases: cell XF and set bit; or style XF and cleared bit.
+ */
+ mbProtUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_PROT ));
+ mbFontUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_FONT ));
+ mbFmtUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_VALFMT ));
+ mbAlignUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_ALIGN ));
+ mbBorderUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_BORDER ));
+ mbAreaUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_AREA ));
+}
+
+XclImpStyle::XclImpStyle( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mnXfId( EXC_XF_NOTFOUND ),
+ mnBuiltinId( EXC_STYLE_USERDEF ),
+ mnLevel( EXC_STYLE_NOLEVEL ),
+ mbBuiltin( false ),
+ mbCustom( false ),
+ mbHidden( false ),
+ mpStyleSheet( nullptr )
+{
+}
+
+void XclImpStyle::ReadStyle( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF3 );
+
+ sal_uInt16 nXFIndex;
+ nXFIndex = rStrm.ReaduInt16();
+ mnXfId = nXFIndex & EXC_STYLE_XFMASK;
+ mbBuiltin = ::get_flag( nXFIndex, EXC_STYLE_BUILTIN );
+
+ if( mbBuiltin )
+ {
+ mnBuiltinId = rStrm.ReaduInt8();
+ mnLevel = rStrm.ReaduInt8();
+ }
+ else
+ {
+ maName = (GetBiff() <= EXC_BIFF5) ? rStrm.ReadByteString( false ) : rStrm.ReadUniString();
+ // #i103281# check if this is a new built-in style introduced in XL2007
+ if( (GetBiff() == EXC_BIFF8) && (rStrm.GetNextRecId() == EXC_ID_STYLEEXT) && rStrm.StartNextRecord() )
+ {
+ sal_uInt8 nExtFlags;
+ rStrm.Ignore( 12 );
+ nExtFlags = rStrm.ReaduInt8();
+ mbBuiltin = ::get_flag( nExtFlags, EXC_STYLEEXT_BUILTIN );
+ mbCustom = ::get_flag( nExtFlags, EXC_STYLEEXT_CUSTOM );
+ mbHidden = ::get_flag( nExtFlags, EXC_STYLEEXT_HIDDEN );
+ if( mbBuiltin )
+ {
+ rStrm.Ignore( 1 ); // category
+ mnBuiltinId = rStrm.ReaduInt8();
+ mnLevel = rStrm.ReaduInt8();
+ }
+ }
+ }
+}
+
+ScStyleSheet* XclImpStyle::CreateStyleSheet()
+{
+ // #i1624# #i1768# ignore unnamed user styles
+ if( !mpStyleSheet && (!maFinalName.isEmpty()) )
+ {
+ bool bCreatePattern = false;
+ XclImpXF* pXF = GetXFBuffer().GetXF( mnXfId );
+
+ bool bDefStyle = mbBuiltin && (mnBuiltinId == EXC_STYLE_NORMAL);
+ if( bDefStyle )
+ {
+ // set all flags to true to get all items in XclImpXF::CreatePattern()
+ if( pXF ) pXF->SetAllUsedFlags( true );
+ // use existing "Default" style sheet
+ mpStyleSheet = static_cast< ScStyleSheet* >( GetStyleSheetPool().Find(
+ ScResId( STR_STYLENAME_STANDARD ), SfxStyleFamily::Para ) );
+ OSL_ENSURE( mpStyleSheet, "XclImpStyle::CreateStyleSheet - Default style not found" );
+ bCreatePattern = true;
+ }
+ else
+ {
+ /* #i103281# do not create another style sheet of the same name,
+ if it exists already. This is needed to prevent that styles
+ pasted from clipboard get duplicated over and over. */
+ mpStyleSheet = static_cast< ScStyleSheet* >( GetStyleSheetPool().Find( maFinalName, SfxStyleFamily::Para ) );
+ if( !mpStyleSheet )
+ {
+ mpStyleSheet = &static_cast< ScStyleSheet& >( GetStyleSheetPool().Make( maFinalName, SfxStyleFamily::Para, SfxStyleSearchBits::UserDefined ) );
+ bCreatePattern = true;
+ }
+ }
+
+ // bDefStyle==true omits default pool items in CreatePattern()
+ if( bCreatePattern && mpStyleSheet && pXF )
+ mpStyleSheet->GetItemSet().Put( pXF->CreatePattern( bDefStyle ).GetItemSet() );
+ }
+ return mpStyleSheet;
+}
+
+void XclImpStyle::CreateUserStyle( const OUString& rFinalName )
+{
+ maFinalName = rFinalName;
+ if( !IsBuiltin() || mbCustom )
+ CreateStyleSheet();
+}
+
+XclImpXFBuffer::XclImpXFBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpXFBuffer::Initialize()
+{
+ maXFList.clear();
+ maBuiltinStyles.clear();
+ maUserStyles.clear();
+ maStylesByXf.clear();
+}
+
+void XclImpXFBuffer::ReadXF( XclImpStream& rStrm )
+{
+ std::unique_ptr<XclImpXF> xXF = std::make_unique<XclImpXF>(GetRoot());
+ xXF->ReadXF(rStrm);
+ maXFList.emplace_back(std::move(xXF));
+}
+
+void XclImpXFBuffer::ReadStyle( XclImpStream& rStrm )
+{
+ std::unique_ptr<XclImpStyle> xStyle(std::make_unique<XclImpStyle>(GetRoot()));
+ xStyle->ReadStyle(rStrm);
+ XclImpStyleList& rStyleList = (xStyle->IsBuiltin() ? maBuiltinStyles : maUserStyles);
+ rStyleList.emplace_back(std::move(xStyle));
+ XclImpStyle* pStyle = rStyleList.back().get();
+ OSL_ENSURE( maStylesByXf.count( pStyle->GetXfId() ) == 0, "XclImpXFBuffer::ReadStyle - multiple styles with equal XF identifier" );
+ maStylesByXf[ pStyle->GetXfId() ] = pStyle;
+}
+
+sal_uInt16 XclImpXFBuffer::GetFontIndex( sal_uInt16 nXFIndex ) const
+{
+ const XclImpXF* pXF = GetXF( nXFIndex );
+ return pXF ? pXF->GetFontIndex() : EXC_FONT_NOTFOUND;
+}
+
+const XclImpFont* XclImpXFBuffer::GetFont( sal_uInt16 nXFIndex ) const
+{
+ return GetFontBuffer().GetFont( GetFontIndex( nXFIndex ) );
+}
+
+namespace {
+
+/** Functor for case-insensitive string comparison, usable in maps etc. */
+struct IgnoreCaseCompare
+{
+ bool operator()( std::u16string_view rName1, std::u16string_view rName2 ) const
+ { return o3tl::compareToIgnoreAsciiCase( rName1, rName2 ) < 0; }
+};
+
+} // namespace
+
+void XclImpXFBuffer::CreateUserStyles()
+{
+ // calculate final names of all styles
+ std::map< OUString, XclImpStyle*, IgnoreCaseCompare > aCellStyles;
+ std::vector< XclImpStyle* > aConflictNameStyles;
+
+ /* 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
+ overwrite these built-in styles. For BIFF4 workbooks (which contain a
+ separate list of cell styles per sheet), reserve all existing styles if
+ current sheet is not the first sheet (this styles buffer will be
+ initialized again for every new sheet). This will create unique names
+ for styles in different sheets with the same name. Assuming that the
+ BIFF4W import filter is never used to import from clipboard... */
+ bool bReserveAll = (GetBiff() == EXC_BIFF4) && (GetCurrScTab() > 0);
+ SfxStyleSheetIterator aStyleIter( GetDoc().GetStyleSheetPool(), SfxStyleFamily::Para );
+ OUString aStandardName = ScResId( STR_STYLENAME_STANDARD );
+ for( SfxStyleSheetBase* pStyleSheet = aStyleIter.First(); pStyleSheet; pStyleSheet = aStyleIter.Next() )
+ if( (pStyleSheet->GetName() != aStandardName) && (bReserveAll || !pStyleSheet->IsUserDefined()) )
+ if( aCellStyles.count( pStyleSheet->GetName() ) == 0 )
+ aCellStyles[ pStyleSheet->GetName() ] = nullptr;
+
+ /* Calculate names of built-in styles. Store styles with reserved names
+ in the aConflictNameStyles list. */
+ for( const auto& rxStyle : maBuiltinStyles )
+ {
+ OUString aStyleName = XclTools::GetBuiltInStyleName( rxStyle->GetBuiltinId(), rxStyle->GetName(), rxStyle->GetLevel() );
+ OSL_ENSURE( bReserveAll || (aCellStyles.count( aStyleName ) == 0),
+ "XclImpXFBuffer::CreateUserStyles - multiple styles with equal built-in identifier" );
+ if( aCellStyles.count( aStyleName ) > 0 )
+ aConflictNameStyles.push_back( rxStyle.get() );
+ else
+ aCellStyles[ aStyleName ] = rxStyle.get();
+ }
+
+ /* Calculate names of user defined styles. Store styles with reserved
+ names in the aConflictNameStyles list. */
+ for( const auto& rxStyle : maUserStyles )
+ {
+ // #i1624# #i1768# ignore unnamed user styles
+ if( !rxStyle->GetName().isEmpty() )
+ {
+ if( aCellStyles.count( rxStyle->GetName() ) > 0 )
+ aConflictNameStyles.push_back( rxStyle.get() );
+ else
+ aCellStyles[ rxStyle->GetName() ] = rxStyle.get();
+ }
+ }
+
+ // find unused names for all styles with conflicting names
+ for( XclImpStyle* pStyle : aConflictNameStyles )
+ {
+ OUString aUnusedName;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ aUnusedName = pStyle->GetName() + " " + OUString::number( ++nIndex );
+ }
+ while( aCellStyles.count( aUnusedName ) > 0 );
+ aCellStyles[ aUnusedName ] = pStyle;
+ }
+
+ // set final names and create user-defined and modified built-in cell styles
+ for( auto& [rStyleName, rpStyle] : aCellStyles )
+ if( rpStyle )
+ rpStyle->CreateUserStyle( rStyleName );
+}
+
+ScStyleSheet* XclImpXFBuffer::CreateStyleSheet( sal_uInt16 nXFIndex )
+{
+ XclImpStyleMap::iterator aIt = maStylesByXf.find( nXFIndex );
+ return (aIt == maStylesByXf.end()) ? nullptr : aIt->second->CreateStyleSheet();
+}
+
+// Buffer for XF indexes in cells =============================================
+
+bool XclImpXFRange::Expand( SCROW nScRow, const XclImpXFIndex& rXFIndex )
+{
+ if( maXFIndex != rXFIndex )
+ return false;
+
+ if( mnScRow2 + 1 == nScRow )
+ {
+ ++mnScRow2;
+ return true;
+ }
+ if( mnScRow1 > 0 && (mnScRow1 - 1 == nScRow) )
+ {
+ --mnScRow1;
+ return true;
+ }
+
+ return false;
+}
+
+bool XclImpXFRange::Expand( const XclImpXFRange& rNextRange )
+{
+ OSL_ENSURE( mnScRow2 < rNextRange.mnScRow1, "XclImpXFRange::Expand - rows out of order" );
+ if( (maXFIndex == rNextRange.maXFIndex) && (mnScRow2 + 1 == rNextRange.mnScRow1) )
+ {
+ mnScRow2 = rNextRange.mnScRow2;
+ return true;
+ }
+ return false;
+}
+
+void XclImpXFRangeColumn::SetDefaultXF( const XclImpXFIndex& rXFIndex, const XclImpRoot& rRoot )
+{
+ // List should be empty when inserting the default column format.
+ // Later explicit SetXF() calls will break up this range.
+ OSL_ENSURE( maIndexList.empty(), "XclImpXFRangeColumn::SetDefaultXF - Setting Default Column XF is not empty" );
+
+ // insert a complete row range with one insert.
+ maIndexList.push_back( std::make_unique<XclImpXFRange>( 0, rRoot.GetDoc().MaxRow(), rXFIndex ) );
+}
+
+void XclImpXFRangeColumn::SetXF( SCROW nScRow, const XclImpXFIndex& rXFIndex )
+{
+ XclImpXFRange* pPrevRange;
+ XclImpXFRange* pNextRange;
+ sal_uLong nNextIndex;
+
+ Find( pPrevRange, pNextRange, nNextIndex, nScRow );
+
+ // previous range:
+ // try to overwrite XF (if row is contained in) or try to expand range
+ if( pPrevRange )
+ {
+ if( pPrevRange->Contains( nScRow ) ) // overwrite old XF
+ {
+ if( rXFIndex == pPrevRange->maXFIndex )
+ return;
+
+ SCROW nFirstScRow = pPrevRange->mnScRow1;
+ SCROW nLastScRow = pPrevRange->mnScRow2;
+ sal_uLong nIndex = nNextIndex - 1;
+ XclImpXFRange* pThisRange = pPrevRange;
+ pPrevRange = (nIndex > 0 && nIndex <= maIndexList.size()) ? maIndexList[ nIndex - 1 ].get() : nullptr;
+
+ if( nFirstScRow == nLastScRow ) // replace solely XF
+ {
+ pThisRange->maXFIndex = rXFIndex;
+ TryConcatPrev( nNextIndex ); // try to concat. next with this
+ TryConcatPrev( nIndex ); // try to concat. this with previous
+ }
+ else if( nFirstScRow == nScRow ) // replace first XF
+ {
+ ++(pThisRange->mnScRow1);
+ // try to concatenate with previous of this
+ if( !pPrevRange || !pPrevRange->Expand( nScRow, rXFIndex ) )
+ Insert( new XclImpXFRange( nScRow, rXFIndex ), nIndex );
+ }
+ else if( nLastScRow == nScRow ) // replace last XF
+ {
+ --(pThisRange->mnScRow2);
+ if( !pNextRange || !pNextRange->Expand( nScRow, rXFIndex ) )
+ Insert( new XclImpXFRange( nScRow, rXFIndex ), nNextIndex );
+ }
+ else // insert in the middle of the range
+ {
+ pThisRange->mnScRow1 = nScRow + 1;
+ // List::Insert() moves entries towards end of list, so insert twice at nIndex
+ Insert( new XclImpXFRange( nScRow, rXFIndex ), nIndex );
+ Insert( new XclImpXFRange( nFirstScRow, nScRow - 1, pThisRange->maXFIndex ), nIndex );
+ }
+ return;
+ }
+ else if( pPrevRange->Expand( nScRow, rXFIndex ) ) // try to expand
+ {
+ TryConcatPrev( nNextIndex ); // try to concatenate next with expanded
+ return;
+ }
+ }
+
+ // try to expand next range
+ if( pNextRange && pNextRange->Expand( nScRow, rXFIndex ) )
+ return;
+
+ // create new range
+ Insert( new XclImpXFRange( nScRow, rXFIndex ), nNextIndex );
+}
+
+void XclImpXFRangeColumn::Insert(XclImpXFRange* pXFRange, sal_uLong nIndex)
+{
+ maIndexList.insert( maIndexList.begin() + nIndex, std::unique_ptr<XclImpXFRange>(pXFRange) );
+}
+
+void XclImpXFRangeColumn::Find(
+ XclImpXFRange*& rpPrevRange, XclImpXFRange*& rpNextRange,
+ sal_uLong& rnNextIndex, SCROW nScRow )
+{
+
+ // test whether list is empty
+ if( maIndexList.empty() )
+ {
+ rpPrevRange = rpNextRange = nullptr;
+ rnNextIndex = 0;
+ return;
+ }
+
+ rpPrevRange = maIndexList.front().get();
+ rpNextRange = maIndexList.back().get();
+
+ // test whether row is at end of list (contained in or behind last range)
+ // rpPrevRange will contain a possible existing row
+ if( rpNextRange->mnScRow1 <= nScRow )
+ {
+ rpPrevRange = rpNextRange;
+ rpNextRange = nullptr;
+ rnNextIndex = maIndexList.size();
+ return;
+ }
+
+ // test whether row is at beginning of list (really before first range)
+ if( nScRow < rpPrevRange->mnScRow1 )
+ {
+ rpNextRange = rpPrevRange;
+ rpPrevRange = nullptr;
+ rnNextIndex = 0;
+ return;
+ }
+
+ // loop: find range entries before and after new row
+ // break the loop if there is no more range between first and last -or-
+ // if rpPrevRange contains nScRow (rpNextRange will never contain nScRow)
+ sal_uLong nPrevIndex = 0;
+ sal_uLong nMidIndex;
+ rnNextIndex = maIndexList.size() - 1;
+ XclImpXFRange* pMidRange;
+ while( ((rnNextIndex - nPrevIndex) > 1) && (rpPrevRange->mnScRow2 < nScRow) )
+ {
+ nMidIndex = (nPrevIndex + rnNextIndex) / 2;
+ pMidRange = maIndexList[nMidIndex].get();
+ OSL_ENSURE( pMidRange, "XclImpXFRangeColumn::Find - missing XF index range" );
+ if( nScRow < pMidRange->mnScRow1 ) // row is really before pMidRange
+ {
+ rpNextRange = pMidRange;
+ rnNextIndex = nMidIndex;
+ }
+ else // row is in or after pMidRange
+ {
+ rpPrevRange = pMidRange;
+ nPrevIndex = nMidIndex;
+ }
+ }
+
+ // find next rpNextRange if rpPrevRange contains nScRow
+ if( nScRow <= rpPrevRange->mnScRow2 )
+ {
+ rnNextIndex = nPrevIndex + 1;
+ rpNextRange = maIndexList[rnNextIndex].get();
+ }
+}
+
+void XclImpXFRangeColumn::TryConcatPrev( sal_uLong nIndex )
+{
+ if( !nIndex || nIndex >= maIndexList.size() )
+ return;
+
+ XclImpXFRange& prevRange = *maIndexList[ nIndex - 1 ];
+ XclImpXFRange& nextRange = *maIndexList[ nIndex ];
+
+ if( prevRange.Expand( nextRange ) )
+ maIndexList.erase( maIndexList.begin() + nIndex );
+}
+
+XclImpXFRangeBuffer::XclImpXFRangeBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+XclImpXFRangeBuffer::~XclImpXFRangeBuffer()
+{
+}
+
+void XclImpXFRangeBuffer::Initialize()
+{
+ maColumns.clear();
+ maHyperlinks.clear();
+ maMergeList.RemoveAll();
+}
+
+void XclImpXFRangeBuffer::SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex, XclImpXFInsertMode eMode )
+{
+ SCCOL nScCol = rScPos.Col();
+ SCROW nScRow = rScPos.Row();
+
+ // set cell XF's
+ size_t nIndex = static_cast< size_t >( nScCol );
+ if( maColumns.size() <= nIndex )
+ maColumns.resize( nIndex + 1 );
+ if( !maColumns[ nIndex ] )
+ maColumns[ nIndex ] = std::make_shared<XclImpXFRangeColumn>();
+ // remember all Boolean cells, they will get 'Standard' number format
+ maColumns[ nIndex ]->SetXF( nScRow, XclImpXFIndex( nXFIndex, eMode == xlXFModeBoolCell ) );
+
+ // set "center across selection" and "fill" attribute for all following empty cells
+ // ignore it on row default XFs
+ if( eMode == xlXFModeRow )
+ return;
+
+ const XclImpXF* pXF = GetXFBuffer().GetXF( nXFIndex );
+ if( pXF && ((pXF->GetHorAlign() == EXC_XF_HOR_CENTER_AS) || (pXF->GetHorAlign() == EXC_XF_HOR_FILL)) )
+ {
+ // expand last merged range if this attribute is set repeatedly
+ ScRange* pRange = maMergeList.empty() ? nullptr : &maMergeList.back();
+ if (pRange && (pRange->aEnd.Row() == nScRow) && (pRange->aEnd.Col() + 1 == nScCol) && (eMode == xlXFModeBlank))
+ pRange->aEnd.IncCol();
+ else if( eMode != xlXFModeBlank ) // do not merge empty cells
+ maMergeList.push_back( ScRange( nScCol, nScRow, 0 ) );
+ }
+}
+
+void XclImpXFRangeBuffer::SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex )
+{
+ SetXF( rScPos, nXFIndex, xlXFModeCell );
+}
+
+void XclImpXFRangeBuffer::SetBlankXF( const ScAddress& rScPos, sal_uInt16 nXFIndex )
+{
+ SetXF( rScPos, nXFIndex, xlXFModeBlank );
+}
+
+void XclImpXFRangeBuffer::SetBoolXF( const ScAddress& rScPos, sal_uInt16 nXFIndex )
+{
+ SetXF( rScPos, nXFIndex, xlXFModeBoolCell );
+}
+
+void XclImpXFRangeBuffer::SetRowDefXF( SCROW nScRow, sal_uInt16 nXFIndex )
+{
+ for( SCCOL nScCol = 0; nScCol <= GetDoc().MaxCol(); ++nScCol )
+ SetXF( ScAddress( nScCol, nScRow, 0 ), nXFIndex, xlXFModeRow );
+}
+
+void XclImpXFRangeBuffer::SetColumnDefXF( SCCOL nScCol, sal_uInt16 nXFIndex )
+{
+ // our array should not have values when creating the default column format.
+ size_t nIndex = static_cast< size_t >( nScCol );
+ if( maColumns.size() <= nIndex )
+ maColumns.resize( nIndex + 1 );
+ OSL_ENSURE( !maColumns[ nIndex ], "XclImpXFRangeBuffer::SetColumnDefXF - default column of XFs already has values" );
+ maColumns[ nIndex ] = std::make_shared<XclImpXFRangeColumn>();
+ maColumns[ nIndex ]->SetDefaultXF( XclImpXFIndex( nXFIndex ), GetRoot());
+}
+
+void XclImpXFRangeBuffer::SetBorderLine( const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
+{
+ SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
+ SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
+ ScDocument& rDoc = GetDoc();
+
+ const SvxBoxItem* pFromItem =
+ rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
+ const SvxBoxItem* pToItem =
+ rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
+
+ SvxBoxItem aNewItem( *pToItem );
+ aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
+ rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
+}
+
+void XclImpXFRangeBuffer::SetHyperlink( const XclRange& rXclRange, const OUString& rUrl )
+{
+ maHyperlinks.emplace_back( rXclRange, rUrl );
+}
+
+void XclImpXFRangeBuffer::SetMerge( SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2 )
+{
+ if( (nScCol1 < nScCol2) || (nScRow1 < nScRow2) )
+ maMergeList.push_back( ScRange( nScCol1, nScRow1, 0, nScCol2, nScRow2, 0 ) );
+}
+
+void XclImpXFRangeBuffer::Finalize()
+{
+ ScDocumentImport& rDocImport = GetDocImport();
+ ScDocument& rDoc = rDocImport.getDoc();
+ SCTAB nScTab = GetCurrScTab();
+
+ // apply patterns
+ XclImpXFBuffer& rXFBuffer = GetXFBuffer();
+ ScDocumentImport::Attrs aPendingAttrParam;
+ SCCOL pendingColStart = -1;
+ SCCOL pendingColEnd = -1;
+ SCCOL nScCol = 0;
+ for( const auto& rxColumn : maColumns )
+ {
+ // apply all cell styles of an existing column
+ if( rxColumn )
+ {
+ XclImpXFRangeColumn& rColumn = *rxColumn;
+ std::vector<ScAttrEntry> aAttrs;
+ aAttrs.reserve(rColumn.end() - rColumn.begin());
+
+ for (const auto& rxStyle : rColumn)
+ {
+ XclImpXFRange& rStyle = *rxStyle;
+ const XclImpXFIndex& rXFIndex = rStyle.maXFIndex;
+ XclImpXF* pXF = rXFBuffer.GetXF( rXFIndex.GetXFIndex() );
+ if (!pXF)
+ continue;
+
+ sal_uInt32 nForceScNumFmt = rXFIndex.IsBoolCell() ?
+ GetNumFmtBuffer().GetStdScNumFmt() : NUMBERFORMAT_ENTRY_NOT_FOUND;
+
+ pXF->ApplyPatternToAttrVector(aAttrs, rStyle.mnScRow1, rStyle.mnScRow2, nForceScNumFmt);
+ }
+
+ if (aAttrs.empty() || aAttrs.back().nEndRow != rDoc.MaxRow())
+ {
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = rDoc.MaxRow();
+ aEntry.pPattern = rDoc.GetDefPattern();
+ aAttrs.push_back(aEntry);
+ }
+
+ aAttrs.shrink_to_fit();
+ assert(aAttrs.size() > 0);
+ ScDocumentImport::Attrs aAttrParam;
+ aAttrParam.mvData.swap(aAttrs);
+ aAttrParam.mbLatinNumFmtOnly = false; // when unsure, set it to false.
+
+ // Compress setting the attributes, set the same set in one call.
+ if( pendingColStart != -1 && pendingColEnd == nScCol - 1 && aAttrParam == aPendingAttrParam )
+ ++pendingColEnd;
+ else
+ {
+ if( pendingColStart != -1 )
+ rDocImport.setAttrEntries(nScTab, pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
+ pendingColStart = pendingColEnd = nScCol;
+ aPendingAttrParam = std::move( aAttrParam );
+ }
+ }
+ ++nScCol;
+ }
+ if( pendingColStart != -1 )
+ rDocImport.setAttrEntries(nScTab, pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
+
+ // insert hyperlink cells
+ for( const auto& [rXclRange, rUrl] : maHyperlinks )
+ XclImpHyperlink::InsertUrl( GetRoot(), rXclRange, rUrl );
+
+ // apply cell merging
+ for ( size_t i = 0, nRange = maMergeList.size(); i < nRange; ++i )
+ {
+ const ScRange & rRange = maMergeList[ i ];
+ const ScAddress& rStart = rRange.aStart;
+ const ScAddress& rEnd = rRange.aEnd;
+ bool bMultiCol = rStart.Col() != rEnd.Col();
+ bool bMultiRow = rStart.Row() != rEnd.Row();
+ // set correct right border
+ if( bMultiCol )
+ SetBorderLine( rRange, nScTab, SvxBoxItemLine::RIGHT );
+ // set correct lower border
+ if( bMultiRow )
+ SetBorderLine( rRange, nScTab, SvxBoxItemLine::BOTTOM );
+ // do merge
+ if( bMultiCol || bMultiRow )
+ rDoc.DoMerge( rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row(), nScTab );
+ // #i93609# merged range in a single row: test if manual row height is needed
+ if( !bMultiRow )
+ {
+ bool bTextWrap = rDoc.GetAttr( rStart, ATTR_LINEBREAK )->GetValue();
+ if( !bTextWrap && (rDoc.GetCellType( rStart ) == CELLTYPE_EDIT) )
+ if (const EditTextObject* pEditObj = rDoc.GetEditText(rStart))
+ bTextWrap = pEditObj->GetParagraphCount() > 1;
+ if( bTextWrap )
+ GetOldRoot().pColRowBuff->SetManualRowHeight( rStart.Row() );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiview.cxx b/sc/source/filter/excel/xiview.cxx
new file mode 100644
index 000000000..49878de6e
--- /dev/null
+++ b/sc/source/filter/excel/xiview.cxx
@@ -0,0 +1,300 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+
+#include <xiview.hxx>
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <viewopti.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xistyle.hxx>
+
+// Document view settings =====================================================
+
+XclImpDocViewSettings::XclImpDocViewSettings( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpDocViewSettings::ReadWindow1( XclImpStream& rStrm )
+{
+ maData.mnWinX = rStrm.ReaduInt16();
+ maData.mnWinY = rStrm.ReaduInt16();
+ maData.mnWinWidth = rStrm.ReaduInt16();
+ maData.mnWinHeight = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+ if( GetBiff() >= EXC_BIFF5 )
+ {
+ maData.mnDisplXclTab = rStrm.ReaduInt16();
+ maData.mnFirstVisXclTab = rStrm.ReaduInt16();
+ maData.mnXclSelectCnt = rStrm.ReaduInt16();
+ maData.mnTabBarWidth = rStrm.ReaduInt16();
+ }
+}
+
+SCTAB XclImpDocViewSettings::GetDisplScTab() const
+{
+ /* Simply cast Excel index to Calc index.
+ TODO: This may fail if the document contains scenarios. */
+ sal_uInt16 nMaxXclTab = static_cast< sal_uInt16 >( GetMaxPos().Tab() );
+ return static_cast< SCTAB >( (maData.mnDisplXclTab <= nMaxXclTab) ? maData.mnDisplXclTab : 0 );
+}
+
+void XclImpDocViewSettings::Finalize()
+{
+ ScViewOptions aViewOpt( GetDoc().GetViewOptions() );
+ aViewOpt.SetOption( VOPT_HSCROLL, ::get_flag( maData.mnFlags, EXC_WIN1_HOR_SCROLLBAR ) );
+ aViewOpt.SetOption( VOPT_VSCROLL, ::get_flag( maData.mnFlags, EXC_WIN1_VER_SCROLLBAR ) );
+ aViewOpt.SetOption( VOPT_TABCONTROLS, ::get_flag( maData.mnFlags, EXC_WIN1_TABBAR ) );
+ GetDoc().SetViewOptions( aViewOpt );
+
+ // displayed sheet
+ GetExtDocOptions().GetDocSettings().mnDisplTab = GetDisplScTab();
+
+ // width of the tabbar with sheet names
+ if( maData.mnTabBarWidth <= 1000 )
+ GetExtDocOptions().GetDocSettings().mfTabBarWidth = static_cast< double >( maData.mnTabBarWidth ) / 1000.0;
+}
+
+// Sheet view settings ========================================================
+
+namespace {
+
+tools::Long lclGetScZoom( sal_uInt16 nXclZoom, sal_uInt16 nDefZoom )
+{
+ return static_cast< tools::Long >( nXclZoom ? nXclZoom : nDefZoom );
+}
+
+} // namespace
+
+XclImpTabViewSettings::XclImpTabViewSettings( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+ Initialize();
+}
+
+void XclImpTabViewSettings::Initialize()
+{
+ maData.SetDefaults();
+}
+
+void XclImpTabViewSettings::ReadTabBgColor( XclImpStream& rStrm, const XclImpPalette& rPal )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF8 );
+ if( GetBiff() < EXC_BIFF8 )
+ return;
+
+ sal_uInt8 ColorIndex;
+
+ rStrm.Ignore( 16 );
+ ColorIndex = rStrm.ReaduInt8() & EXC_SHEETEXT_TABCOLOR; //0x7F
+ if ( ColorIndex >= 8 && ColorIndex <= 63 ) //only accept valid index values
+ {
+ maData.maTabBgColor = rPal.GetColor( ColorIndex );
+ }
+}
+
+void XclImpTabViewSettings::ReadWindow2( XclImpStream& rStrm, bool bChart )
+{
+ if( GetBiff() == EXC_BIFF2 )
+ {
+ maData.mbShowFormulas = rStrm.ReaduInt8() != 0;
+ maData.mbShowGrid = rStrm.ReaduInt8() != 0;
+ maData.mbShowHeadings = rStrm.ReaduInt8() != 0;
+ maData.mbFrozenPanes = rStrm.ReaduInt8() != 0;
+ maData.mbShowZeros = rStrm.ReaduInt8() != 0;
+ rStrm >> maData.maFirstXclPos;
+ maData.mbDefGridColor = rStrm.ReaduInt8() != 0;
+ rStrm >> maData.maGridColor;
+ }
+ else
+ {
+ sal_uInt16 nFlags;
+ nFlags = rStrm.ReaduInt16();
+ rStrm >> maData.maFirstXclPos;
+
+ // #i59590# real life: Excel ignores some view settings in chart sheets
+ maData.mbSelected = ::get_flag( nFlags, EXC_WIN2_SELECTED );
+ maData.mbDisplayed = ::get_flag( nFlags, EXC_WIN2_DISPLAYED );
+ maData.mbMirrored = !bChart && ::get_flag( nFlags, EXC_WIN2_MIRRORED );
+ maData.mbFrozenPanes = !bChart && ::get_flag( nFlags, EXC_WIN2_FROZEN );
+ maData.mbPageMode = !bChart && ::get_flag( nFlags, EXC_WIN2_PAGEBREAKMODE );
+ maData.mbDefGridColor = bChart || ::get_flag( nFlags, EXC_WIN2_DEFGRIDCOLOR );
+ maData.mbShowFormulas = !bChart && ::get_flag( nFlags, EXC_WIN2_SHOWFORMULAS );
+ maData.mbShowGrid = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWGRID );
+ maData.mbShowHeadings = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWHEADINGS );
+ maData.mbShowZeros = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWZEROS );
+ maData.mbShowOutline = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWOUTLINE );
+
+ switch( GetBiff() )
+ {
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ rStrm >> maData.maGridColor;
+ break;
+ case EXC_BIFF8:
+ {
+ sal_uInt16 nGridColorIdx;
+ nGridColorIdx = rStrm.ReaduInt16();
+ // zoom data not included in chart sheets
+ if( rStrm.GetRecLeft() >= 6 )
+ {
+ rStrm.Ignore( 2 );
+ maData.mnPageZoom = rStrm.ReaduInt16();
+ maData.mnNormalZoom = rStrm.ReaduInt16();
+ }
+
+ if( !maData.mbDefGridColor )
+ maData.maGridColor = GetPalette().GetColor( nGridColorIdx );
+ }
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+ }
+
+ // do not scroll chart sheets
+ if( bChart )
+ maData.maFirstXclPos.Set( 0, 0 );
+}
+
+void XclImpTabViewSettings::ReadScl( XclImpStream& rStrm )
+{
+ sal_uInt16 nNum, nDenom;
+ nNum = rStrm.ReaduInt16();
+ nDenom = rStrm.ReaduInt16();
+ OSL_ENSURE( nDenom > 0, "XclImpPageSettings::ReadScl - invalid denominator" );
+ if( nDenom > 0 )
+ maData.mnCurrentZoom = limit_cast< sal_uInt16 >( (nNum * 100) / nDenom );
+}
+
+void XclImpTabViewSettings::ReadPane( XclImpStream& rStrm )
+{
+ maData.mnSplitX = rStrm.ReaduInt16();
+ maData.mnSplitY = rStrm.ReaduInt16();
+
+ rStrm >> maData.maSecondXclPos;
+ maData.mnActivePane = rStrm.ReaduInt8();
+}
+
+void XclImpTabViewSettings::ReadSelection( XclImpStream& rStrm )
+{
+ // pane of this selection
+ sal_uInt8 nPane;
+ nPane = rStrm.ReaduInt8();
+ XclSelectionData& rSelData = maData.CreateSelectionData( nPane );
+ // cursor position and selection
+ rStrm >> rSelData.maXclCursor;
+ rSelData.mnCursorIdx = rStrm.ReaduInt16();
+ rSelData.maXclSelection.Read( rStrm, false );
+}
+
+void XclImpTabViewSettings::Finalize()
+{
+ SCTAB nScTab = GetCurrScTab();
+ ScDocument& rDoc = GetDoc();
+ XclImpAddressConverter& rAddrConv = GetAddressConverter();
+ ScExtTabSettings& rTabSett = GetExtDocOptions().GetOrCreateTabSettings( nScTab );
+ bool bDisplayed = GetDocViewSettings().GetDisplScTab() == nScTab;
+
+ // *** sheet options: cursor, selection, splits, zoom ***
+
+ // sheet flags
+ if( maData.mbMirrored )
+ // do not call this function with sal_False, it would mirror away all drawing objects
+ rDoc.SetLayoutRTL( nScTab, true );
+ rTabSett.mbSelected = maData.mbSelected || bDisplayed;
+
+ // first visible cell in top-left pane and in additional pane(s)
+ rTabSett.maFirstVis = rAddrConv.CreateValidAddress( maData.maFirstXclPos, nScTab, false );
+ rTabSett.maSecondVis = rAddrConv.CreateValidAddress( maData.maSecondXclPos, nScTab, false );
+
+ // cursor position and selection
+ if( const XclSelectionData* pSelData = maData.GetSelectionData( maData.mnActivePane ) )
+ {
+ rTabSett.maCursor = rAddrConv.CreateValidAddress( pSelData->maXclCursor, nScTab, false );
+ rAddrConv.ConvertRangeList( rTabSett.maSelection, pSelData->maXclSelection, nScTab, false );
+ }
+
+ // active pane
+ switch( maData.mnActivePane )
+ {
+ case EXC_PANE_TOPLEFT: rTabSett.meActivePane = SCEXT_PANE_TOPLEFT; break;
+ case EXC_PANE_TOPRIGHT: rTabSett.meActivePane = SCEXT_PANE_TOPRIGHT; break;
+ case EXC_PANE_BOTTOMLEFT: rTabSett.meActivePane = SCEXT_PANE_BOTTOMLEFT; break;
+ case EXC_PANE_BOTTOMRIGHT: rTabSett.meActivePane = SCEXT_PANE_BOTTOMRIGHT; break;
+ }
+
+ // freeze/split position
+ rTabSett.mbFrozenPanes = maData.mbFrozenPanes;
+ if( maData.mbFrozenPanes )
+ {
+ /* Frozen panes: handle split position as row/column positions.
+ #i35812# Excel uses number of visible rows/columns, Calc uses position of freeze. */
+ if( (maData.mnSplitX > 0) && (maData.maFirstXclPos.mnCol + maData.mnSplitX <= GetScMaxPos().Col()) )
+ rTabSett.maFreezePos.SetCol( static_cast< SCCOL >( maData.maFirstXclPos.mnCol + maData.mnSplitX ) );
+ if( (maData.mnSplitY > 0) && (maData.maFirstXclPos.mnRow + maData.mnSplitY <= o3tl::make_unsigned(GetScMaxPos().Row())) )
+ rTabSett.maFreezePos.SetRow( static_cast< SCROW >( maData.maFirstXclPos.mnRow + maData.mnSplitY ) );
+ }
+ else
+ {
+ // split window: position is in twips
+ rTabSett.maSplitPos.setX( static_cast< tools::Long >( maData.mnSplitX ) );
+ rTabSett.maSplitPos.setY( static_cast< tools::Long >( maData.mnSplitY ) );
+ }
+
+ // grid color
+ if( maData.mbDefGridColor )
+ rTabSett.maGridColor = COL_AUTO;
+ else
+ rTabSett.maGridColor = maData.maGridColor;
+
+ // show grid option
+ rTabSett.mbShowGrid = maData.mbShowGrid;
+
+ // view mode and zoom
+ if( maData.mnCurrentZoom != 0 )
+ (maData.mbPageMode ? maData.mnPageZoom : maData.mnNormalZoom) = maData.mnCurrentZoom;
+ rTabSett.mbPageMode = maData.mbPageMode;
+ rTabSett.mnNormalZoom = lclGetScZoom( maData.mnNormalZoom, EXC_WIN2_NORMALZOOM_DEF );
+ rTabSett.mnPageZoom = lclGetScZoom( maData.mnPageZoom, EXC_WIN2_PAGEZOOM_DEF );
+
+ // *** additional handling for displayed sheet ***
+
+ if( bDisplayed )
+ {
+ // set Excel sheet settings globally at Calc document, take settings from displayed sheet
+ ScViewOptions aViewOpt( rDoc.GetViewOptions() );
+ aViewOpt.SetOption( VOPT_FORMULAS, maData.mbShowFormulas );
+ aViewOpt.SetOption( VOPT_HEADER, maData.mbShowHeadings );
+ aViewOpt.SetOption( VOPT_NULLVALS, maData.mbShowZeros );
+ aViewOpt.SetOption( VOPT_OUTLINER, maData.mbShowOutline );
+ rDoc.SetViewOptions( aViewOpt );
+ }
+
+ // *** set tab bg color
+ if ( !maData.IsDefaultTabBgColor() )
+ rDoc.SetTabBgColor(nScTab, maData.maTabBgColor);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xladdress.cxx b/sc/source/filter/excel/xladdress.cxx
new file mode 100644
index 000000000..20ef4fa7f
--- /dev/null
+++ b/sc/source/filter/excel/xladdress.cxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xladdress.hxx>
+#include <xestream.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+void XclAddress::Read( XclImpStream& rStrm )
+{
+ mnRow = rStrm.ReaduInt16();
+ mnCol = rStrm.ReaduInt16();
+}
+
+void XclAddress::Write( XclExpStream& rStrm ) const
+{
+ rStrm << static_cast<sal_uInt16> (mnRow);
+ rStrm << mnCol;
+}
+
+bool XclRange::Contains( const XclAddress& rPos ) const
+{
+ return (maFirst.mnCol <= rPos.mnCol) && (rPos.mnCol <= maLast.mnCol) &&
+ (maFirst.mnRow <= rPos.mnRow) && (rPos.mnRow <= maLast.mnRow);
+}
+
+void XclRange::Read( XclImpStream& rStrm, bool bCol16Bit )
+{
+ maFirst.mnRow = rStrm.ReaduInt16();
+ maLast.mnRow = rStrm.ReaduInt16();
+
+ if( bCol16Bit )
+ {
+ maFirst.mnCol = rStrm.ReaduInt16();
+ maLast.mnCol = rStrm.ReaduInt16();
+ }
+ else
+ {
+ maFirst.mnCol = rStrm.ReaduInt8();
+ maLast.mnCol = rStrm.ReaduInt8();
+ }
+}
+
+void XclRange::Write( XclExpStream& rStrm, bool bCol16Bit ) const
+{
+ rStrm << static_cast<sal_uInt16>(maFirst.mnRow) << static_cast<sal_uInt16>(maLast.mnRow);
+ if( bCol16Bit )
+ rStrm << maFirst.mnCol << maLast.mnCol;
+ else
+ rStrm << static_cast< sal_uInt8 >( maFirst.mnCol ) << static_cast< sal_uInt8 >( maLast.mnCol );
+}
+
+XclRange XclRangeList::GetEnclosingRange() const
+{
+ XclRange aXclRange;
+ if( !mRanges.empty() )
+ {
+ XclRangeVector::const_iterator aIt = mRanges.begin(), aEnd = mRanges.end();
+ aXclRange = *aIt;
+ for( ++aIt; aIt != aEnd; ++aIt )
+ {
+ aXclRange.maFirst.mnCol = ::std::min( aXclRange.maFirst.mnCol, aIt->maFirst.mnCol );
+ aXclRange.maFirst.mnRow = ::std::min( aXclRange.maFirst.mnRow, aIt->maFirst.mnRow );
+ aXclRange.maLast.mnCol = ::std::max( aXclRange.maLast.mnCol, aIt->maLast.mnCol );
+ aXclRange.maLast.mnRow = ::std::max( aXclRange.maLast.mnRow, aIt->maLast.mnRow );
+ }
+ }
+ return aXclRange;
+}
+
+void XclRangeList::Read( XclImpStream& rStrm, bool bCol16Bit, sal_uInt16 nCountInStream )
+{
+ sal_uInt16 nCount;
+ if (nCountInStream)
+ nCount = nCountInStream;
+ else
+ nCount = rStrm.ReaduInt16();
+
+ if (!nCount)
+ return;
+
+ XclRange aRange;
+ while (true)
+ {
+ aRange.Read(rStrm, bCol16Bit);
+ if (!rStrm.IsValid())
+ break;
+ mRanges.emplace_back(aRange);
+ --nCount;
+ if (!nCount)
+ break;
+ }
+}
+
+void XclRangeList::Write( XclExpStream& rStrm, bool bCol16Bit, sal_uInt16 nCountInStream ) const
+{
+ WriteSubList( rStrm, 0, mRanges.size(), bCol16Bit, nCountInStream );
+}
+
+void XclRangeList::WriteSubList( XclExpStream& rStrm, size_t nBegin, size_t nCount, bool bCol16Bit,
+ sal_uInt16 nCountInStream ) const
+{
+ OSL_ENSURE( nBegin <= mRanges.size(), "XclRangeList::WriteSubList - invalid start position" );
+ size_t nEnd = ::std::min< size_t >( nBegin + nCount, mRanges.size() );
+ if (!nCountInStream)
+ {
+ sal_uInt16 nXclCount = ulimit_cast< sal_uInt16 >( nEnd - nBegin );
+ rStrm << nXclCount;
+ }
+ rStrm.SetSliceSize( bCol16Bit ? 8 : 6 );
+ std::for_each(mRanges.begin() + nBegin, mRanges.begin() + nEnd,
+ [&rStrm, &bCol16Bit](const XclRange& rRange) { rRange.Write(rStrm, bCol16Bit); });
+}
+
+XclAddressConverterBase::XclAddressConverterBase( XclTracer& rTracer, const ScAddress& rMaxPos ) :
+ mrTracer( rTracer ),
+ maMaxPos( rMaxPos ),
+ mnMaxCol( static_cast< sal_uInt16 >( rMaxPos.Col() ) ),
+ mnMaxRow( static_cast< sal_uInt16 >( rMaxPos.Row() ) ),
+ mbColTrunc( false ),
+ mbRowTrunc( false ),
+ mbTabTrunc( false )
+{
+ OSL_ENSURE( o3tl::make_unsigned( rMaxPos.Col() ) <= SAL_MAX_UINT16, "XclAddressConverterBase::XclAddressConverterBase - invalid max column" );
+ OSL_ENSURE( o3tl::make_unsigned( rMaxPos.Row() ) <= SAL_MAX_UINT32, "XclAddressConverterBase::XclAddressConverterBase - invalid max row" );
+}
+
+XclAddressConverterBase::~XclAddressConverterBase()
+{
+}
+
+void XclAddressConverterBase::CheckScTab( SCTAB nScTab )
+{
+ bool bValid = (0 <= nScTab) && (nScTab <= maMaxPos.Tab());
+ if( !bValid )
+ {
+ mbTabTrunc |= (nScTab > maMaxPos.Tab()); // do not warn for deleted refs
+ mrTracer.TraceInvalidTab( nScTab, maMaxPos.Tab() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlchart.cxx b/sc/source/filter/excel/xlchart.cxx
new file mode 100644
index 000000000..3547dad16
--- /dev/null
+++ b/sc/source/filter/excel/xlchart.cxx
@@ -0,0 +1,1283 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlchart.hxx>
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/drawing/Hatch.hpp>
+#include <com/sun/star/drawing/LineDash.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/BitmapMode.hpp>
+#include <com/sun/star/chart/DataLabelPlacement.hpp>
+#include <com/sun/star/chart/XAxisXSupplier.hpp>
+#include <com/sun/star/chart/XAxisYSupplier.hpp>
+#include <com/sun/star/chart/XAxisZSupplier.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp>
+#include <com/sun/star/chart2/Symbol.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <o3tl/string_view.hxx>
+#include <sal/macros.h>
+#include <sal/mathconf.h>
+#include <svl/itemset.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/unomid.hxx>
+#include <filter/msfilter/escherex.hxx>
+#include <xlroot.hxx>
+#include <xlstyle.hxx>
+#include <xltools.hxx>
+
+using namespace css;
+
+// Common =====================================================================
+
+XclChRectangle::XclChRectangle() :
+ mnX( 0 ),
+ mnY( 0 ),
+ mnWidth( 0 ),
+ mnHeight( 0 )
+{
+}
+
+XclChDataPointPos::XclChDataPointPos( sal_uInt16 nSeriesIdx, sal_uInt16 nPointIdx ) :
+ mnSeriesIdx( nSeriesIdx ),
+ mnPointIdx( nPointIdx )
+{
+}
+
+bool operator<( const XclChDataPointPos& rL, const XclChDataPointPos& rR )
+{
+ return (rL.mnSeriesIdx < rR.mnSeriesIdx) ||
+ ((rL.mnSeriesIdx == rR.mnSeriesIdx) && (rL.mnPointIdx < rR.mnPointIdx));
+}
+
+XclChFrBlock::XclChFrBlock( sal_uInt16 nType ) :
+ mnType( nType ),
+ mnContext( 0 ),
+ mnValue1( 0 ),
+ mnValue2( 0 )
+{
+}
+
+// Frame formatting ===========================================================
+
+XclChFramePos::XclChFramePos() :
+ mnTLMode( EXC_CHFRAMEPOS_PARENT ),
+ mnBRMode( EXC_CHFRAMEPOS_PARENT )
+{
+}
+
+XclChLineFormat::XclChLineFormat() :
+ maColor( COL_BLACK ),
+ mnPattern( EXC_CHLINEFORMAT_SOLID ),
+ mnWeight( EXC_CHLINEFORMAT_SINGLE ),
+ mnFlags( EXC_CHLINEFORMAT_AUTO )
+{
+}
+
+XclChAreaFormat::XclChAreaFormat() :
+ maPattColor( COL_WHITE ),
+ maBackColor( COL_BLACK ),
+ mnPattern( EXC_PATT_SOLID ),
+ mnFlags( EXC_CHAREAFORMAT_AUTO )
+{
+}
+
+XclChEscherFormat::XclChEscherFormat()
+{
+}
+
+XclChEscherFormat::~XclChEscherFormat()
+{
+}
+
+XclChPicFormat::XclChPicFormat() :
+ mnBmpMode( EXC_CHPICFORMAT_NONE ),
+ mnFlags( EXC_CHPICFORMAT_TOPBOTTOM | EXC_CHPICFORMAT_FRONTBACK | EXC_CHPICFORMAT_LEFTRIGHT ),
+ mfScale( 0.5 )
+{
+}
+
+XclChFrame::XclChFrame() :
+ mnFormat( EXC_CHFRAME_STANDARD ),
+ mnFlags( EXC_CHFRAME_AUTOSIZE | EXC_CHFRAME_AUTOPOS )
+{
+}
+
+// Source links ===============================================================
+
+XclChSourceLink::XclChSourceLink() :
+ mnDestType( EXC_CHSRCLINK_TITLE ),
+ mnLinkType( EXC_CHSRCLINK_DEFAULT ),
+ mnFlags( 0 ),
+ mnNumFmtIdx( 0 )
+{
+}
+
+// Text =======================================================================
+
+XclChObjectLink::XclChObjectLink() :
+ mnTarget( EXC_CHOBJLINK_NONE )
+{
+}
+
+XclChFrLabelProps::XclChFrLabelProps() :
+ mnFlags( 0 )
+{
+}
+
+XclChText::XclChText() :
+ maTextColor( COL_BLACK ),
+ mnHAlign( EXC_CHTEXT_ALIGN_CENTER ),
+ mnVAlign( EXC_CHTEXT_ALIGN_CENTER ),
+ mnBackMode( EXC_CHTEXT_TRANSPARENT ),
+ mnFlags( EXC_CHTEXT_AUTOCOLOR | EXC_CHTEXT_AUTOFILL ),
+ mnFlags2( EXC_CHTEXT_POS_DEFAULT ),
+ mnRotation( EXC_ROT_NONE )
+{
+}
+
+// Data series ================================================================
+
+XclChMarkerFormat::XclChMarkerFormat() :
+ maLineColor( COL_BLACK ),
+ maFillColor( COL_WHITE ),
+ mnMarkerSize( EXC_CHMARKERFORMAT_SINGLESIZE ),
+ mnMarkerType( EXC_CHMARKERFORMAT_NOSYMBOL ),
+ mnFlags( EXC_CHMARKERFORMAT_AUTO )
+{
+};
+
+XclCh3dDataFormat::XclCh3dDataFormat() :
+ mnBase( EXC_CH3DDATAFORMAT_RECT ),
+ mnTop( EXC_CH3DDATAFORMAT_STRAIGHT )
+{
+}
+
+XclChDataFormat::XclChDataFormat() :
+ mnFormatIdx( EXC_CHDATAFORMAT_DEFAULT ),
+ mnFlags( 0 )
+{
+}
+
+XclChSerTrendLine::XclChSerTrendLine() :
+ mfForecastFor( 0.0 ),
+ mfForecastBack( 0.0 ),
+ mnLineType( EXC_CHSERTREND_POLYNOMIAL ),
+ mnOrder( 1 ),
+ mnShowEquation( 0 ),
+ mnShowRSquared( 0 )
+{
+ /* Set all bits in mfIntercept to 1 (that is -1.#NAN) to indicate that
+ there is no interception point. Cannot use ::rtl::math::setNan() here
+ cause it misses the sign bit. */
+ sal_math_Double* pDouble = reinterpret_cast< sal_math_Double* >( &mfIntercept );
+ pDouble->w32_parts.msw = pDouble->w32_parts.lsw = 0xFFFFFFFF;
+}
+
+XclChSerErrorBar::XclChSerErrorBar() :
+ mfValue( 0.0 ),
+ mnValueCount( 1 ),
+ mnBarType( EXC_CHSERERR_NONE ),
+ mnSourceType( EXC_CHSERERR_FIXED ),
+ mnLineEnd( EXC_CHSERERR_END_TSHAPE )
+{
+}
+
+XclChSeries::XclChSeries() :
+ mnCategType( EXC_CHSERIES_NUMERIC ),
+ mnValueType( EXC_CHSERIES_NUMERIC ),
+ mnBubbleType( EXC_CHSERIES_NUMERIC ),
+ mnCategCount( 0 ),
+ mnValueCount( 0 ),
+ mnBubbleCount( 0 )
+{
+}
+
+// Chart type groups ==========================================================
+
+XclChType::XclChType() :
+ mnOverlap( 0 ),
+ mnGap( 150 ),
+ mnRotation( 0 ),
+ mnPieHole( 0 ),
+ mnBubbleSize( 100 ),
+ mnBubbleType( EXC_CHSCATTER_AREA ),
+ mnFlags( 0 )
+{
+}
+
+XclChChart3d::XclChChart3d() :
+ mnRotation( 20 ),
+ mnElevation( 15 ),
+ mnEyeDist( 30 ),
+ mnRelHeight( 100 ),
+ mnRelDepth( 100 ),
+ mnDepthGap( 150 ),
+ mnFlags( EXC_CHCHART3D_AUTOHEIGHT )
+{
+}
+
+XclChLegend::XclChLegend() :
+ mnDockMode( EXC_CHLEGEND_RIGHT ),
+ mnSpacing( EXC_CHLEGEND_MEDIUM ),
+ mnFlags( EXC_CHLEGEND_DOCKED | EXC_CHLEGEND_AUTOSERIES |
+ EXC_CHLEGEND_AUTOPOSX | EXC_CHLEGEND_AUTOPOSY | EXC_CHLEGEND_STACKED )
+{
+}
+
+XclChTypeGroup::XclChTypeGroup() :
+ mnFlags( 0 ),
+ mnGroupIdx( EXC_CHSERGROUP_NONE )
+{
+}
+
+XclChProperties::XclChProperties() :
+ mnFlags( 0 ),
+ mnEmptyMode( EXC_CHPROPS_EMPTY_SKIP )
+{
+}
+
+// Axes =======================================================================
+
+XclChLabelRange::XclChLabelRange() :
+ mnCross( 1 ),
+ mnLabelFreq( 1 ),
+ mnTickFreq( 1 ),
+ mnFlags( 0 )
+{
+}
+
+XclChDateRange::XclChDateRange() :
+ mnMinDate( 0 ),
+ mnMaxDate( 0 ),
+ mnMajorStep( 0 ),
+ mnMajorUnit( EXC_CHDATERANGE_DAYS ),
+ mnMinorStep( 0 ),
+ mnMinorUnit( EXC_CHDATERANGE_DAYS ),
+ mnBaseUnit( EXC_CHDATERANGE_DAYS ),
+ mnCross( 0 ),
+ mnFlags( EXC_CHDATERANGE_AUTOMIN | EXC_CHDATERANGE_AUTOMAX |
+ EXC_CHDATERANGE_AUTOMAJOR | EXC_CHDATERANGE_AUTOMINOR |
+ EXC_CHDATERANGE_AUTOBASE | EXC_CHDATERANGE_AUTOCROSS |
+ EXC_CHDATERANGE_AUTODATE )
+{
+}
+
+XclChValueRange::XclChValueRange() :
+ mfMin( 0.0 ),
+ mfMax( 0.0 ),
+ mfMajorStep( 0.0 ),
+ mfMinorStep( 0.0 ),
+ mfCross( 0.0 ),
+ mnFlags( EXC_CHVALUERANGE_AUTOMIN | EXC_CHVALUERANGE_AUTOMAX |
+ EXC_CHVALUERANGE_AUTOMAJOR | EXC_CHVALUERANGE_AUTOMINOR |
+ EXC_CHVALUERANGE_AUTOCROSS | EXC_CHVALUERANGE_BIT8 )
+{
+}
+
+XclChTick::XclChTick() :
+ maTextColor( COL_BLACK ),
+ mnMajor( EXC_CHTICK_INSIDE | EXC_CHTICK_OUTSIDE ),
+ mnMinor( 0 ),
+ mnLabelPos( EXC_CHTICK_NEXT ),
+ mnBackMode( EXC_CHTICK_TRANSPARENT ),
+ mnFlags( EXC_CHTICK_AUTOCOLOR | EXC_CHTICK_AUTOROT ),
+ mnRotation( EXC_ROT_NONE )
+{
+}
+
+XclChAxis::XclChAxis() :
+ mnType( EXC_CHAXIS_NONE )
+{
+}
+
+sal_Int32 XclChAxis::GetApiAxisDimension() const
+{
+ sal_Int32 nApiAxisDim = EXC_CHART_AXIS_NONE;
+ switch( mnType )
+ {
+ case EXC_CHAXIS_X: nApiAxisDim = EXC_CHART_AXIS_X; break;
+ case EXC_CHAXIS_Y: nApiAxisDim = EXC_CHART_AXIS_Y; break;
+ case EXC_CHAXIS_Z: nApiAxisDim = EXC_CHART_AXIS_Z; break;
+ }
+ return nApiAxisDim;
+}
+
+XclChAxesSet::XclChAxesSet() :
+ mnAxesSetId( EXC_CHAXESSET_PRIMARY )
+{
+}
+
+sal_Int32 XclChAxesSet::GetApiAxesSetIndex() const
+{
+ sal_Int32 nApiAxesSetIdx = EXC_CHART_AXESSET_NONE;
+ switch( mnAxesSetId )
+ {
+ case EXC_CHAXESSET_PRIMARY: nApiAxesSetIdx = EXC_CHART_AXESSET_PRIMARY; break;
+ case EXC_CHAXESSET_SECONDARY: nApiAxesSetIdx = EXC_CHART_AXESSET_SECONDARY; break;
+ }
+ return nApiAxesSetIdx;
+}
+
+// Static helper functions ====================================================
+
+sal_uInt16 XclChartHelper::GetSeriesLineAutoColorIdx( sal_uInt16 nFormatIdx )
+{
+ static const sal_uInt16 spnLineColors[] =
+ {
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 63
+ };
+ return spnLineColors[ nFormatIdx % SAL_N_ELEMENTS( spnLineColors ) ];
+}
+
+sal_uInt16 XclChartHelper::GetSeriesFillAutoColorIdx( sal_uInt16 nFormatIdx )
+{
+ static const sal_uInt16 spnFillColors[] =
+ {
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23
+ };
+ return spnFillColors[ nFormatIdx % SAL_N_ELEMENTS( spnFillColors ) ];
+}
+
+sal_uInt8 XclChartHelper::GetSeriesFillAutoTransp( sal_uInt16 nFormatIdx )
+{
+ static const sal_uInt8 spnTrans[] = { 0x00, 0x40, 0x20, 0x60, 0x70 };
+ return spnTrans[ (nFormatIdx / 56) % SAL_N_ELEMENTS( spnTrans ) ];
+}
+
+sal_uInt16 XclChartHelper::GetAutoMarkerType( sal_uInt16 nFormatIdx )
+{
+ static const sal_uInt16 spnSymbols[] = {
+ EXC_CHMARKERFORMAT_DIAMOND, EXC_CHMARKERFORMAT_SQUARE, EXC_CHMARKERFORMAT_TRIANGLE,
+ EXC_CHMARKERFORMAT_CROSS, EXC_CHMARKERFORMAT_STAR, EXC_CHMARKERFORMAT_CIRCLE,
+ EXC_CHMARKERFORMAT_PLUS, EXC_CHMARKERFORMAT_DOWJ, EXC_CHMARKERFORMAT_STDDEV };
+ return spnSymbols[ nFormatIdx % SAL_N_ELEMENTS( spnSymbols ) ];
+}
+
+bool XclChartHelper::HasMarkerFillColor( sal_uInt16 nMarkerType )
+{
+ static const bool spbFilled[] = {
+ false, true, true, true, false, false, false, false, true, false };
+ return (nMarkerType < SAL_N_ELEMENTS( spbFilled )) && spbFilled[ nMarkerType ];
+}
+
+OUString XclChartHelper::GetErrorBarValuesRole( sal_uInt8 nBarType )
+{
+ switch( nBarType )
+ {
+ case EXC_CHSERERR_XPLUS: return EXC_CHPROP_ROLE_ERRORBARS_POSX;
+ case EXC_CHSERERR_XMINUS: return EXC_CHPROP_ROLE_ERRORBARS_NEGX;
+ case EXC_CHSERERR_YPLUS: return EXC_CHPROP_ROLE_ERRORBARS_POSY;
+ case EXC_CHSERERR_YMINUS: return EXC_CHPROP_ROLE_ERRORBARS_NEGY;
+ default: OSL_FAIL( "XclChartHelper::GetErrorBarValuesRole - unknown bar type" );
+ }
+ return OUString();
+}
+
+// Chart formatting info provider =============================================
+
+namespace {
+
+const XclChFormatInfo spFmtInfos[] =
+{
+ // object type property mode auto line color auto line weight auto pattern color missing frame type create delete isframe
+ { EXC_CHOBJTYPE_BACKGROUND, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, true, true, true },
+ { EXC_CHOBJTYPE_PLOTFRAME, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, true, true, true },
+ { EXC_CHOBJTYPE_WALL3D, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, true, false, true },
+ { EXC_CHOBJTYPE_FLOOR3D, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, 23, EXC_CHFRAMETYPE_AUTO, true, false, true },
+ { EXC_CHOBJTYPE_TEXT, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, true, true },
+ { EXC_CHOBJTYPE_LEGEND, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, true, true, true },
+ { EXC_CHOBJTYPE_LINEARSERIES, EXC_CHPROPMODE_LINEARSERIES, 0xFFFF, EXC_CHLINEFORMAT_SINGLE, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, false, false, false },
+ { EXC_CHOBJTYPE_FILLEDSERIES, EXC_CHPROPMODE_FILLEDSERIES, EXC_COLOR_CHBORDERAUTO, EXC_CHLINEFORMAT_SINGLE, 0xFFFF, EXC_CHFRAMETYPE_AUTO, false, false, true },
+ { EXC_CHOBJTYPE_AXISLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, false, false, false },
+ { EXC_CHOBJTYPE_GRIDLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, true, false },
+ { EXC_CHOBJTYPE_TRENDLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_DOUBLE, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false },
+ { EXC_CHOBJTYPE_ERRORBAR, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_SINGLE, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false },
+ { EXC_CHOBJTYPE_CONNECTLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false },
+ { EXC_CHOBJTYPE_HILOLINE, EXC_CHPROPMODE_LINEARSERIES, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false },
+ { EXC_CHOBJTYPE_WHITEDROPBAR, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, true },
+ { EXC_CHOBJTYPE_BLACKDROPBAR, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWTEXT, EXC_CHFRAMETYPE_INVISIBLE, false, false, true }
+};
+
+}
+
+XclChFormatInfoProvider::XclChFormatInfoProvider()
+{
+ for(auto const &rIt : spFmtInfos)
+ maInfoMap[ rIt.meObjType ] = &rIt;
+}
+
+const XclChFormatInfo& XclChFormatInfoProvider::GetFormatInfo( XclChObjectType eObjType ) const
+{
+ XclFmtInfoMap::const_iterator aIt = maInfoMap.find( eObjType );
+ OSL_ENSURE( aIt != maInfoMap.end(), "XclChFormatInfoProvider::GetFormatInfo - unknown object type" );
+ return (aIt == maInfoMap.end()) ? *spFmtInfos : *aIt->second;
+}
+
+// Chart type info provider ===================================================
+
+namespace {
+
+// chart type service names
+const char SERVICE_CHART2_AREA[] = "com.sun.star.chart2.AreaChartType";
+const char SERVICE_CHART2_CANDLE[] = "com.sun.star.chart2.CandleStickChartType";
+const char SERVICE_CHART2_COLUMN[] = "com.sun.star.chart2.ColumnChartType";
+const char SERVICE_CHART2_LINE[] = "com.sun.star.chart2.LineChartType";
+const char SERVICE_CHART2_NET[] = "com.sun.star.chart2.NetChartType";
+const char SERVICE_CHART2_FILLEDNET[] = "com.sun.star.chart2.FilledNetChartType";
+const char SERVICE_CHART2_PIE[] = "com.sun.star.chart2.PieChartType";
+const char SERVICE_CHART2_SCATTER[] = "com.sun.star.chart2.ScatterChartType";
+const char SERVICE_CHART2_BUBBLE[] = "com.sun.star.chart2.BubbleChartType";
+const char SERVICE_CHART2_SURFACE[] = "com.sun.star.chart2.ColumnChartType"; // Todo
+
+namespace csscd = css::chart::DataLabelPlacement;
+
+const XclChTypeInfo spTypeInfos[] =
+{
+ // chart type chart type category record id service varied point color def label pos comb2d 3d polar area2d area3d 1stvis xcateg swap stack revers betw
+ { EXC_CHTYPEID_BAR, EXC_CHTYPECATEG_BAR, EXC_ID_CHBAR, SERVICE_CHART2_COLUMN, EXC_CHVARPOINT_SINGLE, csscd::OUTSIDE, true, true, false, true, true, false, true, false, true, false, true },
+ { EXC_CHTYPEID_HORBAR, EXC_CHTYPECATEG_BAR, EXC_ID_CHBAR, SERVICE_CHART2_COLUMN, EXC_CHVARPOINT_SINGLE, csscd::OUTSIDE, false, true, false, true, true, false, true, true, true, false, true },
+ { EXC_CHTYPEID_LINE, EXC_CHTYPECATEG_LINE, EXC_ID_CHLINE, SERVICE_CHART2_LINE, EXC_CHVARPOINT_SINGLE, csscd::RIGHT, true, true, false, false, true, false, true, false, true, false, false },
+ { EXC_CHTYPEID_AREA, EXC_CHTYPECATEG_LINE, EXC_ID_CHAREA, SERVICE_CHART2_AREA, EXC_CHVARPOINT_NONE, csscd::CENTER, true, true, false, true, true, false, true, false, true, true, false },
+ { EXC_CHTYPEID_STOCK, EXC_CHTYPECATEG_LINE, EXC_ID_CHLINE, SERVICE_CHART2_CANDLE, EXC_CHVARPOINT_NONE, csscd::RIGHT, true, false, false, false, false, false, true, false, true, false, false },
+ { EXC_CHTYPEID_RADARLINE, EXC_CHTYPECATEG_RADAR, EXC_ID_CHRADARLINE, SERVICE_CHART2_NET, EXC_CHVARPOINT_SINGLE, csscd::TOP, false, false, true, false, true, false, true, false, false, false, false },
+ { EXC_CHTYPEID_RADARAREA, EXC_CHTYPECATEG_RADAR, EXC_ID_CHRADARAREA, SERVICE_CHART2_FILLEDNET, EXC_CHVARPOINT_NONE, csscd::TOP, false, false, true, true, true, false, true, false, false, true, false },
+ { EXC_CHTYPEID_PIE, EXC_CHTYPECATEG_PIE, EXC_ID_CHPIE, SERVICE_CHART2_PIE, EXC_CHVARPOINT_MULTI, csscd::AVOID_OVERLAP, false, true, true, true, true, true, true, false, false, false, false },
+ { EXC_CHTYPEID_DONUT, EXC_CHTYPECATEG_PIE, EXC_ID_CHPIE, SERVICE_CHART2_PIE, EXC_CHVARPOINT_MULTI, csscd::AVOID_OVERLAP, false, true, true, true, true, false, true, false, false, false, false },
+ { EXC_CHTYPEID_PIEEXT, EXC_CHTYPECATEG_PIE, EXC_ID_CHPIEEXT, SERVICE_CHART2_PIE, EXC_CHVARPOINT_MULTI, csscd::AVOID_OVERLAP, false, false, true, true, true, true, true, false, false, false, false },
+ { EXC_CHTYPEID_SCATTER, EXC_CHTYPECATEG_SCATTER, EXC_ID_CHSCATTER, SERVICE_CHART2_SCATTER, EXC_CHVARPOINT_SINGLE, csscd::RIGHT, true, false, false, false, true, false, false, false, false, false, false },
+ { EXC_CHTYPEID_BUBBLES, EXC_CHTYPECATEG_SCATTER, EXC_ID_CHSCATTER, SERVICE_CHART2_BUBBLE, EXC_CHVARPOINT_SINGLE, csscd::RIGHT, false, false, false, true, true, false, false, false, false, false, false },
+ { EXC_CHTYPEID_SURFACE, EXC_CHTYPECATEG_SURFACE, EXC_ID_CHSURFACE, SERVICE_CHART2_SURFACE, EXC_CHVARPOINT_NONE, csscd::RIGHT, false, true, false, true, true, false, true, false, false, false, false },
+ { EXC_CHTYPEID_UNKNOWN, EXC_CHTYPECATEG_BAR, EXC_ID_CHBAR, SERVICE_CHART2_COLUMN, EXC_CHVARPOINT_SINGLE, csscd::OUTSIDE, true, true, false, true, true, false, true, false, true, false, true }
+};
+
+} // namespace
+
+XclChExtTypeInfo::XclChExtTypeInfo( const XclChTypeInfo& rTypeInfo ) :
+ XclChTypeInfo( rTypeInfo ),
+ mb3dChart( false ),
+ mbSpline( false )
+{
+}
+
+void XclChExtTypeInfo::Set( const XclChTypeInfo& rTypeInfo, bool b3dChart, bool bSpline )
+{
+ static_cast< XclChTypeInfo& >( *this ) = rTypeInfo;
+ mb3dChart = mbSupports3d && b3dChart;
+ mbSpline = bSpline;
+}
+
+XclChTypeInfoProvider::XclChTypeInfoProvider()
+{
+ for(const auto &rIt : spTypeInfos)
+ maInfoMap[ rIt.meTypeId ] = &rIt;
+}
+
+const XclChTypeInfo& XclChTypeInfoProvider::GetTypeInfo( XclChTypeId eTypeId ) const
+{
+ XclChTypeInfoMap::const_iterator aIt = maInfoMap.find( eTypeId );
+ OSL_ENSURE( aIt != maInfoMap.end(), "XclChTypeInfoProvider::GetTypeInfo - unknown chart type" );
+ return (aIt == maInfoMap.end()) ? *maInfoMap.rbegin()->second : *aIt->second;
+}
+
+const XclChTypeInfo& XclChTypeInfoProvider::GetTypeInfoFromRecId( sal_uInt16 nRecId ) const
+{
+ for(const auto &rIt : spTypeInfos)
+ {
+ if(rIt.mnRecId == nRecId)
+ return rIt;
+ }
+ OSL_FAIL( "XclChTypeInfoProvider::GetTypeInfoFromRecId - unknown record id" );
+ return GetTypeInfo( EXC_CHTYPEID_UNKNOWN );
+}
+
+const XclChTypeInfo& XclChTypeInfoProvider::GetTypeInfoFromService( std::u16string_view rServiceName ) const
+{
+ for(auto const &rIt : spTypeInfos)
+ if( o3tl::equalsAscii( rServiceName, rIt.mpcServiceName ) )
+ return rIt;
+ OSL_FAIL( "XclChTypeInfoProvider::GetTypeInfoFromService - unknown service name" );
+ return GetTypeInfo( EXC_CHTYPEID_UNKNOWN );
+}
+
+// Property helpers ===========================================================
+
+XclChObjectTable::XclChObjectTable(uno::Reference<lang::XMultiServiceFactory> const & xFactory,
+ const OUString& rServiceName, const OUString& rObjNameBase ) :
+ mxFactory( xFactory ),
+ maServiceName( rServiceName ),
+ maObjNameBase( rObjNameBase ),
+ mnIndex( 0 )
+{
+}
+
+uno::Any XclChObjectTable::GetObject( const OUString& rObjName )
+{
+ // get object table
+ if( !mxContainer.is() )
+ mxContainer.set(ScfApiHelper::CreateInstance( mxFactory, maServiceName ), uno::UNO_QUERY);
+ OSL_ENSURE( mxContainer.is(), "XclChObjectTable::GetObject - container not found" );
+
+ uno::Any aObj;
+ if( mxContainer.is() )
+ {
+ // get object from container
+ try
+ {
+ aObj = mxContainer->getByName( rObjName );
+ }
+ catch (uno::Exception &)
+ {
+ OSL_FAIL( "XclChObjectTable::GetObject - object not found" );
+ }
+ }
+ return aObj;
+}
+
+OUString XclChObjectTable::InsertObject(const uno::Any& rObj)
+{
+
+ // create object table
+ if( !mxContainer.is() )
+ mxContainer.set(ScfApiHelper::CreateInstance( mxFactory, maServiceName ), uno::UNO_QUERY);
+ OSL_ENSURE( mxContainer.is(), "XclChObjectTable::InsertObject - container not found" );
+
+ OUString aObjName;
+ if( mxContainer.is() )
+ {
+ // create new unused identifier
+ do
+ {
+ aObjName = maObjNameBase + OUString::number( ++mnIndex );
+ }
+ while( mxContainer->hasByName( aObjName ) );
+
+ // insert object
+ try
+ {
+ mxContainer->insertByName( aObjName, rObj );
+ }
+ catch (uno::Exception &)
+ {
+ OSL_FAIL( "XclChObjectTable::InsertObject - cannot insert object" );
+ aObjName.clear();
+ }
+ }
+ return aObjName;
+}
+
+// Property names -------------------------------------------------------------
+
+namespace {
+
+/** Property names for line style in common objects. */
+const char* const sppcLineNamesCommon[] =
+ { "LineStyle", "LineWidth", "LineColor", "LineTransparence", "LineDashName", nullptr };
+/** Property names for line style in linear series objects. */
+const char* const sppcLineNamesLinear[] =
+ { "LineStyle", "LineWidth", "Color", "Transparency", "LineDashName", nullptr };
+/** Property names for line style in filled series objects. */
+const char* const sppcLineNamesFilled[] =
+ { "BorderStyle", "BorderWidth", "BorderColor", "BorderTransparency", "BorderDashName", nullptr };
+
+/** Property names for solid area style in common objects. */
+const char* const sppcAreaNamesCommon[] = { "FillStyle", "FillColor", "FillTransparence", nullptr };
+/** Property names for solid area style in filled series objects. */
+const char* const sppcAreaNamesFilled[] = { "FillStyle", "Color", "Transparency", nullptr };
+/** Property names for gradient area style in common objects. */
+const char* const sppcGradNamesCommon[] = { "FillStyle", "FillGradientName", nullptr };
+/** Property names for gradient area style in filled series objects. */
+const char* const sppcGradNamesFilled[] = { "FillStyle", "GradientName", nullptr };
+/** Property names for hatch area style in common objects. */
+const char* const sppcHatchNamesCommon[] = { "FillStyle", "FillHatchName", "FillColor", "FillBackground", nullptr };
+/** Property names for hatch area style in filled series objects. */
+const char* const sppcHatchNamesFilled[] = { "FillStyle", "HatchName", "Color", "FillBackground", nullptr };
+/** Property names for bitmap area style. */
+const char* const sppcBitmapNames[] = { "FillStyle", "FillBitmapName", "FillBitmapMode", nullptr };
+
+} // namespace
+
+XclChPropSetHelper::XclChPropSetHelper() :
+ maLineHlpCommon( sppcLineNamesCommon ),
+ maLineHlpLinear( sppcLineNamesLinear ),
+ maLineHlpFilled( sppcLineNamesFilled ),
+ maAreaHlpCommon( sppcAreaNamesCommon ),
+ maAreaHlpFilled( sppcAreaNamesFilled ),
+ maGradHlpCommon( sppcGradNamesCommon ),
+ maGradHlpFilled( sppcGradNamesFilled ),
+ maHatchHlpCommon( sppcHatchNamesCommon ),
+ maHatchHlpFilled( sppcHatchNamesFilled ),
+ maBitmapHlp( sppcBitmapNames )
+{
+}
+
+// read properties ------------------------------------------------------------
+
+void XclChPropSetHelper::ReadLineProperties(
+ XclChLineFormat& rLineFmt, XclChObjectTable& rDashTable,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode )
+{
+ // read properties from property set
+ drawing::LineStyle eApiStyle = drawing::LineStyle_NONE;
+ sal_Int32 nApiWidth = 0;
+ sal_Int16 nApiTrans = 0;
+ uno::Any aDashNameAny;
+
+ ScfPropSetHelper& rLineHlp = GetLineHelper( ePropMode );
+ rLineHlp.ReadFromPropertySet( rPropSet );
+ rLineHlp >> eApiStyle >> nApiWidth >> rLineFmt.maColor >> nApiTrans >> aDashNameAny;
+
+ // clear automatic flag
+ ::set_flag( rLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, false );
+
+ // line width
+ if( nApiWidth <= 0 ) rLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR;
+ else if( nApiWidth <= 35 ) rLineFmt.mnWeight = EXC_CHLINEFORMAT_SINGLE;
+ else if( nApiWidth <= 70 ) rLineFmt.mnWeight = EXC_CHLINEFORMAT_DOUBLE;
+ else rLineFmt.mnWeight = EXC_CHLINEFORMAT_TRIPLE;
+
+ // line style
+ switch( eApiStyle )
+ {
+ case drawing::LineStyle_NONE:
+ rLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE;
+ break;
+ case drawing::LineStyle_SOLID:
+ {
+ if( nApiTrans < 13 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ else if( nApiTrans < 38 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_DARKTRANS;
+ else if( nApiTrans < 63 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_MEDTRANS;
+ else if( nApiTrans < 100 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_LIGHTTRANS;
+ else rLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE;
+ }
+ break;
+ case drawing::LineStyle_DASH:
+ {
+ rLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ OUString aDashName;
+ drawing::LineDash aApiDash;
+ if( (aDashNameAny >>= aDashName) && (rDashTable.GetObject( aDashName ) >>= aApiDash) )
+ {
+ // reorder dashes that are shorter than dots
+ if( (aApiDash.Dashes == 0) || (aApiDash.DashLen < aApiDash.DotLen) )
+ {
+ ::std::swap( aApiDash.Dashes, aApiDash.Dots );
+ ::std::swap( aApiDash.DashLen, aApiDash.DotLen );
+ }
+ // ignore dots that are nearly equal to dashes
+ if( aApiDash.DotLen * 3 > aApiDash.DashLen * 2 )
+ aApiDash.Dots = 0;
+
+ // convert line dash to predefined Excel dash types
+ if( (aApiDash.Dashes == 1) && (aApiDash.Dots >= 1) )
+ // one dash and one or more dots
+ rLineFmt.mnPattern = (aApiDash.Dots == 1) ?
+ EXC_CHLINEFORMAT_DASHDOT : EXC_CHLINEFORMAT_DASHDOTDOT;
+ else if( aApiDash.Dashes >= 1 )
+ // one or more dashes and no dots (also: dash-dash-dot)
+ rLineFmt.mnPattern = (aApiDash.DashLen < 250) ?
+ EXC_CHLINEFORMAT_DOT : EXC_CHLINEFORMAT_DASH;
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclChPropSetHelper::ReadLineProperties - unknown line style" );
+ rLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ }
+}
+
+bool XclChPropSetHelper::ReadAreaProperties( XclChAreaFormat& rAreaFmt,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode )
+{
+ // read properties from property set
+ drawing::FillStyle eApiStyle = drawing::FillStyle_NONE;
+ sal_Int16 nTransparency = 0;
+
+ ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode );
+ rAreaHlp.ReadFromPropertySet( rPropSet );
+ rAreaHlp >> eApiStyle >> rAreaFmt.maPattColor >> nTransparency;
+
+ // clear automatic flag
+ ::set_flag( rAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, false );
+
+ // set fill style transparent or solid (set solid for anything but transparent)
+ rAreaFmt.mnPattern = (eApiStyle == drawing::FillStyle_NONE) ? EXC_PATT_NONE : EXC_PATT_SOLID;
+
+ // return true to indicate complex fill (gradient, bitmap, solid transparency)
+ return (eApiStyle != drawing::FillStyle_NONE) && ((eApiStyle != drawing::FillStyle_SOLID) || (nTransparency > 0));
+}
+
+void XclChPropSetHelper::ReadEscherProperties(
+ XclChEscherFormat& rEscherFmt, XclChPicFormat& rPicFmt,
+ XclChObjectTable& rGradientTable, XclChObjectTable& rHatchTable, XclChObjectTable& rBitmapTable,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode )
+{
+ // read style and transparency properties from property set
+ drawing::FillStyle eApiStyle = drawing::FillStyle_NONE;
+ Color aColor;
+ sal_Int16 nTransparency = 0;
+
+ ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode );
+ rAreaHlp.ReadFromPropertySet( rPropSet );
+ rAreaHlp >> eApiStyle >> aColor >> nTransparency;
+
+ switch( eApiStyle )
+ {
+ case drawing::FillStyle_SOLID:
+ {
+ OSL_ENSURE( nTransparency > 0, "XclChPropSetHelper::ReadEscherProperties - unexpected solid area without transparency" );
+ if( (0 < nTransparency) && (nTransparency <= 100) )
+ {
+ // convert to Escher properties
+ sal_uInt32 nEscherColor = 0x02000000;
+ ::insert_value( nEscherColor, aColor.GetBlue(), 16, 8 );
+ ::insert_value( nEscherColor, aColor.GetGreen(), 8, 8 );
+ ::insert_value( nEscherColor, aColor.GetRed(), 0, 8 );
+ sal_uInt32 nEscherOpacity = static_cast< sal_uInt32 >( (100 - nTransparency) * 655.36 );
+ rEscherFmt.mxEscherSet = std::make_shared<EscherPropertyContainer>();
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillType, ESCHER_FillSolid );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillColor, nEscherColor );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillOpacity, nEscherOpacity );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillBackColor, 0x02FFFFFF );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillBackOpacity, 0x00010000 );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fNoFillHitTest, 0x001F001C );
+ }
+ }
+ break;
+ case drawing::FillStyle_GRADIENT:
+ {
+ // extract gradient from global gradient table
+ OUString aGradientName;
+ ScfPropSetHelper& rGradHlp = GetGradientHelper( ePropMode );
+ rGradHlp.ReadFromPropertySet( rPropSet );
+ rGradHlp >> eApiStyle >> aGradientName;
+ awt::Gradient aGradient;
+ if( rGradientTable.GetObject( aGradientName ) >>= aGradient )
+ {
+ // convert to Escher properties
+ rEscherFmt.mxEscherSet = std::make_shared<EscherPropertyContainer>();
+ rEscherFmt.mxEscherSet->CreateGradientProperties( aGradient );
+ }
+ }
+ break;
+ case drawing::FillStyle_HATCH:
+ {
+ // extract hatch from global hatch table
+ OUString aHatchName;
+ bool bFillBackground;
+ ScfPropSetHelper& rHatchHlp = GetHatchHelper( ePropMode );
+ rHatchHlp.ReadFromPropertySet( rPropSet );
+ rHatchHlp >> eApiStyle >> aHatchName >> aColor >> bFillBackground;
+ drawing::Hatch aHatch;
+ if( rHatchTable.GetObject( aHatchName ) >>= aHatch )
+ {
+ // convert to Escher properties
+ rEscherFmt.mxEscherSet = std::make_shared<EscherPropertyContainer>();
+ rEscherFmt.mxEscherSet->CreateEmbeddedHatchProperties( aHatch, aColor, bFillBackground );
+ rPicFmt.mnBmpMode = EXC_CHPICFORMAT_STACK;
+ }
+ }
+ break;
+ case drawing::FillStyle_BITMAP:
+ {
+ // extract bitmap URL from global bitmap table
+ OUString aBitmapName;
+ drawing::BitmapMode eApiBmpMode;
+ maBitmapHlp.ReadFromPropertySet( rPropSet );
+ maBitmapHlp >> eApiStyle >> aBitmapName >> eApiBmpMode;
+ uno::Reference<awt::XBitmap> xBitmap;
+ if (rBitmapTable.GetObject( aBitmapName ) >>= xBitmap)
+ {
+ // convert to Escher properties
+ rEscherFmt.mxEscherSet = std::make_shared<EscherPropertyContainer>();
+ rEscherFmt.mxEscherSet->CreateEmbeddedBitmapProperties( xBitmap, eApiBmpMode );
+ rPicFmt.mnBmpMode = (eApiBmpMode == drawing::BitmapMode_REPEAT) ?
+ EXC_CHPICFORMAT_STACK : EXC_CHPICFORMAT_STRETCH;
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclChPropSetHelper::ReadEscherProperties - unknown fill style" );
+ }
+}
+
+void XclChPropSetHelper::ReadMarkerProperties(
+ XclChMarkerFormat& rMarkerFmt, const ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx )
+{
+ chart2::Symbol aApiSymbol;
+ if( !rPropSet.GetProperty( aApiSymbol, EXC_CHPROP_SYMBOL ) )
+ return;
+
+ // clear automatic flag
+ ::set_flag( rMarkerFmt.mnFlags, EXC_CHMARKERFORMAT_AUTO, false );
+
+ // symbol style
+ switch( aApiSymbol.Style )
+ {
+ case chart2::SymbolStyle_NONE:
+ rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_NOSYMBOL;
+ break;
+ case chart2::SymbolStyle_STANDARD:
+ switch( aApiSymbol.StandardSymbol )
+ {
+ case 0: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_SQUARE; break; // square
+ case 1: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_DIAMOND; break; // diamond
+ case 2: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STDDEV; break; // arrow down
+ case 3: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_TRIANGLE; break; // arrow up
+ case 4: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_DOWJ; break; // arrow right, same as import
+ case 5: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_PLUS; break; // arrow left
+ case 6: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_CROSS; break; // bow tie
+ case 7: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STAR; break; // sand glass
+ case 8: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_CIRCLE; break; // circle new in LibO3.5
+ case 9: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_DIAMOND; break; // star new in LibO3.5
+ case 10: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_CROSS; break; // X new in LibO3.5
+ case 11: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_PLUS; break; // plus new in LibO3.5
+ case 12: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STAR; break; // asterisk new in LibO3.5
+ case 13: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STDDEV; break; // horizontal bar new in LibO3.5
+ case 14: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STAR; break; // vertical bar new in LibO3.5
+ default: rMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx );
+ }
+ break;
+ default:
+ rMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx );
+ }
+ bool bHasFillColor = XclChartHelper::HasMarkerFillColor( rMarkerFmt.mnMarkerType );
+ ::set_flag( rMarkerFmt.mnFlags, EXC_CHMARKERFORMAT_NOFILL, !bHasFillColor );
+
+ // symbol size
+ sal_Int32 nApiSize = (aApiSymbol.Size.Width + aApiSymbol.Size.Height + 1) / 2;
+ rMarkerFmt.mnMarkerSize = XclTools::GetTwipsFromHmm( nApiSize );
+
+ // symbol colors
+ rMarkerFmt.maLineColor = Color( ColorTransparency, aApiSymbol.BorderColor );
+ rMarkerFmt.maFillColor = Color( ColorTransparency, aApiSymbol.FillColor );
+}
+
+sal_uInt16 XclChPropSetHelper::ReadRotationProperties( const ScfPropertySet& rPropSet, bool bSupportsStacked )
+{
+ // chart2 handles rotation as double in the range [0,360)
+ double fAngle = 0.0;
+ rPropSet.GetProperty( fAngle, EXC_CHPROP_TEXTROTATION );
+ bool bStacked = bSupportsStacked && rPropSet.GetBoolProperty( EXC_CHPROP_STACKCHARACTERS );
+ return bStacked ? EXC_ROT_STACKED :
+ XclTools::GetXclRotation( Degree100(static_cast< sal_Int32 >( fAngle * 100.0 + 0.5 )) );
+}
+
+// write properties -----------------------------------------------------------
+
+void XclChPropSetHelper::WriteLineProperties(
+ ScfPropertySet& rPropSet, XclChObjectTable& rDashTable,
+ const XclChLineFormat& rLineFmt, XclChPropertyMode ePropMode )
+{
+ // line width
+ sal_Int32 nApiWidth = 0; // 0 is the width of a hair line
+ switch( rLineFmt.mnWeight )
+ {
+ case EXC_CHLINEFORMAT_SINGLE: nApiWidth = 35; break;
+ case EXC_CHLINEFORMAT_DOUBLE: nApiWidth = 70; break;
+ case EXC_CHLINEFORMAT_TRIPLE: nApiWidth = 105; break;
+ }
+
+ // line style
+ drawing::LineStyle eApiStyle = drawing::LineStyle_NONE;
+ sal_Int16 nApiTrans = 0;
+ sal_Int32 nDotLen = ::std::min< sal_Int32 >( rLineFmt.mnWeight + 105, 210 );
+ drawing::LineDash aApiDash( drawing::DashStyle_RECT, 0, nDotLen, 0, 4 * nDotLen, nDotLen );
+
+ switch( rLineFmt.mnPattern )
+ {
+ case EXC_CHLINEFORMAT_SOLID:
+ eApiStyle = drawing::LineStyle_SOLID;
+ break;
+ case EXC_CHLINEFORMAT_DARKTRANS:
+ eApiStyle = drawing::LineStyle_SOLID; nApiTrans = 25;
+ break;
+ case EXC_CHLINEFORMAT_MEDTRANS:
+ eApiStyle = drawing::LineStyle_SOLID; nApiTrans = 50;
+ break;
+ case EXC_CHLINEFORMAT_LIGHTTRANS:
+ eApiStyle = drawing::LineStyle_SOLID; nApiTrans = 75;
+ break;
+ case EXC_CHLINEFORMAT_DASH:
+ eApiStyle = drawing::LineStyle_DASH; aApiDash.Dashes = 1;
+ break;
+ case EXC_CHLINEFORMAT_DOT:
+ eApiStyle = drawing::LineStyle_DASH; aApiDash.Dots = 1;
+ break;
+ case EXC_CHLINEFORMAT_DASHDOT:
+ eApiStyle = drawing::LineStyle_DASH; aApiDash.Dashes = aApiDash.Dots = 1;
+ break;
+ case EXC_CHLINEFORMAT_DASHDOTDOT:
+ eApiStyle = drawing::LineStyle_DASH; aApiDash.Dashes = 1; aApiDash.Dots = 2;
+ break;
+ }
+
+ // line color
+ sal_Int32 nApiColor = sal_Int32( rLineFmt.maColor );
+
+ // try to insert the dash style and receive its name
+ uno::Any aDashNameAny;
+ if( eApiStyle == drawing::LineStyle_DASH )
+ {
+ OUString aDashName = rDashTable.InsertObject( uno::Any( aApiDash ) );
+ if( !aDashName.isEmpty() )
+ aDashNameAny <<= aDashName;
+ }
+
+ // write the properties
+ ScfPropSetHelper& rLineHlp = GetLineHelper( ePropMode );
+ rLineHlp.InitializeWrite();
+ rLineHlp << eApiStyle << nApiWidth << nApiColor << nApiTrans << aDashNameAny;
+ rLineHlp.WriteToPropertySet( rPropSet );
+}
+
+void XclChPropSetHelper::WriteAreaProperties( ScfPropertySet& rPropSet,
+ const XclChAreaFormat& rAreaFmt, XclChPropertyMode ePropMode )
+{
+ drawing::FillStyle eFillStyle = drawing::FillStyle_NONE;
+ Color aColor;
+
+ // fill color
+ if( rAreaFmt.mnPattern != EXC_PATT_NONE )
+ {
+ eFillStyle = drawing::FillStyle_SOLID;
+ aColor = XclTools::GetPatternColor( rAreaFmt.maPattColor, rAreaFmt.maBackColor, rAreaFmt.mnPattern );
+ }
+
+ // write the properties
+ ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode );
+ rAreaHlp.InitializeWrite();
+ rAreaHlp << eFillStyle << aColor << sal_Int16(0)/*nTransparency*/;
+ rAreaHlp.WriteToPropertySet( rPropSet );
+}
+
+void XclChPropSetHelper::WriteEscherProperties( ScfPropertySet& rPropSet,
+ XclChObjectTable& rGradientTable, XclChObjectTable& rBitmapTable,
+ const XclChEscherFormat& rEscherFmt, const XclChPicFormat* pPicFmt,
+ sal_uInt32 nDffFillType, XclChPropertyMode ePropMode )
+{
+ if( !rEscherFmt.mxItemSet )
+ return;
+
+ const XFillStyleItem* pStyleItem = rEscherFmt.mxItemSet->GetItem<XFillStyleItem>( XATTR_FILLSTYLE, false );
+ if( !pStyleItem )
+ return;
+
+ switch( pStyleItem->GetValue() )
+ {
+ case drawing::FillStyle_SOLID:
+ // #i84812# Excel 2007 writes Escher properties for solid fill
+ if( const XFillColorItem* pColorItem = rEscherFmt.mxItemSet->GetItem<XFillColorItem>( XATTR_FILLCOLOR, false ) )
+ {
+ // get solid transparence too
+ const XFillTransparenceItem* pTranspItem = rEscherFmt.mxItemSet->GetItem<XFillTransparenceItem>( XATTR_FILLTRANSPARENCE, false );
+ sal_uInt16 nTransp = pTranspItem ? pTranspItem->GetValue() : 0;
+ ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode );
+ rAreaHlp.InitializeWrite();
+ rAreaHlp << drawing::FillStyle_SOLID << pColorItem->GetColorValue() << static_cast< sal_Int16 >( nTransp );
+ rAreaHlp.WriteToPropertySet( rPropSet );
+ }
+ break;
+ case drawing::FillStyle_GRADIENT:
+ if( const XFillGradientItem* pGradItem = rEscherFmt.mxItemSet->GetItem<XFillGradientItem>( XATTR_FILLGRADIENT, false ) )
+ {
+ uno::Any aGradientAny;
+ if( pGradItem->QueryValue( aGradientAny, MID_FILLGRADIENT ) )
+ {
+ OUString aGradName = rGradientTable.InsertObject( aGradientAny );
+ if( !aGradName.isEmpty() )
+ {
+ ScfPropSetHelper& rGradHlp = GetGradientHelper( ePropMode );
+ rGradHlp.InitializeWrite();
+ rGradHlp << drawing::FillStyle_GRADIENT << aGradName;
+ rGradHlp.WriteToPropertySet( rPropSet );
+ }
+ }
+ }
+ break;
+ case drawing::FillStyle_BITMAP:
+ if( const XFillBitmapItem* pBmpItem = rEscherFmt.mxItemSet->GetItem<XFillBitmapItem>( XATTR_FILLBITMAP, false ) )
+ {
+ uno::Any aBitmapAny;
+ if (pBmpItem->QueryValue(aBitmapAny, MID_BITMAP))
+ {
+ OUString aBmpName = rBitmapTable.InsertObject( aBitmapAny );
+ if( !aBmpName.isEmpty() )
+ {
+ /* #i71810# Caller decides whether to use a CHPICFORMAT record for bitmap mode.
+ If not passed, detect fill mode from the DFF property 'fill-type'. */
+ bool bStretch = pPicFmt ? (pPicFmt->mnBmpMode == EXC_CHPICFORMAT_STRETCH) : (nDffFillType == mso_fillPicture);
+ drawing::BitmapMode eApiBmpMode = bStretch ? drawing::BitmapMode_STRETCH : drawing::BitmapMode_REPEAT;
+ maBitmapHlp.InitializeWrite();
+ maBitmapHlp << drawing::FillStyle_BITMAP << aBmpName << eApiBmpMode;
+ maBitmapHlp.WriteToPropertySet( rPropSet );
+ }
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclChPropSetHelper::WriteEscherProperties - unknown fill mode" );
+ }
+}
+
+void XclChPropSetHelper::WriteMarkerProperties(
+ ScfPropertySet& rPropSet, const XclChMarkerFormat& rMarkerFmt )
+{
+ // symbol style
+ chart2::Symbol aApiSymbol;
+ aApiSymbol.Style = chart2::SymbolStyle_STANDARD;
+ switch( rMarkerFmt.mnMarkerType )
+ {
+ case EXC_CHMARKERFORMAT_NOSYMBOL: aApiSymbol.Style = chart2::SymbolStyle_NONE; break;
+ case EXC_CHMARKERFORMAT_SQUARE: aApiSymbol.StandardSymbol = 0; break; // square
+ case EXC_CHMARKERFORMAT_DIAMOND: aApiSymbol.StandardSymbol = 1; break; // diamond
+ case EXC_CHMARKERFORMAT_TRIANGLE: aApiSymbol.StandardSymbol = 3; break; // arrow up
+ case EXC_CHMARKERFORMAT_CROSS: aApiSymbol.StandardSymbol = 10; break; // X, legacy bow tie
+ case EXC_CHMARKERFORMAT_STAR: aApiSymbol.StandardSymbol = 12; break; // asterisk, legacy sand glass
+ case EXC_CHMARKERFORMAT_DOWJ: aApiSymbol.StandardSymbol = 4; break; // arrow right, same as export
+ case EXC_CHMARKERFORMAT_STDDEV: aApiSymbol.StandardSymbol = 13; break; // horizontal bar, legacy arrow down
+ case EXC_CHMARKERFORMAT_CIRCLE: aApiSymbol.StandardSymbol = 8; break; // circle, legacy arrow right
+ case EXC_CHMARKERFORMAT_PLUS: aApiSymbol.StandardSymbol = 11; break; // plus, legacy arrow left
+ default: break;
+ }
+
+ // symbol size
+ sal_Int32 nApiSize = XclTools::GetHmmFromTwips( rMarkerFmt.mnMarkerSize );
+ aApiSymbol.Size = awt::Size( nApiSize, nApiSize );
+
+ // symbol colors
+ aApiSymbol.FillColor = sal_Int32( rMarkerFmt.maFillColor );
+ aApiSymbol.BorderColor = ::get_flag( rMarkerFmt.mnFlags, EXC_CHMARKERFORMAT_NOLINE ) ?
+ aApiSymbol.FillColor : sal_Int32( rMarkerFmt.maLineColor );
+
+ // set the property
+ rPropSet.SetProperty( EXC_CHPROP_SYMBOL, aApiSymbol );
+}
+
+void XclChPropSetHelper::WriteRotationProperties(
+ ScfPropertySet& rPropSet, sal_uInt16 nRotation, bool bSupportsStacked )
+{
+ if( nRotation != EXC_CHART_AUTOROTATION )
+ {
+ // chart2 handles rotation as double in the range [0,360)
+ double fAngle = XclTools::GetScRotation( nRotation, 0_deg100 ).get() / 100.0;
+ rPropSet.SetProperty( EXC_CHPROP_TEXTROTATION, fAngle );
+ if( bSupportsStacked )
+ rPropSet.SetProperty( EXC_CHPROP_STACKCHARACTERS, nRotation == EXC_ROT_STACKED );
+ }
+}
+
+// private --------------------------------------------------------------------
+
+ScfPropSetHelper& XclChPropSetHelper::GetLineHelper( XclChPropertyMode ePropMode )
+{
+ switch( ePropMode )
+ {
+ case EXC_CHPROPMODE_COMMON: return maLineHlpCommon;
+ case EXC_CHPROPMODE_LINEARSERIES: return maLineHlpLinear;
+ case EXC_CHPROPMODE_FILLEDSERIES: return maLineHlpFilled;
+ default: OSL_FAIL( "XclChPropSetHelper::GetLineHelper - unknown property mode" );
+ }
+ return maLineHlpCommon;
+}
+
+ScfPropSetHelper& XclChPropSetHelper::GetAreaHelper( XclChPropertyMode ePropMode )
+{
+ switch( ePropMode )
+ {
+ case EXC_CHPROPMODE_COMMON: return maAreaHlpCommon;
+ case EXC_CHPROPMODE_FILLEDSERIES: return maAreaHlpFilled;
+ default: OSL_FAIL( "XclChPropSetHelper::GetAreaHelper - unknown property mode" );
+ }
+ return maAreaHlpCommon;
+}
+
+ScfPropSetHelper& XclChPropSetHelper::GetGradientHelper( XclChPropertyMode ePropMode )
+{
+ switch( ePropMode )
+ {
+ case EXC_CHPROPMODE_COMMON: return maGradHlpCommon;
+ case EXC_CHPROPMODE_FILLEDSERIES: return maGradHlpFilled;
+ default: OSL_FAIL( "XclChPropSetHelper::GetGradientHelper - unknown property mode" );
+ }
+ return maGradHlpCommon;
+}
+
+ScfPropSetHelper& XclChPropSetHelper::GetHatchHelper( XclChPropertyMode ePropMode )
+{
+ switch( ePropMode )
+ {
+ case EXC_CHPROPMODE_COMMON: return maHatchHlpCommon;
+ case EXC_CHPROPMODE_FILLEDSERIES: return maHatchHlpFilled;
+ default: OSL_FAIL( "XclChPropSetHelper::GetHatchHelper - unknown property mode" );
+ }
+ return maHatchHlpCommon;
+}
+
+namespace {
+
+/* The following local functions implement getting the XShape interface of all
+ supported title objects (chart and axes). This needs some effort due to the
+ design of the old Chart1 API used to access these objects. */
+
+/** Returns the drawing shape of the main title, if existing. */
+uno::Reference<drawing::XShape> lclGetMainTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc)
+{
+ ScfPropertySet aPropSet(rxChart1Doc);
+ if (rxChart1Doc.is() && aPropSet.GetBoolProperty("HasMainTitle"))
+ return rxChart1Doc->getTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetXAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc)
+{
+ uno::Reference<chart::XAxisXSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasXAxisTitle"))
+ return xAxisSupp->getXAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetYAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc )
+{
+ uno::Reference<chart::XAxisYSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasYAxisTitle"))
+ return xAxisSupp->getYAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetZAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc )
+{
+ uno::Reference<chart::XAxisZSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasZAxisTitle"))
+ return xAxisSupp->getZAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetSecXAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc)
+{
+ uno::Reference<chart::XSecondAxisTitleSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasSecondaryXAxisTitle"))
+ return xAxisSupp->getSecondXAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetSecYAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc)
+{
+ uno::Reference<chart::XSecondAxisTitleSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasSecondaryYAxisTitle"))
+ return xAxisSupp->getSecondYAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+} // namespace
+
+XclChRootData::XclChRootData()
+ : mxTypeInfoProv(std::make_shared<XclChTypeInfoProvider>())
+ , mxFmtInfoProv(std::make_shared<XclChFormatInfoProvider>())
+ , mnBorderGapX(0)
+ , mnBorderGapY(0)
+ , mfUnitSizeX(0.0)
+ , mfUnitSizeY(0.0)
+{
+ // remember some title shape getter functions
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_TITLE ) ] = lclGetMainTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_PRIMARY, EXC_CHAXIS_X ) ] = lclGetXAxisTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_PRIMARY, EXC_CHAXIS_Y ) ] = lclGetYAxisTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_PRIMARY, EXC_CHAXIS_Z ) ] = lclGetZAxisTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_SECONDARY, EXC_CHAXIS_X ) ] = lclGetSecXAxisTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_SECONDARY, EXC_CHAXIS_Y ) ] = lclGetSecYAxisTitleShape;
+}
+
+XclChRootData::~XclChRootData()
+{
+}
+
+void XclChRootData::InitConversion(const XclRoot& rRoot, const uno::Reference<chart2::XChartDocument> & rxChartDoc, const tools::Rectangle& rChartRect)
+{
+ // remember chart document reference and chart shape position/size
+ OSL_ENSURE( rxChartDoc.is(), "XclChRootData::InitConversion - missing chart document" );
+ mxChartDoc = rxChartDoc;
+ maChartRect = rChartRect;
+
+ // Excel excludes a border of 5 pixels in each direction from chart area
+ mnBorderGapX = rRoot.GetHmmFromPixelX( 5.0 );
+ mnBorderGapY = rRoot.GetHmmFromPixelY( 5.0 );
+
+ // size of a chart unit in 1/100 mm
+ mfUnitSizeX = std::max<double>( maChartRect.GetWidth() - 2 * mnBorderGapX, mnBorderGapX ) / EXC_CHART_TOTALUNITS;
+ mfUnitSizeY = std::max<double>( maChartRect.GetHeight() - 2 * mnBorderGapY, mnBorderGapY ) / EXC_CHART_TOTALUNITS;
+
+ // create object tables
+ uno::Reference<lang::XMultiServiceFactory> xFactory(mxChartDoc, uno::UNO_QUERY);
+ mxLineDashTable = std::make_shared<XclChObjectTable>(xFactory, SERVICE_DRAWING_DASHTABLE, "Excel line dash ");
+ mxGradientTable = std::make_shared<XclChObjectTable>(xFactory, SERVICE_DRAWING_GRADIENTTABLE, "Excel gradient ");
+ mxHatchTable = std::make_shared<XclChObjectTable>(xFactory, SERVICE_DRAWING_HATCHTABLE, "Excel hatch ");
+ mxBitmapTable = std::make_shared<XclChObjectTable>(xFactory, SERVICE_DRAWING_BITMAPTABLE, "Excel bitmap ");
+}
+
+void XclChRootData::FinishConversion()
+{
+ // forget formatting object tables
+ mxBitmapTable.reset();
+ mxHatchTable.reset();
+ mxGradientTable.reset();
+ mxLineDashTable.reset();
+ // forget chart document reference
+ mxChartDoc.clear();
+}
+
+uno::Reference<drawing::XShape> XclChRootData::GetTitleShape(const XclChTextKey& rTitleKey) const
+{
+ XclChGetShapeFuncMap::const_iterator aIt = maGetShapeFuncs.find( rTitleKey );
+ OSL_ENSURE( aIt != maGetShapeFuncs.end(), "XclChRootData::GetTitleShape - invalid title key" );
+ uno::Reference<chart::XChartDocument> xChart1Doc( mxChartDoc, uno::UNO_QUERY );
+ uno::Reference<drawing::XShape> xTitleShape;
+ if (xChart1Doc.is() && (aIt != maGetShapeFuncs.end()))
+ xTitleShape = (aIt->second)(xChart1Doc);
+ return xTitleShape;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlescher.cxx b/sc/source/filter/excel/xlescher.cxx
new file mode 100644
index 000000000..8ae663cdd
--- /dev/null
+++ b/sc/source/filter/excel/xlescher.cxx
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <xlescher.hxx>
+
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <tools/UnitConversion.hxx>
+#include <document.hxx>
+#include <xistream.hxx>
+#include <xlroot.hxx>
+#include <xltools.hxx>
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::drawing::XControlShape;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::script::ScriptEventDescriptor;
+
+namespace {
+
+/** Returns the scaling factor to calculate coordinates from twips. */
+double lclGetTwipsScale( MapUnit eMapUnit )
+{
+ double fScale = 1.0;
+ if (const auto eTo = MapToO3tlLength(eMapUnit); eTo != o3tl::Length::invalid)
+ fScale = o3tl::convert(1.0, o3tl::Length::twip, eTo);
+ else
+ OSL_FAIL("lclGetTwipsScale - map unit not implemented");
+ return fScale;
+}
+
+/** Calculates a drawing layer X position (in twips) from an object column position. */
+tools::Long lclGetXFromCol( const ScDocument& rDoc, SCTAB nScTab, sal_uInt16 nXclCol, sal_uInt16 nOffset, double fScale )
+{
+ SCCOL nScCol = static_cast< SCCOL >( nXclCol );
+ return static_cast< tools::Long >( fScale * (rDoc.GetColOffset( nScCol, nScTab ) +
+ ::std::min( nOffset / 1024.0, 1.0 ) * rDoc.GetColWidth( nScCol, nScTab )) + 0.5 );
+}
+
+/** Calculates a drawing layer Y position (in twips) from an object row position. */
+tools::Long lclGetYFromRow( const ScDocument& rDoc, SCTAB nScTab, sal_uInt16 nXclRow, sal_uInt16 nOffset, double fScale )
+{
+ SCROW nScRow = static_cast< SCROW >( nXclRow );
+ return static_cast< tools::Long >( fScale * (rDoc.GetRowOffset( nScRow, nScTab ) +
+ ::std::min( nOffset / 256.0, 1.0 ) * rDoc.GetRowHeight( nScRow, nScTab )) + 0.5 );
+}
+
+/** Calculates an object column position from a drawing layer X position (in twips). */
+void lclGetColFromX(
+ const ScDocument& rDoc, SCTAB nScTab, sal_uInt16& rnXclCol,
+ sal_uInt16& rnOffset, sal_uInt16 nXclStartCol, sal_uInt16 nXclMaxCol,
+ tools::Long& rnStartW, tools::Long nX, double fScale )
+{
+ // rnStartW in conjunction with nXclStartCol is used as buffer for previously calculated width
+ tools::Long nTwipsX = static_cast< tools::Long >( nX / fScale + 0.5 );
+ tools::Long nColW = 0;
+ for( rnXclCol = nXclStartCol; rnXclCol <= nXclMaxCol; ++rnXclCol )
+ {
+ nColW = rDoc.GetColWidth( static_cast< SCCOL >( rnXclCol ), nScTab );
+ if( rnStartW + nColW > nTwipsX )
+ break;
+ rnStartW += nColW;
+ }
+ rnOffset = nColW ? static_cast< sal_uInt16 >( (nTwipsX - rnStartW) * 1024.0 / nColW + 0.5 ) : 0;
+}
+
+/** Calculates an object row position from a drawing layer Y position (in twips). */
+void lclGetRowFromY(
+ const ScDocument& rDoc, SCTAB nScTab, sal_uInt32& rnXclRow,
+ sal_uInt32& rnOffset, sal_uInt32 nXclStartRow, sal_uInt32 nXclMaxRow,
+ tools::Long& rnStartH, tools::Long nY, double fScale )
+{
+ // rnStartH in conjunction with nXclStartRow is used as buffer for previously calculated height
+ tools::Long nTwipsY = static_cast< tools::Long >( nY / fScale + 0.5 );
+ tools::Long nRowH = 0;
+ bool bFound = false;
+ for( sal_uInt32 nRow = nXclStartRow; nRow <= nXclMaxRow; ++nRow )
+ {
+ nRowH = rDoc.GetRowHeight( nRow, nScTab );
+ if( rnStartH + nRowH > nTwipsY )
+ {
+ rnXclRow = nRow;
+ bFound = true;
+ break;
+ }
+ rnStartH += nRowH;
+ }
+ if( !bFound )
+ rnXclRow = nXclMaxRow;
+ rnOffset = static_cast< sal_uInt32 >( nRowH ? std::max((nTwipsY - rnStartH) * 256.0 / nRowH + 0.5, 0.0) : 0 );
+}
+
+/** Mirrors a rectangle (from LTR to RTL layout or vice versa). */
+void lclMirrorRectangle( tools::Rectangle& rRect )
+{
+ tools::Long nLeft = rRect.Left();
+ rRect.SetLeft( -rRect.Right() );
+ rRect.SetRight( -nLeft );
+}
+
+sal_uInt16 lclGetEmbeddedScale( tools::Long nPageSize, sal_Int32 nPageScale, tools::Long nPos, double fPosScale )
+{
+ return static_cast< sal_uInt16 >( nPos * fPosScale / nPageSize * nPageScale + 0.5 );
+}
+
+} // namespace
+
+XclObjAnchor::XclObjAnchor() :
+ mnLX( 0 ),
+ mnTY( 0 ),
+ mnRX( 0 ),
+ mnBY( 0 )
+{
+}
+
+tools::Rectangle XclObjAnchor::GetRect( const XclRoot& rRoot, SCTAB nScTab, MapUnit eMapUnit ) const
+{
+ ScDocument& rDoc = rRoot.GetDoc();
+ double fScale = lclGetTwipsScale( eMapUnit );
+ tools::Rectangle aRect(
+ lclGetXFromCol(rDoc, nScTab, std::min<SCCOL>(maFirst.mnCol, rDoc.MaxCol()), mnLX, fScale),
+ lclGetYFromRow(rDoc, nScTab, std::min<SCROW>(maFirst.mnRow, rDoc.MaxRow()), mnTY, fScale),
+ lclGetXFromCol(rDoc, nScTab, std::min<SCCOL>(maLast.mnCol, rDoc.MaxCol()), mnRX + 1, fScale),
+ lclGetYFromRow(rDoc, nScTab, std::min<SCROW>(maLast.mnRow, rDoc.MaxRow()), mnBY, fScale));
+
+ // adjust coordinates in mirrored sheets
+ if( rDoc.IsLayoutRTL( nScTab ) )
+ lclMirrorRectangle( aRect );
+ return aRect;
+}
+
+void XclObjAnchor::SetRect( const XclRoot& rRoot, SCTAB nScTab, const tools::Rectangle& rRect, MapUnit eMapUnit )
+{
+ ScDocument& rDoc = rRoot.GetDoc();
+ sal_uInt16 nXclMaxCol = rRoot.GetXclMaxPos().Col();
+ sal_uInt16 nXclMaxRow = static_cast<sal_uInt16>( rRoot.GetXclMaxPos().Row());
+
+ // adjust coordinates in mirrored sheets
+ tools::Rectangle aRect( rRect );
+ if( rDoc.IsLayoutRTL( nScTab ) )
+ lclMirrorRectangle( aRect );
+
+ double fScale = lclGetTwipsScale( eMapUnit );
+ tools::Long nDummy = 0;
+ lclGetColFromX( rDoc, nScTab, maFirst.mnCol, mnLX, 0, nXclMaxCol, nDummy, aRect.Left(), fScale );
+ lclGetColFromX( rDoc, nScTab, maLast.mnCol, mnRX, maFirst.mnCol, nXclMaxCol, nDummy, aRect.Right(), fScale );
+ nDummy = 0;
+ lclGetRowFromY( rDoc, nScTab, maFirst.mnRow, mnTY, 0, nXclMaxRow, nDummy, aRect.Top(), fScale );
+ lclGetRowFromY( rDoc, nScTab, maLast.mnRow, mnBY, maFirst.mnRow, nXclMaxRow, nDummy, aRect.Bottom(), fScale );
+}
+
+void XclObjAnchor::SetRect( const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY,
+ const tools::Rectangle& rRect, MapUnit eMapUnit )
+{
+ double fScale = 1.0;
+ if (const auto eFrom = MapToO3tlLength(eMapUnit); eFrom != o3tl::Length::invalid)
+ fScale = o3tl::convert(1.0, eFrom, o3tl::Length::mm100);
+ else
+ OSL_FAIL("XclObjAnchor::SetRect - map unit not implemented");
+
+ /* In objects with DFF client anchor, the position of the shape is stored
+ in the cell address components of the client anchor. In old BIFF3-BIFF5
+ objects, the position is stored in the offset components of the anchor. */
+ maFirst.mnCol = lclGetEmbeddedScale( rPageSize.Width(), nScaleX, rRect.Left(), fScale );
+ maFirst.mnRow = lclGetEmbeddedScale( rPageSize.Height(), nScaleY, rRect.Top(), fScale );
+ maLast.mnCol = lclGetEmbeddedScale( rPageSize.Width(), nScaleX, rRect.Right(), fScale );
+ maLast.mnRow = lclGetEmbeddedScale( rPageSize.Height(), nScaleY, rRect.Bottom(), fScale );
+
+ // for safety, clear the other members
+ mnLX = mnTY = mnRX = mnBY = 0;
+}
+
+XclObjLineData::XclObjLineData() :
+ mnColorIdx( EXC_OBJ_LINE_AUTOCOLOR ),
+ mnStyle( EXC_OBJ_LINE_SOLID ),
+ mnWidth( EXC_OBJ_LINE_HAIR ),
+ mnAuto( EXC_OBJ_LINE_AUTO )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclObjLineData& rLineData )
+{
+ rLineData.mnColorIdx = rStrm.ReaduInt8();
+ rLineData.mnStyle = rStrm.ReaduInt8();
+ rLineData.mnWidth = rStrm.ReaduInt8();
+ rLineData.mnAuto = rStrm.ReaduInt8();
+ return rStrm;
+}
+
+XclObjFillData::XclObjFillData() :
+ mnBackColorIdx( EXC_OBJ_LINE_AUTOCOLOR ),
+ mnPattColorIdx( EXC_OBJ_FILL_AUTOCOLOR ),
+ mnPattern( EXC_PATT_SOLID ),
+ mnAuto( EXC_OBJ_FILL_AUTO )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclObjFillData& rFillData )
+{
+ rFillData.mnBackColorIdx = rStrm.ReaduInt8();
+ rFillData.mnPattColorIdx = rStrm.ReaduInt8();
+ rFillData.mnPattern = rStrm.ReaduInt8();
+ rFillData.mnAuto = rStrm.ReaduInt8();
+ return rStrm;
+}
+
+XclObjTextData::XclObjTextData() :
+ mnTextLen( 0 ),
+ mnFormatSize( 0 ),
+ mnLinkSize( 0 ),
+ mnDefFontIdx( EXC_FONT_APP ),
+ mnFlags( 0 ),
+ mnOrient( EXC_OBJ_ORIENT_NONE ),
+ mnButtonFlags( 0 ),
+ mnShortcut( 0 ),
+ mnShortcutEA( 0 )
+{
+}
+
+void XclObjTextData::ReadObj3( XclImpStream& rStrm )
+{
+ mnTextLen = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnFormatSize = rStrm.ReaduInt16();
+ mnDefFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnFlags = rStrm.ReaduInt16();
+ mnOrient = rStrm.ReaduInt16();
+ rStrm.Ignore( 8 );
+}
+
+void XclObjTextData::ReadObj5( XclImpStream& rStrm )
+{
+ mnTextLen = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnFormatSize = rStrm.ReaduInt16();
+ mnDefFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnFlags = rStrm.ReaduInt16();
+ mnOrient = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnLinkSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnButtonFlags = rStrm.ReaduInt16();
+ mnShortcut = rStrm.ReaduInt16();
+ mnShortcutEA = rStrm.ReaduInt16();
+}
+
+void XclObjTextData::ReadTxo8( XclImpStream& rStrm )
+{
+ mnFlags = rStrm.ReaduInt16();
+ mnOrient = rStrm.ReaduInt16();
+ mnButtonFlags = rStrm.ReaduInt16();
+ mnShortcut = rStrm.ReaduInt16();
+ mnShortcutEA = rStrm.ReaduInt16();
+ mnTextLen = rStrm.ReaduInt16();
+ mnFormatSize = rStrm.ReaduInt16();
+}
+
+Reference< XControlModel > XclControlHelper::GetControlModel( Reference< XShape > const & xShape )
+{
+ Reference< XControlModel > xCtrlModel;
+ Reference< XControlShape > xCtrlShape( xShape, UNO_QUERY );
+ if( xCtrlShape.is() )
+ xCtrlModel = xCtrlShape->getControl();
+ return xCtrlModel;
+}
+
+namespace {
+
+const struct
+{
+ const char* mpcListenerType;
+ const char* mpcEventMethod;
+}
+spTbxListenerData[] =
+{
+ // Attention: MUST be in order of the XclTbxEventType enum!
+ /*EXC_TBX_EVENT_ACTION*/ { "XActionListener", "actionPerformed" },
+ /*EXC_TBX_EVENT_MOUSE*/ { "XMouseListener", "mouseReleased" },
+ /*EXC_TBX_EVENT_TEXT*/ { "XTextListener", "textChanged" },
+ /*EXC_TBX_EVENT_VALUE*/ { "XAdjustmentListener", "adjustmentValueChanged" },
+ /*EXC_TBX_EVENT_CHANGE*/ { "XChangeListener", "changed" }
+};
+
+} // namespace
+
+bool XclControlHelper::FillMacroDescriptor( ScriptEventDescriptor& rDescriptor,
+ XclTbxEventType eEventType, const OUString& rXclMacroName, SfxObjectShell* pDocShell )
+{
+ if( !rXclMacroName.isEmpty() )
+ {
+ rDescriptor.ListenerType = OUString::createFromAscii( spTbxListenerData[ eEventType ].mpcListenerType );
+ rDescriptor.EventMethod = OUString::createFromAscii( spTbxListenerData[ eEventType ].mpcEventMethod );
+ rDescriptor.ScriptType = "Script";
+ rDescriptor.ScriptCode = XclTools::GetSbMacroUrl( rXclMacroName, pDocShell );
+ return true;
+ }
+ return false;
+}
+
+OUString XclControlHelper::ExtractFromMacroDescriptor(
+ const ScriptEventDescriptor& rDescriptor, XclTbxEventType eEventType )
+{
+ if( (!rDescriptor.ScriptCode.isEmpty()) &&
+ rDescriptor.ScriptType.equalsIgnoreAsciiCase("Script") &&
+ rDescriptor.ListenerType.equalsAscii( spTbxListenerData[ eEventType ].mpcListenerType ) &&
+ rDescriptor.EventMethod.equalsAscii( spTbxListenerData[ eEventType ].mpcEventMethod ) )
+ return XclTools::GetXclMacroName( rDescriptor.ScriptCode );
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlformula.cxx b/sc/source/filter/excel/xlformula.cxx
new file mode 100644
index 000000000..e2e082ac2
--- /dev/null
+++ b/sc/source/filter/excel/xlformula.cxx
@@ -0,0 +1,1022 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlformula.hxx>
+
+#include <refdata.hxx>
+#include <tokenarray.hxx>
+#include <xestream.hxx>
+#include <xistream.hxx>
+#include <xlroot.hxx>
+
+#include <comphelper/string.hxx>
+#include <svl/sharedstringpool.hxx>
+
+using namespace ::formula;
+
+// Function data ==============================================================
+
+OUString XclFunctionInfo::GetMacroFuncName() const
+{
+ if( IsMacroFunc() )
+ return OUString( mpcMacroName, strlen(mpcMacroName), RTL_TEXTENCODING_UTF8 );
+ return OUString();
+}
+
+OUString XclFunctionInfo::GetAddInEquivalentFuncName() const
+{
+ if( IsAddInEquivalent() )
+ return OUString( mpcMacroName, strlen(mpcMacroName), RTL_TEXTENCODING_UTF8 );
+ return OUString();
+}
+
+// abbreviations for function return token class
+const sal_uInt8 R = EXC_TOKCLASS_REF;
+const sal_uInt8 V = EXC_TOKCLASS_VAL;
+const sal_uInt8 A = EXC_TOKCLASS_ARR;
+
+// abbreviations for parameter infos
+#define RO { EXC_PARAM_REGULAR, EXC_PARAMCONV_ORG, false }
+#define RA { EXC_PARAM_REGULAR, EXC_PARAMCONV_ARR, false }
+#define RR { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPT, false }
+#define RX { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPX, false }
+#define VO { EXC_PARAM_REGULAR, EXC_PARAMCONV_ORG, true }
+#define VV { EXC_PARAM_REGULAR, EXC_PARAMCONV_VAL, true }
+#define VA { EXC_PARAM_REGULAR, EXC_PARAMCONV_ARR, true }
+#define VR { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPT, true }
+#define VX { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPX, true }
+#define RO_E { EXC_PARAM_EXCELONLY, EXC_PARAMCONV_ORG, false }
+#define VR_E { EXC_PARAM_EXCELONLY, EXC_PARAMCONV_RPT, true }
+#define C { EXC_PARAM_CALCONLY, EXC_PARAMCONV_ORG, false }
+
+const sal_uInt16 NOID = SAL_MAX_UINT16; /// No BIFF/OOBIN function identifier available.
+const sal_uInt8 MX = 30; /// Maximum parameter count.
+
+#define EXC_FUNCNAME( ascii ) "_xlfn." ascii
+#define EXC_FUNCNAME_ODF( ascii ) "_xlfnodf." ascii
+#define EXC_FUNCNAME_ADDIN( ascii ) "com.sun.star.sheet.addin." ascii
+
+/** Functions new in BIFF2. */
+const XclFunctionInfo saFuncTable_2[] =
+{
+ { ocCount, 0, 0, MX, V, { RX }, 0, nullptr },
+ { ocIf, 1, 2, 3, R, { VO, RO }, 0, nullptr },
+ { ocIsNA, 2, 1, 1, V, { VR }, 0, nullptr },
+ { ocIsError, 3, 1, 1, V, { VR }, 0, nullptr },
+ { ocSum, 4, 0, MX, V, { RX }, 0, nullptr },
+ { ocAverage, 5, 1, MX, V, { RX }, 0, nullptr },
+ { ocMin, 6, 1, MX, V, { RX }, 0, nullptr },
+ { ocMax, 7, 1, MX, V, { RX }, 0, nullptr },
+ { ocRow, 8, 0, 1, V, { RO }, 0, nullptr },
+ { ocColumn, 9, 0, 1, V, { RO }, 0, nullptr },
+ { ocNotAvail, 10, 0, 0, V, {}, 0, nullptr },
+ { ocNPV, 11, 2, MX, V, { VR, RX }, 0, nullptr },
+ { ocStDev, 12, 1, MX, V, { RX }, 0, nullptr },
+ { ocCurrency, 13, 1, 2, V, { VR }, 0, nullptr },
+ { ocFixed, 14, 1, 2, V, { VR, VR, C }, 0, nullptr },
+ { ocSin, 15, 1, 1, V, { VR }, 0, nullptr },
+ { ocCosecant, 15, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocCos, 16, 1, 1, V, { VR }, 0, nullptr },
+ { ocSecant, 16, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocTan, 17, 1, 1, V, { VR }, 0, nullptr },
+ { ocCot, 17, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocArcTan, 18, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcCot, 18, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocPi, 19, 0, 0, V, {}, 0, nullptr },
+ { ocSqrt, 20, 1, 1, V, { VR }, 0, nullptr },
+ { ocExp, 21, 1, 1, V, { VR }, 0, nullptr },
+ { ocLn, 22, 1, 1, V, { VR }, 0, nullptr },
+ { ocLog10, 23, 1, 1, V, { VR }, 0, nullptr },
+ { ocAbs, 24, 1, 1, V, { VR }, 0, nullptr },
+ { ocInt, 25, 1, 1, V, { VR }, 0, nullptr },
+ { ocPlusMinus, 26, 1, 1, V, { VR }, 0, nullptr },
+ { ocRound, 27, 2, 2, V, { VR }, 0, nullptr },
+ { ocLookup, 28, 2, 3, V, { VR, RA }, 0, nullptr },
+ { ocIndex, 29, 2, 4, R, { RA, VV }, 0, nullptr },
+ { ocRept, 30, 2, 2, V, { VR }, 0, nullptr },
+ { ocMid, 31, 3, 3, V, { VR }, 0, nullptr },
+ { ocLen, 32, 1, 1, V, { VR }, 0, nullptr },
+ { ocValue, 33, 1, 1, V, { VR }, 0, nullptr },
+ { ocTrue, 34, 0, 0, V, {}, 0, nullptr },
+ { ocFalse, 35, 0, 0, V, {}, 0, nullptr },
+ { ocAnd, 36, 1, MX, V, { RX }, 0, nullptr },
+ { ocOr, 37, 1, MX, V, { RX }, 0, nullptr },
+ { ocNot, 38, 1, 1, V, { VR }, 0, nullptr },
+ { ocMod, 39, 2, 2, V, { VR }, 0, nullptr },
+ { ocDBCount, 40, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBSum, 41, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBAverage, 42, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBMin, 43, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBMax, 44, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBStdDev, 45, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocVar, 46, 1, MX, V, { RX }, 0, nullptr },
+ { ocDBVar, 47, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocText, 48, 2, 2, V, { VR }, 0, nullptr },
+ { ocLinest, 49, 1, 2, A, { RA, RA, C, C }, 0, nullptr },
+ { ocTrend, 50, 1, 3, A, { RA, RA, RA, C }, 0, nullptr },
+ { ocLogest, 51, 1, 2, A, { RA, RA, C, C }, 0, nullptr },
+ { ocGrowth, 52, 1, 3, A, { RA, RA, RA, C }, 0, nullptr },
+ { ocPV, 56, 3, 5, V, { VR }, 0, nullptr },
+ { ocFV, 57, 3, 5, V, { VR }, 0, nullptr },
+ { ocNper, 58, 3, 5, V, { VR }, 0, nullptr },
+ { ocPMT, 59, 3, 5, V, { VR }, 0, nullptr },
+ { ocRate, 60, 3, 6, V, { VR }, 0, nullptr },
+ { ocMIRR, 61, 3, 3, V, { RA, VR }, 0, nullptr },
+ { ocIRR, 62, 1, 2, V, { RA, VR }, 0, nullptr },
+ { ocRandom, 63, 0, 0, V, {}, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocMatch, 64, 2, 3, V, { VR, RX, RR }, 0, nullptr },
+ { ocGetDate, 65, 3, 3, V, { VR }, 0, nullptr },
+ { ocGetTime, 66, 3, 3, V, { VR }, 0, nullptr },
+ { ocGetDay, 67, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetMonth, 68, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetYear, 69, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetDayOfWeek, 70, 1, 1, V, { VR, C }, 0, nullptr },
+ { ocGetHour, 71, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetMin, 72, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetSec, 73, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetActTime, 74, 0, 0, V, {}, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocAreas, 75, 1, 1, V, { RO }, 0, nullptr },
+ { ocRows, 76, 1, 1, V, { RO }, 0, nullptr },
+ { ocColumns, 77, 1, 1, V, { RO }, 0, nullptr },
+ { ocOffset, 78, 3, 5, R, { RO, VR }, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocSearch, 82, 2, 3, V, { VR }, 0, nullptr },
+ { ocMatTrans, 83, 1, 1, A, { VO }, 0, nullptr },
+ { ocType, 86, 1, 1, V, { VX }, 0, nullptr },
+ { ocArcTan2, 97, 2, 2, V, { VR }, 0, nullptr },
+ { ocArcSin, 98, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcCos, 99, 1, 1, V, { VR }, 0, nullptr },
+ { ocChoose, 100, 2, MX, R, { VO, RO }, 0, nullptr },
+ { ocHLookup, 101, 3, 3, V, { VV, RO, RO, C }, 0, nullptr },
+ { ocVLookup, 102, 3, 3, V, { VV, RO, RO, C }, 0, nullptr },
+ { ocIsRef, 105, 1, 1, V, { RX }, 0, nullptr },
+ { ocLog, 109, 1, 2, V, { VR }, 0, nullptr },
+ { ocChar, 111, 1, 1, V, { VR }, 0, nullptr },
+ { ocLower, 112, 1, 1, V, { VR }, 0, nullptr },
+ { ocUpper, 113, 1, 1, V, { VR }, 0, nullptr },
+ { ocProper, 114, 1, 1, V, { VR }, 0, nullptr },
+ { ocLeft, 115, 1, 2, V, { VR }, 0, nullptr },
+ { ocRight, 116, 1, 2, V, { VR }, 0, nullptr },
+ { ocExact, 117, 2, 2, V, { VR }, 0, nullptr },
+ { ocTrim, 118, 1, 1, V, { VR }, 0, nullptr },
+ { ocReplace, 119, 4, 4, V, { VR }, 0, nullptr },
+ { ocSubstitute, 120, 3, 4, V, { VR }, 0, nullptr },
+ { ocCode, 121, 1, 1, V, { VR }, 0, nullptr },
+ { ocFind, 124, 2, 3, V, { VR }, 0, nullptr },
+ { ocCell, 125, 1, 2, V, { VV, RO }, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocIsErr, 126, 1, 1, V, { VR }, 0, nullptr },
+ { ocIsString, 127, 1, 1, V, { VR }, 0, nullptr },
+ { ocIsValue, 128, 1, 1, V, { VR }, 0, nullptr },
+ { ocIsEmpty, 129, 1, 1, V, { VR }, 0, nullptr },
+ { ocT, 130, 1, 1, V, { RO }, 0, nullptr },
+ { ocN, 131, 1, 1, V, { RO }, 0, nullptr },
+ { ocGetDateValue, 140, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetTimeValue, 141, 1, 1, V, { VR }, 0, nullptr },
+ { ocSLN, 142, 3, 3, V, { VR }, 0, nullptr },
+ { ocSYD, 143, 4, 4, V, { VR }, 0, nullptr },
+ { ocDDB, 144, 4, 5, V, { VR }, 0, nullptr },
+ { ocIndirect, 148, 1, 2, R, { VR }, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocClean, 162, 1, 1, V, { VR }, 0, nullptr },
+ { ocMatDet, 163, 1, 1, V, { VA }, 0, nullptr },
+ { ocMatInv, 164, 1, 1, A, { VA }, 0, nullptr },
+ { ocMatMult, 165, 2, 2, A, { VA }, 0, nullptr },
+ { ocIpmt, 167, 4, 6, V, { VR }, 0, nullptr },
+ { ocPpmt, 168, 4, 6, V, { VR }, 0, nullptr },
+ { ocCount2, 169, 0, MX, V, { RX }, 0, nullptr },
+ { ocProduct, 183, 0, MX, V, { RX }, 0, nullptr },
+ { ocFact, 184, 1, 1, V, { VR }, 0, nullptr },
+ { ocDBProduct, 189, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocIsNonString, 190, 1, 1, V, { VR }, 0, nullptr },
+ { ocStDevP, 193, 1, MX, V, { RX }, 0, nullptr },
+ { ocVarP, 194, 1, MX, V, { RX }, 0, nullptr },
+ { ocDBStdDevP, 195, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBVarP, 196, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocTrunc, 197, 1, 1, V, { VR, C }, 0, nullptr },
+ { ocIsLogical, 198, 1, 1, V, { VR }, 0, nullptr },
+ { ocDBCount2, 199, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocCurrency, 204, 1, 2, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, nullptr },
+ { ocFindB, 205, 2, 3, V, { VR }, 0, nullptr },
+ { ocSearchB, 206, 2, 3, V, { VR }, 0, nullptr },
+ { ocReplaceB, 207, 4, 4, V, { VR }, 0, nullptr },
+ { ocLeftB, 208, 1, 2, V, { VR }, 0, nullptr },
+ { ocRightB, 209, 1, 2, V, { VR }, 0, nullptr },
+ { ocMidB, 210, 3, 3, V, { VR }, 0, nullptr },
+ { ocLenB, 211, 1, 1, V, { VR }, 0, nullptr },
+ { ocRoundUp, 212, 2, 2, V, { VR }, 0, nullptr },
+ { ocRoundDown, 213, 2, 2, V, { VR }, 0, nullptr },
+ { ocExternal, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_IMPORTONLY, nullptr }
+};
+
+/** Functions new in BIFF3. */
+const XclFunctionInfo saFuncTable_3[] =
+{
+ { ocLinest, 49, 1, 4, A, { RA, RA, VV }, 0, nullptr }, // BIFF2: 1-2, BIFF3: 1-4
+ { ocTrend, 50, 1, 4, A, { RA, RA, RA, VV }, 0, nullptr }, // BIFF2: 1-3, BIFF3: 1-4
+ { ocLogest, 51, 1, 4, A, { RA, RA, VV }, 0, nullptr }, // BIFF2: 1-2, BIFF3: 1-4
+ { ocGrowth, 52, 1, 4, A, { RA, RA, RA, VV }, 0, nullptr }, // BIFF2: 1-3, BIFF3: 1-4
+ { ocTrunc, 197, 1, 2, V, { VR }, 0, nullptr }, // BIFF2: 1, BIFF3: 1-2
+ { ocAddress, 219, 2, 5, V, { VR }, 0, nullptr },
+ { ocGetDiffDate360, 220, 2, 2, V, { VR, VR, C }, 0, nullptr },
+ { ocGetActDate, 221, 0, 0, V, {}, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocVBD, 222, 5, 7, V, { VR }, 0, nullptr },
+ { ocMedian, 227, 1, MX, V, { RX }, 0, nullptr },
+ { ocSumProduct, 228, 1, MX, V, { VA }, 0, nullptr },
+ { ocSinHyp, 229, 1, 1, V, { VR }, 0, nullptr },
+ { ocCosecantHyp, 229, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocCosHyp, 230, 1, 1, V, { VR }, 0, nullptr },
+ { ocSecantHyp, 230, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocTanHyp, 231, 1, 1, V, { VR }, 0, nullptr },
+ { ocCotHyp, 231, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocArcSinHyp, 232, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcCosHyp, 233, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcTanHyp, 234, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcCotHyp, 234, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocDBGet, 235, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocInfo, 244, 1, 1, V, { VR }, EXC_FUNCFLAG_VOLATILE, nullptr }
+};
+
+/** Functions new in BIFF4. */
+const XclFunctionInfo saFuncTable_4[] =
+{
+ { ocFixed, 14, 1, 3, V, { VR }, 0, nullptr }, // BIFF2-3: 1-2, BIFF4: 1-3
+ { ocAsc, 214, 1, 1, V, { VR }, 0, nullptr },
+ { ocJis, 215, 1, 1, V, { VR }, 0, nullptr },
+ { ocRank, 216, 2, 3, V, { VR, RO, VR }, 0, nullptr },
+ { ocDB, 247, 4, 5, V, { VR }, 0, nullptr },
+ { ocFrequency, 252, 2, 2, A, { RA }, 0, nullptr },
+ { ocErrorType_ODF, 261, 1, 1, V, { VR }, 0, nullptr },
+ { ocAveDev, 269, 1, MX, V, { RX }, 0, nullptr },
+ { ocBetaDist, 270, 3, 5, V, { VR }, 0, nullptr },
+ { ocGammaLn, 271, 1, 1, V, { VR }, 0, nullptr },
+ { ocBetaInv, 272, 3, 5, V, { VR }, 0, nullptr },
+ { ocBinomDist, 273, 4, 4, V, { VR }, 0, nullptr },
+ { ocChiDist, 274, 2, 2, V, { VR }, 0, nullptr },
+ { ocChiInv, 275, 2, 2, V, { VR }, 0, nullptr },
+ { ocCombin, 276, 2, 2, V, { VR }, 0, nullptr },
+ { ocConfidence, 277, 3, 3, V, { VR }, 0, nullptr },
+ { ocCritBinom, 278, 3, 3, V, { VR }, 0, nullptr },
+ { ocEven, 279, 1, 1, V, { VR }, 0, nullptr },
+ { ocExpDist, 280, 3, 3, V, { VR }, 0, nullptr },
+ { ocFDist, 281, 3, 3, V, { VR }, 0, nullptr },
+ { ocFInv, 282, 3, 3, V, { VR }, 0, nullptr },
+ { ocFisher, 283, 1, 1, V, { VR }, 0, nullptr },
+ { ocFisherInv, 284, 1, 1, V, { VR }, 0, nullptr },
+ { ocFloor_MS, 285, 2, 2, V, { VR }, 0, nullptr },
+ { ocGammaDist, 286, 4, 4, V, { VR }, 0, nullptr },
+ { ocGammaInv, 287, 3, 3, V, { VR }, 0, nullptr },
+ { ocCeil_MS, 288, 2, 2, V, { VR }, 0, nullptr },
+ { ocHypGeomDist, 289, 4, 4, V, { VR }, 0, nullptr },
+ { ocLogNormDist, 290, 3, 3, V, { VR }, 0, nullptr },
+ { ocLogInv, 291, 3, 3, V, { VR }, 0, nullptr },
+ { ocNegBinomVert, 292, 3, 3, V, { VR }, 0, nullptr },
+ { ocNormDist, 293, 4, 4, V, { VR }, 0, nullptr },
+ { ocStdNormDist, 294, 1, 1, V, { VR }, 0, nullptr },
+ { ocNormInv, 295, 3, 3, V, { VR }, 0, nullptr },
+ { ocSNormInv, 296, 1, 1, V, { VR }, 0, nullptr },
+ { ocStandard, 297, 3, 3, V, { VR }, 0, nullptr },
+ { ocOdd, 298, 1, 1, V, { VR }, 0, nullptr },
+ { ocPermut, 299, 2, 2, V, { VR }, 0, nullptr },
+ { ocPoissonDist, 300, 3, 3, V, { VR }, 0, nullptr },
+ { ocTDist, 301, 3, 3, V, { VR }, 0, nullptr },
+ { ocWeibull, 302, 4, 4, V, { VR }, 0, nullptr },
+ { ocSumXMY2, 303, 2, 2, V, { VA }, 0, nullptr },
+ { ocSumX2MY2, 304, 2, 2, V, { VA }, 0, nullptr },
+ { ocSumX2DY2, 305, 2, 2, V, { VA }, 0, nullptr },
+ { ocChiTest, 306, 2, 2, V, { VA }, 0, nullptr },
+ { ocCorrel, 307, 2, 2, V, { VA }, 0, nullptr },
+ { ocCovar, 308, 2, 2, V, { VA }, 0, nullptr },
+ { ocForecast, 309, 3, 3, V, { VR, VA }, 0, nullptr },
+ { ocFTest, 310, 2, 2, V, { VA }, 0, nullptr },
+ { ocIntercept, 311, 2, 2, V, { VA }, 0, nullptr },
+ { ocPearson, 312, 2, 2, V, { VA }, 0, nullptr },
+ { ocRSQ, 313, 2, 2, V, { VA }, 0, nullptr },
+ { ocSTEYX, 314, 2, 2, V, { VA }, 0, nullptr },
+ { ocSlope, 315, 2, 2, V, { VA }, 0, nullptr },
+ { ocTTest, 316, 4, 4, V, { VA, VA, VR }, 0, nullptr },
+ { ocProb, 317, 3, 4, V, { VA, VA, VR }, 0, nullptr },
+ { ocDevSq, 318, 1, MX, V, { RX }, 0, nullptr },
+ { ocGeoMean, 319, 1, MX, V, { RX }, 0, nullptr },
+ { ocHarMean, 320, 1, MX, V, { RX }, 0, nullptr },
+ { ocSumSQ, 321, 0, MX, V, { RX }, 0, nullptr },
+ { ocKurt, 322, 1, MX, V, { RX }, 0, nullptr },
+ { ocSkew, 323, 1, MX, V, { RX }, 0, nullptr },
+ { ocZTest, 324, 2, 3, V, { RX, VR }, 0, nullptr },
+ { ocLarge, 325, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocSmall, 326, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocQuartile, 327, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocPercentile, 328, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocPercentrank, 329, 2, 3, V, { RX, VR, VR_E }, 0, nullptr },
+ { ocModalValue, 330, 1, MX, V, { VA }, 0, nullptr },
+ { ocTrimMean, 331, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocTInv, 332, 2, 2, V, { VR }, 0, nullptr },
+ // Functions equivalent to add-in functions, use same parameters as
+ // ocExternal but add programmatical function name (here without
+ // "com.sun.star.sheet.addin.") so it can be looked up and stored as
+ // add-in, as older Excel versions only know them as add-in.
+ { ocIsEven, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getIseven" ) },
+ { ocIsOdd, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getIsodd" ) },
+ { ocGCD, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getGcd" ) },
+ { ocLCM, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getLcm" ) },
+ { ocEffect, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getEffect" ) },
+ { ocCumPrinc, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getCumprinc" ) },
+ { ocCumIpmt, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getCumipmt" ) },
+ { ocNominal, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getNominal" ) },
+ { ocNetWorkdays, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getNetworkdays" ) }
+};
+
+/** Functions new in BIFF5/BIFF7. Unsupported functions: DATESTRING, NUMBERSTRING. */
+const XclFunctionInfo saFuncTable_5[] =
+{
+ { ocGetDayOfWeek, 70, 1, 2, V, { VR }, 0, nullptr }, // BIFF2-4: 1, BIFF5: 1-2
+ { ocHLookup, 101, 3, 4, V, { VV, RO, RO, VV }, 0, nullptr }, // BIFF2-4: 3, BIFF5: 3-4
+ { ocVLookup, 102, 3, 4, V, { VV, RO, RO, VV }, 0, nullptr }, // BIFF2-4: 3, BIFF5: 3-4
+ { ocGetDiffDate360, 220, 2, 3, V, { VR }, 0, nullptr }, // BIFF3-4: 2, BIFF5: 2-3
+ { ocMacro, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocExternal, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocConcat, 336, 0, MX, V, { VR }, 0, nullptr },
+ { ocPower, 337, 2, 2, V, { VR }, 0, nullptr },
+ { ocRad, 342, 1, 1, V, { VR }, 0, nullptr },
+ { ocDeg, 343, 1, 1, V, { VR }, 0, nullptr },
+ { ocSubTotal, 344, 2, MX, V, { VR, RO }, 0, nullptr },
+ { ocSumIf, 345, 2, 3, V, { RO, VR, RO }, 0, nullptr },
+ { ocCountIf, 346, 2, 2, V, { RO, VR }, 0, nullptr },
+ { ocCountEmptyCells, 347, 1, 1, V, { RO }, 0, nullptr },
+ { ocISPMT, 350, 4, 4, V, { VR }, 0, nullptr },
+ { ocGetDateDif, 351, 3, 3, V, { VR }, 0, nullptr },
+ { ocNoName, 352, 1, 1, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, nullptr }, // DATESTRING
+ { ocNoName, 353, 2, 2, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, nullptr }, // NUMBERSTRING
+ { ocRoman, 354, 1, 2, V, { VR }, 0, nullptr }
+};
+
+/** Functions new in BIFF8. Unsupported functions: PHONETIC. */
+const XclFunctionInfo saFuncTable_8[] =
+{
+ { ocGetPivotData, 358, 2, MX, V, { RR, RR, VR }, 0, nullptr },
+ { ocHyperLink, 359, 1, 2, V, { VV, VO }, 0, nullptr },
+ { ocNoName, 360, 1, 1, V, { RO }, EXC_FUNCFLAG_IMPORTONLY, nullptr }, // PHONETIC
+ { ocAverageA, 361, 1, MX, V, { RX }, 0, nullptr },
+ { ocMaxA, 362, 1, MX, V, { RX }, 0, nullptr },
+ { ocMinA, 363, 1, MX, V, { RX }, 0, nullptr },
+ { ocStDevPA, 364, 1, MX, V, { RX }, 0, nullptr },
+ { ocVarPA, 365, 1, MX, V, { RX }, 0, nullptr },
+ { ocStDevA, 366, 1, MX, V, { RX }, 0, nullptr },
+ { ocVarA, 367, 1, MX, V, { RX }, 0, nullptr },
+ { ocBahtText, 368, 1, 1, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "BAHTTEXT" ) },
+ { ocBahtText, 255, 2, 2, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "BAHTTEXT" ) },
+ { ocEuroConvert, 255, 4, 6, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, "EUROCONVERT" }
+};
+
+#define EXC_FUNCENTRY_V_VR( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+/** Functions new in OOXML. */
+const XclFunctionInfo saFuncTable_Oox[] =
+{
+ { ocCountIfs, NOID, 2, MX, V, { RO, VR }, EXC_FUNCFLAG_IMPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "COUNTIFS" ) },
+ { ocCountIfs, 255, 3, MX, V, { RO_E, RO, VR }, EXC_FUNCFLAG_EXPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "COUNTIFS" ) },
+ { ocSumIfs, NOID, 3, MX, V, { RO, RO, VR }, EXC_FUNCFLAG_IMPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "SUMIFS" ) },
+ { ocSumIfs, 255, 4, MX, V, { RO_E, RO, RO, VR }, EXC_FUNCFLAG_EXPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "SUMIFS" ) },
+ { ocAverageIf, NOID, 2, 3, V, { RO, VR, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "AVERAGEIF" ) },
+ { ocAverageIf, 255, 3, 4, V, { RO_E, RO, VR, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "AVERAGEIF" ) },
+ { ocAverageIfs, NOID, 3, MX, V, { RO, RO, VR }, EXC_FUNCFLAG_IMPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "AVERAGEIFS" ) },
+ { ocAverageIfs, 255, 4, MX, V, { RO_E, RO, RO, VR }, EXC_FUNCFLAG_EXPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "AVERAGEIFS" ) },
+ { ocIfError, NOID, 2, 2, V, { VO, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "IFERROR" ) },
+ { ocIfError, 255, 3, 3, V, { RO_E, VO, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "IFERROR" ) },
+ { ocNetWorkdays_MS, NOID, 2, 4, V, { VR, VR, RO, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "NETWORKDAYS.INTL" ) },
+ { ocNetWorkdays_MS, 255, 3, 5, V, { RO_E, VR, VR, RO, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "NETWORKDAYS.INTL" ) },
+ { ocWorkday_MS, NOID, 2, 4, V, { VR, VR, VR, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "WORKDAY.INTL" ) },
+ { ocWorkday_MS, 255, 3, 5, V, { RO_E, VR, VR, VR, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "WORKDAY.INTL" ) },
+ EXC_FUNCENTRY_V_VR( ocCeil_ISO, 1, 2, 0, "ISO.CEILING" )
+};
+
+#define EXC_FUNCENTRY_V_VR_IMPORT( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+#define EXC_FUNCENTRY_V_RO_EXPORT( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+#define EXC_FUNCENTRY_A_VR( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, A, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, A, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+#define EXC_FUNCENTRY_V_RO( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { RO }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+// implicit maxparam=MX
+#define EXC_FUNCENTRY_V_RX( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, MX, V, { RX }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, MX, V, { RO_E, RX }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+#define EXC_FUNCENTRY_V_VA( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VA }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+/** Functions new in Excel 2010.
+
+ See http://office.microsoft.com/en-us/excel-help/what-s-new-changes-made-to-excel-functions-HA010355760.aspx
+ A lot of statistical functions have been renamed (the 'old' function names still exist).
+
+ @See sc/source/filter/oox/formulabase.cxx saFuncTable2010 for V,VR,RO,...
+ */
+const XclFunctionInfo saFuncTable_2010[] =
+{
+ EXC_FUNCENTRY_V_VA( ocCovarianceP, 2, 2, 0, "COVARIANCE.P" ),
+ EXC_FUNCENTRY_V_VA( ocCovarianceS, 2, 2, 0, "COVARIANCE.S" ),
+ EXC_FUNCENTRY_V_RX( ocStDevP_MS, 1, MX, 0, "STDEV.P" ),
+ EXC_FUNCENTRY_V_RX( ocStDevS, 1, MX, 0, "STDEV.S" ),
+ EXC_FUNCENTRY_V_RX( ocVarP_MS, 1, MX, 0, "VAR.P" ),
+ EXC_FUNCENTRY_V_RX( ocVarS, 1, MX, 0, "VAR.S" ),
+ EXC_FUNCENTRY_V_VR( ocBetaDist_MS, 4, 6, 0, "BETA.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocBetaInv_MS, 3, 5, 0, "BETA.INV" ),
+ EXC_FUNCENTRY_V_VR( ocBinomDist_MS, 4, 4, 0, "BINOM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocBinomInv, 3, 3, 0, "BINOM.INV" ),
+ EXC_FUNCENTRY_V_VR( ocChiSqDist_MS, 3, 3, 0, "CHISQ.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocChiSqInv_MS, 2, 2, 0, "CHISQ.INV" ),
+ EXC_FUNCENTRY_V_VR( ocChiDist_MS, 2, 2, 0, "CHISQ.DIST.RT" ),
+ EXC_FUNCENTRY_V_VR( ocChiInv_MS, 2, 2, 0, "CHISQ.INV.RT" ),
+ EXC_FUNCENTRY_V_VR( ocChiTest_MS, 2, 2, 0, "CHISQ.TEST" ),
+ EXC_FUNCENTRY_V_VR( ocConfidence_N, 3, 3, 0, "CONFIDENCE.NORM" ),
+ EXC_FUNCENTRY_V_VR( ocConfidence_T, 3, 3, 0, "CONFIDENCE.T" ),
+ EXC_FUNCENTRY_V_VR( ocFDist_LT, 4, 4, 0, "F.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocFDist_RT, 3, 3, 0, "F.DIST.RT" ),
+ EXC_FUNCENTRY_V_VR( ocFInv_LT, 3, 3, 0, "F.INV" ),
+ EXC_FUNCENTRY_V_VR( ocFInv_RT, 3, 3, 0, "F.INV.RT" ),
+ EXC_FUNCENTRY_V_VR( ocFTest_MS, 2, 2, 0, "F.TEST" ),
+ EXC_FUNCENTRY_V_VR( ocExpDist_MS, 3, 3, 0, "EXPON.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocHypGeomDist_MS, 5, 5, 0, "HYPGEOM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocPoissonDist_MS, 3, 3, 0, "POISSON.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocWeibull_MS, 4, 4, 0, "WEIBULL.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocGammaDist_MS, 4, 4, 0, "GAMMA.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocGammaInv_MS, 3, 3, 0, "GAMMA.INV" ),
+ EXC_FUNCENTRY_V_VR( ocGammaLn_MS, 1, 1, 0, "GAMMALN.PRECISE" ),
+ EXC_FUNCENTRY_V_VR( ocLogNormDist_MS, 4, 4, 0, "LOGNORM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocLogInv_MS, 3, 3, 0, "LOGNORM.INV" ),
+ EXC_FUNCENTRY_V_VR( ocNormDist_MS, 4, 4, 0, "NORM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocNormInv_MS, 3, 3, 0, "NORM.INV" ),
+ EXC_FUNCENTRY_V_VR( ocStdNormDist_MS, 2, 2, 0, "NORM.S.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocSNormInv_MS, 1, 1, 0, "NORM.S.INV" ),
+ EXC_FUNCENTRY_V_VR( ocTDist_2T, 2, 2, 0, "T.DIST.2T" ),
+ EXC_FUNCENTRY_V_VR( ocTDist_MS, 3, 3, 0, "T.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocTDist_RT, 2, 2, 0, "T.DIST.RT" ),
+ EXC_FUNCENTRY_V_VR( ocTInv_2T, 2, 2, 0, "T.INV.2T" ),
+ EXC_FUNCENTRY_V_VR( ocTInv_MS, 2, 2, 0, "T.INV" ),
+ EXC_FUNCENTRY_V_VR( ocTTest_MS, 4, 4, 0, "T.TEST" ),
+ EXC_FUNCENTRY_V_VR( ocPercentile_Inc, 2, 2, 0, "PERCENTILE.INC" ),
+ EXC_FUNCENTRY_V_VR( ocPercentrank_Inc, 2, 3, 0, "PERCENTRANK.INC" ),
+ EXC_FUNCENTRY_V_VR( ocQuartile_Inc, 2, 2, 0, "QUARTILE.INC" ),
+ EXC_FUNCENTRY_V_VR( ocRank_Eq, 2, 3, 0, "RANK.EQ" ),
+ EXC_FUNCENTRY_V_VR( ocPercentile_Exc, 2, 2, 0, "PERCENTILE.EXC" ),
+ EXC_FUNCENTRY_V_VR( ocPercentrank_Exc, 2, 3, 0, "PERCENTRANK.EXC" ),
+ EXC_FUNCENTRY_V_VR( ocQuartile_Exc, 2, 2, 0, "QUARTILE.EXC" ),
+ EXC_FUNCENTRY_V_VR( ocRank_Avg, 2, 3, 0, "RANK.AVG" ),
+ EXC_FUNCENTRY_V_RX( ocModalValue_MS, 1, MX, 0, "MODE.SNGL" ),
+ EXC_FUNCENTRY_V_RX( ocModalValue_Multi, 1, MX, 0, "MODE.MULT" ),
+ EXC_FUNCENTRY_V_VR( ocNegBinomDist_MS, 4, 4, 0, "NEGBINOM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocZTest_MS, 2, 3, 0, "Z.TEST" ),
+ EXC_FUNCENTRY_V_VR( ocCeil_Precise, 1, 2, 0, "CEILING.PRECISE" ),
+ EXC_FUNCENTRY_V_VR( ocFloor_Precise, 1, 2, 0, "FLOOR.PRECISE" ),
+ EXC_FUNCENTRY_V_VR( ocErf_MS, 1, 1, 0, "ERF.PRECISE" ),
+ EXC_FUNCENTRY_V_VR( ocErfc_MS, 1, 1, 0, "ERFC.PRECISE" ),
+ EXC_FUNCENTRY_V_RX( ocAggregate, 3, MX, 0, "AGGREGATE" ),
+};
+
+/** Functions new in Excel 2013.
+
+ See http://office.microsoft.com/en-us/excel-help/new-functions-in-excel-2013-HA103980604.aspx
+ Most functions apparently were added for ODF1.2 ODFF / OpenFormula
+ compatibility.
+
+ Functions with EXC_FUNCENTRY_V_VR_IMPORT are rewritten in
+ sc/source/filter/excel/xeformula.cxx during export for BIFF, OOXML export
+ uses a different mapping but still uses this mapping here to determine the
+ feature set.
+
+ FIXME: either have the exporter determine the feature set from the active
+ mapping, preferred, or enhance this mapping here such that for OOXML the
+ rewrite can be overridden.
+
+ @See sc/source/filter/oox/formulabase.cxx saFuncTable2013 for V,VR,RO,...
+ */
+const XclFunctionInfo saFuncTable_2013[] =
+{
+ EXC_FUNCENTRY_V_VR_IMPORT( ocArcCot, 1, 1, 0, "ACOT" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocArcCotHyp, 1, 1, 0, "ACOTH" ),
+ EXC_FUNCENTRY_V_VR( ocArabic, 1, 1, 0, "ARABIC" ),
+ EXC_FUNCENTRY_V_VR( ocBase, 2, 3, 0, "BASE" ),
+ EXC_FUNCENTRY_V_VR( ocB, 3, 4, 0, "BINOM.DIST.RANGE" ),
+ EXC_FUNCENTRY_V_VR( ocBitAnd, 2, 2, 0, "BITAND" ),
+ EXC_FUNCENTRY_V_VR( ocBitLshift, 2, 2, 0, "BITLSHIFT" ),
+ EXC_FUNCENTRY_V_VR( ocBitOr, 2, 2, 0, "BITOR" ),
+ EXC_FUNCENTRY_V_VR( ocBitRshift, 2, 2, 0, "BITRSHIFT" ),
+ EXC_FUNCENTRY_V_VR( ocBitXor, 2, 2, 0, "BITXOR" ),
+ EXC_FUNCENTRY_V_VR( ocCeil_Math, 1, 3, 0, "CEILING.MATH" ),
+ EXC_FUNCENTRY_V_RO_EXPORT( ocCeil, 1, 3, 0, "CEILING.MATH" ),
+ EXC_FUNCENTRY_V_VR( ocCombinA, 2, 2, 0, "COMBINA" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocCot, 1, 1, 0, "COT" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocCotHyp, 1, 1, 0, "COTH" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocCosecant, 1, 1, 0, "CSC" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocCosecantHyp, 1, 1, 0, "CSCH" ),
+ EXC_FUNCENTRY_V_VR( ocGetDiffDate, 2, 2, 0, "DAYS" ),
+ EXC_FUNCENTRY_V_VR( ocDecimal, 2, 2, 0, "DECIMAL" ),
+ EXC_FUNCENTRY_V_VR( ocEncodeURL, 1, 1, 0, "ENCODEURL" ),
+ // NOTE: this FDIST is not our LEGACY.FDIST
+ EXC_FUNCENTRY_V_VR( ocNoName, 3, 4, 0, "FDIST" ),
+ // NOTE: this FINV is not our LEGACY.FINV
+ EXC_FUNCENTRY_V_VR( ocNoName, 3, 3, 0, "FINV" ),
+ EXC_FUNCENTRY_V_VR( ocFilterXML, 2, 2, 0, "FILTERXML" ),
+ EXC_FUNCENTRY_V_VR( ocFloor_Math, 1, 3, 0, "FLOOR.MATH" ),
+ EXC_FUNCENTRY_V_RO_EXPORT( ocFloor, 1, 3, 0, "FLOOR.MATH" ),
+ EXC_FUNCENTRY_V_RO( ocFormula, 1, 1, 0, "FORMULATEXT" ),
+ EXC_FUNCENTRY_V_VR( ocGamma, 1, 1, 0, "GAMMA" ),
+ EXC_FUNCENTRY_V_VR( ocGauss, 1, 1, 0, "GAUSS" ),
+ { ocIfNA, NOID, 2, 2, V, { VO, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "IFNA" ) },
+ { ocIfNA, 255, 3, 3, V, { RO_E, VO, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "IFNA" ) },
+ // IMCOSH, IMCOT, IMCSC, IMCSCH, IMSEC, IMSECH, IMSINH and IMTAN are
+ // implemented in the Analysis Add-In.
+ EXC_FUNCENTRY_V_RO( ocIsFormula, 1, 1, 0, "ISFORMULA" ),
+ EXC_FUNCENTRY_V_VR( ocWeek, 1, 2, 0, "WEEKNUM" ),
+ EXC_FUNCENTRY_V_VR( ocIsoWeeknum, 1, 1, 0, "ISOWEEKNUM" ),
+ EXC_FUNCENTRY_A_VR( ocMatrixUnit, 1, 1, 0, "MUNIT" ),
+ EXC_FUNCENTRY_V_VR( ocNumberValue, 1, 3, 0, "NUMBERVALUE" ),
+ EXC_FUNCENTRY_V_VR( ocPDuration, 3, 3, 0, "PDURATION" ),
+ EXC_FUNCENTRY_V_VR( ocPermutationA, 2, 2, 0, "PERMUTATIONA" ),
+ EXC_FUNCENTRY_V_VR( ocPhi, 1, 1, 0, "PHI" ),
+ EXC_FUNCENTRY_V_VR( ocRRI, 3, 3, 0, "RRI" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocSecant, 1, 1, 0, "SEC" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocSecantHyp, 1, 1, 0, "SECH" ),
+ EXC_FUNCENTRY_V_RO( ocSheet, 0, 1, 0, "SHEET" ),
+ EXC_FUNCENTRY_V_RO( ocSheets, 0, 1, 0, "SHEETS" ),
+ EXC_FUNCENTRY_V_RX( ocSkewp, 1, MX, 0, "SKEW.P" ),
+ EXC_FUNCENTRY_V_VR( ocUnichar, 1, 1, 0, "UNICHAR" ),
+ EXC_FUNCENTRY_V_VR( ocUnicode, 1, 1, 0, "UNICODE" ),
+ EXC_FUNCENTRY_V_VR( ocWebservice, 1, 1, 0, "WEBSERVICE" ),
+ EXC_FUNCENTRY_V_RX( ocXor, 1, MX, 0, "XOR" ),
+ EXC_FUNCENTRY_V_VR( ocErrorType_ODF, 1, 1, 0, "ERROR.TYPE" )
+};
+
+/** Functions new in Excel 2016.
+
+ See https://support.office.com/en-us/article/Forecasting-functions-897a2fe9-6595-4680-a0b0-93e0308d5f6e?ui=en-US&rs=en-US&ad=US#_forecast.ets
+ and https://support.office.com/en-us/article/What-s-New-and-Improved-in-Office-2016-for-Office-365-95c8d81d-08ba-42c1-914f-bca4603e1426?ui=en-US&rs=en-US&ad=US
+
+ @See sc/source/filter/oox/formulabase.cxx saFuncTable2016 for V,VR,RO,...
+ */
+const XclFunctionInfo saFuncTable_2016[] =
+{
+ EXC_FUNCENTRY_V_VR( ocForecast_ETS_ADD, 3, 6, 0, "FORECAST.ETS" ),
+ EXC_FUNCENTRY_V_VR( ocForecast_ETS_PIA, 3, 7, 0, "FORECAST.ETS.CONFINT" ),
+ EXC_FUNCENTRY_V_VR( ocForecast_ETS_SEA, 2, 4, 0, "FORECAST.ETS.SEASONALITY" ),
+ EXC_FUNCENTRY_V_VR( ocForecast_ETS_STA, 3, 6, 0, "FORECAST.ETS.STAT" ),
+ EXC_FUNCENTRY_V_VR( ocForecast_LIN, 3, 3, 0, "FORECAST.LINEAR" ),
+ EXC_FUNCENTRY_V_VR( ocConcat_MS, 1, MX, 0, "CONCAT" ),
+ EXC_FUNCENTRY_V_VR( ocTextJoin_MS, 3, MX, 0, "TEXTJOIN" ),
+ EXC_FUNCENTRY_V_VR( ocIfs_MS, 2, MX, 0, "IFS" ),
+ EXC_FUNCENTRY_V_VR( ocSwitch_MS, 3, MX, 0, "SWITCH" ),
+ EXC_FUNCENTRY_V_VR( ocMinIfs_MS, 3, MX, 0, "MINIFS" ),
+ EXC_FUNCENTRY_V_VR( ocMaxIfs_MS, 3, MX, 0, "MAXIFS" )
+};
+
+#define EXC_FUNCENTRY_ODF( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME_ODF( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME_ODF( asciiname ) }
+
+/** Functions defined by OpenFormula, but not supported by Calc (ocNoName) or by Excel (defined op-code). */
+const XclFunctionInfo saFuncTable_Odf[] =
+{
+ EXC_FUNCENTRY_ODF( ocChiSqDist, 2, 3, 0, "CHISQDIST" ),
+ EXC_FUNCENTRY_ODF( ocChiSqInv, 2, 2, 0, "CHISQINV" )
+};
+
+#undef EXC_FUNCENTRY_ODF
+
+#define EXC_FUNCENTRY_OOO( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+// Import Broken Raw ... even without leading _xlfn.
+#define EXC_FUNCENTRY_OOO_IBR( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), asciiname }
+
+/** Functions defined by Calc, but not in OpenFormula nor supported by Excel. */
+const XclFunctionInfo saFuncTable_OOoLO[] =
+{
+ EXC_FUNCENTRY_OOO( ocErrorType, 1, 1, 0, "ORG.OPENOFFICE.ERRORTYPE" ),
+ EXC_FUNCENTRY_OOO_IBR( ocErrorType, 1, 1, 0, "ERRORTYPE" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocMultiArea, 1, MX, 0, "ORG.OPENOFFICE.MULTIRANGE" ),
+ EXC_FUNCENTRY_OOO_IBR( ocMultiArea, 1, MX, 0, "MULTIRANGE" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocBackSolver, 3, 3, 0, "ORG.OPENOFFICE.GOALSEEK" ),
+ EXC_FUNCENTRY_OOO_IBR( ocBackSolver,3, 3, 0, "GOALSEEK" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocEasterSunday, 1, 1, 0, "ORG.OPENOFFICE.EASTERSUNDAY" ),
+ EXC_FUNCENTRY_OOO_IBR( ocEasterSunday,1,1, 0, "EASTERSUNDAY" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocCurrent, 0, 0, 0, "ORG.OPENOFFICE.CURRENT" ),
+ EXC_FUNCENTRY_OOO_IBR( ocCurrent, 0, 0, 0, "CURRENT" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocStyle, 1, 3, 0, "ORG.OPENOFFICE.STYLE" ),
+ EXC_FUNCENTRY_OOO_IBR( ocStyle, 1, 3, 0, "STYLE" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocConvertOOo, 3, 3, 0, "ORG.OPENOFFICE.CONVERT" ),
+ EXC_FUNCENTRY_OOO( ocColor, 3, 4, 0, "ORG.LIBREOFFICE.COLOR" ),
+ EXC_FUNCENTRY_OOO( ocRawSubtract, 2, MX, 0, "ORG.LIBREOFFICE.RAWSUBTRACT" ),
+ EXC_FUNCENTRY_OOO( ocWeeknumOOo, 2, 2, 0, "ORG.LIBREOFFICE.WEEKNUM_OOO" ),
+ EXC_FUNCENTRY_OOO( ocForecast_ETS_MUL, 3, 6, 0, "ORG.LIBREOFFICE.FORECAST.ETS.MULT" ),
+ EXC_FUNCENTRY_OOO( ocForecast_ETS_PIM, 3, 7, 0, "ORG.LIBREOFFICE.FORECAST.ETS.PI.MULT" ),
+ EXC_FUNCENTRY_OOO( ocForecast_ETS_STM, 3, 6, 0, "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT" ),
+ EXC_FUNCENTRY_OOO( ocRoundSig, 2, 2, 0, "ORG.LIBREOFFICE.ROUNDSIG" ),
+ EXC_FUNCENTRY_OOO( ocRegex, 2, 4, 0, "ORG.LIBREOFFICE.REGEX" ),
+ EXC_FUNCENTRY_OOO( ocFourier, 2, 5, 0, "ORG.LIBREOFFICE.FOURIER" ),
+ EXC_FUNCENTRY_OOO( ocRandomNV, 0, 0, 0, "ORG.LIBREOFFICE.RAND.NV" ),
+ EXC_FUNCENTRY_OOO( ocRandbetweenNV, 2, 2, 0, "ORG.LIBREOFFICE.RANDBETWEEN.NV" )
+};
+
+#undef EXC_FUNCENTRY_OOO_IBR
+#undef EXC_FUNCENTRY_OOO
+
+XclFunctionProvider::XclFunctionProvider( const XclRoot& rRoot )
+{
+ void (XclFunctionProvider::*pFillFunc)( const XclFunctionInfo*, const XclFunctionInfo* ) =
+ rRoot.IsImport() ? &XclFunctionProvider::FillXclFuncMap : &XclFunctionProvider::FillScFuncMap;
+
+ /* Only read/write functions supported in the current BIFF version.
+ Function tables from later BIFF versions may overwrite single functions
+ from earlier tables. */
+ XclBiff eBiff = rRoot.GetBiff();
+ if( eBiff >= EXC_BIFF2 )
+ (this->*pFillFunc)(saFuncTable_2, saFuncTable_2 + SAL_N_ELEMENTS(saFuncTable_2));
+ if( eBiff >= EXC_BIFF3 )
+ (this->*pFillFunc)(saFuncTable_3, saFuncTable_3 + SAL_N_ELEMENTS(saFuncTable_3));
+ if( eBiff >= EXC_BIFF4 )
+ (this->*pFillFunc)(saFuncTable_4, saFuncTable_4 + SAL_N_ELEMENTS(saFuncTable_4));
+ if( eBiff >= EXC_BIFF5 )
+ (this->*pFillFunc)(saFuncTable_5, saFuncTable_5 + SAL_N_ELEMENTS(saFuncTable_5));
+ if( eBiff >= EXC_BIFF8 )
+ (this->*pFillFunc)(saFuncTable_8, saFuncTable_8 + SAL_N_ELEMENTS(saFuncTable_8));
+ (this->*pFillFunc)(saFuncTable_Oox, saFuncTable_Oox + SAL_N_ELEMENTS(saFuncTable_Oox));
+ (this->*pFillFunc)(saFuncTable_2010, saFuncTable_2010 + SAL_N_ELEMENTS(saFuncTable_2010));
+ (this->*pFillFunc)(saFuncTable_2013, saFuncTable_2013 + SAL_N_ELEMENTS(saFuncTable_2013));
+ (this->*pFillFunc)(saFuncTable_2016, saFuncTable_2016 + SAL_N_ELEMENTS(saFuncTable_2016));
+ (this->*pFillFunc)(saFuncTable_Odf, saFuncTable_Odf + SAL_N_ELEMENTS(saFuncTable_Odf));
+ (this->*pFillFunc)(saFuncTable_OOoLO, saFuncTable_OOoLO + SAL_N_ELEMENTS(saFuncTable_OOoLO));
+}
+
+const XclFunctionInfo* XclFunctionProvider::GetFuncInfoFromXclFunc( sal_uInt16 nXclFunc ) const
+{
+ // only in import filter allowed
+ OSL_ENSURE( !maXclFuncMap.empty(), "XclFunctionProvider::GetFuncInfoFromXclFunc - wrong filter" );
+ XclFuncMap::const_iterator aIt = maXclFuncMap.find( nXclFunc );
+ return (aIt == maXclFuncMap.end()) ? nullptr : aIt->second;
+}
+
+const XclFunctionInfo* XclFunctionProvider::GetFuncInfoFromXclMacroName( const OUString& rXclMacroName ) const
+{
+ // only in import filter allowed, but do not test maXclMacroNameMap, it may be empty for old BIFF versions
+ OSL_ENSURE( !maXclFuncMap.empty(), "XclFunctionProvider::GetFuncInfoFromXclMacroName - wrong filter" );
+ XclMacroNameMap::const_iterator aIt = maXclMacroNameMap.find( rXclMacroName );
+ return (aIt == maXclMacroNameMap.end()) ? nullptr : aIt->second;
+}
+
+const XclFunctionInfo* XclFunctionProvider::GetFuncInfoFromOpCode( OpCode eOpCode ) const
+{
+ // only in export filter allowed
+ OSL_ENSURE( !maScFuncMap.empty(), "XclFunctionProvider::GetFuncInfoFromOpCode - wrong filter" );
+ ScFuncMap::const_iterator aIt = maScFuncMap.find( eOpCode );
+ return (aIt == maScFuncMap.end()) ? nullptr : aIt->second;
+}
+
+void XclFunctionProvider::FillXclFuncMap( const XclFunctionInfo* pBeg, const XclFunctionInfo* pEnd )
+{
+ for( const XclFunctionInfo* pIt = pBeg; pIt != pEnd; ++pIt )
+ {
+ if( !::get_flag( pIt->mnFlags, EXC_FUNCFLAG_EXPORTONLY ) )
+ {
+ if( pIt->mnXclFunc != NOID )
+ maXclFuncMap[ pIt->mnXclFunc ] = pIt;
+ if( pIt->IsMacroFunc() )
+ maXclMacroNameMap[ pIt->GetMacroFuncName() ] = pIt;
+ }
+ }
+}
+
+void XclFunctionProvider::FillScFuncMap( const XclFunctionInfo* pBeg, const XclFunctionInfo* pEnd )
+{
+ for( const XclFunctionInfo* pIt = pBeg; pIt != pEnd; ++pIt )
+ if( !::get_flag( pIt->mnFlags, EXC_FUNCFLAG_IMPORTONLY ) )
+ maScFuncMap[ pIt->meOpCode ] = pIt;
+}
+
+// Token array ================================================================
+
+XclTokenArray::XclTokenArray( bool bVolatile ) :
+ mbVolatile( bVolatile )
+{
+}
+
+XclTokenArray::XclTokenArray( ScfUInt8Vec& rTokVec, ScfUInt8Vec& rExtDataVec, bool bVolatile ) :
+ mbVolatile( bVolatile )
+{
+ maTokVec.swap( rTokVec );
+ maExtDataVec.swap( rExtDataVec );
+}
+
+sal_uInt16 XclTokenArray::GetSize() const
+{
+ OSL_ENSURE( maTokVec.size() <= 0xFFFF, "XclTokenArray::GetSize - array too long" );
+ return limit_cast< sal_uInt16 >( maTokVec.size() );
+}
+
+sal_uInt16 XclTokenArray::ReadSize(XclImpStream& rStrm)
+{
+ return rStrm.ReaduInt16();
+}
+
+void XclTokenArray::ReadArray(sal_uInt16 nSize, XclImpStream& rStrm)
+{
+ maTokVec.resize(0);
+
+ const std::size_t nMaxBuffer = 4096;
+ std::size_t nBytesLeft = nSize;
+ std::size_t nTotalRead = 0;
+
+ while (true)
+ {
+ if (!nBytesLeft)
+ break;
+ std::size_t nReadRequest = o3tl::sanitizing_min(nBytesLeft, nMaxBuffer);
+ maTokVec.resize(maTokVec.size() + nReadRequest);
+ auto nRead = rStrm.Read(maTokVec.data() + nTotalRead, nReadRequest);
+ nTotalRead += nRead;
+ if (nRead != nReadRequest)
+ {
+ maTokVec.resize(nTotalRead);
+ break;
+ }
+ nBytesLeft -= nRead;
+ }
+}
+
+void XclTokenArray::Read( XclImpStream& rStrm )
+{
+ ReadArray(ReadSize(rStrm), rStrm);
+}
+
+void XclTokenArray::WriteSize( XclExpStream& rStrm ) const
+{
+ rStrm << GetSize();
+}
+
+void XclTokenArray::WriteArray( XclExpStream& rStrm ) const
+{
+ if( !maTokVec.empty() )
+ rStrm.Write(maTokVec.data(), GetSize());
+ if( !maExtDataVec.empty() )
+ rStrm.Write(maExtDataVec.data(), maExtDataVec.size());
+}
+
+void XclTokenArray::Write( XclExpStream& rStrm ) const
+{
+ WriteSize( rStrm );
+ WriteArray( rStrm );
+}
+
+bool XclTokenArray::operator==( const XclTokenArray& rTokArr ) const
+{
+ return (mbVolatile == rTokArr.mbVolatile) && (maTokVec == rTokArr.maTokVec) && (maExtDataVec == rTokArr.maExtDataVec);
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclTokenArray& rTokArr )
+{
+ rTokArr.Read( rStrm );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclTokenArray& rTokArr )
+{
+ rTokArr.Write( rStrm );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclTokenArrayRef& rxTokArr )
+{
+ if( rxTokArr )
+ rxTokArr->Write( rStrm );
+ else
+ rStrm << sal_uInt16( 0 );
+ return rStrm;
+}
+
+XclTokenArrayIterator::XclTokenArrayIterator() :
+ mppScTokenBeg( nullptr ),
+ mppScTokenEnd( nullptr ),
+ mppScToken( nullptr ),
+ mbSkipSpaces( false )
+{
+}
+
+XclTokenArrayIterator::XclTokenArrayIterator( const ScTokenArray& rScTokArr, bool bSkipSpaces )
+{
+ Init( rScTokArr, bSkipSpaces );
+}
+
+XclTokenArrayIterator::XclTokenArrayIterator( const XclTokenArrayIterator& rTokArrIt, bool bSkipSpaces ) :
+ mppScTokenBeg( rTokArrIt.mppScTokenBeg ),
+ mppScTokenEnd( rTokArrIt.mppScTokenEnd ),
+ mppScToken( rTokArrIt.mppScToken ),
+ mbSkipSpaces( bSkipSpaces )
+{
+ SkipSpaces();
+}
+
+void XclTokenArrayIterator::Init( const ScTokenArray& rScTokArr, bool bSkipSpaces )
+{
+ sal_uInt16 nTokArrLen = rScTokArr.GetLen();
+ mppScTokenBeg = static_cast< const FormulaToken* const* >( nTokArrLen ? rScTokArr.GetArray() : nullptr );
+ mppScTokenEnd = mppScTokenBeg ? (mppScTokenBeg + nTokArrLen) : nullptr;
+ mppScToken = (mppScTokenBeg != mppScTokenEnd) ? mppScTokenBeg : nullptr;
+ mbSkipSpaces = bSkipSpaces;
+ SkipSpaces();
+}
+
+XclTokenArrayIterator& XclTokenArrayIterator::operator++()
+{
+ NextRawToken();
+ SkipSpaces();
+ return *this;
+}
+
+void XclTokenArrayIterator::NextRawToken()
+{
+ if( mppScToken )
+ if( (++mppScToken == mppScTokenEnd) || !*mppScToken )
+ mppScToken = nullptr;
+}
+
+void XclTokenArrayIterator::SkipSpaces()
+{
+ if( mbSkipSpaces )
+ {
+ OpCode eOp;
+ while( Is() && (((eOp = (*this)->GetOpCode()) == ocSpaces) || eOp == ocWhitespace) )
+ NextRawToken();
+ }
+}
+
+// strings and string lists ---------------------------------------------------
+
+bool XclTokenArrayHelper::GetTokenString( OUString& rString, const FormulaToken& rScToken )
+{
+ bool bIsStr = (rScToken.GetType() == svString) && (rScToken.GetOpCode() == ocPush);
+ if( bIsStr ) rString = rScToken.GetString().getString();
+ return bIsStr;
+}
+
+bool XclTokenArrayHelper::GetString( OUString& rString, const ScTokenArray& rScTokArr )
+{
+ XclTokenArrayIterator aIt( rScTokArr, true );
+ // something is following the string token -> error
+ return aIt.Is() && GetTokenString( rString, *aIt ) && !++aIt;
+}
+
+bool XclTokenArrayHelper::GetStringList( OUString& rStringList, const ScTokenArray& rScTokArr, sal_Unicode cSep )
+{
+ bool bRet = true;
+ XclTokenArrayIterator aIt( rScTokArr, true );
+ enum { STATE_START, STATE_STR, STATE_SEP, STATE_END } eState = STATE_START;
+ while( eState != STATE_END ) switch( eState )
+ {
+ case STATE_START:
+ eState = aIt.Is() ? STATE_STR : STATE_END;
+ break;
+ case STATE_STR:
+ {
+ OUString aString;
+ bRet = GetTokenString( aString, *aIt );
+ if( bRet ) rStringList += aString ;
+ eState = (bRet && (++aIt).Is()) ? STATE_SEP : STATE_END;
+ break;
+ }
+ case STATE_SEP:
+ bRet = aIt->GetOpCode() == ocSep;
+ if( bRet ) rStringList += OUStringChar(cSep);
+ eState = (bRet && (++aIt).Is()) ? STATE_STR : STATE_END;
+ break;
+ default:;
+ }
+ return bRet;
+}
+
+void XclTokenArrayHelper::ConvertStringToList(
+ ScTokenArray& rScTokArr, svl::SharedStringPool& rSPool, sal_Unicode cStringSep )
+{
+ OUString aString;
+ if( !GetString( aString, rScTokArr ) )
+ return;
+
+ rScTokArr.Clear();
+ if (aString.isEmpty())
+ return;
+ sal_Int32 nStringIx = 0;
+ for (;;)
+ {
+ OUString aToken( aString.getToken( 0, cStringSep, nStringIx ) );
+ rScTokArr.AddString(rSPool.intern(comphelper::string::stripStart(aToken, ' ')));
+ if (nStringIx<0)
+ break;
+ rScTokArr.AddOpCode( ocSep );
+ }
+}
+
+// multiple operations --------------------------------------------------------
+
+namespace {
+
+bool lclGetAddress( const ScDocument& rDoc, ScAddress& rAddress, const FormulaToken& rToken, const ScAddress& rPos )
+{
+ OpCode eOpCode = rToken.GetOpCode();
+ bool bIsSingleRef = (eOpCode == ocPush) && (rToken.GetType() == svSingleRef);
+ if( bIsSingleRef )
+ {
+ const ScSingleRefData& rRef = *rToken.GetSingleRef();
+ rAddress = rRef.toAbs(rDoc, rPos);
+ bIsSingleRef = !rRef.IsDeleted();
+ }
+ return bIsSingleRef;
+}
+
+} // namespace
+
+bool XclTokenArrayHelper::GetMultipleOpRefs(
+ const ScDocument& rDoc,
+ XclMultipleOpRefs& rRefs, const ScTokenArray& rScTokArr, const ScAddress& rScPos )
+{
+ rRefs.mbDblRefMode = false;
+ enum
+ {
+ stBegin, stTableOp, stOpen, stFormula, stFormulaSep,
+ stColFirst, stColFirstSep, stColRel, stColRelSep,
+ stRowFirst, stRowFirstSep, stRowRel, stClose, stError
+ } eState = stBegin; // last read token
+ for( XclTokenArrayIterator aIt( rScTokArr, true ); aIt.Is() && (eState != stError); ++aIt )
+ {
+ OpCode eOpCode = aIt->GetOpCode();
+ bool bIsSep = eOpCode == ocSep;
+ switch( eState )
+ {
+ case stBegin:
+ eState = (eOpCode == ocTableOp) ? stTableOp : stError;
+ break;
+ case stTableOp:
+ eState = (eOpCode == ocOpen) ? stOpen : stError;
+ break;
+ case stOpen:
+ eState = lclGetAddress(rDoc, rRefs.maFmlaScPos, *aIt, rScPos) ? stFormula : stError;
+ break;
+ case stFormula:
+ eState = bIsSep ? stFormulaSep : stError;
+ break;
+ case stFormulaSep:
+ eState = lclGetAddress(rDoc, rRefs.maColFirstScPos, *aIt, rScPos) ? stColFirst : stError;
+ break;
+ case stColFirst:
+ eState = bIsSep ? stColFirstSep : stError;
+ break;
+ case stColFirstSep:
+ eState = lclGetAddress(rDoc, rRefs.maColRelScPos, *aIt, rScPos) ? stColRel : stError;
+ break;
+ case stColRel:
+ eState = bIsSep ? stColRelSep : ((eOpCode == ocClose) ? stClose : stError);
+ break;
+ case stColRelSep:
+ eState = lclGetAddress(rDoc, rRefs.maRowFirstScPos, *aIt, rScPos) ? stRowFirst : stError;
+ rRefs.mbDblRefMode = true;
+ break;
+ case stRowFirst:
+ eState = bIsSep ? stRowFirstSep : stError;
+ break;
+ case stRowFirstSep:
+ eState = lclGetAddress(rDoc, rRefs.maRowRelScPos, *aIt, rScPos) ? stRowRel : stError;
+ break;
+ case stRowRel:
+ eState = (eOpCode == ocClose) ? stClose : stError;
+ break;
+ default:
+ eState = stError;
+ }
+ }
+ return eState == stClose;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlpage.cxx b/sc/source/filter/excel/xlpage.cxx
new file mode 100644
index 000000000..937aa9427
--- /dev/null
+++ b/sc/source/filter/excel/xlpage.cxx
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlpage.hxx>
+#include <xltools.hxx>
+#include <editeng/paperinf.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <sal/macros.h>
+#include <editeng/brushitem.hxx>
+
+namespace{
+
+struct XclPaperSize
+{
+ Paper mePaper; /// SVX paper size identifier.
+ tools::Long mnWidth; /// Paper width in twips.
+ tools::Long mnHeight; /// Paper height in twips.
+};
+
+constexpr tools::Long in2twips(double n_inch)
+{
+ return o3tl::convert(n_inch, o3tl::Length::in, o3tl::Length::twip) + 0.5;
+}
+constexpr tools::Long mm2twips(double n_mm)
+{
+ return o3tl::convert(n_mm, o3tl::Length::mm, o3tl::Length::twip) + 0.5;
+}
+constexpr tools::Long twips2mm(tools::Long n_twips)
+{
+ return o3tl::convert(n_twips, o3tl::Length::twip, o3tl::Length::mm);
+}
+
+// see ApiPaperSize spPaperSizeTable in filter and aDinTab in i18nutil
+constexpr XclPaperSize pPaperSizeTable[] =
+{
+/* 0*/ { PAPER_USER, 0, 0 }, // undefined
+ { PAPER_LETTER, in2twips( 8.5 ), in2twips( 11 ) }, // Letter
+ { PAPER_USER, in2twips( 8.5 ), in2twips( 11 ) }, // Letter Small
+ { PAPER_TABLOID, in2twips( 11 ), in2twips( 17 ) }, // Tabloid
+ { PAPER_LEDGER, in2twips( 17 ), in2twips( 11 ) }, // Ledger
+/* 5*/ { PAPER_LEGAL, in2twips( 8.5 ), in2twips( 14 ) }, // Legal
+ { PAPER_STATEMENT, in2twips( 5.5 ), in2twips( 8.5 ) }, // Statement
+ { PAPER_EXECUTIVE, in2twips( 7.25 ), in2twips( 10.5 ) }, // Executive
+ { PAPER_A3, mm2twips( 297 ), mm2twips( 420 ) }, // A3
+ { PAPER_A4, mm2twips( 210 ), mm2twips( 297 ) }, // A4
+/* 10*/ { PAPER_USER, mm2twips( 210 ), mm2twips( 297 ) }, // A4 Small
+ { PAPER_A5, mm2twips( 148 ), mm2twips( 210 ) }, // A5
+ /* for JIS vs ISO B confusion see:
+ https://docs.microsoft.com/en-us/windows/win32/intl/paper-sizes
+ http://wiki.openoffice.org/wiki/DefaultPaperSize comments
+ http://partners.adobe.com/public/developer/en/ps/5003.PPD_Spec_v4.3.pdf */
+ { PAPER_B4_JIS, mm2twips( 257 ), mm2twips( 364 ) }, // B4 (JIS)
+ { PAPER_B5_JIS, mm2twips( 182 ), mm2twips( 257 ) }, // B5 (JIS)
+ { PAPER_USER, in2twips( 8.5 ), in2twips( 13 ) }, // Folio
+/* 15*/ { PAPER_QUARTO, mm2twips( 215 ), mm2twips( 275 ) }, // Quarto
+ { PAPER_10x14, in2twips( 10 ), in2twips( 14 ) }, // 10x14
+ { PAPER_USER, in2twips( 11 ), in2twips( 17 ) }, // 11x17
+ { PAPER_USER, in2twips( 8.5 ), in2twips( 11 ) }, // Note
+ { PAPER_ENV_9, in2twips( 3.875 ), in2twips( 8.875 ) }, // Envelope #9
+/* 20*/ { PAPER_ENV_10, in2twips( 4.125 ), in2twips( 9.5 ) }, // Envelope #10
+ { PAPER_ENV_11, in2twips( 4.5 ), in2twips( 10.375 ) }, // Envelope #11
+ { PAPER_ENV_12, in2twips( 4.75 ), in2twips( 11 ) }, // Envelope #12
+ { PAPER_ENV_14, in2twips( 5 ), in2twips( 11.5 ) }, // Envelope #14
+ { PAPER_C, in2twips( 17 ), in2twips( 22 ) }, // ANSI-C
+/* 25*/ { PAPER_D, in2twips( 22 ), in2twips( 34 ) }, // ANSI-D
+ { PAPER_E, in2twips( 34 ), in2twips( 44 ) }, // ANSI-E
+ { PAPER_ENV_DL, mm2twips( 110 ), mm2twips( 220 ) }, // Envelope DL
+ { PAPER_ENV_C5, mm2twips( 162 ), mm2twips( 229 ) }, // Envelope C5
+ { PAPER_ENV_C3, mm2twips( 324 ), mm2twips( 458 ) }, // Envelope C3
+/* 30*/ { PAPER_ENV_C4, mm2twips( 229 ), mm2twips( 324 ) }, // Envelope C4
+ { PAPER_ENV_C6, mm2twips( 114 ), mm2twips( 162 ) }, // Envelope C6
+ { PAPER_ENV_C65, mm2twips( 114 ), mm2twips( 229 ) }, // Envelope C65
+ { PAPER_B4_ISO, mm2twips( 250 ), mm2twips( 353 ) }, // B4 (ISO)
+ { PAPER_B5_ISO, mm2twips( 176 ), mm2twips( 250 ) }, // B5 (ISO)
+/* 35*/ { PAPER_B6_ISO, mm2twips( 125 ), mm2twips( 176 ) }, // B6 (ISO)
+ { PAPER_ENV_ITALY, mm2twips( 110 ), mm2twips( 230 ) }, // Envelope Italy
+ { PAPER_ENV_MONARCH, in2twips( 3.875 ), in2twips( 7.5 ) }, // Envelope Monarch
+ { PAPER_ENV_PERSONAL, in2twips( 3.625 ), in2twips( 6.5 ) }, // 6 3/4 Envelope
+ { PAPER_FANFOLD_US, in2twips( 14.875 ), in2twips( 11 ) }, // US Std Fanfold
+/* 40*/ { PAPER_FANFOLD_DE, in2twips( 8.5 ), in2twips( 12 ) }, // German Std Fanfold
+ { PAPER_FANFOLD_LEGAL_DE, in2twips( 8.5 ), in2twips( 13 ) }, // German Legal Fanfold
+ { PAPER_B4_ISO, mm2twips( 250 ), mm2twips( 353 ) }, // B4 (ISO)
+ { PAPER_POSTCARD_JP,mm2twips( 100 ), mm2twips( 148 ) }, // Japanese Postcard
+ { PAPER_9x11, in2twips( 9 ), in2twips( 11 ) }, // 9x11
+/* 45*/ { PAPER_10x11, in2twips( 10 ), in2twips( 11 ) }, // 10x11
+ { PAPER_15x11, in2twips( 15 ), in2twips( 11 ) }, // 15x11
+ { PAPER_ENV_INVITE, mm2twips( 220 ), mm2twips( 220 ) }, // Envelope Invite
+ { PAPER_USER, 0, 0 }, // undefined
+ { PAPER_USER, 0, 0 }, // undefined
+ /* See: https://docs.microsoft.com/en-us/windows/win32/intl/paper-sizes */
+/* 50*/ { PAPER_USER, in2twips( 9.5 ), in2twips( 12 ) }, // Letter Extra
+ { PAPER_USER, in2twips( 9.5 ), in2twips( 15 ) }, // Legal Extra
+ { PAPER_USER, in2twips( 11.69 ), in2twips( 18 ) }, // Tabloid Extra
+ { PAPER_USER, mm2twips( 235 ), mm2twips( 322 ) }, // A4 Extra
+ { PAPER_USER, in2twips( 8.5 ), in2twips( 11 ) }, // Letter Transverse
+/* 55*/ { PAPER_USER, mm2twips( 210 ), mm2twips( 297 ) }, // A4 Transverse
+ { PAPER_USER, in2twips( 9.5 ), in2twips( 12 ) }, // Letter Extra Transverse
+ { PAPER_A_PLUS, mm2twips( 227 ), mm2twips( 356 ) }, // Super A/A4
+ { PAPER_B_PLUS, mm2twips( 305 ), mm2twips( 487 ) }, // Super B/A3
+ { PAPER_LETTER_PLUS,in2twips( 8.5 ), in2twips( 12.69 ) }, // Letter Plus
+/* 60*/ { PAPER_A4_PLUS, mm2twips( 210 ), mm2twips( 330 ) }, // A4 Plus
+ { PAPER_USER, mm2twips( 148 ), mm2twips( 210 ) }, // A5 Transverse
+ { PAPER_USER, mm2twips( 182 ), mm2twips( 257 ) }, // B5 (JIS) Transverse
+ { PAPER_USER, mm2twips( 322 ), mm2twips( 445 ) }, // A3 Extra
+ { PAPER_USER, mm2twips( 174 ), mm2twips( 235 ) }, // A5 Extra
+/* 65*/ { PAPER_USER, mm2twips( 201 ), mm2twips( 276 ) }, // B5 (ISO) Extra
+ { PAPER_A2, mm2twips( 420 ), mm2twips( 594 ) }, // A2
+ { PAPER_USER, mm2twips( 297 ), mm2twips( 420 ) }, // A3 Transverse
+ { PAPER_USER, mm2twips( 322 ), mm2twips( 445 ) }, // A3 Extra Transverse
+ { PAPER_DOUBLEPOSTCARD_JP, mm2twips( 200 ), mm2twips( 148 ) }, // Double Japanese Postcard
+/* 70*/ { PAPER_A6, mm2twips( 105 ), mm2twips( 148 ) }, // A6
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Kaku #2
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Kaku #3
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Chou #3
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Chou #4
+/* 75*/ { PAPER_USER, in2twips( 11 ), in2twips( 8.5 ) }, // Letter Rotated
+ { PAPER_USER, mm2twips( 420 ), mm2twips( 297 ) }, // A3 Rotated
+ { PAPER_USER, mm2twips( 297 ), mm2twips( 210 ) }, // A4 Rotated
+ { PAPER_USER, mm2twips( 210 ), mm2twips( 148 ) }, // A5 Rotated
+ { PAPER_USER, mm2twips( 364 ), mm2twips( 257 ) }, // B4 (JIS) Rotated
+/* 80*/ { PAPER_USER, mm2twips( 257 ), mm2twips( 182 ) }, // B5 (JIS) Rotated
+ { PAPER_USER, mm2twips( 148 ), mm2twips( 100 ) }, // Japanese Postcard Rotated
+ { PAPER_USER, mm2twips( 148 ), mm2twips( 200 ) }, // Double Japanese Postcard Rotated
+ { PAPER_USER, mm2twips( 148 ), mm2twips( 105 ) }, // A6 Rotated
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Kaku #2 Rotated
+/* 85*/ { PAPER_USER, 0, 0 }, // Japanese Envelope Kaku #3 Rotated
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Chou #3 Rotated
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Chou #4 Rotated
+ { PAPER_B6_JIS, mm2twips( 128 ), mm2twips( 182 ) }, // B6 (JIS)
+ { PAPER_USER, mm2twips( 182 ), mm2twips( 128 ) }, // B6 (JIS) Rotated
+/* 90*/ { PAPER_12x11, in2twips( 12 ), in2twips( 11 ) } // 12x11
+};
+
+} //namespace
+
+// Page settings ==============================================================
+
+XclPageData::XclPageData()
+{
+ SetDefaults();
+}
+
+XclPageData::~XclPageData()
+{
+ // SvxBrushItem incomplete in header
+}
+
+void XclPageData::SetDefaults()
+{
+ maHorPageBreaks.clear();
+ maVerPageBreaks.clear();
+ mxBrushItem.reset();
+ maHeader.clear();
+ maFooter.clear();
+ maHeaderEven.clear();
+ maFooterEven.clear();
+ mfLeftMargin = mfRightMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_LR );
+ mfTopMargin = mfBottomMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_TB );
+ mfHeaderMargin = mfFooterMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_HF );
+ mfHdrLeftMargin = mfHdrRightMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_HLR );
+ mfFtrLeftMargin = mfFtrRightMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_FLR );
+ mnStrictPaperSize = mnPaperSize = EXC_PAPERSIZE_DEFAULT;
+ mnPaperWidth = 0;
+ mnPaperHeight = 0;
+ mnCopies = 1;
+ mnStartPage = 0;
+ mnScaling = 100;
+ mnFitToWidth = mnFitToHeight = 1;
+ mnHorPrintRes = mnVerPrintRes = 300;
+ mbUseEvenHF = mbUseFirstHF = false;
+ mbValid = false;
+ mbPortrait = true;
+ mbPrintInRows = mbBlackWhite = mbDraftQuality = mbPrintNotes = mbManualStart = mbFitToPages = false;
+ mbHorCenter = mbVerCenter = mbPrintHeadings = mbPrintGrid = false;
+}
+
+Size XclPageData::GetScPaperSize() const
+{
+ const XclPaperSize* pEntry = pPaperSizeTable;
+ if( mnPaperSize < SAL_N_ELEMENTS( pPaperSizeTable ) )
+ pEntry += mnPaperSize;
+
+ Size aSize;
+ if( pEntry->mePaper == PAPER_USER )
+ aSize = Size( pEntry->mnWidth, pEntry->mnHeight );
+ else
+ aSize = SvxPaperInfo::GetPaperSize( pEntry->mePaper );
+
+ // invalid size -> back to default
+ if( !aSize.Width() || !aSize.Height() )
+ aSize = SvxPaperInfo::GetDefaultPaperSize();
+
+ if( !mbPortrait )
+ {
+ // swap width and height
+ tools::Long n = aSize.Width();
+ aSize.setWidth(aSize.Height());
+ aSize.setHeight(n);
+ }
+
+ return aSize;
+}
+
+void XclPageData::SetScPaperSize( const Size& rSize, bool bPortrait, bool bStrictSize )
+{
+ mbPortrait = bPortrait;
+ mnPaperSize = 0;
+ tools::Long nWidth = bPortrait ? rSize.Width() : rSize.Height();
+ tools::Long nHeight = bPortrait ? rSize.Height() : rSize.Width();
+ tools::Long nMaxWDiff = 80;
+ tools::Long nMaxHDiff = 50;
+
+ mnPaperWidth = twips2mm( nWidth );
+ mnPaperHeight = twips2mm( nHeight );
+ if( bStrictSize )
+ {
+ nMaxWDiff = 5;
+ nMaxHDiff = 5;
+ mnStrictPaperSize = EXC_PAPERSIZE_USER;
+ }
+ else
+ {
+ mnPaperSize = EXC_PAPERSIZE_DEFAULT;
+ }
+
+ for( const auto &rEntry : pPaperSizeTable)
+ {
+ tools::Long nWDiff = std::abs( rEntry.mnWidth - nWidth );
+ tools::Long nHDiff = std::abs( rEntry.mnHeight - nHeight );
+ if( ((nWDiff <= nMaxWDiff) && (nHDiff < nMaxHDiff)) ||
+ ((nWDiff < nMaxWDiff) && (nHDiff <= nMaxHDiff)) )
+ {
+ sal_uInt16 nIndex = static_cast< sal_uInt16 >( &rEntry - pPaperSizeTable );
+ mnPaperSize = nIndex;
+ if( bStrictSize )
+ mnStrictPaperSize = nIndex;
+
+ nMaxWDiff = nWDiff;
+ nMaxHDiff = nHDiff;
+ }
+ }
+ if( !bStrictSize )
+ SetScPaperSize( rSize, bPortrait, true );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlpivot.cxx b/sc/source/filter/excel/xlpivot.cxx
new file mode 100644
index 000000000..d18ab7416
--- /dev/null
+++ b/sc/source/filter/excel/xlpivot.cxx
@@ -0,0 +1,1043 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dpsave.hxx>
+#include <xestream.hxx>
+#include <xistream.hxx>
+#include <xestring.hxx>
+#include <xlpivot.hxx>
+#include <generalfunction.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+
+using ::com::sun::star::sheet::DataPilotFieldOrientation;
+
+namespace ScDPSortMode = ::com::sun::star::sheet::DataPilotFieldSortMode;
+namespace ScDPShowItemsMode = ::com::sun::star::sheet::DataPilotFieldShowItemsMode;
+namespace ScDPLayoutMode = ::com::sun::star::sheet::DataPilotFieldLayoutMode;
+namespace ScDPRefItemType = ::com::sun::star::sheet::DataPilotFieldReferenceItemType;
+namespace ScDPGroupBy = ::com::sun::star::sheet::DataPilotFieldGroupBy;
+
+// Pivot cache
+
+XclPCItem::XclPCItem() :
+ meType( EXC_PCITEM_INVALID ),
+ maDateTime( DateTime::EMPTY )
+{
+}
+
+XclPCItem::~XclPCItem()
+{
+}
+
+void XclPCItem::SetEmpty()
+{
+ meType = EXC_PCITEM_EMPTY;
+ maText.clear();
+}
+
+void XclPCItem::SetText( const OUString& rText )
+{
+ meType = EXC_PCITEM_TEXT;
+ maText = rText;
+}
+
+void XclPCItem::SetDouble( double fValue, const OUString& rText )
+{
+ meType = EXC_PCITEM_DOUBLE;
+ maText = rText;
+ mfValue = fValue;
+}
+
+void XclPCItem::SetDateTime( const DateTime& rDateTime, const OUString& rText )
+{
+ meType = EXC_PCITEM_DATETIME;
+ maText = rText;
+ maDateTime = rDateTime;
+}
+
+void XclPCItem::SetInteger( sal_Int16 nValue )
+{
+ meType = EXC_PCITEM_INTEGER;
+ maText = OUString::number(nValue);
+ mnValue = nValue;
+}
+
+void XclPCItem::SetError( sal_uInt16 nError )
+{
+ meType = EXC_PCITEM_ERROR;
+ maText.clear();
+ mnError = nError;
+ switch( nError )
+ {
+ case 0x00: maText = "#nullptr!"; break;
+ case 0x07: maText = "#DIV/0!"; break;
+ case 0x0F: maText = "#VALUE!"; break;
+ case 0x17: maText = "#REF!"; break;
+ case 0x1D: maText = "#NAME?"; break;
+ case 0x24: maText = "#NUM!"; break;
+ case 0x2A: maText = "#N/A"; break;
+ default: break;
+ }
+}
+
+void XclPCItem::SetBool( bool bValue, const OUString& rText )
+{
+ meType = EXC_PCITEM_BOOL;
+ maText = rText;
+ mbValue = bValue;
+}
+
+bool XclPCItem::IsEqual( const XclPCItem& rItem ) const
+{
+ if( meType == rItem.meType ) switch( meType )
+ {
+ case EXC_PCITEM_INVALID: return true;
+ case EXC_PCITEM_EMPTY: return true;
+ case EXC_PCITEM_TEXT: return maText == rItem.maText;
+ case EXC_PCITEM_DOUBLE: return mfValue == rItem.mfValue;
+ case EXC_PCITEM_DATETIME: return maDateTime == rItem.maDateTime;
+ case EXC_PCITEM_INTEGER: return mnValue == rItem.mnValue;
+ case EXC_PCITEM_BOOL: return mbValue == rItem.mbValue;
+ case EXC_PCITEM_ERROR: return mnError == rItem.mnError;
+ default: OSL_FAIL( "XclPCItem::IsEqual - unknown pivot cache item type" );
+ }
+ return false;
+}
+
+bool XclPCItem::IsEmpty() const
+{
+ return meType == EXC_PCITEM_EMPTY;
+}
+
+const OUString* XclPCItem::GetText() const
+{
+ return (meType == EXC_PCITEM_TEXT || meType == EXC_PCITEM_ERROR) ? &maText : nullptr;
+}
+
+const double* XclPCItem::GetDouble() const
+{
+ return (meType == EXC_PCITEM_DOUBLE) ? &mfValue : nullptr;
+}
+
+const DateTime* XclPCItem::GetDateTime() const
+{
+ return (meType == EXC_PCITEM_DATETIME) ? &maDateTime : nullptr;
+}
+
+const sal_Int16* XclPCItem::GetInteger() const
+{
+ return (meType == EXC_PCITEM_INTEGER) ? &mnValue : nullptr;
+}
+
+const sal_uInt16* XclPCItem::GetError() const
+{
+ return (meType == EXC_PCITEM_ERROR) ? &mnError : nullptr;
+}
+
+const bool* XclPCItem::GetBool() const
+{
+ return (meType == EXC_PCITEM_BOOL) ? &mbValue : nullptr;
+}
+
+XclPCItemType XclPCItem::GetType() const
+{
+ return meType;
+}
+
+// Field settings =============================================================
+
+XclPCFieldInfo::XclPCFieldInfo() :
+ mnFlags( 0 ),
+ mnGroupChild( 0 ),
+ mnGroupBase( 0 ),
+ mnVisItems( 0 ),
+ mnGroupItems( 0 ),
+ mnBaseItems( 0 ),
+ mnOrigItems( 0 )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCFieldInfo& rInfo )
+{
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ rInfo.mnGroupChild = rStrm.ReaduInt16();
+ rInfo.mnGroupBase = rStrm.ReaduInt16();
+ rInfo.mnVisItems = rStrm.ReaduInt16();
+ rInfo.mnGroupItems = rStrm.ReaduInt16();
+ rInfo.mnBaseItems = rStrm.ReaduInt16();
+ rInfo.mnOrigItems = rStrm.ReaduInt16();
+ if( rStrm.GetRecLeft() >= 3 )
+ rInfo.maName = rStrm.ReadUniString();
+ else
+ rInfo.maName.clear();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCFieldInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnFlags
+ << rInfo.mnGroupChild
+ << rInfo.mnGroupBase
+ << rInfo.mnVisItems
+ << rInfo.mnGroupItems
+ << rInfo.mnBaseItems
+ << rInfo.mnOrigItems
+ << XclExpString( rInfo.maName );
+}
+
+// Numeric grouping field settings ============================================
+
+XclPCNumGroupInfo::XclPCNumGroupInfo() :
+ mnFlags( EXC_SXNUMGROUP_AUTOMIN | EXC_SXNUMGROUP_AUTOMAX )
+{
+ SetNumType();
+}
+
+void XclPCNumGroupInfo::SetNumType()
+{
+ SetXclDataType( EXC_SXNUMGROUP_TYPE_NUM );
+}
+
+sal_Int32 XclPCNumGroupInfo::GetScDateType() const
+{
+ sal_Int32 nScType = 0;
+ switch( GetXclDataType() )
+ {
+ case EXC_SXNUMGROUP_TYPE_SEC: nScType = ScDPGroupBy::SECONDS; break;
+ case EXC_SXNUMGROUP_TYPE_MIN: nScType = ScDPGroupBy::MINUTES; break;
+ case EXC_SXNUMGROUP_TYPE_HOUR: nScType = ScDPGroupBy::HOURS; break;
+ case EXC_SXNUMGROUP_TYPE_DAY: nScType = ScDPGroupBy::DAYS; break;
+ case EXC_SXNUMGROUP_TYPE_MONTH: nScType = ScDPGroupBy::MONTHS; break;
+ case EXC_SXNUMGROUP_TYPE_QUART: nScType = ScDPGroupBy::QUARTERS; break;
+ case EXC_SXNUMGROUP_TYPE_YEAR: nScType = ScDPGroupBy::YEARS; break;
+ default: SAL_WARN("sc.filter", "XclPCNumGroupInfo::GetScDateType - unexpected date type " << GetXclDataType() );
+ }
+ return nScType;
+}
+
+void XclPCNumGroupInfo::SetScDateType( sal_Int32 nScType )
+{
+ sal_uInt16 nXclType = EXC_SXNUMGROUP_TYPE_NUM;
+ switch( nScType )
+ {
+ case ScDPGroupBy::SECONDS: nXclType = EXC_SXNUMGROUP_TYPE_SEC; break;
+ case ScDPGroupBy::MINUTES: nXclType = EXC_SXNUMGROUP_TYPE_MIN; break;
+ case ScDPGroupBy::HOURS: nXclType = EXC_SXNUMGROUP_TYPE_HOUR; break;
+ case ScDPGroupBy::DAYS: nXclType = EXC_SXNUMGROUP_TYPE_DAY; break;
+ case ScDPGroupBy::MONTHS: nXclType = EXC_SXNUMGROUP_TYPE_MONTH; break;
+ case ScDPGroupBy::QUARTERS: nXclType = EXC_SXNUMGROUP_TYPE_QUART; break;
+ case ScDPGroupBy::YEARS: nXclType = EXC_SXNUMGROUP_TYPE_YEAR; break;
+ default:
+ SAL_INFO("sc.filter", "unexpected date type " << nScType);
+ }
+ SetXclDataType( nXclType );
+}
+
+sal_uInt16 XclPCNumGroupInfo::GetXclDataType() const
+{
+ return ::extract_value< sal_uInt16 >( mnFlags, 2, 4 );
+}
+
+void XclPCNumGroupInfo::SetXclDataType( sal_uInt16 nXclType )
+{
+ ::insert_value( mnFlags, nXclType, 2, 4 );
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCNumGroupInfo& rInfo )
+{
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCNumGroupInfo& rInfo )
+{
+ return rStrm << rInfo.mnFlags;
+}
+
+// Base class for pivot cache fields ==========================================
+
+XclPCField::XclPCField( XclPCFieldType eFieldType, sal_uInt16 nFieldIdx ) :
+ meFieldType( eFieldType ),
+ mnFieldIdx( nFieldIdx )
+{
+}
+
+XclPCField::~XclPCField()
+{
+}
+
+bool XclPCField::IsSupportedField() const
+{
+ return (meFieldType != EXC_PCFIELD_CALCED) && (meFieldType != EXC_PCFIELD_UNKNOWN);
+}
+
+bool XclPCField::IsStandardField() const
+{
+ return meFieldType == EXC_PCFIELD_STANDARD;
+}
+
+bool XclPCField::IsStdGroupField() const
+{
+ return meFieldType == EXC_PCFIELD_STDGROUP;
+}
+
+bool XclPCField::IsNumGroupField() const
+{
+ return meFieldType == EXC_PCFIELD_NUMGROUP;
+}
+
+bool XclPCField::IsDateGroupField() const
+{
+ return (meFieldType == EXC_PCFIELD_DATEGROUP) || (meFieldType == EXC_PCFIELD_DATECHILD);
+}
+
+bool XclPCField::IsGroupField() const
+{
+ return IsStdGroupField() || IsNumGroupField() || IsDateGroupField();
+}
+
+bool XclPCField::IsGroupBaseField() const
+{
+ return ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
+}
+
+bool XclPCField::IsGroupChildField() const
+{
+ return (meFieldType == EXC_PCFIELD_STDGROUP) || (meFieldType == EXC_PCFIELD_DATECHILD);
+}
+
+bool XclPCField::HasOrigItems() const
+{
+ return IsSupportedField() && ((maFieldInfo.mnOrigItems > 0) || HasPostponedItems());
+}
+
+bool XclPCField::HasInlineItems() const
+{
+ return (IsStandardField() || IsGroupField()) && ((maFieldInfo.mnGroupItems > 0) || (maFieldInfo.mnOrigItems > 0));
+}
+
+bool XclPCField::HasPostponedItems() const
+{
+ return IsStandardField() && ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_POSTPONE );
+}
+
+bool XclPCField::Has16BitIndexes() const
+{
+ return IsStandardField() && ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT );
+}
+
+// Pivot cache settings =======================================================
+
+/** Contains data for a pivot cache (SXDB record). */
+XclPCInfo::XclPCInfo() :
+ mnSrcRecs( 0 ),
+ mnStrmId( 0xFFFF ),
+ mnFlags( EXC_SXDB_DEFAULTFLAGS ),
+ mnBlockRecs( EXC_SXDB_BLOCKRECS ),
+ mnStdFields( 0 ),
+ mnTotalFields( 0 ),
+ mnSrcType( EXC_SXDB_SRC_SHEET )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCInfo& rInfo )
+{
+ rInfo.mnSrcRecs = rStrm.ReaduInt32();
+ rInfo.mnStrmId = rStrm.ReaduInt16();
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ rInfo.mnBlockRecs = rStrm.ReaduInt16();
+ rInfo.mnStdFields = rStrm.ReaduInt16();
+ rInfo.mnTotalFields = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ rInfo.mnSrcType = rStrm.ReaduInt16();
+ rInfo.maUserName = rStrm.ReadUniString();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnSrcRecs
+ << rInfo.mnStrmId
+ << rInfo.mnFlags
+ << rInfo.mnBlockRecs
+ << rInfo.mnStdFields
+ << rInfo.mnTotalFields
+ << sal_uInt16( 0 )
+ << rInfo.mnSrcType
+ << XclExpString( rInfo.maUserName );
+}
+
+// Pivot table
+
+// cached name ================================================================
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTCachedName& rCachedName )
+{
+ sal_uInt16 nStrLen;
+ nStrLen = rStrm.ReaduInt16();
+ rCachedName.mbUseCache = nStrLen == EXC_PT_NOSTRING;
+ if( rCachedName.mbUseCache )
+ rCachedName.maName.clear();
+ else
+ rCachedName.maName = rStrm.ReadUniString( nStrLen );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTCachedName& rCachedName )
+{
+ if( rCachedName.mbUseCache )
+ rStrm << EXC_PT_NOSTRING;
+ else
+ rStrm << XclExpString( rCachedName.maName, XclStrFlags::NONE, EXC_PT_MAXSTRLEN );
+ return rStrm;
+}
+
+const OUString* XclPTVisNameInfo::GetVisName() const
+{
+ return HasVisName() ? &maVisName.maName : nullptr;
+}
+
+void XclPTVisNameInfo::SetVisName( const OUString& rName )
+{
+ maVisName.maName = rName;
+ maVisName.mbUseCache = rName.isEmpty();
+}
+
+// Field item settings ========================================================
+
+XclPTItemInfo::XclPTItemInfo() :
+ mnType( EXC_SXVI_TYPE_DATA ),
+ mnFlags( EXC_SXVI_DEFAULTFLAGS ),
+ mnCacheIdx( EXC_SXVI_DEFAULT_CACHE )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTItemInfo& rInfo )
+{
+ rInfo.mnType = rStrm.ReaduInt16();
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ rInfo.mnCacheIdx = rStrm.ReaduInt16();
+ rStrm >> rInfo.maVisName;
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTItemInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnType
+ << rInfo.mnFlags
+ << rInfo.mnCacheIdx
+ << rInfo.maVisName;
+}
+
+// General field settings =====================================================
+
+XclPTFieldInfo::XclPTFieldInfo() :
+ mnAxes( EXC_SXVD_AXIS_NONE ),
+ mnSubtCount( 1 ),
+ mnSubtotals( EXC_SXVD_SUBT_DEFAULT ),
+ mnItemCount( 0 ),
+ mnCacheIdx( EXC_SXVD_DEFAULT_CACHE )
+{
+}
+
+DataPilotFieldOrientation XclPTFieldInfo::GetApiOrient( sal_uInt16 nMask ) const
+{
+ using namespace ::com::sun::star::sheet;
+ DataPilotFieldOrientation eOrient = DataPilotFieldOrientation_HIDDEN;
+ sal_uInt16 nUsedAxes = mnAxes & nMask;
+ if( nUsedAxes & EXC_SXVD_AXIS_ROW )
+ eOrient = DataPilotFieldOrientation_ROW;
+ else if( nUsedAxes & EXC_SXVD_AXIS_COL )
+ eOrient = DataPilotFieldOrientation_COLUMN;
+ else if( nUsedAxes & EXC_SXVD_AXIS_PAGE )
+ eOrient = DataPilotFieldOrientation_PAGE;
+ else if( nUsedAxes & EXC_SXVD_AXIS_DATA )
+ eOrient = DataPilotFieldOrientation_DATA;
+ return eOrient;
+}
+
+void XclPTFieldInfo::AddApiOrient( DataPilotFieldOrientation eOrient )
+{
+ using namespace ::com::sun::star::sheet;
+ switch( eOrient )
+ {
+ case DataPilotFieldOrientation_ROW: mnAxes |= EXC_SXVD_AXIS_ROW; break;
+ case DataPilotFieldOrientation_COLUMN: mnAxes |= EXC_SXVD_AXIS_COL; break;
+ case DataPilotFieldOrientation_PAGE: mnAxes |= EXC_SXVD_AXIS_PAGE; break;
+ case DataPilotFieldOrientation_DATA: mnAxes |= EXC_SXVD_AXIS_DATA; break;
+ default:;
+ }
+}
+
+//TODO: should be a Sequence<GeneralFunction> in ScDPSaveData
+void XclPTFieldInfo::GetSubtotals( XclPTSubtotalVec& rSubtotals ) const
+{
+ rSubtotals.clear();
+ rSubtotals.reserve( 16 );
+
+ if( mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) rSubtotals.push_back( ScGeneralFunction::AUTO );
+ if( mnSubtotals & EXC_SXVD_SUBT_SUM ) rSubtotals.push_back( ScGeneralFunction::SUM );
+ if( mnSubtotals & EXC_SXVD_SUBT_COUNT ) rSubtotals.push_back( ScGeneralFunction::COUNT );
+ if( mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) rSubtotals.push_back( ScGeneralFunction::AVERAGE );
+ if( mnSubtotals & EXC_SXVD_SUBT_MAX ) rSubtotals.push_back( ScGeneralFunction::MAX );
+ if( mnSubtotals & EXC_SXVD_SUBT_MIN ) rSubtotals.push_back( ScGeneralFunction::MIN );
+ if( mnSubtotals & EXC_SXVD_SUBT_PROD ) rSubtotals.push_back( ScGeneralFunction::PRODUCT );
+ if( mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) rSubtotals.push_back( ScGeneralFunction::COUNTNUMS );
+ if( mnSubtotals & EXC_SXVD_SUBT_STDDEV ) rSubtotals.push_back( ScGeneralFunction::STDEV );
+ if( mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) rSubtotals.push_back( ScGeneralFunction::STDEVP );
+ if( mnSubtotals & EXC_SXVD_SUBT_VAR ) rSubtotals.push_back( ScGeneralFunction::VAR );
+ if( mnSubtotals & EXC_SXVD_SUBT_VARP ) rSubtotals.push_back( ScGeneralFunction::VARP );
+}
+
+void XclPTFieldInfo::SetSubtotals( const XclPTSubtotalVec& rSubtotals )
+{
+ mnSubtotals = EXC_SXVD_SUBT_NONE;
+ for( const auto& rSubtotal : rSubtotals )
+ {
+ switch( rSubtotal )
+ {
+ case ScGeneralFunction::AUTO: mnSubtotals |= EXC_SXVD_SUBT_DEFAULT; break;
+ case ScGeneralFunction::SUM: mnSubtotals |= EXC_SXVD_SUBT_SUM; break;
+ case ScGeneralFunction::COUNT: mnSubtotals |= EXC_SXVD_SUBT_COUNT; break;
+ case ScGeneralFunction::AVERAGE: mnSubtotals |= EXC_SXVD_SUBT_AVERAGE; break;
+ case ScGeneralFunction::MAX: mnSubtotals |= EXC_SXVD_SUBT_MAX; break;
+ case ScGeneralFunction::MIN: mnSubtotals |= EXC_SXVD_SUBT_MIN; break;
+ case ScGeneralFunction::PRODUCT: mnSubtotals |= EXC_SXVD_SUBT_PROD; break;
+ case ScGeneralFunction::COUNTNUMS: mnSubtotals |= EXC_SXVD_SUBT_COUNTNUM; break;
+ case ScGeneralFunction::STDEV: mnSubtotals |= EXC_SXVD_SUBT_STDDEV; break;
+ case ScGeneralFunction::STDEVP: mnSubtotals |= EXC_SXVD_SUBT_STDDEVP; break;
+ case ScGeneralFunction::VAR: mnSubtotals |= EXC_SXVD_SUBT_VAR; break;
+ case ScGeneralFunction::VARP: mnSubtotals |= EXC_SXVD_SUBT_VARP; break;
+ default: break;
+ }
+ }
+
+ mnSubtCount = 0;
+ for( sal_uInt16 nMask = 0x8000; nMask; nMask >>= 1 )
+ if( mnSubtotals & nMask )
+ ++mnSubtCount;
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldInfo& rInfo )
+{
+ // rInfo.mnCacheIdx is not part of the SXVD record
+ rInfo.mnAxes = rStrm.ReaduInt16();
+ rInfo.mnSubtCount = rStrm.ReaduInt16();
+ rInfo.mnSubtotals = rStrm.ReaduInt16();
+ rInfo.mnItemCount = rStrm.ReaduInt16();
+ rStrm >> rInfo.maVisName;
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldInfo& rInfo )
+{
+ // rInfo.mnCacheIdx is not part of the SXVD record
+ return rStrm
+ << rInfo.mnAxes
+ << rInfo.mnSubtCount
+ << rInfo.mnSubtotals
+ << rInfo.mnItemCount
+ << rInfo.maVisName;
+}
+
+// Extended field settings ====================================================
+
+XclPTFieldExtInfo::XclPTFieldExtInfo() :
+ mnFlags( EXC_SXVDEX_DEFAULTFLAGS ),
+ mnSortField( EXC_SXVDEX_SORT_OWN ),
+ mnShowField( EXC_SXVDEX_SHOW_NONE ),
+ mnNumFmt(0)
+{
+}
+
+sal_Int32 XclPTFieldExtInfo::GetApiSortMode() const
+{
+ sal_Int32 nSortMode = ScDPSortMode::MANUAL;
+ if( ::get_flag( mnFlags, EXC_SXVDEX_SORT ) )
+ nSortMode = (mnSortField == EXC_SXVDEX_SORT_OWN) ? ScDPSortMode::NAME : ScDPSortMode::DATA;
+ return nSortMode;
+}
+
+void XclPTFieldExtInfo::SetApiSortMode( sal_Int32 nSortMode )
+{
+ bool bSort = (nSortMode == ScDPSortMode::NAME) || (nSortMode == ScDPSortMode::DATA);
+ ::set_flag( mnFlags, EXC_SXVDEX_SORT, bSort );
+ if( nSortMode == ScDPSortMode::NAME )
+ mnSortField = EXC_SXVDEX_SORT_OWN; // otherwise sort field has to be set by caller
+}
+
+sal_Int32 XclPTFieldExtInfo::GetApiAutoShowMode() const
+{
+ return ::get_flagvalue( mnFlags, EXC_SXVDEX_AUTOSHOW_ASC,
+ ScDPShowItemsMode::FROM_TOP, ScDPShowItemsMode::FROM_BOTTOM );
+}
+
+void XclPTFieldExtInfo::SetApiAutoShowMode( sal_Int32 nShowMode )
+{
+ ::set_flag( mnFlags, EXC_SXVDEX_AUTOSHOW_ASC, nShowMode == ScDPShowItemsMode::FROM_TOP );
+}
+
+sal_Int32 XclPTFieldExtInfo::GetApiAutoShowCount() const
+{
+ return ::extract_value< sal_Int32 >( mnFlags, 24, 8 );
+}
+
+void XclPTFieldExtInfo::SetApiAutoShowCount( sal_Int32 nShowCount )
+{
+ ::insert_value( mnFlags, limit_cast< sal_uInt8 >( nShowCount ), 24, 8 );
+}
+
+sal_Int32 XclPTFieldExtInfo::GetApiLayoutMode() const
+{
+ sal_Int32 nLayoutMode = ScDPLayoutMode::TABULAR_LAYOUT;
+ if( ::get_flag( mnFlags, EXC_SXVDEX_LAYOUT_REPORT ) )
+ nLayoutMode = ::get_flag( mnFlags, EXC_SXVDEX_LAYOUT_TOP ) ?
+ ScDPLayoutMode::OUTLINE_SUBTOTALS_TOP : ScDPLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
+ return nLayoutMode;
+}
+
+void XclPTFieldExtInfo::SetApiLayoutMode( sal_Int32 nLayoutMode )
+{
+ ::set_flag( mnFlags, EXC_SXVDEX_LAYOUT_REPORT, nLayoutMode != ScDPLayoutMode::TABULAR_LAYOUT );
+ ::set_flag( mnFlags, EXC_SXVDEX_LAYOUT_TOP, nLayoutMode == ScDPLayoutMode::OUTLINE_SUBTOTALS_TOP );
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldExtInfo& rInfo )
+{
+ sal_uInt8 nNameLen = 0;
+ rInfo.mnFlags = rStrm.ReaduInt32();
+ rInfo.mnSortField = rStrm.ReaduInt16();
+ rInfo.mnShowField = rStrm.ReaduInt16();
+ rInfo.mnNumFmt = rStrm.ReaduInt16();
+ nNameLen = rStrm.ReaduInt8();
+
+ rStrm.Ignore(10);
+ if (nNameLen != 0xFF)
+ // Custom field total name is used. Pick it up.
+ rInfo.mpFieldTotalName = rStrm.ReadUniString(nNameLen, 0);
+
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldExtInfo& rInfo )
+{
+ rStrm << rInfo.mnFlags
+ << rInfo.mnSortField
+ << rInfo.mnShowField
+ << EXC_SXVDEX_FORMAT_NONE;
+
+ if (rInfo.mpFieldTotalName && !rInfo.mpFieldTotalName->isEmpty())
+ {
+ OUString aFinalName = *rInfo.mpFieldTotalName;
+ if (aFinalName.getLength() >= 254)
+ aFinalName = aFinalName.copy(0, 254);
+ sal_uInt8 nNameLen = static_cast<sal_uInt8>(aFinalName.getLength());
+ rStrm << nNameLen;
+ rStrm.WriteZeroBytes(10);
+ rStrm << XclExpString(aFinalName, XclStrFlags::NoHeader);
+ }
+ else
+ {
+ rStrm << sal_uInt16(0xFFFF);
+ rStrm.WriteZeroBytes(8);
+ }
+ return rStrm;
+}
+
+// Page field settings ========================================================
+
+XclPTPageFieldInfo::XclPTPageFieldInfo() :
+ mnField( 0 ),
+ mnSelItem( EXC_SXPI_ALLITEMS ),
+ mnObjId( 0xFFFF )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTPageFieldInfo& rInfo )
+{
+ rInfo.mnField = rStrm.ReaduInt16();
+ rInfo.mnSelItem = rStrm.ReaduInt16();
+ rInfo.mnObjId = rStrm.ReaduInt16();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTPageFieldInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnField
+ << rInfo.mnSelItem
+ << rInfo.mnObjId;
+}
+
+// Data field settings ========================================================
+
+XclPTDataFieldInfo::XclPTDataFieldInfo() :
+ mnField( 0 ),
+ mnAggFunc( EXC_SXDI_FUNC_SUM ),
+ mnRefType( EXC_SXDI_REF_NORMAL ),
+ mnRefField( 0 ),
+ mnRefItem( 0 ),
+ mnNumFmt( 0 )
+{
+}
+
+ScGeneralFunction XclPTDataFieldInfo::GetApiAggFunc() const
+{
+ ScGeneralFunction eAggFunc;
+ switch( mnAggFunc )
+ {
+ case EXC_SXDI_FUNC_SUM: eAggFunc = ScGeneralFunction::SUM; break;
+ case EXC_SXDI_FUNC_COUNT: eAggFunc = ScGeneralFunction::COUNT; break;
+ case EXC_SXDI_FUNC_AVERAGE: eAggFunc = ScGeneralFunction::AVERAGE; break;
+ case EXC_SXDI_FUNC_MAX: eAggFunc = ScGeneralFunction::MAX; break;
+ case EXC_SXDI_FUNC_MIN: eAggFunc = ScGeneralFunction::MIN; break;
+ case EXC_SXDI_FUNC_PRODUCT: eAggFunc = ScGeneralFunction::PRODUCT; break;
+ case EXC_SXDI_FUNC_COUNTNUM: eAggFunc = ScGeneralFunction::COUNTNUMS; break;
+ case EXC_SXDI_FUNC_STDDEV: eAggFunc = ScGeneralFunction::STDEV; break;
+ case EXC_SXDI_FUNC_STDDEVP: eAggFunc = ScGeneralFunction::STDEVP; break;
+ case EXC_SXDI_FUNC_VAR: eAggFunc = ScGeneralFunction::VAR; break;
+ case EXC_SXDI_FUNC_VARP: eAggFunc = ScGeneralFunction::VARP; break;
+ default: eAggFunc = ScGeneralFunction::SUM;
+ }
+ return eAggFunc;
+}
+
+void XclPTDataFieldInfo::SetApiAggFunc( ScGeneralFunction eAggFunc )
+{
+ switch( eAggFunc )
+ {
+ case ScGeneralFunction::SUM: mnAggFunc = EXC_SXDI_FUNC_SUM; break;
+ case ScGeneralFunction::COUNT: mnAggFunc = EXC_SXDI_FUNC_COUNT; break;
+ case ScGeneralFunction::AVERAGE: mnAggFunc = EXC_SXDI_FUNC_AVERAGE; break;
+ case ScGeneralFunction::MAX: mnAggFunc = EXC_SXDI_FUNC_MAX; break;
+ case ScGeneralFunction::MIN: mnAggFunc = EXC_SXDI_FUNC_MIN; break;
+ case ScGeneralFunction::PRODUCT: mnAggFunc = EXC_SXDI_FUNC_PRODUCT; break;
+ case ScGeneralFunction::COUNTNUMS: mnAggFunc = EXC_SXDI_FUNC_COUNTNUM; break;
+ case ScGeneralFunction::STDEV: mnAggFunc = EXC_SXDI_FUNC_STDDEV; break;
+ case ScGeneralFunction::STDEVP: mnAggFunc = EXC_SXDI_FUNC_STDDEVP; break;
+ case ScGeneralFunction::VAR: mnAggFunc = EXC_SXDI_FUNC_VAR; break;
+ case ScGeneralFunction::VARP: mnAggFunc = EXC_SXDI_FUNC_VARP; break;
+ default: mnAggFunc = EXC_SXDI_FUNC_SUM;
+ }
+}
+
+sal_Int32 XclPTDataFieldInfo::GetApiRefType() const
+{
+ namespace ScDPRefType = ::com::sun::star::sheet::DataPilotFieldReferenceType;
+ sal_Int32 nRefType;
+ switch( mnRefType )
+ {
+ case EXC_SXDI_REF_DIFF: nRefType = ScDPRefType::ITEM_DIFFERENCE; break;
+ case EXC_SXDI_REF_PERC: nRefType = ScDPRefType::ITEM_PERCENTAGE; break;
+ case EXC_SXDI_REF_PERC_DIFF: nRefType = ScDPRefType::ITEM_PERCENTAGE_DIFFERENCE; break;
+ case EXC_SXDI_REF_RUN_TOTAL: nRefType = ScDPRefType::RUNNING_TOTAL; break;
+ case EXC_SXDI_REF_PERC_ROW: nRefType = ScDPRefType::ROW_PERCENTAGE; break;
+ case EXC_SXDI_REF_PERC_COL: nRefType = ScDPRefType::COLUMN_PERCENTAGE; break;
+ case EXC_SXDI_REF_PERC_TOTAL: nRefType = ScDPRefType::TOTAL_PERCENTAGE; break;
+ case EXC_SXDI_REF_INDEX: nRefType = ScDPRefType::INDEX; break;
+ default: nRefType = ScDPRefType::NONE;
+ }
+ return nRefType;
+}
+
+void XclPTDataFieldInfo::SetApiRefType( sal_Int32 nRefType )
+{
+ namespace ScDPRefType = ::com::sun::star::sheet::DataPilotFieldReferenceType;
+ switch( nRefType )
+ {
+ case ScDPRefType::ITEM_DIFFERENCE: mnRefType = EXC_SXDI_REF_DIFF; break;
+ case ScDPRefType::ITEM_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC; break;
+ case ScDPRefType::ITEM_PERCENTAGE_DIFFERENCE: mnRefType = EXC_SXDI_REF_PERC_DIFF; break;
+ case ScDPRefType::RUNNING_TOTAL: mnRefType = EXC_SXDI_REF_RUN_TOTAL; break;
+ case ScDPRefType::ROW_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC_ROW; break;
+ case ScDPRefType::COLUMN_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC_COL; break;
+ case ScDPRefType::TOTAL_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC_TOTAL;break;
+ case ScDPRefType::INDEX: mnRefType = EXC_SXDI_REF_INDEX; break;
+ default: mnRefType = EXC_SXDI_REF_NORMAL;
+ }
+}
+
+sal_Int32 XclPTDataFieldInfo::GetApiRefItemType() const
+{
+ sal_Int32 nRefItemType;
+ switch( mnRefItem )
+ {
+ case EXC_SXDI_PREVITEM: nRefItemType = ScDPRefItemType::PREVIOUS; break;
+ case EXC_SXDI_NEXTITEM: nRefItemType = ScDPRefItemType::NEXT; break;
+ default: nRefItemType = ScDPRefItemType::NAMED;
+ }
+ return nRefItemType;
+}
+
+void XclPTDataFieldInfo::SetApiRefItemType( sal_Int32 nRefItemType )
+{
+ switch( nRefItemType )
+ {
+ case ScDPRefItemType::PREVIOUS: mnRefItem = EXC_SXDI_PREVITEM; break;
+ case ScDPRefItemType::NEXT: mnRefItem = EXC_SXDI_NEXTITEM; break;
+ // nothing for named item reference
+ }
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTDataFieldInfo& rInfo )
+{
+ rInfo.mnField = rStrm.ReaduInt16();
+ rInfo.mnAggFunc = rStrm.ReaduInt16();
+ rInfo.mnRefType = rStrm.ReaduInt16();
+ rInfo.mnRefField = rStrm.ReaduInt16();
+ rInfo.mnRefItem = rStrm.ReaduInt16();
+ rInfo.mnNumFmt = rStrm.ReaduInt16();
+ rStrm >> rInfo.maVisName;
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTDataFieldInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnField
+ << rInfo.mnAggFunc
+ << rInfo.mnRefType
+ << rInfo.mnRefField
+ << rInfo.mnRefItem
+ << rInfo.mnNumFmt
+ << rInfo.maVisName;
+}
+
+// Pivot table settings =======================================================
+
+XclPTInfo::XclPTInfo() :
+ mnFirstHeadRow( 0 ),
+ mnCacheIdx( 0xFFFF ),
+ mnDataAxis( EXC_SXVD_AXIS_NONE ),
+ mnDataPos( EXC_SXVIEW_DATALAST ),
+ mnFields( 0 ),
+ mnRowFields( 0 ),
+ mnColFields( 0 ),
+ mnPageFields( 0 ),
+ mnDataFields( 0 ),
+ mnDataRows( 0 ),
+ mnDataCols( 0 ),
+ mnFlags( EXC_SXVIEW_DEFAULTFLAGS ),
+ mnAutoFmtIdx( EXC_SXVIEW_AUTOFMT )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTInfo& rInfo )
+{
+ sal_uInt16 nTabLen, nDataLen;
+
+ rStrm >> rInfo.maOutXclRange;
+ rInfo.mnFirstHeadRow = rStrm.ReaduInt16();
+ rStrm >> rInfo.maDataXclPos;
+ rInfo.mnCacheIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ rInfo.mnDataAxis = rStrm.ReaduInt16();
+ rInfo.mnDataPos = rStrm.ReaduInt16();
+ rInfo.mnFields = rStrm.ReaduInt16();
+ rInfo.mnRowFields = rStrm.ReaduInt16();
+ rInfo.mnColFields = rStrm.ReaduInt16();
+ rInfo.mnPageFields = rStrm.ReaduInt16();
+ rInfo.mnDataFields = rStrm.ReaduInt16();
+ rInfo.mnDataRows = rStrm.ReaduInt16();
+ rInfo.mnDataCols = rStrm.ReaduInt16();
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ rInfo.mnAutoFmtIdx = rStrm.ReaduInt16();
+ nTabLen = rStrm.ReaduInt16();
+ nDataLen = rStrm.ReaduInt16();
+ rInfo.maTableName = rStrm.ReadUniString( nTabLen );
+ rInfo.maDataName = rStrm.ReadUniString( nDataLen );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTInfo& rInfo )
+{
+ XclExpString aXclTableName( rInfo.maTableName );
+ XclExpString aXclDataName( rInfo.maDataName );
+
+ rStrm << rInfo.maOutXclRange
+ << rInfo.mnFirstHeadRow
+ << rInfo.maDataXclPos
+ << rInfo.mnCacheIdx
+ << sal_uInt16( 0 )
+ << rInfo.mnDataAxis << rInfo.mnDataPos
+ << rInfo.mnFields
+ << rInfo.mnRowFields << rInfo.mnColFields
+ << rInfo.mnPageFields << rInfo.mnDataFields
+ << rInfo.mnDataRows << rInfo.mnDataCols
+ << rInfo.mnFlags
+ << rInfo.mnAutoFmtIdx
+ << aXclTableName.Len() << aXclDataName.Len();
+ aXclTableName.WriteFlagField( rStrm );
+ aXclTableName.WriteBuffer( rStrm );
+ aXclDataName.WriteFlagField( rStrm );
+ aXclDataName.WriteBuffer( rStrm );
+ return rStrm;
+}
+
+// Extended pivot table settings ==============================================
+
+XclPTExtInfo::XclPTExtInfo() :
+ mnSxformulaRecs( 0 ),
+ mnSxselectRecs( 0 ),
+ mnPagePerRow( 0 ),
+ mnPagePerCol( 0 ),
+ mnFlags( EXC_SXEX_DEFAULTFLAGS )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTExtInfo& rInfo )
+{
+ rInfo.mnSxformulaRecs = rStrm.ReaduInt16();
+ rStrm.Ignore( 6 );
+ rInfo.mnSxselectRecs = rStrm.ReaduInt16();
+ rInfo.mnPagePerRow = rStrm.ReaduInt16();
+ rInfo.mnPagePerCol = rStrm.ReaduInt16();
+ rInfo.mnFlags = rStrm.ReaduInt32();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTExtInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnSxformulaRecs
+ << EXC_PT_NOSTRING // length of alt. error text
+ << EXC_PT_NOSTRING // length of alt. empty text
+ << EXC_PT_NOSTRING // length of tag
+ << rInfo.mnSxselectRecs
+ << rInfo.mnPagePerRow
+ << rInfo.mnPagePerCol
+ << rInfo.mnFlags
+ << EXC_PT_NOSTRING // length of page field style name
+ << EXC_PT_NOSTRING // length of table style name
+ << EXC_PT_NOSTRING; // length of vacate style name
+}
+
+// Pivot table autoformat settings ============================================
+
+/**
+classic : 10 08 00 00 00 00 00 00 20 00 00 00 01 00 00 00 00
+default : 10 08 00 00 00 00 00 00 20 00 00 00 01 00 00 00 00
+report01 : 10 08 02 00 00 00 00 00 20 00 00 00 00 10 00 00 00
+report02 : 10 08 02 00 00 00 00 00 20 00 00 00 01 10 00 00 00
+report03 : 10 08 02 00 00 00 00 00 20 00 00 00 02 10 00 00 00
+report04 : 10 08 02 00 00 00 00 00 20 00 00 00 03 10 00 00 00
+report05 : 10 08 02 00 00 00 00 00 20 00 00 00 04 10 00 00 00
+report06 : 10 08 02 00 00 00 00 00 20 00 00 00 05 10 00 00 00
+report07 : 10 08 02 00 00 00 00 00 20 00 00 00 06 10 00 00 00
+report08 : 10 08 02 00 00 00 00 00 20 00 00 00 07 10 00 00 00
+report09 : 10 08 02 00 00 00 00 00 20 00 00 00 08 10 00 00 00
+report10 : 10 08 02 00 00 00 00 00 20 00 00 00 09 10 00 00 00
+table01 : 10 08 00 00 00 00 00 00 20 00 00 00 0a 10 00 00 00
+table02 : 10 08 00 00 00 00 00 00 20 00 00 00 0b 10 00 00 00
+table03 : 10 08 00 00 00 00 00 00 20 00 00 00 0c 10 00 00 00
+table04 : 10 08 00 00 00 00 00 00 20 00 00 00 0d 10 00 00 00
+table05 : 10 08 00 00 00 00 00 00 20 00 00 00 0e 10 00 00 00
+table06 : 10 08 00 00 00 00 00 00 20 00 00 00 0f 10 00 00 00
+table07 : 10 08 00 00 00 00 00 00 20 00 00 00 10 10 00 00 00
+table08 : 10 08 00 00 00 00 00 00 20 00 00 00 11 10 00 00 00
+table09 : 10 08 00 00 00 00 00 00 20 00 00 00 12 10 00 00 00
+table10 : 10 08 00 00 00 00 00 00 20 00 00 00 13 10 00 00 00
+none : 10 08 00 00 00 00 00 00 20 00 00 00 15 10 00 00 00
+**/
+
+XclPTViewEx9Info::XclPTViewEx9Info() :
+ mbReport( 0 ),
+ mnAutoFormat( 0 ),
+ mnGridLayout( 0x10 )
+{
+}
+
+void XclPTViewEx9Info::Init( const ScDPObject& rDPObj )
+{
+ if( rDPObj.GetHeaderLayout() )
+ {
+ mbReport = 0;
+ mnAutoFormat = 1;
+ mnGridLayout = 0;
+ }
+ else
+ {
+ // Report1 for now
+ // TODO : sync with autoformat indices
+ mbReport = 2;
+ mnAutoFormat = 1;
+ mnGridLayout = 0x10;
+ }
+
+ const ScDPSaveData* pData = rDPObj.GetSaveData();
+ if (pData)
+ {
+ const std::optional<OUString> & pGrandTotal = pData->GetGrandTotalName();
+ if (pGrandTotal)
+ maGrandTotalName = *pGrandTotal;
+ }
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTViewEx9Info& rInfo )
+{
+ rStrm.Ignore( 2 );
+ rInfo.mbReport = rStrm.ReaduInt32(); /// 2 for report* fmts ?
+ rStrm.Ignore( 6 );
+ rInfo.mnAutoFormat = rStrm.ReaduInt8();
+ rInfo.mnGridLayout = rStrm.ReaduInt8();
+ rInfo.maGrandTotalName = rStrm.ReadUniString();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTViewEx9Info& rInfo )
+{
+ return rStrm
+ << EXC_PT_AUTOFMT_HEADER
+ << rInfo.mbReport
+ << EXC_PT_AUTOFMT_ZERO
+ << EXC_PT_AUTOFMT_FLAGS
+ << rInfo.mnAutoFormat
+ << rInfo.mnGridLayout
+ << XclExpString(rInfo.maGrandTotalName, XclStrFlags::NONE, EXC_PT_MAXSTRLEN);
+}
+
+XclPTAddl::XclPTAddl() :
+ mbCompactMode(false)
+{
+}
+
+XclImpStream& operator>>(XclImpStream& rStrm, XclPTAddl& rInfo)
+{
+ rStrm.Ignore(4);
+ sal_uInt8 sxc = rStrm.ReaduInt8();
+ sal_uInt8 sxd = rStrm.ReaduInt8();
+ if(sxc == 0x00 && sxd == 0x19) // SxcView / sxdVer12Info
+ {
+ sal_uInt32 nFlags = rStrm.ReaduInt32();
+ rInfo.mbCompactMode = ((nFlags & 0x00000008) != 0);
+ }
+ return rStrm;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlroot.cxx b/sc/source/filter/excel/xlroot.cxx
new file mode 100644
index 000000000..bac3ca1b3
--- /dev/null
+++ b/sc/source/filter/excel/xlroot.cxx
@@ -0,0 +1,438 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <xlroot.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <sot/storage.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/numformat.hxx>
+#include <svl/stritem.hxx>
+#include <svl/languageoptions.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <vcl/font.hxx>
+#include <vcl/settings.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <editeng/editstat.hxx>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include <docuno.hxx>
+#include <editutil.hxx>
+#include <drwlayer.hxx>
+#include <scextopt.hxx>
+#include <patattr.hxx>
+#include <fapihelper.hxx>
+#include <xlconst.hxx>
+#include <xlstyle.hxx>
+#include <xlchart.hxx>
+#include <xltracer.hxx>
+#include <xltools.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/useroptions.hxx>
+#include <root.hxx>
+
+namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::awt::XDevice;
+using ::com::sun::star::awt::DeviceInfo;
+using ::com::sun::star::frame::XFrame;
+
+using namespace ::com::sun::star;
+
+// Global data ================================================================
+
+#ifdef DBG_UTIL
+XclDebugObjCounter::~XclDebugObjCounter()
+{
+ OSL_ENSURE( mnObjCnt == 0, "XclDebugObjCounter::~XclDebugObjCounter - wrong root object count" );
+}
+#endif
+
+XclRootData::XclRootData( XclBiff eBiff, SfxMedium& rMedium,
+ tools::SvRef<SotStorage> const & xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc, bool bExport ) :
+ meBiff( eBiff ),
+ meOutput( EXC_OUTPUT_BINARY ),
+ mrMedium( rMedium ),
+ mxRootStrg( xRootStrg ),
+ mrDoc( rDoc ),
+ meTextEnc( eTextEnc ),
+ meSysLang( Application::GetSettings().GetLanguageTag().getLanguageType() ),
+ meDocLang( Application::GetSettings().GetLanguageTag().getLanguageType() ),
+ meUILang( Application::GetSettings().GetUILanguageTag().getLanguageType() ),
+ mnDefApiScript( ApiScriptType::LATIN ),
+ maScMaxPos( mrDoc.MaxCol(), mrDoc.MaxRow(), MAXTAB ),
+ maXclMaxPos( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ),
+ maMaxPos( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ),
+ mxFontPropSetHlp( std::make_shared<XclFontPropSetHelper>() ),
+ mxChPropSetHlp( std::make_shared<XclChPropSetHelper>() ),
+ mxRD( std::make_shared<RootData>() ),
+ mfScreenPixelX( 50.0 ),
+ mfScreenPixelY( 50.0 ),
+ mnCharWidth( 110 ),
+ mnSpaceWidth(45),
+ mnScTab( 0 ),
+ mbExport( bExport )
+{
+ if (!utl::ConfigManager::IsFuzzing())
+ maUserName = SvtUserOptions().GetLastName();
+ if (maUserName.isEmpty())
+ maUserName = "Calc";
+
+ switch( ScGlobal::GetDefaultScriptType() )
+ {
+ case SvtScriptType::LATIN: mnDefApiScript = ApiScriptType::LATIN; break;
+ case SvtScriptType::ASIAN: mnDefApiScript = ApiScriptType::ASIAN; break;
+ case SvtScriptType::COMPLEX: mnDefApiScript = ApiScriptType::COMPLEX; break;
+ default: SAL_WARN( "sc", "XclRootData::XclRootData - unknown script type" );
+ }
+
+ // maximum cell position
+ switch( meBiff )
+ {
+ case EXC_BIFF2: maXclMaxPos.Set( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ); break;
+ case EXC_BIFF3: maXclMaxPos.Set( EXC_MAXCOL3, EXC_MAXROW3, EXC_MAXTAB3 ); break;
+ case EXC_BIFF4: maXclMaxPos.Set( EXC_MAXCOL4, EXC_MAXROW4, EXC_MAXTAB4 ); break;
+ case EXC_BIFF5: maXclMaxPos.Set( EXC_MAXCOL5, EXC_MAXROW5, EXC_MAXTAB5 ); break;
+ case EXC_BIFF8: maXclMaxPos.Set( EXC_MAXCOL8, EXC_MAXROW8, EXC_MAXTAB8 ); break;
+ default: DBG_ERROR_BIFF();
+ }
+ maMaxPos.SetCol( ::std::min( maScMaxPos.Col(), maXclMaxPos.Col() ) );
+ maMaxPos.SetRow( ::std::min( maScMaxPos.Row(), maXclMaxPos.Row() ) );
+ maMaxPos.SetTab( ::std::min( maScMaxPos.Tab(), maXclMaxPos.Tab() ) );
+
+ // document URL and path
+ if( const SfxItemSet* pItemSet = mrMedium.GetItemSet() )
+ if( const SfxStringItem* pItem = pItemSet->GetItem<SfxStringItem>( SID_FILE_NAME ) )
+ maDocUrl = pItem->GetValue();
+ maBasePath = maDocUrl.copy( 0, maDocUrl.lastIndexOf( '/' ) + 1 );
+
+ // extended document options - always own object, try to copy existing data from document
+ if( const ScExtDocOptions* pOldDocOpt = mrDoc.GetExtDocOptions() )
+ mxExtDocOpt = std::make_shared<ScExtDocOptions>( *pOldDocOpt );
+ else
+ mxExtDocOpt = std::make_shared<ScExtDocOptions>();
+
+ // screen pixel size
+ try
+ {
+ Reference< frame::XDesktop2 > xFramesSupp = frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+ Reference< XFrame > xFrame( xFramesSupp->getActiveFrame(), UNO_SET_THROW );
+ Reference< XDevice > xDevice( xFrame->getContainerWindow(), UNO_QUERY_THROW );
+ DeviceInfo aDeviceInfo = xDevice->getInfo();
+ mfScreenPixelX = (aDeviceInfo.PixelPerMeterX > 0) ? (100000.0 / aDeviceInfo.PixelPerMeterX) : 50.0;
+ mfScreenPixelY = (aDeviceInfo.PixelPerMeterY > 0) ? (100000.0 / aDeviceInfo.PixelPerMeterY) : 50.0;
+ }
+ catch( const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "XclRootData::XclRootData - cannot get output device info");
+ }
+}
+
+XclRootData::~XclRootData()
+{
+}
+
+XclRoot::XclRoot( XclRootData& rRootData ) :
+ mrData( rRootData )
+{
+#if defined(DBG_UTIL) && OSL_DEBUG_LEVEL > 0
+ ++mrData.mnObjCnt;
+#endif
+
+ // filter tracer
+ mrData.mxTracer = std::make_shared<XclTracer>( GetDocUrl() );
+}
+
+XclRoot::XclRoot( const XclRoot& rRoot ) :
+ mrData( rRoot.mrData )
+{
+#if defined(DBG_UTIL) && OSL_DEBUG_LEVEL > 0
+ ++mrData.mnObjCnt;
+#endif
+}
+
+XclRoot::~XclRoot()
+{
+#if defined(DBG_UTIL) && OSL_DEBUG_LEVEL > 0
+ --mrData.mnObjCnt;
+#endif
+}
+
+XclRoot& XclRoot::operator=( const XclRoot& rRoot )
+{
+ (void)rRoot; // avoid compiler warning
+ // allowed for assignment in derived classes - but test if the same root data is used
+ OSL_ENSURE( &mrData == &rRoot.mrData, "XclRoot::operator= - incompatible root data" );
+ return *this;
+}
+
+void XclRoot::SetTextEncoding( rtl_TextEncoding eTextEnc )
+{
+ if( eTextEnc != RTL_TEXTENCODING_DONTKNOW )
+ mrData.meTextEnc = eTextEnc;
+}
+
+void XclRoot::SetCharWidth( const XclFontData& rFontData )
+{
+ mrData.mnCharWidth = 0;
+ if( OutputDevice* pPrinter = GetPrinter() )
+ {
+ vcl::Font aFont( rFontData.maName, Size( 0, rFontData.mnHeight ) );
+ aFont.SetFamily( rFontData.GetScFamily( GetTextEncoding() ) );
+ aFont.SetCharSet( rFontData.GetFontEncoding() );
+ aFont.SetWeight( rFontData.GetScWeight() );
+ pPrinter->SetFont( aFont );
+ // Usually digits have the same width, but in some fonts they don't ...
+ // Match the import in sc/source/filter/oox/unitconverter.cxx
+ // UnitConverter::finalizeImport()
+ for (sal_Unicode cChar = '0'; cChar <= '9'; ++cChar)
+ mrData.mnCharWidth = std::max( pPrinter->GetTextWidth( OUString(cChar)), mrData.mnCharWidth);
+
+ // Set the width of space ' ' character.
+ mrData.mnSpaceWidth = pPrinter->GetTextWidth(OUString(' '));
+ }
+ if( mrData.mnCharWidth <= 0 )
+ {
+ // #i48717# Win98 with HP LaserJet returns 0
+ SAL_WARN( "sc", "XclRoot::SetCharWidth - invalid character width (no printer?)" );
+ mrData.mnCharWidth = 11 * rFontData.mnHeight / 20;
+ }
+ if (mrData.mnSpaceWidth <= 0)
+ {
+ SAL_WARN( "sc", "XclRoot::SetCharWidth - invalid character width (no printer?)" );
+ mrData.mnSpaceWidth = 45;
+ }
+}
+
+sal_Int32 XclRoot::GetHmmFromPixelX( double fPixelX ) const
+{
+ return static_cast< sal_Int32 >( fPixelX * mrData.mfScreenPixelX + 0.5 );
+}
+
+sal_Int32 XclRoot::GetHmmFromPixelY( double fPixelY ) const
+{
+ return static_cast< sal_Int32 >( fPixelY * mrData.mfScreenPixelY + 0.5 );
+}
+
+uno::Sequence< beans::NamedValue > XclRoot::RequestEncryptionData( ::comphelper::IDocPasswordVerifier& rVerifier ) const
+{
+ ::std::vector< OUString > aDefaultPasswords { XclRootData::gaDefPassword };
+ return ScfApiHelper::QueryEncryptionDataForMedium( mrData.mrMedium, rVerifier, &aDefaultPasswords );
+}
+
+bool XclRoot::HasVbaStorage() const
+{
+ tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
+ return xRootStrg.is() && xRootStrg->IsContained( EXC_STORAGE_VBA_PROJECT );
+}
+
+tools::SvRef<SotStorage> XclRoot::OpenStorage( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName ) const
+{
+ return mrData.mbExport ?
+ ScfTools::OpenStorageWrite( xStrg, rStrgName ) :
+ ScfTools::OpenStorageRead( xStrg, rStrgName );
+}
+
+tools::SvRef<SotStorage> XclRoot::OpenStorage( const OUString& rStrgName ) const
+{
+ return OpenStorage( GetRootStorage(), rStrgName );
+}
+
+tools::SvRef<SotStorageStream> XclRoot::OpenStream( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName ) const
+{
+ return mrData.mbExport ?
+ ScfTools::OpenStorageStreamWrite( xStrg, rStrmName ) :
+ ScfTools::OpenStorageStreamRead( xStrg, rStrmName );
+}
+
+tools::SvRef<SotStorageStream> XclRoot::OpenStream( const OUString& rStrmName ) const
+{
+ return OpenStream( GetRootStorage(), rStrmName );
+}
+
+ScDocument& XclRoot::GetDoc() const
+{
+ return mrData.mrDoc;
+}
+
+SfxObjectShell* XclRoot::GetDocShell() const
+{
+ return GetDoc().GetDocumentShell();
+}
+
+ScModelObj* XclRoot::GetDocModelObj() const
+{
+ SfxObjectShell* pDocShell = GetDocShell();
+ return pDocShell ? comphelper::getFromUnoTunnel<ScModelObj>( pDocShell->GetModel() ) : nullptr;
+}
+
+OutputDevice* XclRoot::GetPrinter() const
+{
+ return GetDoc().GetRefDevice();
+}
+
+ScStyleSheetPool& XclRoot::GetStyleSheetPool() const
+{
+ return *GetDoc().GetStyleSheetPool();
+}
+
+ScRangeName& XclRoot::GetNamedRanges() const
+{
+ return *GetDoc().GetRangeName();
+}
+
+SdrPage* XclRoot::GetSdrPage( SCTAB nScTab ) const
+{
+ return ((nScTab >= 0) && GetDoc().GetDrawLayer()) ?
+ GetDoc().GetDrawLayer()->GetPage( static_cast< sal_uInt16 >( nScTab ) ) : nullptr;
+}
+
+SvNumberFormatter& XclRoot::GetFormatter() const
+{
+ return *GetDoc().GetFormatTable();
+}
+
+DateTime XclRoot::GetNullDate() const
+{
+ return GetFormatter().GetNullDate();
+}
+
+sal_uInt16 XclRoot::GetBaseYear() const
+{
+ // return 1904 for 1904-01-01, and 1900 for 1899-12-30
+ return (GetNullDate().GetYear() == 1904) ? 1904 : 1900;
+}
+
+const DateTime theOurCompatNullDate( Date( 30, 12, 1899 ));
+const DateTime theExcelCutOverDate( Date( 1, 3, 1900 ));
+
+double XclRoot::GetDoubleFromDateTime( const DateTime& rDateTime ) const
+{
+ double fValue = rDateTime - GetNullDate();
+ // adjust dates before 1900-03-01 to get correct time values in the range [0.0,1.0)
+ /* XXX: this is only used when reading BIFF, otherwise we'd have to check
+ * for dateCompatibility==true as mentioned below. */
+ if( rDateTime < theExcelCutOverDate && GetNullDate() == theOurCompatNullDate )
+ fValue -= 1.0;
+ return fValue;
+}
+
+DateTime XclRoot::GetDateTimeFromDouble( double fValue ) const
+{
+ DateTime aDateTime = GetNullDate() + fValue;
+ // adjust dates before 1900-03-01 to get correct time values
+ /* FIXME: correction should only be done when writing BIFF or OOXML
+ * transitional with dateCompatibility==true (or absent for default true),
+ * but not if strict ISO/IEC 29500 which does not have the Excel error
+ * compatibility and the null date is the same 1899-12-30 as ours. */
+ if( aDateTime < theExcelCutOverDate && GetNullDate() == theOurCompatNullDate )
+ aDateTime.AddDays(1);
+ return aDateTime;
+}
+
+ScEditEngineDefaulter& XclRoot::GetEditEngine() const
+{
+ if( !mrData.mxEditEngine )
+ {
+ mrData.mxEditEngine = std::make_shared<ScEditEngineDefaulter>( GetDoc().GetEnginePool() );
+ ScEditEngineDefaulter& rEE = *mrData.mxEditEngine;
+ rEE.SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ rEE.SetEditTextObjectPool( GetDoc().GetEditPool() );
+ rEE.SetUpdateLayout( false );
+ rEE.EnableUndo( false );
+ rEE.SetControlWord( rEE.GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
+ }
+ return *mrData.mxEditEngine;
+}
+
+ScHeaderEditEngine& XclRoot::GetHFEditEngine() const
+{
+ if( !mrData.mxHFEditEngine )
+ {
+ mrData.mxHFEditEngine = std::make_shared<ScHeaderEditEngine>( EditEngine::CreatePool().get() );
+ ScHeaderEditEngine& rEE = *mrData.mxHFEditEngine;
+ rEE.SetRefMapMode(MapMode(MapUnit::MapTwip)); // headers/footers use twips as default metric
+ rEE.SetUpdateLayout( false );
+ rEE.EnableUndo( false );
+ rEE.SetControlWord( rEE.GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
+
+ // set Calc header/footer defaults
+ auto pEditSet = std::make_unique<SfxItemSet>( rEE.GetEmptyItemSet() );
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aItemSet( *GetDoc().GetPool() );
+ ScPatternAttr::FillToEditItemSet( *pEditSet, aItemSet );
+ // FillToEditItemSet() adjusts font height to 1/100th mm, we need twips
+ pEditSet->Put( aItemSet.Get( ATTR_FONT_HEIGHT ).CloneSetWhich(EE_CHAR_FONTHEIGHT) );
+ pEditSet->Put( aItemSet.Get( ATTR_CJK_FONT_HEIGHT ).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) );
+ pEditSet->Put( aItemSet.Get( ATTR_CTL_FONT_HEIGHT ).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) );
+ rEE.SetDefaults( std::move(pEditSet) ); // takes ownership
+ }
+ return *mrData.mxHFEditEngine;
+}
+
+EditEngine& XclRoot::GetDrawEditEngine() const
+{
+ if( !mrData.mxDrawEditEng )
+ {
+ mrData.mxDrawEditEng = std::make_shared<EditEngine>( &GetDoc().GetDrawLayer()->GetItemPool() );
+ EditEngine& rEE = *mrData.mxDrawEditEng;
+ rEE.SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ rEE.SetUpdateLayout( false );
+ rEE.EnableUndo( false );
+ rEE.SetControlWord( rEE.GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
+ }
+ return *mrData.mxDrawEditEng;
+}
+
+XclFontPropSetHelper& XclRoot::GetFontPropSetHelper() const
+{
+ return *mrData.mxFontPropSetHlp;
+}
+
+XclChPropSetHelper& XclRoot::GetChartPropSetHelper() const
+{
+ return *mrData.mxChPropSetHlp;
+}
+
+ScExtDocOptions& XclRoot::GetExtDocOptions() const
+{
+ return *mrData.mxExtDocOpt;
+}
+
+XclTracer& XclRoot::GetTracer() const
+{
+ return *mrData.mxTracer;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlstyle.cxx b/sc/source/filter/excel/xlstyle.cxx
new file mode 100644
index 000000000..ae4a6f1f6
--- /dev/null
+++ b/sc/source/filter/excel/xlstyle.cxx
@@ -0,0 +1,1744 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlstyle.hxx>
+#include <com/sun/star/awt/FontFamily.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/font.hxx>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <rtl/tencinfo.h>
+#include <svl/numformat.hxx>
+#include <svtools/colorcfg.hxx>
+#include <vcl/unohelp.hxx>
+#include <editeng/svxfont.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <global.hxx>
+#include <xlroot.hxx>
+#include <xltools.hxx>
+// Color data =================================================================
+
+/** Standard EGA colors, bright. */
+#define EXC_PALETTE_EGA_COLORS_LIGHT \
+ Color(0x000000), Color(0xFFFFFF), Color(0xFF0000), Color(0x00FF00), Color(0x0000FF), Color(0xFFFF00), Color(0xFF00FF), Color(0x00FFFF)
+/** Standard EGA colors, dark. */
+#define EXC_PALETTE_EGA_COLORS_DARK \
+ Color(0x800000), Color(0x008000), Color(0x000080), Color(0x808000), Color(0x800080), Color(0x008080), Color(0xC0C0C0), Color(0x808080)
+
+/** Default color table for BIFF2. */
+const Color spnDefColorTable2[] =
+{
+/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT
+};
+
+/** Default color table for BIFF3/BIFF4. */
+const Color spnDefColorTable3[] =
+{
+/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 16 */ EXC_PALETTE_EGA_COLORS_DARK
+};
+
+/** Default color table for BIFF5/BIFF7. */
+const Color spnDefColorTable5[] =
+{
+/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 16 */ EXC_PALETTE_EGA_COLORS_DARK,
+/* 24 */ Color(0x8080FF), Color(0x802060), Color(0xFFFFC0), Color(0xA0E0E0), Color(0x600080), Color(0xFF8080), Color(0x0080C0), Color(0xC0C0FF),
+/* 32 */ Color(0x000080), Color(0xFF00FF), Color(0xFFFF00), Color(0x00FFFF), Color(0x800080), Color(0x800000), Color(0x008080), Color(0x0000FF),
+/* 40 */ Color(0x00CFFF), Color(0x69FFFF), Color(0xE0FFE0), Color(0xFFFF80), Color(0xA6CAF0), Color(0xDD9CB3), Color(0xB38FEE), Color(0xE3E3E3),
+/* 48 */ Color(0x2A6FF9), Color(0x3FB8CD), Color(0x488436), Color(0x958C41), Color(0x8E5E42), Color(0xA0627A), Color(0x624FAC), Color(0x969696),
+/* 56 */ Color(0x1D2FBE), Color(0x286676), Color(0x004500), Color(0x453E01), Color(0x6A2813), Color(0x85396A), Color(0x4A3285), Color(0x424242)
+};
+
+/** Default color table for BIFF8. */
+const Color spnDefColorTable8[] =
+{
+/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 16 */ EXC_PALETTE_EGA_COLORS_DARK,
+/* 24 */ Color(0x9999FF), Color(0x993366), Color(0xFFFFCC), Color(0xCCFFFF), Color(0x660066), Color(0xFF8080), Color(0x0066CC), Color(0xCCCCFF),
+/* 32 */ Color(0x000080), Color(0xFF00FF), Color(0xFFFF00), Color(0x00FFFF), Color(0x800080), Color(0x800000), Color(0x008080), Color(0x0000FF),
+/* 40 */ Color(0x00CCFF), Color(0xCCFFFF), Color(0xCCFFCC), Color(0xFFFF99), Color(0x99CCFF), Color(0xFF99CC), Color(0xCC99FF), Color(0xFFCC99),
+/* 48 */ Color(0x3366FF), Color(0x33CCCC), Color(0x99CC00), Color(0xFFCC00), Color(0xFF9900), Color(0xFF6600), Color(0x666699), Color(0x969696),
+/* 56 */ Color(0x003366), Color(0x339966), Color(0x003300), Color(0x333300), Color(0x993300), Color(0x993366), Color(0x333399), Color(0x333333)
+};
+
+#undef EXC_PALETTE_EGA_COLORS_LIGHT
+#undef EXC_PALETTE_EGA_COLORS_DARK
+
+XclDefaultPalette::XclDefaultPalette( const XclRoot& rRoot ) :
+ mpnColorTable( nullptr ),
+ mnTableSize( 0 )
+{
+ const StyleSettings& rSett = Application::GetSettings().GetStyleSettings();
+ mnFaceColor = rSett.GetFaceColor();
+ // Don't use the system HelpBack and HelpText colours as it causes problems
+ // with modern gnome. This is because mnNoteText and mnNoteBack are used
+ // when colour indices ( instead of real colours ) are specified.
+ // Note: That this it is not an unusual scenario that we get the Note
+ // background specified as a real colour and the text specified as a
+ // colour index. That means the text colour would be picked from
+ // the system where the note background would be picked from a real colour.
+ // Previously the note text colour was picked from the system tooltip
+ // text colour, on modern gnome(e.g. 3) that tends to be 'white' with the
+ // default theme.
+ // Using the Libreoffice defaults ( instead of system specific colours
+ // ) lessens the chance of the one colour being an unsuitable combination
+ // because by default the note text is black and the note background is
+ // a light yellow colour ( very similar to Excel's normal defaults )
+ mnNoteText = svtools::ColorConfig::GetDefaultColor( svtools::FONTCOLOR );
+ mnNoteBack = svtools::ColorConfig::GetDefaultColor( svtools::CALCNOTESBACKGROUND );
+
+ // default colors
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF2:
+ mpnColorTable = spnDefColorTable2;
+ mnTableSize = SAL_N_ELEMENTS( spnDefColorTable2 );
+ break;
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ mpnColorTable = spnDefColorTable3;
+ mnTableSize = SAL_N_ELEMENTS( spnDefColorTable3 );
+ break;
+ case EXC_BIFF5:
+ mpnColorTable = spnDefColorTable5;
+ mnTableSize = SAL_N_ELEMENTS( spnDefColorTable5 );
+ break;
+ case EXC_BIFF8:
+ mpnColorTable = spnDefColorTable8;
+ mnTableSize = SAL_N_ELEMENTS( spnDefColorTable8 );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+Color XclDefaultPalette::GetDefColor( sal_uInt16 nXclIndex ) const
+{
+ Color nColor;
+ if( nXclIndex < mnTableSize )
+ nColor = mpnColorTable[ nXclIndex ];
+ else switch( nXclIndex )
+ {
+ case EXC_COLOR_WINDOWTEXT3:
+ case EXC_COLOR_WINDOWTEXT:
+ case EXC_COLOR_CHWINDOWTEXT: nColor = COL_BLACK; break;
+ case EXC_COLOR_WINDOWBACK3:
+ case EXC_COLOR_WINDOWBACK:
+ case EXC_COLOR_CHWINDOWBACK: nColor = COL_WHITE; break;
+ case EXC_COLOR_BUTTONBACK: nColor = mnFaceColor; break;
+ case EXC_COLOR_CHBORDERAUTO: nColor = COL_BLACK; break; // TODO: really always black?
+ case EXC_COLOR_NOTEBACK: nColor = mnNoteBack; break;
+ case EXC_COLOR_NOTETEXT: nColor = mnNoteText; break;
+ case EXC_COLOR_FONTAUTO: nColor = COL_AUTO; break;
+ default:
+ SAL_WARN("sc", "XclDefaultPalette::GetDefColor - unknown default color index: " << nXclIndex );
+ nColor = COL_AUTO;
+ }
+ return nColor;
+}
+
+// Font Data ==================================================================
+
+namespace Awt = ::com::sun::star::awt;
+namespace AwtFontFamily = Awt::FontFamily;
+namespace AwtFontLineStyle = Awt::FontUnderline;
+namespace AwtFontStrikeout = Awt::FontStrikeout;
+
+XclFontData::XclFontData()
+{
+ Clear();
+}
+
+XclFontData::XclFontData( const vcl::Font& rFont )
+{
+ Clear();
+ FillFromVclFont( rFont );
+}
+
+XclFontData::XclFontData( const SvxFont& rFont )
+{
+ FillFromSvxFont( rFont );
+}
+
+void XclFontData::Clear()
+{
+ maName.clear();
+ maStyle.clear();
+ maColor = COL_AUTO;
+ mnHeight = 0;
+ mnWeight = EXC_FONTWGHT_DONTKNOW;
+ mnEscapem = EXC_FONTESC_NONE;
+ mnFamily = EXC_FONTFAM_SYSTEM;
+ mnCharSet = EXC_FONTCSET_ANSI_LATIN;
+ mnUnderline = EXC_FONTUNDERL_NONE;
+ mbItalic = mbStrikeout = mbOutline = mbShadow = false;
+}
+
+void XclFontData::FillFromVclFont( const vcl::Font& rFont )
+{
+ maName = XclTools::GetXclFontName( rFont.GetFamilyName() ); // substitute with MS fonts
+ maStyle.clear();
+ maColor = rFont.GetColor();
+ SetScUnderline( rFont.GetUnderline() );
+ mnEscapem = EXC_FONTESC_NONE;
+ SetScHeight( rFont.GetFontSize().Height() );
+ SetScWeight( rFont.GetWeight() );
+ SetScFamily( rFont.GetFamilyType() );
+ SetFontEncoding( rFont.GetCharSet() );
+ SetScPosture( rFont.GetItalic() );
+ SetScStrikeout( rFont.GetStrikeout() );
+ mbOutline = rFont.IsOutline();
+ mbShadow = rFont.IsShadow();
+}
+
+void XclFontData::FillFromSvxFont( const SvxFont& rFont )
+{
+ FillFromVclFont( rFont );
+ SetScEscapement( rFont.GetEscapement() );
+}
+
+// *** conversion of VCL/SVX constants *** ------------------------------------
+
+FontFamily XclFontData::GetScFamily( rtl_TextEncoding eDefTextEnc ) const
+{
+ FontFamily eScFamily;
+ // ! format differs from Windows documentation: family is in lower nibble, pitch unknown
+ switch( mnFamily & 0x0F )
+ {
+ case EXC_FONTFAM_ROMAN: eScFamily = FAMILY_ROMAN; break;
+ case EXC_FONTFAM_SWISS: eScFamily = FAMILY_SWISS; break;
+ case EXC_FONTFAM_MODERN: eScFamily = FAMILY_MODERN; break;
+ case EXC_FONTFAM_SCRIPT: eScFamily = FAMILY_SCRIPT; break;
+ case EXC_FONTFAM_DECORATIVE: eScFamily = FAMILY_DECORATIVE; break;
+ default:
+ eScFamily =
+ ((eDefTextEnc == RTL_TEXTENCODING_APPLE_ROMAN) &&
+ (maName.equalsIgnoreAsciiCase( "Geneva" ) || maName.equalsIgnoreAsciiCase( "Chicago" ))) ?
+ FAMILY_SWISS : FAMILY_DONTKNOW;
+ }
+ return eScFamily;
+}
+
+rtl_TextEncoding XclFontData::GetFontEncoding() const
+{
+ // convert Windows character set to text encoding identifier
+ return rtl_getTextEncodingFromWindowsCharset( mnCharSet );
+}
+
+FontItalic XclFontData::GetScPosture() const
+{
+ return mbItalic ? ITALIC_NORMAL : ITALIC_NONE;
+}
+
+FontWeight XclFontData::GetScWeight() const
+{
+ FontWeight eScWeight;
+
+ if( !mnWeight ) eScWeight = WEIGHT_DONTKNOW;
+ else if( mnWeight < 150 ) eScWeight = WEIGHT_THIN;
+ else if( mnWeight < 250 ) eScWeight = WEIGHT_ULTRALIGHT;
+ else if( mnWeight < 325 ) eScWeight = WEIGHT_LIGHT;
+ else if( mnWeight < 375 ) eScWeight = WEIGHT_SEMILIGHT;
+ else if( mnWeight < 450 ) eScWeight = WEIGHT_NORMAL;
+ else if( mnWeight < 550 ) eScWeight = WEIGHT_MEDIUM;
+ else if( mnWeight < 650 ) eScWeight = WEIGHT_SEMIBOLD;
+ else if( mnWeight < 750 ) eScWeight = WEIGHT_BOLD;
+ else if( mnWeight < 850 ) eScWeight = WEIGHT_ULTRABOLD;
+ else eScWeight = WEIGHT_BLACK;
+
+ return eScWeight;
+}
+
+FontLineStyle XclFontData::GetScUnderline() const
+{
+ FontLineStyle eScUnderl = LINESTYLE_NONE;
+ switch( mnUnderline )
+ {
+ case EXC_FONTUNDERL_SINGLE:
+ case EXC_FONTUNDERL_SINGLE_ACC: eScUnderl = LINESTYLE_SINGLE; break;
+ case EXC_FONTUNDERL_DOUBLE:
+ case EXC_FONTUNDERL_DOUBLE_ACC: eScUnderl = LINESTYLE_DOUBLE; break;
+ }
+ return eScUnderl;
+}
+
+SvxEscapement XclFontData::GetScEscapement() const
+{
+ SvxEscapement eScEscapem = SvxEscapement::Off;
+ switch( mnEscapem )
+ {
+ case EXC_FONTESC_SUPER: eScEscapem = SvxEscapement::Superscript; break;
+ case EXC_FONTESC_SUB: eScEscapem = SvxEscapement::Subscript; break;
+ }
+ return eScEscapem;
+}
+
+FontStrikeout XclFontData::GetScStrikeout() const
+{
+ return mbStrikeout ? STRIKEOUT_SINGLE : STRIKEOUT_NONE;
+}
+
+void XclFontData::SetScHeight( sal_Int32 nTwips )
+{
+ mnHeight = static_cast< sal_uInt16 >( ::std::min( nTwips, static_cast<sal_Int32>(0x7FFFL) ) );
+}
+
+void XclFontData::SetScFamily( FontFamily eScFamily )
+{
+ switch( eScFamily )
+ {
+ case FAMILY_DONTKNOW: mnFamily = EXC_FONTFAM_DONTKNOW; break;
+ case FAMILY_DECORATIVE: mnFamily = EXC_FONTFAM_DECORATIVE; break;
+ case FAMILY_MODERN: mnFamily = EXC_FONTFAM_MODERN; break;
+ case FAMILY_ROMAN: mnFamily = EXC_FONTFAM_ROMAN; break;
+ case FAMILY_SCRIPT: mnFamily = EXC_FONTFAM_SCRIPT; break;
+ case FAMILY_SWISS: mnFamily = EXC_FONTFAM_SWISS; break;
+ case FAMILY_SYSTEM: mnFamily = EXC_FONTFAM_SYSTEM; break;
+ default:
+ OSL_FAIL( "XclFontData::SetScFamily - unknown font family" );
+ mnFamily = EXC_FONTFAM_DONTKNOW;
+ }
+}
+
+void XclFontData::SetFontEncoding( rtl_TextEncoding eFontEnc )
+{
+ // convert text encoding identifier to Windows character set
+ mnCharSet = rtl_getBestWindowsCharsetFromTextEncoding( eFontEnc );
+}
+
+void XclFontData::SetScPosture( FontItalic eScPosture )
+{
+ mbItalic = (eScPosture == ITALIC_OBLIQUE) || (eScPosture == ITALIC_NORMAL);
+}
+
+void XclFontData::SetScWeight( FontWeight eScWeight )
+{
+ switch( eScWeight )
+ {
+ case WEIGHT_DONTKNOW: mnWeight = EXC_FONTWGHT_DONTKNOW; break;
+ case WEIGHT_THIN: mnWeight = EXC_FONTWGHT_THIN; break;
+ case WEIGHT_ULTRALIGHT: mnWeight = EXC_FONTWGHT_ULTRALIGHT; break;
+ case WEIGHT_LIGHT: mnWeight = EXC_FONTWGHT_LIGHT; break;
+ case WEIGHT_SEMILIGHT: mnWeight = EXC_FONTWGHT_SEMILIGHT; break;
+ case WEIGHT_NORMAL: mnWeight = EXC_FONTWGHT_NORMAL; break;
+ case WEIGHT_MEDIUM: mnWeight = EXC_FONTWGHT_MEDIUM; break;
+ case WEIGHT_SEMIBOLD: mnWeight = EXC_FONTWGHT_SEMIBOLD; break;
+ case WEIGHT_BOLD: mnWeight = EXC_FONTWGHT_BOLD; break;
+ case WEIGHT_ULTRABOLD: mnWeight = EXC_FONTWGHT_ULTRABOLD; break;
+ case WEIGHT_BLACK: mnWeight = EXC_FONTWGHT_BLACK; break;
+ default: mnWeight = EXC_FONTWGHT_NORMAL;
+ }
+}
+
+void XclFontData::SetScUnderline( FontLineStyle eScUnderl )
+{
+ switch( eScUnderl )
+ {
+ case LINESTYLE_NONE:
+ case LINESTYLE_DONTKNOW: mnUnderline = EXC_FONTUNDERL_NONE; break;
+ case LINESTYLE_DOUBLE:
+ case LINESTYLE_DOUBLEWAVE: mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
+ default: mnUnderline = EXC_FONTUNDERL_SINGLE;
+ }
+}
+
+void XclFontData::SetScEscapement( short nScEscapem )
+{
+ if( nScEscapem > 0 )
+ mnEscapem = EXC_FONTESC_SUPER;
+ else if( nScEscapem < 0 )
+ mnEscapem = EXC_FONTESC_SUB;
+ else
+ mnEscapem = EXC_FONTESC_NONE;
+}
+
+void XclFontData::SetScStrikeout( FontStrikeout eScStrikeout )
+{
+ mbStrikeout =
+ (eScStrikeout == STRIKEOUT_SINGLE) || (eScStrikeout == STRIKEOUT_DOUBLE) ||
+ (eScStrikeout == STRIKEOUT_BOLD) || (eScStrikeout == STRIKEOUT_SLASH) ||
+ (eScStrikeout == STRIKEOUT_X);
+}
+
+// *** conversion of API constants *** ----------------------------------------
+
+float XclFontData::GetApiHeight() const
+{
+ return o3tl::convert<double>(mnHeight, o3tl::Length::twip, o3tl::Length::pt);
+}
+
+sal_Int16 XclFontData::GetApiFamily() const
+{
+ sal_Int16 nApiFamily = AwtFontFamily::DONTKNOW;
+ switch( mnFamily )
+ {
+ case FAMILY_DECORATIVE: nApiFamily = AwtFontFamily::DECORATIVE; break;
+ case FAMILY_MODERN: nApiFamily = AwtFontFamily::MODERN; break;
+ case FAMILY_ROMAN: nApiFamily = AwtFontFamily::ROMAN; break;
+ case FAMILY_SCRIPT: nApiFamily = AwtFontFamily::SCRIPT; break;
+ case FAMILY_SWISS: nApiFamily = AwtFontFamily::SWISS; break;
+ case FAMILY_SYSTEM: nApiFamily = AwtFontFamily::SYSTEM; break;
+ }
+ return nApiFamily;
+}
+
+sal_Int16 XclFontData::GetApiFontEncoding() const
+{
+ // API constants are equal to rtl_TextEncoding constants
+ return static_cast< sal_Int16 >( GetFontEncoding() );
+}
+
+Awt::FontSlant XclFontData::GetApiPosture() const
+{
+ return mbItalic ? Awt::FontSlant_ITALIC : Awt::FontSlant_NONE;
+}
+
+float XclFontData::GetApiWeight() const
+{
+ return vcl::unohelper::ConvertFontWeight( GetScWeight() );
+}
+
+sal_Int16 XclFontData::GetApiUnderline() const
+{
+ sal_Int16 nApiUnderl = AwtFontLineStyle::NONE;
+ switch( mnUnderline )
+ {
+ case EXC_FONTUNDERL_SINGLE:
+ case EXC_FONTUNDERL_SINGLE_ACC: nApiUnderl = AwtFontLineStyle::SINGLE; break;
+ case EXC_FONTUNDERL_DOUBLE:
+ case EXC_FONTUNDERL_DOUBLE_ACC: nApiUnderl = AwtFontLineStyle::DOUBLE; break;
+ }
+ return nApiUnderl;
+}
+
+sal_Int16 XclFontData::GetApiEscapement() const
+{
+ sal_Int16 nApiEscapem = 0;
+ switch( mnEscapem )
+ {
+ case EXC_FONTESC_SUPER: nApiEscapem = 33; break;
+ case EXC_FONTESC_SUB: nApiEscapem = -33; break;
+ }
+ return nApiEscapem;
+}
+
+sal_Int16 XclFontData::GetApiStrikeout() const
+{
+ return mbStrikeout ? AwtFontStrikeout::SINGLE : AwtFontStrikeout::NONE;
+}
+
+void XclFontData::SetApiHeight( float fPoint )
+{
+ mnHeight = std::min(o3tl::convert(fPoint, o3tl::Length::pt, o3tl::Length::twip) + 0.5, 32767.0);
+}
+
+void XclFontData::SetApiFamily( sal_Int16 nApiFamily )
+{
+ switch( nApiFamily )
+ {
+ case AwtFontFamily::DECORATIVE: mnFamily = FAMILY_DECORATIVE; break;
+ case AwtFontFamily::MODERN: mnFamily = FAMILY_MODERN; break;
+ case AwtFontFamily::ROMAN: mnFamily = FAMILY_ROMAN; break;
+ case AwtFontFamily::SCRIPT: mnFamily = FAMILY_SCRIPT; break;
+ case AwtFontFamily::SWISS: mnFamily = FAMILY_SWISS; break;
+ case AwtFontFamily::SYSTEM: mnFamily = FAMILY_SYSTEM; break;
+ default: mnFamily = FAMILY_DONTKNOW;
+ }
+}
+
+void XclFontData::SetApiPosture( Awt::FontSlant eApiPosture )
+{
+ mbItalic =
+ (eApiPosture == Awt::FontSlant_OBLIQUE) ||
+ (eApiPosture == Awt::FontSlant_ITALIC) ||
+ (eApiPosture == Awt::FontSlant_REVERSE_OBLIQUE) ||
+ (eApiPosture == Awt::FontSlant_REVERSE_ITALIC);
+}
+
+void XclFontData::SetApiWeight( float fApiWeight )
+{
+ SetScWeight( vcl::unohelper::ConvertFontWeight( fApiWeight ) );
+}
+
+void XclFontData::SetApiUnderline( sal_Int16 nApiUnderl )
+{
+ switch( nApiUnderl )
+ {
+ case AwtFontLineStyle::NONE:
+ case AwtFontLineStyle::DONTKNOW: mnUnderline = EXC_FONTUNDERL_NONE; break;
+ case AwtFontLineStyle::DOUBLE:
+ case AwtFontLineStyle::DOUBLEWAVE: mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
+ default: mnUnderline = EXC_FONTUNDERL_SINGLE;
+ }
+}
+
+void XclFontData::SetApiEscapement( sal_Int16 nApiEscapem )
+{
+ if( nApiEscapem > 0 )
+ mnEscapem = EXC_FONTESC_SUPER;
+ else if( nApiEscapem < 0 )
+ mnEscapem = EXC_FONTESC_SUB;
+ else
+ mnEscapem = EXC_FONTESC_NONE;
+}
+
+void XclFontData::SetApiStrikeout( sal_Int16 nApiStrikeout )
+{
+ mbStrikeout =
+ (nApiStrikeout != AwtFontStrikeout::NONE) &&
+ (nApiStrikeout != AwtFontStrikeout::DONTKNOW);
+}
+
+bool operator==( const XclFontData& rLeft, const XclFontData& rRight )
+{
+ return
+ (rLeft.mnHeight == rRight.mnHeight) &&
+ (rLeft.mnWeight == rRight.mnWeight) &&
+ (rLeft.mnUnderline == rRight.mnUnderline) &&
+ (rLeft.maColor == rRight.maColor) &&
+ (rLeft.mnEscapem == rRight.mnEscapem) &&
+ (rLeft.mnFamily == rRight.mnFamily) &&
+ (rLeft.mnCharSet == rRight.mnCharSet) &&
+ (rLeft.mbItalic == rRight.mbItalic) &&
+ (rLeft.mbStrikeout == rRight.mbStrikeout) &&
+ (rLeft.mbOutline == rRight.mbOutline) &&
+ (rLeft.mbShadow == rRight.mbShadow) &&
+ (rLeft.maName == rRight.maName);
+}
+
+namespace {
+
+/** Property names for common font settings. */
+const char *const sppcPropNamesChCommon[] =
+{
+ "CharUnderline", "CharStrikeout", "CharColor", "CharContoured", "CharShadowed", nullptr
+};
+/** Property names for Western font settings. */
+const char *const sppcPropNamesChWstrn[] =
+{
+ "CharFontName", "CharHeight", "CharPosture", "CharWeight", nullptr
+};
+/** Property names for Asian font settings. */
+const char *const sppcPropNamesChAsian[] =
+{
+ "CharFontNameAsian", "CharHeightAsian", "CharPostureAsian", "CharWeightAsian", nullptr
+};
+/** Property names for Complex font settings. */
+const char *const sppcPropNamesChCmplx[] =
+{
+ "CharFontNameComplex", "CharHeightComplex", "CharPostureComplex", "CharWeightComplex", nullptr
+};
+/** Property names for escapement. */
+const char *const sppcPropNamesChEscapement[] =
+{
+ "CharEscapement", "CharEscapementHeight", nullptr
+};
+const sal_Int8 EXC_API_ESC_HEIGHT = 58; /// Default escapement font height.
+
+/** Property names for Western font settings without font name. */
+const char *const *const sppcPropNamesChWstrnNoName = sppcPropNamesChWstrn + 1;
+/** Property names for Asian font settings without font name. */
+const char *const *const sppcPropNamesChAsianNoName = sppcPropNamesChAsian + 1;
+/** Property names for Complex font settings without font name. */
+const char *const *const sppcPropNamesChCmplxNoName = sppcPropNamesChCmplx + 1;
+
+/** Property names for font settings in form controls. */
+const char *const sppcPropNamesControl[] =
+{
+ "FontName", "FontFamily", "FontCharset", "FontHeight", "FontSlant",
+ "FontWeight", "FontLineStyle", "FontStrikeout", "TextColor", nullptr
+};
+
+/** Inserts all passed API font settings into the font data object. */
+void lclSetApiFontSettings( XclFontData& rFontData,
+ const OUString& rApiFontName, float fApiHeight, float fApiWeight,
+ Awt::FontSlant eApiPosture, sal_Int16 nApiUnderl, sal_Int16 nApiStrikeout )
+{
+ rFontData.maName = XclTools::GetXclFontName( rApiFontName );
+ rFontData.SetApiHeight( fApiHeight );
+ rFontData.SetApiWeight( fApiWeight );
+ rFontData.SetApiPosture( eApiPosture );
+ rFontData.SetApiUnderline( nApiUnderl );
+ rFontData.SetApiStrikeout( nApiStrikeout );
+}
+
+/** Writes script dependent properties to a font property set helper. */
+void lclWriteChartFont( ScfPropertySet& rPropSet,
+ ScfPropSetHelper& rHlpName, ScfPropSetHelper& rHlpNoName,
+ const XclFontData& rFontData, bool bHasFontName )
+{
+ // select the font helper
+ ScfPropSetHelper& rPropSetHlp = bHasFontName ? rHlpName : rHlpNoName;
+ // initialize the font helper (must be called before writing any properties)
+ rPropSetHlp.InitializeWrite();
+ // write font name
+ if( bHasFontName )
+ rPropSetHlp << rFontData.maName;
+ // write remaining properties
+ rPropSetHlp << rFontData.GetApiHeight() << rFontData.GetApiPosture() << rFontData.GetApiWeight();
+ // write properties to property set
+ rPropSetHlp.WriteToPropertySet( rPropSet );
+}
+
+} // namespace
+
+XclFontPropSetHelper::XclFontPropSetHelper() :
+ maHlpChCommon( sppcPropNamesChCommon ),
+ maHlpChWstrn( sppcPropNamesChWstrn ),
+ maHlpChAsian( sppcPropNamesChAsian ),
+ maHlpChCmplx( sppcPropNamesChCmplx ),
+ maHlpChWstrnNoName( sppcPropNamesChWstrnNoName ),
+ maHlpChAsianNoName( sppcPropNamesChAsianNoName ),
+ maHlpChCmplxNoName( sppcPropNamesChCmplxNoName ),
+ maHlpChEscapement( sppcPropNamesChEscapement ),
+ maHlpControl( sppcPropNamesControl )
+{
+}
+
+void XclFontPropSetHelper::ReadFontProperties( XclFontData& rFontData,
+ const ScfPropertySet& rPropSet, XclFontPropSetType eType, sal_Int16 nScript )
+{
+ switch( eType )
+ {
+ case EXC_FONTPROPSET_CHART:
+ {
+ OUString aApiFontName;
+ float fApiHeight, fApiWeight;
+ sal_Int16 nApiUnderl = 0, nApiStrikeout = 0;
+ Awt::FontSlant eApiPosture;
+
+ // read script type dependent properties
+ ScfPropSetHelper& rPropSetHlp = GetChartHelper( nScript );
+ rPropSetHlp.ReadFromPropertySet( rPropSet );
+ rPropSetHlp >> aApiFontName >> fApiHeight >> eApiPosture >> fApiWeight;
+ // read common properties
+ maHlpChCommon.ReadFromPropertySet( rPropSet );
+ maHlpChCommon >> nApiUnderl
+ >> nApiStrikeout
+ >> rFontData.maColor
+ >> rFontData.mbOutline
+ >> rFontData.mbShadow;
+
+ // convert API property values to Excel settings
+ lclSetApiFontSettings( rFontData, aApiFontName,
+ fApiHeight, fApiWeight, eApiPosture, nApiUnderl, nApiStrikeout );
+
+ // font escapement
+ sal_Int16 nApiEscapement = 0;
+ sal_Int8 nApiEscHeight = 0;
+ maHlpChEscapement.ReadFromPropertySet( rPropSet );
+ maHlpChEscapement.ReadFromPropertySet( rPropSet );
+ maHlpChEscapement.ReadFromPropertySet( rPropSet );
+ maHlpChEscapement >> nApiEscapement >> nApiEscHeight;
+ rFontData.SetApiEscapement( nApiEscapement );
+ }
+ break;
+
+ case EXC_FONTPROPSET_CONTROL:
+ {
+ OUString aApiFontName;
+ float fApiHeight(0.0), fApiWeight(0.0);
+ sal_Int16 nApiFamily(0), nApiCharSet(0), nApiPosture(0), nApiUnderl(0), nApiStrikeout(0);
+
+ // read font properties
+ maHlpControl.ReadFromPropertySet( rPropSet );
+ maHlpControl >> aApiFontName
+ >> nApiFamily
+ >> nApiCharSet
+ >> fApiHeight
+ >> nApiPosture
+ >> fApiWeight
+ >> nApiUnderl
+ >> nApiStrikeout
+ >> rFontData.maColor;
+
+ // convert API property values to Excel settings
+ Awt::FontSlant eApiPosture = static_cast< Awt::FontSlant >( nApiPosture );
+ lclSetApiFontSettings( rFontData, aApiFontName,
+ fApiHeight, fApiWeight, eApiPosture, nApiUnderl, nApiStrikeout );
+ rFontData.SetApiFamily( nApiFamily );
+ rFontData.SetFontEncoding( nApiCharSet );
+ }
+ break;
+ }
+}
+
+void XclFontPropSetHelper::WriteFontProperties(
+ ScfPropertySet& rPropSet, XclFontPropSetType eType,
+ const XclFontData& rFontData, bool bHasWstrn, bool bHasAsian, bool bHasCmplx,
+ const Color* pFontColor )
+{
+ switch( eType )
+ {
+ case EXC_FONTPROPSET_CHART:
+ {
+ // write common properties
+ maHlpChCommon.InitializeWrite();
+ const Color& rColor = pFontColor ? *pFontColor : rFontData.maColor;
+ maHlpChCommon << rFontData.GetApiUnderline()
+ << rFontData.GetApiStrikeout()
+ << rColor
+ << rFontData.mbOutline
+ << rFontData.mbShadow;
+ maHlpChCommon.WriteToPropertySet( rPropSet );
+
+ // write script type dependent properties
+ lclWriteChartFont( rPropSet, maHlpChWstrn, maHlpChWstrnNoName, rFontData, bHasWstrn );
+ lclWriteChartFont( rPropSet, maHlpChAsian, maHlpChAsianNoName, rFontData, bHasAsian );
+ lclWriteChartFont( rPropSet, maHlpChCmplx, maHlpChCmplxNoName, rFontData, bHasCmplx );
+
+ // font escapement
+ if( rFontData.GetScEscapement() != SvxEscapement::Off )
+ {
+ maHlpChEscapement.InitializeWrite();
+ maHlpChEscapement << rFontData.GetApiEscapement() << EXC_API_ESC_HEIGHT;
+ maHlpChEscapement.WriteToPropertySet( rPropSet );
+ }
+ }
+ break;
+
+ case EXC_FONTPROPSET_CONTROL:
+ {
+ maHlpControl.InitializeWrite();
+ maHlpControl << rFontData.maName
+ << rFontData.GetApiFamily()
+ << rFontData.GetApiFontEncoding()
+ << static_cast< sal_Int16 >( rFontData.GetApiHeight() + 0.5 )
+ << rFontData.GetApiPosture()
+ << rFontData.GetApiWeight()
+ << rFontData.GetApiUnderline()
+ << rFontData.GetApiStrikeout()
+ << rFontData.maColor;
+ maHlpControl.WriteToPropertySet( rPropSet );
+ }
+ break;
+ }
+}
+
+ScfPropSetHelper& XclFontPropSetHelper::GetChartHelper( sal_Int16 nScript )
+{
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+ switch( nScript )
+ {
+ case ApiScriptType::LATIN: return maHlpChWstrn;
+ case ApiScriptType::ASIAN: return maHlpChAsian;
+ case ApiScriptType::COMPLEX: return maHlpChCmplx;
+ default: OSL_FAIL( "XclFontPropSetHelper::GetChartHelper - unknown script type" );
+ }
+ return maHlpChWstrn;
+}
+
+// Number formats =============================================================
+
+namespace {
+
+/** Special number format index describing a reused format. */
+const NfIndexTableOffset PRV_NF_INDEX_REUSE = NF_INDEX_TABLE_ENTRIES;
+
+/** German primary language not defined, LANGUAGE_GERMAN belongs to Germany. */
+constexpr LanguageType PRV_LANGUAGE_GERMAN_PRIM = primary(LANGUAGE_GERMAN);
+/** French primary language not defined, LANGUAGE_FRENCH belongs to France. */
+constexpr LanguageType PRV_LANGUAGE_FRENCH_PRIM = primary(LANGUAGE_FRENCH);
+/** Parent language identifier for Asian languages. */
+constexpr LanguageType PRV_LANGUAGE_ASIAN_PRIM = primary(LANGUAGE_CHINESE);
+
+/** Stores the number format used in Calc for an Excel built-in number format. */
+struct XclBuiltInFormat
+{
+ sal_uInt16 mnXclNumFmt; /// Excel built-in index.
+ const char* mpFormat; /// Format string, may be 0 (meOffset used then).
+ NfIndexTableOffset meOffset; /// SvNumberFormatter format index, if mpFormat==0.
+ sal_uInt16 mnXclReuseFmt; /// Use this Excel format, if meOffset==PRV_NF_INDEX_REUSE.
+};
+
+/** Defines a literal Excel built-in number format. */
+#define EXC_NUMFMT_STRING( nXclNumFmt, pcUtf8 ) \
+ { nXclNumFmt, pcUtf8, NF_NUMBER_STANDARD, 0 }
+
+/** Defines an Excel built-in number format that maps to an own built-in format. */
+#define EXC_NUMFMT_OFFSET( nXclNumFmt, eOffset ) \
+ { nXclNumFmt, nullptr, eOffset, 0 }
+
+/** Defines an Excel built-in number format that is the same as the specified. */
+#define EXC_NUMFMT_REUSE( nXclNumFmt, nXclReuse ) \
+ { nXclNumFmt, nullptr, PRV_NF_INDEX_REUSE, nXclReuse }
+
+/** Terminates an Excel built-in number format table. */
+#define EXC_NUMFMT_ENDTABLE() \
+ { EXC_FORMAT_NOTFOUND, nullptr, NF_NUMBER_STANDARD, 0 }
+
+// Currency unit characters
+#define UTF8_BAHT "\340\270\277"
+#define UTF8_EURO "\342\202\254"
+#define UTF8_POUND_UK "\302\243"
+#define UTF8_SHEQEL "\342\202\252"
+#define UTF8_WON "\357\277\246"
+#define UTF8_YEN_CS "\357\277\245"
+#define UTF8_YEN_JP "\302\245"
+
+// Japanese/Chinese date/time characters
+#define UTF8_CJ_YEAR "\345\271\264"
+#define UTF8_CJ_MON "\346\234\210"
+#define UTF8_CJ_DAY "\346\227\245"
+#define UTF8_CJ_HOUR "\346\231\202"
+#define UTF8_CJ_MIN "\345\210\206"
+#define UTF8_CJ_SEC "\347\247\222"
+
+// Chinese Simplified date/time characters
+#define UTF8_CS_HOUR "\346\227\266"
+
+// Korean date/time characters
+#define UTF8_KO_YEAR "\353\205\204"
+#define UTF8_KO_MON "\354\233\224"
+#define UTF8_KO_DAY "\354\235\274"
+#define UTF8_KO_HOUR "\354\213\234"
+#define UTF8_KO_MIN "\353\266\204"
+#define UTF8_KO_SEC "\354\264\210"
+
+/** Default number format table. Last parent of all other tables, used for unknown languages. */
+const XclBuiltInFormat spBuiltInFormats_DONTKNOW[] =
+{
+ EXC_NUMFMT_OFFSET( 0, NF_NUMBER_STANDARD ), // General
+ EXC_NUMFMT_OFFSET( 1, NF_NUMBER_INT ), // 0
+ EXC_NUMFMT_OFFSET( 2, NF_NUMBER_DEC2 ), // 0.00
+ EXC_NUMFMT_OFFSET( 3, NF_NUMBER_1000INT ), // #,##0
+ EXC_NUMFMT_OFFSET( 4, NF_NUMBER_1000DEC2 ), // #,##0.00
+ // 5...8 contained in file
+ EXC_NUMFMT_OFFSET( 9, NF_PERCENT_INT ), // 0%
+ EXC_NUMFMT_OFFSET( 10, NF_PERCENT_DEC2 ), // 0.00%
+ EXC_NUMFMT_OFFSET( 11, NF_SCIENTIFIC_000E00 ), // 0.00E+00
+ EXC_NUMFMT_OFFSET( 12, NF_FRACTION_1D ), // # ?/?
+ EXC_NUMFMT_OFFSET( 13, NF_FRACTION_2D ), // # ??/??
+
+ // 14...22 date and time formats
+ EXC_NUMFMT_OFFSET( 14, NF_DATE_SYS_DDMMYYYY ),
+ EXC_NUMFMT_OFFSET( 15, NF_DATE_SYS_DMMMYY ),
+ EXC_NUMFMT_OFFSET( 16, NF_DATE_SYS_DDMMM ),
+ EXC_NUMFMT_OFFSET( 17, NF_DATE_SYS_MMYY ),
+ EXC_NUMFMT_OFFSET( 18, NF_TIME_HHMMAMPM ),
+ EXC_NUMFMT_OFFSET( 19, NF_TIME_HHMMSSAMPM ),
+ EXC_NUMFMT_OFFSET( 20, NF_TIME_HHMM ),
+ EXC_NUMFMT_OFFSET( 21, NF_TIME_HHMMSS ),
+ EXC_NUMFMT_OFFSET( 22, NF_DATETIME_SYSTEM_SHORT_HHMM ),
+
+ // 23...36 international formats
+ EXC_NUMFMT_REUSE( 23, 0 ),
+ EXC_NUMFMT_REUSE( 24, 0 ),
+ EXC_NUMFMT_REUSE( 25, 0 ),
+ EXC_NUMFMT_REUSE( 26, 0 ),
+ EXC_NUMFMT_REUSE( 27, 14 ),
+ EXC_NUMFMT_REUSE( 28, 14 ),
+ EXC_NUMFMT_REUSE( 29, 14 ),
+ EXC_NUMFMT_REUSE( 30, 14 ),
+ EXC_NUMFMT_REUSE( 31, 14 ),
+ EXC_NUMFMT_REUSE( 32, 21 ),
+ EXC_NUMFMT_REUSE( 33, 21 ),
+ EXC_NUMFMT_REUSE( 34, 21 ),
+ EXC_NUMFMT_REUSE( 35, 21 ),
+ EXC_NUMFMT_REUSE( 36, 14 ),
+
+ // 37...44 accounting formats
+ // 41...44 contained in file
+ EXC_NUMFMT_STRING( 37, "#,##0;-#,##0" ),
+ EXC_NUMFMT_STRING( 38, "#,##0;[RED]-#,##0" ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00;-#,##0.00" ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00;[RED]-#,##0.00" ),
+
+ // 45...49 more special formats
+ EXC_NUMFMT_STRING( 45, "mm:ss" ),
+ EXC_NUMFMT_STRING( 46, "[h]:mm:ss" ),
+ EXC_NUMFMT_STRING( 47, "mm:ss.0" ),
+ EXC_NUMFMT_STRING( 48, "##0.0E+0" ),
+ EXC_NUMFMT_OFFSET( 49, NF_TEXT ),
+
+ // 50...81 international formats
+ EXC_NUMFMT_REUSE( 50, 14 ),
+ EXC_NUMFMT_REUSE( 51, 14 ),
+ EXC_NUMFMT_REUSE( 52, 14 ),
+ EXC_NUMFMT_REUSE( 53, 14 ),
+ EXC_NUMFMT_REUSE( 54, 14 ),
+ EXC_NUMFMT_REUSE( 55, 14 ),
+ EXC_NUMFMT_REUSE( 56, 14 ),
+ EXC_NUMFMT_REUSE( 57, 14 ),
+ EXC_NUMFMT_REUSE( 58, 14 ),
+ EXC_NUMFMT_REUSE( 59, 1 ),
+ EXC_NUMFMT_REUSE( 60, 2 ),
+ EXC_NUMFMT_REUSE( 61, 3 ),
+ EXC_NUMFMT_REUSE( 62, 4 ),
+ EXC_NUMFMT_REUSE( 67, 9 ),
+ EXC_NUMFMT_REUSE( 68, 10 ),
+ EXC_NUMFMT_REUSE( 69, 12 ),
+ EXC_NUMFMT_REUSE( 70, 13 ),
+ EXC_NUMFMT_REUSE( 71, 14 ),
+ EXC_NUMFMT_REUSE( 72, 14 ),
+ EXC_NUMFMT_REUSE( 73, 15 ),
+ EXC_NUMFMT_REUSE( 74, 16 ),
+ EXC_NUMFMT_REUSE( 75, 17 ),
+ EXC_NUMFMT_REUSE( 76, 20 ),
+ EXC_NUMFMT_REUSE( 77, 21 ),
+ EXC_NUMFMT_REUSE( 78, 22 ),
+ EXC_NUMFMT_REUSE( 79, 45 ),
+ EXC_NUMFMT_REUSE( 80, 46 ),
+ EXC_NUMFMT_REUSE( 81, 47 ),
+
+ // 82...163 not used, must not occur in a file (Excel may crash)
+
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// ENGLISH --------------------------------------------------------------------
+
+/** Base table for English locales. */
+const XclBuiltInFormat spBuiltInFormats_ENGLISH[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "DD/MM/YYYY hh:mm" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_UK[] =
+{
+ EXC_NUMFMT_STRING( 63, UTF8_POUND_UK "#,##0;-" UTF8_POUND_UK "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_POUND_UK "#,##0;[RED]-" UTF8_POUND_UK "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_POUND_UK "#,##0.00;-" UTF8_POUND_UK "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_POUND_UK "#,##0.00;[RED]-" UTF8_POUND_UK "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_EIRE[] =
+{
+ EXC_NUMFMT_STRING( 63, UTF8_EURO "#,##0;-" UTF8_EURO "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_EURO "#,##0;[RED]-" UTF8_EURO "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_EURO "#,##0.00;-" UTF8_EURO "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_EURO "#,##0.00;[RED]-" UTF8_EURO "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_US[] =
+{
+ EXC_NUMFMT_STRING( 14, "M/D/YYYY" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "M/D/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0_);(#,##0)" ),
+ EXC_NUMFMT_STRING( 38, "#,##0_);[RED](#,##0)" ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00_);(#,##0.00)" ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00_);[RED](#,##0.00)" ),
+ EXC_NUMFMT_STRING( 63, "$#,##0_);($#,##0)" ),
+ EXC_NUMFMT_STRING( 64, "$#,##0_);[RED]($#,##0)" ),
+ EXC_NUMFMT_STRING( 65, "$#,##0.00_);($#,##0.00)" ),
+ EXC_NUMFMT_STRING( 66, "$#,##0.00_);[RED]($#,##0.00)" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_CAN[] =
+{
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "DD/MM/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 63, "$#,##0;-$#,##0" ),
+ EXC_NUMFMT_STRING( 64, "$#,##0;[RED]-$#,##0" ),
+ EXC_NUMFMT_STRING( 65, "$#,##0.00;-$#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "$#,##0.00;[RED]-$#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_AUS[] =
+{
+ EXC_NUMFMT_STRING( 14, "D/MM/YYYY" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "D/MM/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 63, "$#,##0;-$#,##0" ),
+ EXC_NUMFMT_STRING( 64, "$#,##0;[RED]-$#,##0" ),
+ EXC_NUMFMT_STRING( 65, "$#,##0.00;-$#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "$#,##0.00;[RED]-$#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_SAFRICA[] =
+{
+ EXC_NUMFMT_STRING( 14, "YYYY/MM/DD" ),
+ EXC_NUMFMT_OFFSET( 18, NF_TIME_HHMMAMPM ),
+ EXC_NUMFMT_OFFSET( 19, NF_TIME_HHMMSSAMPM ),
+ EXC_NUMFMT_STRING( 22, "YYYY/MM/DD hh:mm" ),
+ EXC_NUMFMT_STRING( 63, "\\R #,##0;\\R -#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\\R #,##0;[RED]\\R -#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\\R #,##0.00;\\R -#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\\R #,##0.00;[RED]\\R -#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// FRENCH ---------------------------------------------------------------------
+
+/** Base table for French locales. */
+const XclBuiltInFormat spBuiltInFormats_FRENCH[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_FRENCH_FRANCE[] =
+{
+ EXC_NUMFMT_STRING( 22, "DD/MM/YYYY hh:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0\\ _" UTF8_EURO ";-#,##0\\ _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 38, "#,##0\\ _" UTF8_EURO ";[RED]-#,##0\\ _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00\\ _" UTF8_EURO ";-#,##0.00\\ _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00\\ _" UTF8_EURO ";[RED]-#,##0.00\\ _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 63, "#,##0\\ " UTF8_EURO ";-#,##0\\ " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 64, "#,##0\\ " UTF8_EURO ";[RED]-#,##0\\ " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00\\ " UTF8_EURO ";-#,##0.00\\ " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00\\ " UTF8_EURO ";[RED]-#,##0.00\\ " UTF8_EURO ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_FRENCH_CANADIAN[] =
+{
+ EXC_NUMFMT_STRING( 22, "YYYY-MM-DD hh:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0\\ _$_-;#,##0\\ _$-" ),
+ EXC_NUMFMT_STRING( 38, "#,##0\\ _$_-;[RED]#,##0\\ _$-" ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00\\ _$_-;#,##0.00\\ _$-" ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00\\ _$_-;[RED]#,##0.00\\ _$-" ),
+ EXC_NUMFMT_STRING( 63, "#,##0\\ $_-;#,##0\\ $-" ),
+ EXC_NUMFMT_STRING( 64, "#,##0\\ $_-;[RED]#,##0\\ $-" ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00\\ $_-;#,##0.00\\ $-" ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00\\ $_-;[RED]#,##0.00\\ $-" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_FRENCH_SWISS[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 22, "DD.MM.YYYY hh:mm" ),
+ EXC_NUMFMT_STRING( 63, "\"SFr. \"#,##0;\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\"SFr. \"#,##0;[RED]\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\"SFr. \"#,##0.00;\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\"SFr. \"#,##0.00;[RED]\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_FRENCH_BELGIAN[] =
+{
+ EXC_NUMFMT_STRING( 14, "D/MM/YYYY" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "D/MM/YYYY h:mm" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// GERMAN ---------------------------------------------------------------------
+
+/** Base table for German locales. */
+const XclBuiltInFormat spBuiltInFormats_GERMAN[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD. MMM YY" ),
+ EXC_NUMFMT_STRING( 16, "DD. MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "DD.MM.YYYY hh:mm" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_GERMANY[] =
+{
+ EXC_NUMFMT_STRING( 37, "#,##0 _" UTF8_EURO ";-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 38, "#,##0 _" UTF8_EURO ";[RED]-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00 _" UTF8_EURO ";-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00 _" UTF8_EURO ";[RED]-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 63, "#,##0 " UTF8_EURO ";-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 64, "#,##0 " UTF8_EURO ";[RED]-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00 " UTF8_EURO ";-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00 " UTF8_EURO ";[RED]-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_AUSTRIAN[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 63, UTF8_EURO " #,##0;-" UTF8_EURO " #,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_EURO " #,##0;[RED]-" UTF8_EURO " #,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_EURO " #,##0.00;-" UTF8_EURO " #,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_EURO " #,##0.00;[RED]-" UTF8_EURO " #,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_SWISS[] =
+{
+ EXC_NUMFMT_STRING( 63, "\"SFr. \"#,##0;\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\"SFr. \"#,##0;[RED]\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\"SFr. \"#,##0.00;\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\"SFr. \"#,##0.00;[RED]\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_LUXEMBOURG[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 37, "#,##0 _" UTF8_EURO ";-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 38, "#,##0 _" UTF8_EURO ";[RED]-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00 _" UTF8_EURO ";-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00 _" UTF8_EURO ";[RED]-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 63, "#,##0 " UTF8_EURO ";-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 64, "#,##0 " UTF8_EURO ";[RED]-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00 " UTF8_EURO ";-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00 " UTF8_EURO ";[RED]-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_LIECHTENSTEIN[] =
+{
+ EXC_NUMFMT_STRING( 63, "\"CHF \"#,##0;\"CHF \"-#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\"CHF \"#,##0;[RED]\"CHF \"-#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\"CHF \"#,##0.00;\"CHF \"-#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\"CHF \"#,##0.00;[RED]\"CHF \"-#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// ITALIAN --------------------------------------------------------------------
+
+const XclBuiltInFormat spBuiltInFormats_ITALIAN_ITALY[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "DD/MM/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 63, UTF8_EURO " #,##0;-" UTF8_EURO " #,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_EURO " #,##0;[RED]-" UTF8_EURO " #,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_EURO " #,##0.00;-" UTF8_EURO " #,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_EURO " #,##0.00;[RED]-" UTF8_EURO " #,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ITALIAN_SWISS[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "DD.MM.YYYY hh:mm" ),
+ EXC_NUMFMT_STRING( 63, "\"SFr. \"#,##0;\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\"SFr. \"#,##0;[RED]\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\"SFr. \"#,##0.00;\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\"SFr. \"#,##0.00;[RED]\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// SWEDISH --------------------------------------------------------------------
+
+const XclBuiltInFormat spBuiltInFormats_SWEDISH_SWEDEN[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "YYYY-MM-DD hh:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0 _k_r;-#,##0 _k_r" ),
+ EXC_NUMFMT_STRING( 38, "#,##0 _k_r;[RED]-#,##0 _k_r" ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00 _k_r;-#,##0.00 _k_r" ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00 _k_r;[RED]-#,##0.00 _k_r" ),
+ EXC_NUMFMT_STRING( 63, "#,##0 \"kr\";-#,##0 \"kr\"" ),
+ EXC_NUMFMT_STRING( 64, "#,##0 \"kr\";[RED]-#,##0 \"kr\"" ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00 \"kr\";-#,##0.00 \"kr\"" ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00 \"kr\";[RED]-#,##0.00 \"kr\"" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_SWEDISH_FINLAND[] =
+{
+ EXC_NUMFMT_STRING( 9, "0 %" ),
+ EXC_NUMFMT_STRING( 10, "0.00 %" ),
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "D.M.YYYY hh:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0 _" UTF8_EURO ";-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 38, "#,##0 _" UTF8_EURO ";[RED]-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00 _" UTF8_EURO ";-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00 _" UTF8_EURO ";[RED]-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 63, "#,##0 " UTF8_EURO ";-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 64, "#,##0 " UTF8_EURO ";[RED]-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00 " UTF8_EURO ";-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00 " UTF8_EURO ";[RED]-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// ASIAN ----------------------------------------------------------------------
+
+/** Base table for Asian locales. */
+const XclBuiltInFormat spBuiltInFormats_ASIAN[] =
+{
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 23, "$#,##0_);($#,##0)" ),
+ EXC_NUMFMT_STRING( 24, "$#,##0_);[RED]($#,##0)" ),
+ EXC_NUMFMT_STRING( 25, "$#,##0.00_);($#,##0.00)" ),
+ EXC_NUMFMT_STRING( 26, "$#,##0.00_);[RED]($#,##0.00)" ),
+ EXC_NUMFMT_REUSE( 29, 28 ),
+ EXC_NUMFMT_REUSE( 36, 27 ),
+ EXC_NUMFMT_REUSE( 50, 27 ),
+ EXC_NUMFMT_REUSE( 51, 28 ),
+ EXC_NUMFMT_REUSE( 52, 34 ),
+ EXC_NUMFMT_REUSE( 53, 35 ),
+ EXC_NUMFMT_REUSE( 54, 28 ),
+ EXC_NUMFMT_REUSE( 55, 34 ),
+ EXC_NUMFMT_REUSE( 56, 35 ),
+ EXC_NUMFMT_REUSE( 57, 27 ),
+ EXC_NUMFMT_REUSE( 58, 28 ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_JAPANESE[] =
+{
+ EXC_NUMFMT_STRING( 14, "YYYY/M/D" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 22, "YYYY/M/D h:mm" ),
+ EXC_NUMFMT_STRING( 27, "[$-0411]GE.M.D" ),
+ EXC_NUMFMT_STRING( 28, "[$-0411]GGGE" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 30, "[$-0411]M/D/YY" ),
+ EXC_NUMFMT_STRING( 31, "[$-0411]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 32, "[$-0411]h" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 33, "[$-0411]h" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_STRING( 34, "[$-0411]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON ),
+ EXC_NUMFMT_STRING( 35, "[$-0411]M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 63, UTF8_YEN_JP "#,##0;-" UTF8_YEN_JP "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_YEN_JP "#,##0;[RED]-" UTF8_YEN_JP "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_YEN_JP "#,##0.00;-" UTF8_YEN_JP "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_YEN_JP "#,##0.00;[RED]-" UTF8_YEN_JP "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_KOREAN[] =
+{
+ EXC_NUMFMT_STRING( 14, "YYYY-MM-DD" ),
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 22, "YYYY-MM-DD h:mm" ),
+ EXC_NUMFMT_STRING( 27, "[$-0412]YYYY" UTF8_CJ_YEAR " MM" UTF8_CJ_MON " DD" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 28, "[$-0412]MM-DD" ),
+ EXC_NUMFMT_STRING( 30, "[$-0412]MM-DD-YY" ),
+ EXC_NUMFMT_STRING( 31, "[$-0412]YYYY" UTF8_KO_YEAR " MM" UTF8_KO_MON " DD" UTF8_KO_DAY ),
+ EXC_NUMFMT_STRING( 32, "[$-0412]h" UTF8_KO_HOUR " mm" UTF8_KO_MIN ),
+ EXC_NUMFMT_STRING( 33, "[$-0412]h" UTF8_KO_HOUR " mm" UTF8_KO_MIN " ss" UTF8_KO_SEC ),
+ EXC_NUMFMT_STRING( 34, "[$-0412]YYYY\"/\"MM\"/\"DD" ),
+ EXC_NUMFMT_STRING( 35, "[$-0412]YYYY-MM-DD" ),
+ EXC_NUMFMT_STRING( 63, UTF8_WON "#,##0;-" UTF8_WON "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_WON "#,##0;[RED]-" UTF8_WON "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_WON "#,##0.00;-" UTF8_WON "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_WON "#,##0.00;[RED]-" UTF8_WON "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_CHINESE_SIMPLIFIED[] =
+{
+ EXC_NUMFMT_STRING( 14, "YYYY-M-D" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 22, "YYYY-M-D h:mm" ),
+ EXC_NUMFMT_STRING( 27, "[$-0804]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON ),
+ EXC_NUMFMT_STRING( 28, "[$-0804]M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 30, "[$-0804]M-D-YY" ),
+ EXC_NUMFMT_STRING( 31, "[$-0804]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 32, "[$-0804]h" UTF8_CS_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 33, "[$-0804]h" UTF8_CS_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_STRING( 34, "[$-0804]AM/PMh" UTF8_CS_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 35, "[$-0804]AM/PMh" UTF8_CS_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_REUSE( 52, 27 ),
+ EXC_NUMFMT_REUSE( 53, 28 ),
+ EXC_NUMFMT_STRING( 63, UTF8_YEN_CS "#,##0;-" UTF8_YEN_CS "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_YEN_CS "#,##0;[RED]-" UTF8_YEN_CS "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_YEN_CS "#,##0.00;-" UTF8_YEN_CS "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_YEN_CS "#,##0.00;[RED]-" UTF8_YEN_CS "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_CHINESE_TRADITIONAL[] =
+{
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "hh:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "hh:mm:ss AM/PM" ),
+ EXC_NUMFMT_OFFSET( 20, NF_TIME_HHMM ),
+ EXC_NUMFMT_OFFSET( 21, NF_TIME_HHMMSS ),
+ EXC_NUMFMT_STRING( 22, "YYYY/M/D hh:mm" ),
+ EXC_NUMFMT_STRING( 23, "US$#,##0_);(US$#,##0)" ),
+ EXC_NUMFMT_STRING( 24, "US$#,##0_);[RED](US$#,##0)" ),
+ EXC_NUMFMT_STRING( 25, "US$#,##0.00_);(US$#,##0.00)" ),
+ EXC_NUMFMT_STRING( 26, "US$#,##0.00_);[RED](US$#,##0.00)" ),
+ EXC_NUMFMT_STRING( 27, "[$-0404]E/M/D" ),
+ EXC_NUMFMT_STRING( 28, "[$-0404]E" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 30, "[$-0404]M/D/YY" ),
+ EXC_NUMFMT_STRING( 31, "[$-0404]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 32, "[$-0404]hh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 33, "[$-0404]hh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_STRING( 34, "[$-0404]AM/PMhh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 35, "[$-0404]AM/PMhh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_STRING( 63, "$#,##0;-$#,##0" ),
+ EXC_NUMFMT_STRING( 64, "$#,##0;[RED]-$#,##0" ),
+ EXC_NUMFMT_STRING( 65, "$#,##0.00;-$#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "$#,##0.00;[RED]-$#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// OTHER ----------------------------------------------------------------------
+
+const XclBuiltInFormat spBuiltInFormats_HEBREW[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMMM" ),
+ EXC_NUMFMT_STRING( 17, "MMMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 63, UTF8_SHEQEL " #,##0;" UTF8_SHEQEL " -#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_SHEQEL " #,##0;[RED]" UTF8_SHEQEL " -#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_SHEQEL " #,##0.00;" UTF8_SHEQEL " -#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_SHEQEL " #,##0.00;[RED]" UTF8_SHEQEL " -#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_THAI[] =
+{
+ EXC_NUMFMT_STRING( 14, "D/M/YYYY" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "D/M/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 59, "t0" ),
+ EXC_NUMFMT_STRING( 60, "t0.00" ),
+ EXC_NUMFMT_STRING( 61, "t#,##0" ),
+ EXC_NUMFMT_STRING( 62, "t#,##0.00" ),
+ EXC_NUMFMT_STRING( 63, "t" UTF8_BAHT "#,##0_);t(" UTF8_BAHT "#,##0)" ),
+ EXC_NUMFMT_STRING( 64, "t" UTF8_BAHT "#,##0_);[RED]t(" UTF8_BAHT "#,##0)" ),
+ EXC_NUMFMT_STRING( 65, "t" UTF8_BAHT "#,##0.00_);t(" UTF8_BAHT "#,##0.00)" ),
+ EXC_NUMFMT_STRING( 66, "t" UTF8_BAHT "#,##0.00_);[RED]t(" UTF8_BAHT "#,##0.00)" ),
+ EXC_NUMFMT_STRING( 67, "t0%" ),
+ EXC_NUMFMT_STRING( 68, "t0.00%" ),
+ EXC_NUMFMT_STRING( 69, "t# ?/?" ),
+ EXC_NUMFMT_STRING( 70, "t# ?\?/?\?" ),
+ EXC_NUMFMT_STRING( 71, "tD/M/EE" ),
+ EXC_NUMFMT_STRING( 72, "tD-MMM-E" ),
+ EXC_NUMFMT_STRING( 73, "tD-MMM" ),
+ EXC_NUMFMT_STRING( 74, "tMMM-E" ),
+ EXC_NUMFMT_STRING( 75, "th:mm" ),
+ EXC_NUMFMT_STRING( 76, "th:mm:ss" ),
+ EXC_NUMFMT_STRING( 77, "tD/M/EE h:mm" ),
+ EXC_NUMFMT_STRING( 78, "tmm:ss" ),
+ EXC_NUMFMT_STRING( 79, "t[h]:mm:ss" ),
+ EXC_NUMFMT_STRING( 80, "tmm:ss.0" ),
+ EXC_NUMFMT_STRING( 81, "D/M/E" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+#undef EXC_NUMFMT_ENDTABLE
+#undef EXC_NUMFMT_REUSE
+#undef EXC_NUMFMT_OFFSET
+#undef EXC_NUMFMT_STRING
+
+/** Specifies a number format table for a specific language. */
+struct XclBuiltInFormatTable
+{
+ LanguageType meLanguage; /// The language of this table.
+ LanguageType meParentLang; /// The language of the parent table.
+ const XclBuiltInFormat* mpFormats; /// The number format table.
+};
+
+const XclBuiltInFormatTable spBuiltInFormatTables[] =
+{ // language parent language format table
+ { LANGUAGE_DONTKNOW, LANGUAGE_NONE, spBuiltInFormats_DONTKNOW },
+
+ { LANGUAGE_ENGLISH, LANGUAGE_DONTKNOW, spBuiltInFormats_ENGLISH },
+ { LANGUAGE_ENGLISH_UK, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_UK },
+ { LANGUAGE_ENGLISH_EIRE, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_EIRE },
+ { LANGUAGE_ENGLISH_US, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_US },
+ { LANGUAGE_ENGLISH_CAN, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_CAN },
+ { LANGUAGE_ENGLISH_AUS, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_AUS },
+ { LANGUAGE_ENGLISH_SAFRICA, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_SAFRICA },
+ { LANGUAGE_ENGLISH_NZ, LANGUAGE_ENGLISH_AUS, nullptr },
+
+ { PRV_LANGUAGE_FRENCH_PRIM, LANGUAGE_DONTKNOW, spBuiltInFormats_FRENCH },
+ { LANGUAGE_FRENCH, PRV_LANGUAGE_FRENCH_PRIM, spBuiltInFormats_FRENCH_FRANCE },
+ { LANGUAGE_FRENCH_CANADIAN, PRV_LANGUAGE_FRENCH_PRIM, spBuiltInFormats_FRENCH_CANADIAN },
+ { LANGUAGE_FRENCH_SWISS, PRV_LANGUAGE_FRENCH_PRIM, spBuiltInFormats_FRENCH_SWISS },
+ { LANGUAGE_FRENCH_BELGIAN, LANGUAGE_FRENCH, spBuiltInFormats_FRENCH_BELGIAN },
+ { LANGUAGE_FRENCH_LUXEMBOURG, LANGUAGE_FRENCH, nullptr },
+ { LANGUAGE_FRENCH_MONACO, LANGUAGE_FRENCH, nullptr },
+
+ { PRV_LANGUAGE_GERMAN_PRIM, LANGUAGE_DONTKNOW, spBuiltInFormats_GERMAN },
+ { LANGUAGE_GERMAN, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_GERMANY },
+ { LANGUAGE_GERMAN_AUSTRIAN, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_AUSTRIAN },
+ { LANGUAGE_GERMAN_SWISS, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_SWISS },
+ { LANGUAGE_GERMAN_LUXEMBOURG, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_LUXEMBOURG },
+ { LANGUAGE_GERMAN_LIECHTENSTEIN, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_LIECHTENSTEIN },
+
+ { LANGUAGE_ITALIAN, LANGUAGE_DONTKNOW, spBuiltInFormats_ITALIAN_ITALY },
+ { LANGUAGE_ITALIAN_SWISS, LANGUAGE_DONTKNOW, spBuiltInFormats_ITALIAN_SWISS },
+
+ { LANGUAGE_SWEDISH, LANGUAGE_DONTKNOW, spBuiltInFormats_SWEDISH_SWEDEN },
+ { LANGUAGE_SWEDISH_FINLAND, LANGUAGE_DONTKNOW, spBuiltInFormats_SWEDISH_FINLAND },
+
+ { PRV_LANGUAGE_ASIAN_PRIM, LANGUAGE_DONTKNOW, spBuiltInFormats_ASIAN },
+ { LANGUAGE_JAPANESE, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_JAPANESE },
+ { LANGUAGE_KOREAN, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_KOREAN },
+ { LANGUAGE_CHINESE_SIMPLIFIED, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_CHINESE_SIMPLIFIED },
+ { LANGUAGE_CHINESE_TRADITIONAL, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_CHINESE_TRADITIONAL },
+
+ { LANGUAGE_HEBREW, LANGUAGE_DONTKNOW, spBuiltInFormats_HEBREW },
+ { LANGUAGE_THAI, LANGUAGE_DONTKNOW, spBuiltInFormats_THAI }
+};
+
+} // namespace
+
+XclNumFmtBuffer::XclNumFmtBuffer( const XclRoot& rRoot ) :
+ meSysLang( rRoot.GetSysLanguage() ),
+ mnStdScNumFmt( rRoot.GetFormatter().GetStandardIndex( ScGlobal::eLnge ) )
+{
+ // *** insert default formats (BIFF5+ only)***
+
+ if( rRoot.GetBiff() >= EXC_BIFF5 )
+ InsertBuiltinFormats();
+}
+
+void XclNumFmtBuffer::InitializeImport()
+{
+ maFmtMap.clear();
+}
+
+void XclNumFmtBuffer::InsertFormat( sal_uInt16 nXclNumFmt, const OUString& rFormat )
+{
+ XclNumFmt& rNumFmt = maFmtMap[ nXclNumFmt ];
+ rNumFmt.maFormat = rFormat;
+ // #i62053# rFormat may be an empty string, meOffset must be initialized
+ rNumFmt.meOffset = NF_NUMBER_STANDARD;
+ rNumFmt.meLanguage = LANGUAGE_SYSTEM;
+}
+
+void XclNumFmtBuffer::InsertBuiltinFormats()
+{
+ // build a map containing tables for all languages
+ typedef ::std::map< LanguageType, const XclBuiltInFormatTable* > XclBuiltInMap;
+ XclBuiltInMap aBuiltInMap;
+ for(const auto &rTable : spBuiltInFormatTables)
+ aBuiltInMap[ rTable.meLanguage ] = &rTable;
+
+ // build a list of table pointers for the current language, with all parent tables
+ typedef ::std::vector< const XclBuiltInFormatTable* > XclBuiltInVec;
+ XclBuiltInVec aBuiltInVec;
+ for( XclBuiltInMap::const_iterator aMIt = aBuiltInMap.find( meSysLang ), aMEnd = aBuiltInMap.end();
+ aMIt != aMEnd; aMIt = aBuiltInMap.find( aMIt->second->meParentLang ) )
+ aBuiltInVec.push_back( aMIt->second );
+ // language not supported
+ if( aBuiltInVec.empty() )
+ {
+ SAL_WARN("sc", "XclNumFmtBuffer::InsertBuiltinFormats - language not supported (#i29949#) 0x" << std::hex << meSysLang );
+ XclBuiltInMap::const_iterator aMIt = aBuiltInMap.find( LANGUAGE_DONTKNOW );
+ OSL_ENSURE( aMIt != aBuiltInMap.end(), "XclNumFmtBuffer::InsertBuiltinFormats - default map not found" );
+ if( aMIt != aBuiltInMap.end() )
+ aBuiltInVec.push_back( aMIt->second );
+ }
+
+ // insert the default formats in the format map, from root parent to system language
+ std::map< sal_uInt16, sal_uInt16 > aReuseMap;
+ for( XclBuiltInVec::reverse_iterator aVIt = aBuiltInVec.rbegin(), aVEnd = aBuiltInVec.rend(); aVIt != aVEnd; ++aVIt )
+ {
+ // put LANGUAGE_SYSTEM for all entries in default table
+ LanguageType eLang = ((*aVIt)->meLanguage == LANGUAGE_DONTKNOW) ? LANGUAGE_SYSTEM : meSysLang;
+ for( const XclBuiltInFormat* pBuiltIn = (*aVIt)->mpFormats; pBuiltIn && (pBuiltIn->mnXclNumFmt != EXC_FORMAT_NOTFOUND); ++pBuiltIn )
+ {
+ XclNumFmt& rNumFmt = maFmtMap[ pBuiltIn->mnXclNumFmt ];
+
+ rNumFmt.meOffset = pBuiltIn->meOffset;
+ rNumFmt.meLanguage = eLang;
+
+ if( pBuiltIn->mpFormat )
+ rNumFmt.maFormat = OUString( pBuiltIn->mpFormat, strlen(pBuiltIn->mpFormat), RTL_TEXTENCODING_UTF8 );
+ else
+ rNumFmt.maFormat.clear();
+
+ if( pBuiltIn->meOffset == PRV_NF_INDEX_REUSE )
+ aReuseMap[ pBuiltIn->mnXclNumFmt ] = pBuiltIn->mnXclReuseFmt;
+ else
+ aReuseMap.erase( pBuiltIn->mnXclNumFmt );
+ }
+ }
+
+ // copy reused number formats
+ for( const auto& [rXclNumFmt, rXclReuseFmt] : aReuseMap )
+ maFmtMap[ rXclNumFmt ] = maFmtMap[ rXclReuseFmt ];
+}
+
+// Cell formatting data (XF) ==================================================
+
+XclCellProt::XclCellProt() :
+ mbLocked( true ), // default in Excel and Calc
+ mbHidden( false )
+{
+}
+
+bool operator==( const XclCellProt& rLeft, const XclCellProt& rRight )
+{
+ return (rLeft.mbLocked == rRight.mbLocked) && (rLeft.mbHidden == rRight.mbHidden);
+}
+
+XclCellAlign::XclCellAlign() :
+ mnHorAlign( EXC_XF_HOR_GENERAL ),
+ mnVerAlign( EXC_XF_VER_BOTTOM ),
+ mnOrient( EXC_ORIENT_NONE ),
+ mnTextDir( EXC_XF_TEXTDIR_CONTEXT ),
+ mnRotation( EXC_ROT_NONE ),
+ mnIndent( 0 ),
+ mbLineBreak( false ),
+ mbShrink( false )
+{
+}
+
+SvxCellHorJustify XclCellAlign::GetScHorAlign() const
+{
+ SvxCellHorJustify eHorJust = SvxCellHorJustify::Standard;
+ switch( mnHorAlign )
+ {
+ case EXC_XF_HOR_GENERAL: eHorJust = SvxCellHorJustify::Standard; break;
+ case EXC_XF_HOR_LEFT: eHorJust = SvxCellHorJustify::Left; break;
+ case EXC_XF_HOR_CENTER_AS:
+ case EXC_XF_HOR_CENTER: eHorJust = SvxCellHorJustify::Center; break;
+ case EXC_XF_HOR_RIGHT: eHorJust = SvxCellHorJustify::Right; break;
+ case EXC_XF_HOR_FILL: eHorJust = SvxCellHorJustify::Repeat; break;
+ case EXC_XF_HOR_JUSTIFY:
+ case EXC_XF_HOR_DISTRIB: eHorJust = SvxCellHorJustify::Block; break;
+ default: OSL_FAIL( "XclCellAlign::GetScHorAlign - unknown horizontal alignment" );
+ }
+ return eHorJust;
+}
+
+SvxCellJustifyMethod XclCellAlign::GetScHorJustifyMethod() const
+{
+ return (mnHorAlign == EXC_XF_HOR_DISTRIB) ? SvxCellJustifyMethod::Distribute : SvxCellJustifyMethod::Auto;
+}
+
+SvxCellVerJustify XclCellAlign::GetScVerAlign() const
+{
+ SvxCellVerJustify eVerJust = SvxCellVerJustify::Standard;
+ switch( mnVerAlign )
+ {
+ case EXC_XF_VER_TOP: eVerJust = SvxCellVerJustify::Top; break;
+ case EXC_XF_VER_CENTER: eVerJust = SvxCellVerJustify::Center; break;
+ case EXC_XF_VER_BOTTOM: eVerJust = SvxCellVerJustify::Standard; break;
+ case EXC_XF_VER_JUSTIFY:
+ case EXC_XF_VER_DISTRIB: eVerJust = SvxCellVerJustify::Block; break;
+ default: OSL_FAIL( "XclCellAlign::GetScVerAlign - unknown vertical alignment" );
+ }
+ return eVerJust;
+}
+
+SvxCellJustifyMethod XclCellAlign::GetScVerJustifyMethod() const
+{
+ return (mnVerAlign == EXC_XF_VER_DISTRIB) ? SvxCellJustifyMethod::Distribute : SvxCellJustifyMethod::Auto;
+}
+
+SvxFrameDirection XclCellAlign::GetScFrameDir() const
+{
+ SvxFrameDirection eFrameDir = SvxFrameDirection::Environment;
+ switch( mnTextDir )
+ {
+ case EXC_XF_TEXTDIR_CONTEXT: eFrameDir = SvxFrameDirection::Environment; break;
+ case EXC_XF_TEXTDIR_LTR: eFrameDir = SvxFrameDirection::Horizontal_LR_TB; break;
+ case EXC_XF_TEXTDIR_RTL: eFrameDir = SvxFrameDirection::Horizontal_RL_TB; break;
+ default: OSL_FAIL( "XclCellAlign::GetScFrameDir - unknown CTL text direction" );
+ }
+ return eFrameDir;
+}
+
+void XclCellAlign::SetScHorAlign( SvxCellHorJustify eHorJust )
+{
+ switch( eHorJust )
+ {
+ case SvxCellHorJustify::Standard: mnHorAlign = EXC_XF_HOR_GENERAL; break;
+ case SvxCellHorJustify::Left: mnHorAlign = EXC_XF_HOR_LEFT; break;
+ case SvxCellHorJustify::Center: mnHorAlign = EXC_XF_HOR_CENTER; break;
+ case SvxCellHorJustify::Right: mnHorAlign = EXC_XF_HOR_RIGHT; break;
+ case SvxCellHorJustify::Block: mnHorAlign = EXC_XF_HOR_JUSTIFY; break;
+ case SvxCellHorJustify::Repeat: mnHorAlign = EXC_XF_HOR_FILL; break;
+ default: mnHorAlign = EXC_XF_HOR_GENERAL;
+ OSL_FAIL( "XclCellAlign::SetScHorAlign - unknown horizontal alignment" );
+ }
+}
+
+void XclCellAlign::SetScVerAlign( SvxCellVerJustify eVerJust )
+{
+ switch( eVerJust )
+ {
+ case SvxCellVerJustify::Standard: mnVerAlign = EXC_XF_VER_BOTTOM; break;
+ case SvxCellVerJustify::Top: mnVerAlign = EXC_XF_VER_TOP; break;
+ case SvxCellVerJustify::Center: mnVerAlign = EXC_XF_VER_CENTER; break;
+ case SvxCellVerJustify::Bottom: mnVerAlign = EXC_XF_VER_BOTTOM; break;
+ default: mnVerAlign = EXC_XF_VER_BOTTOM;
+ OSL_FAIL( "XclCellAlign::SetScVerAlign - unknown vertical alignment" );
+ }
+}
+
+void XclCellAlign::SetScFrameDir( SvxFrameDirection eFrameDir )
+{
+ switch( eFrameDir )
+ {
+ case SvxFrameDirection::Environment: mnTextDir = EXC_XF_TEXTDIR_CONTEXT; break;
+ case SvxFrameDirection::Horizontal_LR_TB: mnTextDir = EXC_XF_TEXTDIR_LTR; break;
+ case SvxFrameDirection::Horizontal_RL_TB: mnTextDir = EXC_XF_TEXTDIR_RTL; break;
+ default: mnTextDir = EXC_XF_TEXTDIR_CONTEXT;
+ OSL_FAIL( "XclCellAlign::SetScFrameDir - unknown CTL text direction" );
+ }
+}
+
+bool operator==( const XclCellAlign& rLeft, const XclCellAlign& rRight )
+{
+ return
+ (rLeft.mnHorAlign == rRight.mnHorAlign) && (rLeft.mnVerAlign == rRight.mnVerAlign) &&
+ (rLeft.mnTextDir == rRight.mnTextDir) && (rLeft.mnOrient == rRight.mnOrient) &&
+ (rLeft.mnRotation == rRight.mnRotation) && (rLeft.mnIndent == rRight.mnIndent) &&
+ (rLeft.mbLineBreak == rRight.mbLineBreak) && (rLeft.mbShrink == rRight.mbShrink);
+}
+
+XclCellBorder::XclCellBorder() :
+ mnLeftColor( 0 ),
+ mnRightColor( 0 ),
+ mnTopColor( 0 ),
+ mnBottomColor( 0 ),
+ mnDiagColor( 0 ),
+ mnLeftLine( EXC_LINE_NONE ),
+ mnRightLine( EXC_LINE_NONE ),
+ mnTopLine( EXC_LINE_NONE ),
+ mnBottomLine( EXC_LINE_NONE ),
+ mnDiagLine( EXC_LINE_NONE ),
+ mbDiagTLtoBR( false ),
+ mbDiagBLtoTR( false )
+{
+}
+
+bool operator==( const XclCellBorder& rLeft, const XclCellBorder& rRight )
+{
+ return
+ (rLeft.mnLeftColor == rRight.mnLeftColor) && (rLeft.mnRightColor == rRight.mnRightColor) &&
+ (rLeft.mnTopColor == rRight.mnTopColor) && (rLeft.mnBottomColor == rRight.mnBottomColor) &&
+ (rLeft.mnLeftLine == rRight.mnLeftLine) && (rLeft.mnRightLine == rRight.mnRightLine) &&
+ (rLeft.mnTopLine == rRight.mnTopLine) && (rLeft.mnBottomLine == rRight.mnBottomLine) &&
+ (rLeft.mnDiagColor == rRight.mnDiagColor) && (rLeft.mnDiagLine == rRight.mnDiagLine) &&
+ (rLeft.mbDiagTLtoBR == rRight.mbDiagTLtoBR) && (rLeft.mbDiagBLtoTR == rRight.mbDiagBLtoTR);
+}
+
+XclCellArea::XclCellArea() :
+ mnForeColor( EXC_COLOR_WINDOWTEXT ),
+ mnBackColor( EXC_COLOR_WINDOWBACK ),
+ mnPattern( EXC_PATT_NONE )
+{
+}
+
+XclCellArea::XclCellArea(sal_uInt8 nPattern) :
+ mnForeColor( EXC_COLOR_WINDOWTEXT ),
+ mnBackColor( EXC_COLOR_WINDOWBACK ),
+ mnPattern( nPattern )
+{
+}
+
+bool XclCellArea::IsTransparent() const
+{
+ return (mnPattern == EXC_PATT_NONE) && (mnBackColor == EXC_COLOR_WINDOWBACK);
+}
+
+bool operator==( const XclCellArea& rLeft, const XclCellArea& rRight )
+{
+ return
+ (rLeft.mnForeColor == rRight.mnForeColor) && (rLeft.mnBackColor == rRight.mnBackColor) &&
+ (rLeft.mnPattern == rRight.mnPattern);
+}
+
+XclXFBase::XclXFBase( bool bCellXF ) :
+ mnParent( bCellXF ? EXC_XF_DEFAULTSTYLE : EXC_XF_STYLEPARENT ),
+ mbCellXF( bCellXF )
+{
+ SetAllUsedFlags( false );
+}
+
+XclXFBase::~XclXFBase()
+{
+}
+
+void XclXFBase::SetAllUsedFlags( bool bUsed )
+{
+ mbProtUsed = mbFontUsed = mbFmtUsed = mbAlignUsed = mbBorderUsed = mbAreaUsed = bUsed;
+}
+
+bool XclXFBase::HasUsedFlags() const
+{
+ return mbProtUsed || mbFontUsed || mbFmtUsed || mbAlignUsed || mbBorderUsed || mbAreaUsed;
+}
+
+bool XclXFBase::Equals( const XclXFBase& rCmp ) const
+{
+ return
+ (mbCellXF == rCmp.mbCellXF) && (mnParent == rCmp.mnParent) &&
+ (mbProtUsed == rCmp.mbProtUsed) && (mbFontUsed == rCmp.mbFontUsed) &&
+ (mbFmtUsed == rCmp.mbFmtUsed) && (mbAlignUsed == rCmp.mbAlignUsed) &&
+ (mbBorderUsed == rCmp.mbBorderUsed) && (mbAreaUsed == rCmp.mbAreaUsed);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xltoolbar.cxx b/sc/source/filter/excel/xltoolbar.cxx
new file mode 100644
index 000000000..efb54925b
--- /dev/null
+++ b/sc/source/filter/excel/xltoolbar.cxx
@@ -0,0 +1,439 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#include "xltoolbar.hxx"
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/ui/XUIConfigurationPersistence.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <rtl/ref.hxx>
+#include <map>
+
+using namespace com::sun::star;
+
+typedef std::map< sal_Int16, OUString > IdToString;
+
+namespace {
+
+class MSOExcelCommandConvertor : public MSOCommandConvertor
+{
+ IdToString msoToOOcmd;
+ IdToString tcidToOOcmd;
+public:
+ MSOExcelCommandConvertor();
+ virtual OUString MSOCommandToOOCommand( sal_Int16 msoCmd ) override;
+ virtual OUString MSOTCIDToOOCommand( sal_Int16 key ) override;
+};
+
+}
+
+MSOExcelCommandConvertor::MSOExcelCommandConvertor()
+{
+/*
+ // mso command id to ooo command string
+ // #FIXME and *HUNDREDS* of id's to added here
+ msoToOOcmd[ 0x20b ] = ".uno:CloseDoc";
+ msoToOOcmd[ 0x50 ] = ".uno:Open";
+
+ // mso tcid to ooo command string
+ // #FIXME and *HUNDREDS* of id's to added here
+ tcidToOOcmd[ 0x9d9 ] = ".uno:Print";
+*/
+}
+
+OUString MSOExcelCommandConvertor::MSOCommandToOOCommand( sal_Int16 key )
+{
+ OUString sResult;
+ IdToString::iterator it = msoToOOcmd.find( key );
+ if ( it != msoToOOcmd.end() )
+ sResult = it->second;
+ return sResult;
+}
+
+OUString MSOExcelCommandConvertor::MSOTCIDToOOCommand( sal_Int16 key )
+{
+ OUString sResult;
+ IdToString::iterator it = tcidToOOcmd.find( key );
+ if ( it != tcidToOOcmd.end() )
+ sResult = it->second;
+ return sResult;
+}
+
+CTBS::CTBS() : bSignature(0), bVersion(0), reserved1(0), reserved2(0), reserved3(0), ctb(0), ctbViews(0), ictbView(0)
+{
+}
+
+ScCTB::ScCTB(sal_uInt16 nNum ) : nViews( nNum ), ectbid(0)
+{
+}
+
+bool ScCTB::Read( SvStream &rS )
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ tb.Read( rS );
+
+ {
+ const size_t nMinRecordSize = 20; // TBVisualData reads 20 bytes
+ const size_t nMaxPossibleRecords = rS.remainingSize() / nMinRecordSize;
+ if (nViews > nMaxPossibleRecords)
+ {
+ SAL_WARN("sc.filter", "ScCTB::Read more entries claimed than stream could contain");
+ return false;
+ }
+ }
+
+ for ( sal_uInt16 index = 0; index < nViews; ++index )
+ {
+ TBVisualData aVisData;
+ aVisData.Read( rS );
+ rVisualData.push_back( aVisData );
+ }
+ rS.ReadUInt32( ectbid );
+
+ sal_Int16 nCL = tb.getcCL();
+ if (nCL > 0)
+ {
+ auto nIndexes = o3tl::make_unsigned(nCL);
+
+ const size_t nMinRecordSize = 11; // ScTBC's TBCHeader reads min 11 bytes
+ const size_t nMaxPossibleRecords = rS.remainingSize() / nMinRecordSize;
+ if (nIndexes > nMaxPossibleRecords)
+ {
+ SAL_WARN("sc.filter", "ScCTB::Read more entries claimed than stream could contain");
+ return false;
+ }
+
+ for (decltype(nIndexes) index = 0; index < nIndexes; ++index)
+ {
+ ScTBC aTBC;
+ aTBC.Read( rS );
+ rTBC.push_back( aTBC );
+ }
+ }
+
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void ScCTB::Print( FILE* fp )
+{
+ Indent a;
+ indent_printf( fp, "[ 0x%x ] ScCTB -- dump\n", nOffSet );
+ indent_printf( fp, " nViews 0x%x\n", nViews);
+ tb.Print( fp );
+
+ sal_Int32 counter = 0;
+ for ( auto& rItem : rVisualData )
+ {
+ indent_printf( fp, " TBVisualData [%d]\n", counter++ );
+ Indent b;
+ rItem.Print( fp );
+ }
+ indent_printf( fp, " ectbid 0x%x\n", ectbid);
+ counter = 0;
+ for ( auto& rItem : rTBC )
+ {
+ indent_printf( fp, " ScTBC [%d]\n", counter++);
+ Indent c;
+ rItem.Print( fp );
+ }
+}
+#endif
+
+bool ScCTB::IsMenuToolbar() const
+{
+ return tb.IsMenuToolbar();
+}
+
+bool ScCTB::ImportMenuTB( ScCTBWrapper& rWrapper, const css::uno::Reference< css::container::XIndexContainer >& xMenuDesc, CustomToolBarImportHelper& helper )
+{
+ for ( auto& rItem : rTBC )
+ {
+ if ( !rItem.ImportToolBarControl( rWrapper, xMenuDesc, helper, IsMenuToolbar() ) )
+ return false;
+ }
+ return true;
+}
+
+bool ScCTB::ImportCustomToolBar( ScCTBWrapper& rWrapper, CustomToolBarImportHelper& helper )
+{
+
+ bool bRes = false;
+ try
+ {
+ if ( !tb.IsEnabled() )
+ return true; // didn't fail, just ignoring
+
+ // Create default setting
+ uno::Reference< container::XIndexContainer > xIndexContainer( helper.getCfgManager()->createSettings(), uno::UNO_SET_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xIndexContainer, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xProps( xIndexContainer, uno::UNO_QUERY_THROW );
+ WString& name = tb.getName();
+ // set UI name for toolbar
+ xProps->setPropertyValue("UIName", uno::Any( name.getString() ) );
+
+ OUString sToolBarName = "private:resource/toolbar/custom_" + name.getString();
+ for ( auto& rItem : rTBC )
+ {
+ if ( !rItem.ImportToolBarControl( rWrapper, xIndexContainer, helper, IsMenuToolbar() ) )
+ return false;
+ }
+
+ helper.getCfgManager()->insertSettings( sToolBarName, xIndexAccess );
+ helper.applyIcons();
+
+ uno::Reference< ui::XUIConfigurationPersistence > xPersistence( helper.getCfgManager()->getImageManager(), uno::UNO_QUERY_THROW );
+ xPersistence->store();
+
+ xPersistence.set( helper.getCfgManager(), uno::UNO_QUERY_THROW );
+ xPersistence->store();
+
+ bRes = true;
+ }
+ catch( uno::Exception& )
+ {
+ bRes = false;
+ }
+ return bRes;
+}
+bool CTBS::Read( SvStream &rS )
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ rS.ReadUChar( bSignature ).ReadUChar( bVersion ).ReadUInt16( reserved1 ).ReadUInt16( reserved2 ).ReadUInt16( reserved3 ).ReadUInt16( ctb ).ReadUInt16( ctbViews ).ReadUInt16( ictbView );
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void CTBS::Print( FILE* fp )
+{
+ Indent a;
+ indent_printf( fp, "[ 0x%x ] CTBS -- dump\n", nOffSet );
+
+ indent_printf( fp, " bSignature 0x%x\n", bSignature);
+ indent_printf( fp, " bVersion 0x%x\n", bVersion);
+
+ indent_printf( fp, " reserved1 0x%x\n", reserved1 );
+ indent_printf( fp, " reserved2 0x%x\n", reserved2 );
+ indent_printf( fp, " reserved3 0x%x\n", reserved3 );
+
+ indent_printf( fp, " ctb 0x%x\n", ctb );
+ indent_printf( fp, " ctbViews 0x%x\n", ctbViews );
+ indent_printf( fp, " ictbView 0x%x\n", ictbView );
+}
+#endif
+
+ScTBC::ScTBC()
+{
+}
+
+bool
+ScTBC::Read(SvStream &rS)
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ if ( !tbch.Read( rS ) )
+ return false;
+ sal_uInt16 tcid = tbch.getTcID();
+ sal_uInt8 tct = tbch.getTct();
+ if ( ( tcid != 0x0001 && tcid != 0x06CC && tcid != 0x03D8 && tcid != 0x03EC && tcid != 0x1051 ) && ( ( tct > 0 && tct < 0x0B ) || ( ( tct > 0x0B && tct < 0x10 ) || tct == 0x15 ) ) )
+ {
+ tbcCmd = std::make_shared<TBCCmd>();
+ if ( ! tbcCmd->Read( rS ) )
+ return false;
+ }
+ if ( tct != 0x16 )
+ {
+ tbcd = std::make_shared<TBCData>( tbch );
+ if ( !tbcd->Read( rS ) )
+ return false;
+ }
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void
+ScTBC::Print(FILE* fp)
+{
+ Indent a;
+ indent_printf( fp, "[ 0x%x ] ScTBC -- dump\n", nOffSet );
+ tbch.Print( fp );
+ if ( tbcCmd.get() )
+ tbcCmd->Print( fp );
+ if ( tbcd.get() )
+ tbcd->Print( fp );
+}
+#endif
+
+bool ScTBC::ImportToolBarControl( ScCTBWrapper& rWrapper, const css::uno::Reference< css::container::XIndexContainer >& toolbarcontainer, CustomToolBarImportHelper& helper, bool bIsMenuToolbar )
+{
+ // how to identify built-in-command ?
+// bool bBuiltin = false;
+ if ( tbcd )
+ {
+ std::vector< css::beans::PropertyValue > props;
+ bool bBeginGroup = false;
+ tbcd->ImportToolBarControl( helper, props, bBeginGroup, bIsMenuToolbar );
+ TBCMenuSpecific* pMenu = tbcd->getMenuSpecific();
+ if ( pMenu )
+ {
+ // search for ScCTB with the appropriate name ( it contains the
+ // menu items, although we cannot import ( or create ) a menu on
+ // a custom toolbar we can import the menu items in a separate
+ // toolbar ( better than nothing )
+ ScCTB* pCustTB = rWrapper.GetCustomizationData( pMenu->Name() );
+ if ( pCustTB )
+ {
+ rtl::Reference< comphelper::IndexedPropertyValuesContainer > xMenuDesc = new comphelper::IndexedPropertyValuesContainer();
+ if ( !pCustTB->ImportMenuTB( rWrapper, xMenuDesc, helper ) )
+ return false;
+ if ( !bIsMenuToolbar )
+ {
+ if ( !helper.createMenu( pMenu->Name(), xMenuDesc ) )
+ return false;
+ }
+ else
+ {
+ beans::PropertyValue aProp;
+ aProp.Name = "ItemDescriptorContainer";
+ aProp.Value <<= uno::Reference< container::XIndexContainer >(xMenuDesc);
+ props.push_back( aProp );
+ }
+ }
+ }
+
+ if ( bBeginGroup )
+ {
+ // insert spacer
+ uno::Sequence sProps{ comphelper::makePropertyValue("Type",
+ ui::ItemType::SEPARATOR_LINE) };
+ toolbarcontainer->insertByIndex( toolbarcontainer->getCount(), uno::Any( sProps ) );
+ }
+ toolbarcontainer->insertByIndex( toolbarcontainer->getCount(), uno::Any( comphelper::containerToSequence(props) ) );
+ }
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void
+TBCCmd::Print(FILE* fp)
+{
+ Indent a;
+ indent_printf( fp, " TBCCmd -- dump\n" );
+ indent_printf( fp, " cmdID 0x%x\n", cmdID );
+ indent_printf( fp, " A ( fHideDrawing ) %s\n", A ? "true" : "false" );
+ indent_printf( fp, " B ( reserved - ignored ) %s\n", A ? "true" : "false" );
+ indent_printf( fp, " cmdType 0x%x\n", cmdType );
+ indent_printf( fp, " C ( reserved - ignored ) %s\n", A ? "true" : "false" );
+ indent_printf( fp, " reserved3 0x%x\n", reserved3 );
+}
+#endif
+
+bool TBCCmd::Read( SvStream &rS )
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ rS.ReadUInt16( cmdID );
+ sal_uInt16 temp;
+ rS.ReadUInt16( temp );
+ A = (temp & 0x8000 ) == 0x8000;
+ B = (temp & 0x4000) == 0x4000;
+ cmdType = ( temp & 0x3E00 ) >> 9;
+ C = ( temp & 0x100 ) == 0x100;
+ reserved3 = ( temp & 0xFF );
+ return true;
+}
+
+ScCTBWrapper::ScCTBWrapper()
+{
+}
+
+ScCTBWrapper::~ScCTBWrapper()
+{
+}
+
+bool
+ScCTBWrapper::Read( SvStream &rS)
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ if (!ctbSet.Read(rS))
+ return false;
+
+ //ScCTB is 1 TB which is min 15bytes, nViews TBVisualData which is min 20bytes
+ //and one 32bit number (4 bytes)
+ const size_t nMinRecordSize = 19 + o3tl::sanitizing_min(ctbSet.ctbViews * 20, 0);
+ const size_t nMaxPossibleRecords = rS.remainingSize()/nMinRecordSize;
+ if (ctbSet.ctb > nMaxPossibleRecords)
+ return false;
+
+ for ( sal_uInt16 index = 0; index < ctbSet.ctb; ++index )
+ {
+ ScCTB aCTB( ctbSet.ctbViews );
+ if ( !aCTB.Read( rS ) )
+ return false;
+ rCTB.push_back( aCTB );
+ }
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void
+ScCTBWrapper::Print( FILE* fp )
+{
+ Indent a;
+ indent_printf( fp, "[ 0x%x ] ScCTBWrapper -- dump\n", nOffSet );
+ ctbSet.Print( fp );
+ for ( auto& rItem : rCTB )
+ {
+ Indent b;
+ rItem.Print( fp );
+ }
+}
+#endif
+
+ScCTB* ScCTBWrapper::GetCustomizationData( const OUString& sTBName )
+{
+ ScCTB* pCTB = nullptr;
+ auto it = std::find_if(rCTB.begin(), rCTB.end(), [&sTBName](ScCTB& rItem) { return rItem.GetName() == sTBName; });
+ if (it != rCTB.end())
+ pCTB = &(*it);
+ return pCTB;
+}
+
+void ScCTBWrapper::ImportCustomToolBar( SfxObjectShell& rDocSh )
+{
+ if(rCTB.empty())
+ return;
+
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference< ui::XModuleUIConfigurationManagerSupplier > xAppCfgSupp( ui::theModuleUIConfigurationManagerSupplier::get(xContext) );
+
+ for ( auto& rItem : rCTB )
+ {
+ // for each customtoolbar
+ CustomToolBarImportHelper helper( rDocSh, xAppCfgSupp->getUIConfigurationManager( "com.sun.star.sheet.SpreadsheetDocument" ) );
+ helper.setMSOCommandMap( new MSOExcelCommandConvertor() );
+ // Ignore menu toolbars, excel doesn't ( afaics ) store
+ // menu customizations ( but you can have menus in a customtoolbar
+ // such menus will be dealt with when they are encountered
+ // as part of importing the appropriate MenuSpecific toolbar control )
+
+ if ( !rItem.IsMenuToolbar() && !rItem.ImportCustomToolBar( *this, helper ) )
+ return;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xltoolbar.hxx b/sc/source/filter/excel/xltoolbar.hxx
new file mode 100644
index 000000000..f7da78b2f
--- /dev/null
+++ b/sc/source/filter/excel/xltoolbar.hxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include <filter/msfilter/mstoolbar.hxx>
+
+namespace com::sun::star::container { class XIndexContainer; }
+
+class ScCTBWrapper;
+// hmm I don't normally use these packed structures
+// but... hey always good to do something different
+class TBCCmd : public TBBase
+{
+public:
+ TBCCmd() : cmdID(0), A(false), B(false), cmdType(0), C(false), reserved3(0)
+ {}
+ sal_uInt16 cmdID;
+ bool A:1;
+ bool B:1;
+ sal_uInt16 cmdType:5;
+ bool C:1;
+ sal_uInt16 reserved3:8;
+ bool Read( SvStream& rS ) override;
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print(FILE* fp) override;
+#endif
+};
+
+class ScTBC : public TBBase
+{
+ TBCHeader tbch;
+ std::shared_ptr<TBCCmd> tbcCmd; // optional
+ std::shared_ptr<TBCData> tbcd;
+public:
+ ScTBC();
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print( FILE* ) override;
+#endif
+ bool Read(SvStream &rS) override;
+ bool ImportToolBarControl( ScCTBWrapper&, const css::uno::Reference< css::container::XIndexContainer >& toolbarcontainer, CustomToolBarImportHelper& helper, bool bIsMenuBar );
+};
+
+class ScCTB : public TBBase
+{
+ sal_uInt16 nViews;
+ TB tb;
+ std::vector<TBVisualData> rVisualData;
+ sal_uInt32 ectbid;
+ std::vector< ScTBC > rTBC;
+public:
+ explicit ScCTB(sal_uInt16);
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print( FILE* ) override;
+#endif
+ bool Read(SvStream &rS) override;
+ bool IsMenuToolbar() const;
+ bool ImportCustomToolBar( ScCTBWrapper&, CustomToolBarImportHelper& );
+ bool ImportMenuTB( ScCTBWrapper&, const css::uno::Reference< css::container::XIndexContainer >&, CustomToolBarImportHelper& );
+ const OUString& GetName() { return tb.getName().getString(); }
+
+};
+
+class CTBS : public TBBase
+{
+public:
+ sal_uInt8 bSignature;
+ sal_uInt8 bVersion;
+ sal_uInt16 reserved1;
+ sal_uInt16 reserved2;
+ sal_uInt16 reserved3;
+ sal_uInt16 ctb;
+ sal_uInt16 ctbViews;
+ sal_uInt16 ictbView;
+ CTBS(const CTBS&);
+ CTBS& operator = ( const CTBS&);
+ CTBS();
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print( FILE* ) override;
+#endif
+ bool Read(SvStream &rS) override;
+};
+
+class ScCTBWrapper : public TBBase
+{
+ CTBS ctbSet;
+
+ std::vector< ScCTB > rCTB;
+
+public:
+ ScCTBWrapper();
+ virtual ~ScCTBWrapper() override;
+ bool Read(SvStream &rS) override;
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print( FILE* ) override;
+#endif
+ void ImportCustomToolBar( SfxObjectShell& rDocSh );
+ ScCTB* GetCustomizationData( const OUString& name );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xltools.cxx b/sc/source/filter/excel/xltools.cxx
new file mode 100644
index 000000000..3a69e7da0
--- /dev/null
+++ b/sc/source/filter/excel/xltools.cxx
@@ -0,0 +1,744 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <algorithm>
+#include <math.h>
+#include <string_view>
+
+#include <o3tl/unit_conversion.hxx>
+#include <sal/mathconf.h>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <tools/solar.h>
+#include <o3tl/string_view.hxx>
+#include <unotools/fontdefs.hxx>
+#include <filter/msfilter/msvbahelper.hxx>
+#include <xestream.hxx>
+#include <formula/errorcodes.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <xlstyle.hxx>
+#include <xlname.hxx>
+#include <xistream.hxx>
+#include <xltools.hxx>
+
+// GUID import/export
+
+XclGuid::XclGuid()
+ : mpnData{}
+{
+}
+
+XclGuid::XclGuid(
+ sal_uInt32 nData1, sal_uInt16 nData2, sal_uInt16 nData3,
+ sal_uInt8 nData41, sal_uInt8 nData42, sal_uInt8 nData43, sal_uInt8 nData44,
+ sal_uInt8 nData45, sal_uInt8 nData46, sal_uInt8 nData47, sal_uInt8 nData48 )
+{
+ // convert to little endian -> makes streaming easy
+ UInt32ToSVBT32( nData1, mpnData );
+ ShortToSVBT16( nData2, mpnData + 4 );
+ ShortToSVBT16( nData3, mpnData + 6 );
+ mpnData[ 8 ] = nData41;
+ mpnData[ 9 ] = nData42;
+ mpnData[ 10 ] = nData43;
+ mpnData[ 11 ] = nData44;
+ mpnData[ 12 ] = nData45;
+ mpnData[ 13 ] = nData46;
+ mpnData[ 14 ] = nData47;
+ mpnData[ 15 ] = nData48;
+}
+
+bool operator==( const XclGuid& rCmp1, const XclGuid& rCmp2 )
+{
+ return ::std::equal( rCmp1.mpnData, std::end( rCmp1.mpnData ), rCmp2.mpnData );
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclGuid& rGuid )
+{
+ rStrm.Read( rGuid.mpnData, 16 ); // mpnData always in little endian
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclGuid& rGuid )
+{
+ rStrm.Write( rGuid.mpnData, 16 ); // mpnData already in little endian
+ return rStrm;
+}
+
+// Excel Tools
+
+// GUID's
+const XclGuid XclTools::maGuidStdLink(
+ 0x79EAC9D0, 0xBAF9, 0x11CE, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B );
+
+const XclGuid XclTools::maGuidUrlMoniker(
+ 0x79EAC9E0, 0xBAF9, 0x11CE, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B );
+
+const XclGuid XclTools::maGuidFileMoniker(
+ 0x00000303, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 );
+
+// numeric conversion
+
+double XclTools::GetDoubleFromRK( sal_Int32 nRKValue )
+{
+ union
+ {
+ double fVal;
+ sal_math_Double smD;
+ };
+ fVal = 0.0;
+
+ if( ::get_flag( nRKValue, EXC_RK_INTFLAG ) )
+ {
+ sal_Int32 nTemp = nRKValue >> 2;
+ ::set_flag< sal_Int32 >( nTemp, 0xE0000000, nRKValue < 0 );
+ fVal = nTemp;
+ }
+ else
+ {
+ smD.w32_parts.msw = nRKValue & EXC_RK_VALUEMASK;
+ }
+
+ if( ::get_flag( nRKValue, EXC_RK_100FLAG ) )
+ fVal /= 100.0;
+
+ return fVal;
+}
+
+bool XclTools::GetRKFromDouble( sal_Int32& rnRKValue, double fValue )
+{
+ double fFrac, fInt;
+
+ // integer
+ fFrac = modf( fValue, &fInt );
+ if( (fFrac == 0.0) && (fInt >= -536870912.0) && (fInt <= 536870911.0) ) // 2^29
+ {
+ rnRKValue
+ = static_cast<sal_Int32>(
+ static_cast<sal_uInt32>(static_cast<sal_Int32>(fInt)) << 2)
+ | EXC_RK_INT;
+ return true;
+ }
+
+ // integer/100
+ fFrac = modf( fValue * 100.0, &fInt );
+ if( (fFrac == 0.0) && (fInt >= -536870912.0) && (fInt <= 536870911.0) )
+ {
+ rnRKValue
+ = static_cast<sal_Int32>(
+ static_cast<sal_uInt32>(static_cast<sal_Int32>(fInt)) << 2)
+ | EXC_RK_INT100;
+ return true;
+ }
+
+ // double
+ return false;
+}
+
+Degree100 XclTools::GetScRotation( sal_uInt16 nXclRot, Degree100 nRotForStacked )
+{
+ if( nXclRot == EXC_ROT_STACKED )
+ return nRotForStacked;
+ OSL_ENSURE( nXclRot <= 180, "XclTools::GetScRotation - illegal rotation angle" );
+ return Degree100(static_cast< sal_Int32 >( (nXclRot <= 180) ? (100 * ((nXclRot > 90) ? (450 - nXclRot) : nXclRot)) : 0 ));
+}
+
+sal_uInt8 XclTools::GetXclRotation( Degree100 nScRot )
+{
+ sal_Int32 nXclRot = nScRot.get() / 100;
+ if( (0 <= nXclRot) && (nXclRot <= 90) )
+ return static_cast< sal_uInt8 >( nXclRot );
+ if( nXclRot < 180 )
+ return static_cast< sal_uInt8 >( 270 - nXclRot );
+ if( nXclRot < 270 )
+ return static_cast< sal_uInt8 >( nXclRot - 180 );
+ if( nXclRot < 360 )
+ return static_cast< sal_uInt8 >( 450 - nXclRot );
+ return 0;
+}
+
+sal_uInt8 XclTools::GetXclRotFromOrient( sal_uInt8 nXclOrient )
+{
+ switch( nXclOrient )
+ {
+ case EXC_ORIENT_NONE: return EXC_ROT_NONE;
+ case EXC_ORIENT_STACKED: return EXC_ROT_STACKED;
+ case EXC_ORIENT_90CCW: return EXC_ROT_90CCW;
+ case EXC_ORIENT_90CW: return EXC_ROT_90CW;
+ default: OSL_FAIL( "XclTools::GetXclRotFromOrient - unknown text orientation" );
+ }
+ return EXC_ROT_NONE;
+}
+
+sal_uInt8 XclTools::GetXclOrientFromRot( sal_uInt16 nXclRot )
+{
+ if( nXclRot == EXC_ROT_STACKED )
+ return EXC_ORIENT_STACKED;
+ OSL_ENSURE( nXclRot <= 180, "XclTools::GetXclOrientFromRot - unknown text rotation" );
+ if( (45 < nXclRot) && (nXclRot <= 90) )
+ return EXC_ORIENT_90CCW;
+ if( (135 < nXclRot) && (nXclRot <= 180) )
+ return EXC_ORIENT_90CW;
+ return EXC_ORIENT_NONE;
+}
+
+sal_uInt8 XclTools::GetXclErrorCode( FormulaError nScError )
+{
+ switch( nScError )
+ {
+ case FormulaError::IllegalArgument: return EXC_ERR_VALUE;
+ case FormulaError::IllegalFPOperation: return EXC_ERR_NUM; // maybe DIV/0 or NUM...
+ case FormulaError::DivisionByZero: return EXC_ERR_DIV0;
+ case FormulaError::IllegalParameter: return EXC_ERR_VALUE;
+ case FormulaError::PairExpected: return EXC_ERR_VALUE;
+ case FormulaError::OperatorExpected: return EXC_ERR_VALUE;
+ case FormulaError::VariableExpected: return EXC_ERR_VALUE;
+ case FormulaError::ParameterExpected: return EXC_ERR_VALUE;
+ case FormulaError::NoValue: return EXC_ERR_VALUE;
+ case FormulaError::CircularReference: return EXC_ERR_VALUE;
+ case FormulaError::NoCode: return EXC_ERR_NULL;
+ case FormulaError::NoRef: return EXC_ERR_REF;
+ case FormulaError::NoName: return EXC_ERR_NAME;
+ case FormulaError::NoAddin: return EXC_ERR_NAME;
+ case FormulaError::NoMacro: return EXC_ERR_NAME;
+ case FormulaError::NotAvailable: return EXC_ERR_NA;
+ default: break;
+ }
+ return EXC_ERR_NA;
+}
+
+FormulaError XclTools::GetScErrorCode( sal_uInt8 nXclError )
+{
+ switch( nXclError )
+ {
+ case EXC_ERR_NULL: return FormulaError::NoCode;
+ case EXC_ERR_DIV0: return FormulaError::DivisionByZero;
+ case EXC_ERR_VALUE: return FormulaError::NoValue;
+ case EXC_ERR_REF: return FormulaError::NoRef;
+ case EXC_ERR_NAME: return FormulaError::NoName;
+ case EXC_ERR_NUM: return FormulaError::IllegalFPOperation;
+ case EXC_ERR_NA: return FormulaError::NotAvailable;
+ default: OSL_FAIL( "XclTools::GetScErrorCode - unknown error code" );
+ }
+ return FormulaError::NotAvailable;
+}
+
+double XclTools::ErrorToDouble( sal_uInt8 nXclError )
+{
+ return CreateDoubleError(GetScErrorCode( nXclError ));
+}
+
+XclBoolError XclTools::ErrorToEnum( double& rfDblValue, bool bErrOrBool, sal_uInt8 nValue )
+{
+ XclBoolError eType;
+ if( bErrOrBool )
+ {
+ // error value
+ switch( nValue )
+ {
+ case EXC_ERR_NULL: eType = xlErrNull; break;
+ case EXC_ERR_DIV0: eType = xlErrDiv0; break;
+ case EXC_ERR_VALUE: eType = xlErrValue; break;
+ case EXC_ERR_REF: eType = xlErrRef; break;
+ case EXC_ERR_NAME: eType = xlErrName; break;
+ case EXC_ERR_NUM: eType = xlErrNum; break;
+ case EXC_ERR_NA: eType = xlErrNA; break;
+ default: eType = xlErrUnknown;
+ }
+ rfDblValue = 0.0;
+ }
+ else
+ {
+ // Boolean value
+ eType = nValue ? xlErrTrue : xlErrFalse;
+ rfDblValue = nValue ? 1.0 : 0.0;
+ }
+ return eType;
+}
+
+template <typename N> static N to(double f) { return limit_cast<N>(f + 0.5); }
+
+sal_uInt16 XclTools::GetTwipsFromInch( double fInches )
+{
+ return to<sal_uInt16>(o3tl::convert(fInches, o3tl::Length::in, o3tl::Length::twip));
+}
+
+sal_uInt16 XclTools::GetTwipsFromHmm( sal_Int32 nHmm )
+{
+ return limit_cast<sal_uInt16>(o3tl::convert(nHmm, o3tl::Length::mm100, o3tl::Length::twip));
+}
+
+double XclTools::GetInchFromTwips( sal_Int32 nTwips )
+{
+ return o3tl::convert<double>(nTwips, o3tl::Length::twip, o3tl::Length::in);
+}
+
+double XclTools::GetInchFromHmm( sal_Int32 nHmm )
+{
+ return o3tl::convert<double>(nHmm, o3tl::Length::mm100, o3tl::Length::in);
+}
+
+sal_Int32 XclTools::GetHmmFromInch( double fInches )
+{
+ return to<sal_Int32>(o3tl::convert(fInches, o3tl::Length::in, o3tl::Length::mm100));
+}
+
+sal_Int32 XclTools::GetHmmFromTwips( sal_Int32 nTwips )
+{
+ return limit_cast<sal_Int32>(o3tl::convert(nTwips, o3tl::Length::twip, o3tl::Length::mm100));
+}
+
+sal_uInt16 XclTools::GetScColumnWidth( sal_uInt16 nXclWidth, tools::Long nScCharWidth )
+{
+ double fScWidth = static_cast< double >( nXclWidth ) / 256.0 * nScCharWidth - 0.5;
+ return limit_cast< sal_uInt16 >( fScWidth );
+}
+
+sal_uInt16 XclTools::GetXclColumnWidth( sal_uInt16 nScWidth, tools::Long nScCharWidth )
+{
+ double fXclWidth = ( static_cast< double >( nScWidth ) + 0.5 ) * 256.0 / nScCharWidth;
+ return limit_cast< sal_uInt16 >( fXclWidth );
+}
+
+// takes font height in twips (1/20 pt = 1/1440 in)
+// returns correction value in 1/256th of *digit width* of default font
+double XclTools::GetXclDefColWidthCorrection( tools::Long nXclDefFontHeight )
+{
+ // Excel uses *max digit width of default font* (W) as cell width unit. Also it has 5-pixel
+ // "correction" to cell widths (ECMA-376-1:2016 18.3.1.81): each cell has 1-pixel padding, then
+ // 3 pixels for the border (which may be 1-pixel - hairline - then it will have 2 additional
+ // 1-pixel spacings from each side; or e.g. 2 hairlines with 1-pixel spacing in the middle; or
+ // thick 3-pixel). Obviously, correction size entirely depends on pixel size (and it is actually
+ // different in Excel on monitors with different resolution). Thus actual (displayed/printed)
+ // cell widths consist of X*W+5px; stored in file is the X (or X*256 if 1/256th of digit width
+ // units are used) value.
+ // This formula apparently converts this 5-pixel correction to 1/256th of digit width units.
+ // Looks like it is created from
+ //
+ // 5 * 256 * 1440 * 2.1333 / (96 * max(N-15, 60)) + 50.0
+ //
+ // where 5 - pixel correction; 256 - used to produce 1/256th of digit width; 1440 - used to
+ // convert font height N (in twips) to inches; 2.1333 - an (empirical?) quotient to convert
+ // font *height* into digit *width*; 96 - "standard" monitor resolution (DPI).
+ // Additionally the formula uses 15 (of unknown origin), 60 (minimal font height 3 pt), and
+ // 50.0 (also of unknown origin).
+ //
+ // TODO: convert this to take font digit width directly (and possibly DPI?), to avoid guessing
+ // the digit width and pixel size. Or DPI might stay 96, to not follow Excel dependency on DPI
+ // in addition to used font, and have absolute size of the correction fixed 5/96 in.
+ return 40960.0 / ::std::max( nXclDefFontHeight - 15, tools::Long(60) ) + 50.0;
+}
+
+// formatting
+
+Color XclTools::GetPatternColor( const Color& rPattColor, const Color& rBackColor, sal_uInt16 nXclPattern )
+{
+ // 0x00 == 0% transparence (full rPattColor)
+ // 0x80 == 100% transparence (full rBackColor)
+ static const sal_uInt8 pnRatioTable[] =
+ {
+ 0x80, 0x00, 0x40, 0x20, 0x60, 0x40, 0x40, 0x40, // 00 - 07
+ 0x40, 0x40, 0x20, 0x60, 0x60, 0x60, 0x60, 0x48, // 08 - 15
+ 0x50, 0x70, 0x78 // 16 - 18
+ };
+ return (nXclPattern < SAL_N_ELEMENTS( pnRatioTable )) ?
+ ScfTools::GetMixedColor( rPattColor, rBackColor, pnRatioTable[ nXclPattern ] ) : rPattColor;
+}
+
+// text encoding
+
+namespace {
+
+const struct XclCodePageEntry
+{
+ sal_uInt16 mnCodePage;
+ rtl_TextEncoding meTextEnc;
+}
+pCodePageTable[] =
+{
+ { 437, RTL_TEXTENCODING_IBM_437 }, // OEM US
+// { 720, RTL_TEXTENCODING_IBM_720 }, // OEM Arabic
+ { 737, RTL_TEXTENCODING_IBM_737 }, // OEM Greek
+ { 775, RTL_TEXTENCODING_IBM_775 }, // OEM Baltic
+ { 850, RTL_TEXTENCODING_IBM_850 }, // OEM Latin I
+ { 852, RTL_TEXTENCODING_IBM_852 }, // OEM Latin II (Central European)
+ { 855, RTL_TEXTENCODING_IBM_855 }, // OEM Cyrillic
+ { 857, RTL_TEXTENCODING_IBM_857 }, // OEM Turkish
+// { 858, RTL_TEXTENCODING_IBM_858 }, // OEM Multilingual Latin I with Euro
+ { 860, RTL_TEXTENCODING_IBM_860 }, // OEM Portuguese
+ { 861, RTL_TEXTENCODING_IBM_861 }, // OEM Icelandic
+ { 862, RTL_TEXTENCODING_IBM_862 }, // OEM Hebrew
+ { 863, RTL_TEXTENCODING_IBM_863 }, // OEM Canadian (French)
+ { 864, RTL_TEXTENCODING_IBM_864 }, // OEM Arabic
+ { 865, RTL_TEXTENCODING_IBM_865 }, // OEM Nordic
+ { 866, RTL_TEXTENCODING_IBM_866 }, // OEM Cyrillic (Russian)
+ { 869, RTL_TEXTENCODING_IBM_869 }, // OEM Greek (Modern)
+ { 874, RTL_TEXTENCODING_MS_874 }, // MS Windows Thai
+ { 932, RTL_TEXTENCODING_MS_932 }, // MS Windows Japanese Shift-JIS
+ { 936, RTL_TEXTENCODING_MS_936 }, // MS Windows Chinese Simplified GBK
+ { 949, RTL_TEXTENCODING_MS_949 }, // MS Windows Korean (Wansung)
+ { 950, RTL_TEXTENCODING_MS_950 }, // MS Windows Chinese Traditional BIG5
+ { 1200, RTL_TEXTENCODING_DONTKNOW }, // Unicode (BIFF8) - return *_DONTKNOW to preserve old code page
+ { 1250, RTL_TEXTENCODING_MS_1250 }, // MS Windows Latin II (Central European)
+ { 1251, RTL_TEXTENCODING_MS_1251 }, // MS Windows Cyrillic
+ { 1252, RTL_TEXTENCODING_MS_1252 }, // MS Windows Latin I (BIFF4-BIFF8)
+ { 1253, RTL_TEXTENCODING_MS_1253 }, // MS Windows Greek
+ { 1254, RTL_TEXTENCODING_MS_1254 }, // MS Windows Turkish
+ { 1255, RTL_TEXTENCODING_MS_1255 }, // MS Windows Hebrew
+ { 1256, RTL_TEXTENCODING_MS_1256 }, // MS Windows Arabic
+ { 1257, RTL_TEXTENCODING_MS_1257 }, // MS Windows Baltic
+ { 1258, RTL_TEXTENCODING_MS_1258 }, // MS Windows Vietnamese
+ { 1361, RTL_TEXTENCODING_MS_1361 }, // MS Windows Korean (Johab)
+ { 10000, RTL_TEXTENCODING_APPLE_ROMAN }, // Apple Roman
+ { 32768, RTL_TEXTENCODING_APPLE_ROMAN }, // Apple Roman
+ { 32769, RTL_TEXTENCODING_MS_1252 } // MS Windows Latin I (BIFF2-BIFF3)
+};
+const XclCodePageEntry* const pCodePageTableEnd = std::end(pCodePageTable);
+
+struct XclCodePageEntry_CPPred
+{
+ explicit XclCodePageEntry_CPPred( sal_uInt16 nCodePage ) : mnCodePage( nCodePage ) {}
+ bool operator()( const XclCodePageEntry& rEntry ) const { return rEntry.mnCodePage == mnCodePage; }
+ sal_uInt16 mnCodePage;
+};
+
+struct XclCodePageEntry_TEPred
+{
+ explicit XclCodePageEntry_TEPred( rtl_TextEncoding eTextEnc ) : meTextEnc( eTextEnc ) {}
+ bool operator()( const XclCodePageEntry& rEntry ) const { return rEntry.meTextEnc == meTextEnc; }
+ rtl_TextEncoding meTextEnc;
+};
+
+} // namespace
+
+rtl_TextEncoding XclTools::GetTextEncoding( sal_uInt16 nCodePage )
+{
+ const XclCodePageEntry* pEntry = ::std::find_if( pCodePageTable, pCodePageTableEnd, XclCodePageEntry_CPPred( nCodePage ) );
+ if( pEntry == pCodePageTableEnd )
+ {
+ SAL_WARN("sc", "XclTools::GetTextEncoding - unknown code page: 0x" << std::hex << nCodePage );
+ return RTL_TEXTENCODING_DONTKNOW;
+ }
+ return pEntry->meTextEnc;
+}
+
+sal_uInt16 XclTools::GetXclCodePage( rtl_TextEncoding eTextEnc )
+{
+ if( eTextEnc == RTL_TEXTENCODING_UNICODE )
+ return 1200; // for BIFF8
+
+ const XclCodePageEntry* pEntry = ::std::find_if( pCodePageTable, pCodePageTableEnd, XclCodePageEntry_TEPred( eTextEnc ) );
+ if( pEntry == pCodePageTableEnd )
+ {
+ SAL_WARN("sc", "XclTools::GetXclCodePage - unsupported text encoding: 0x" << std::hex << eTextEnc );
+ return 1252;
+ }
+ return pEntry->mnCodePage;
+}
+
+OUString XclTools::GetXclFontName( const OUString& rFontName )
+{
+ // substitute with MS fonts
+ OUString aNewName = GetSubsFontName(rFontName, SubsFontFlags::ONLYONE | SubsFontFlags::MS);
+ return aNewName.isEmpty() ? rFontName : aNewName;
+}
+
+// built-in defined names
+const char maDefNamePrefix[] = "Excel_BuiltIn_"; /// Prefix for built-in defined names.
+const char maDefNamePrefixXml[] = "_xlnm."; /// Prefix for built-in defined names for OOX
+
+const char* const ppcDefNames[] =
+{
+ "Consolidate_Area",
+ "Auto_Open",
+ "Auto_Close",
+ "Extract",
+ "Database",
+ "Criteria",
+ "Print_Area",
+ "Print_Titles",
+ "Recorder",
+ "Data_Form",
+ "Auto_Activate",
+ "Auto_Deactivate",
+ "Sheet_Title",
+ "_FilterDatabase"
+};
+
+OUString XclTools::GetXclBuiltInDefName( sal_Unicode cBuiltIn )
+{
+ OSL_ENSURE( SAL_N_ELEMENTS( ppcDefNames ) == EXC_BUILTIN_UNKNOWN,
+ "XclTools::GetXclBuiltInDefName - built-in defined name list modified" );
+
+ if( cBuiltIn < SAL_N_ELEMENTS( ppcDefNames ) )
+ return OUString::createFromAscii(ppcDefNames[cBuiltIn]);
+ else
+ return OUString::number(cBuiltIn);
+}
+
+OUString XclTools::GetBuiltInDefName( sal_Unicode cBuiltIn )
+{
+ return maDefNamePrefix + GetXclBuiltInDefName(cBuiltIn);
+}
+
+OUString XclTools::GetBuiltInDefNameXml( sal_Unicode cBuiltIn )
+{
+ return maDefNamePrefixXml + GetXclBuiltInDefName(cBuiltIn);
+}
+
+sal_Unicode XclTools::GetBuiltInDefNameIndex( const OUString& rDefName )
+{
+ sal_Int32 nPrefixLen = 0;
+ if( rDefName.startsWithIgnoreAsciiCase( maDefNamePrefix ) )
+ nPrefixLen = strlen(maDefNamePrefix);
+ else if( rDefName.startsWithIgnoreAsciiCase( maDefNamePrefixXml ) )
+ nPrefixLen = strlen(maDefNamePrefixXml);
+ if( nPrefixLen > 0 )
+ {
+ for( sal_Unicode cBuiltIn = 0; cBuiltIn < EXC_BUILTIN_UNKNOWN; ++cBuiltIn )
+ {
+ OUString aBuiltInName(GetXclBuiltInDefName(cBuiltIn));
+ sal_Int32 nBuiltInLen = aBuiltInName.getLength();
+ if( rDefName.matchIgnoreAsciiCase( aBuiltInName, nPrefixLen ) )
+ {
+ // name can be followed by underline or space character
+ sal_Int32 nNextCharPos = nPrefixLen + nBuiltInLen;
+ sal_Unicode cNextChar = (rDefName.getLength() > nNextCharPos) ? rDefName[nNextCharPos] : '\0';
+ if( (cNextChar == '\0') || (cNextChar == ' ') || (cNextChar == '_') )
+ return cBuiltIn;
+ }
+ }
+ }
+ return EXC_BUILTIN_UNKNOWN;
+}
+
+// built-in style names
+
+const char maStyleNamePrefix1[] = "Excel_BuiltIn_"; /// Prefix for built-in cell style names.
+const char maStyleNamePrefix2[] = "Excel Built-in "; /// Prefix for built-in cell style names from OOX filter.
+
+const char* const ppcStyleNames[] =
+{
+ "", // "Normal" not used directly, but localized "Default"
+ "RowLevel_", // outline level will be appended
+ "ColumnLevel_", // outline level will be appended
+ "Comma",
+ "Currency",
+ "Percent",
+ "Comma_0",
+ "Currency_0",
+ "Hyperlink",
+ "Followed_Hyperlink"
+};
+
+OUString XclTools::GetBuiltInStyleName( sal_uInt8 nStyleId, std::u16string_view rName, sal_uInt8 nLevel )
+{
+ OUString aStyleName;
+
+ if( nStyleId == EXC_STYLE_NORMAL ) // "Normal" becomes "Default" style
+ {
+ aStyleName = ScResId( STR_STYLENAME_STANDARD );
+ }
+ else
+ {
+ OUStringBuffer aBuf(maStyleNamePrefix1);
+ if( nStyleId < SAL_N_ELEMENTS( ppcStyleNames ) )
+ aBuf.appendAscii(ppcStyleNames[nStyleId]);
+ else if (!rName.empty())
+ aBuf.append(rName);
+ else
+ aBuf.append(static_cast<sal_Int32>(nStyleId));
+
+ if( (nStyleId == EXC_STYLE_ROWLEVEL) || (nStyleId == EXC_STYLE_COLLEVEL) )
+ aBuf.append(static_cast<sal_Int32>(nLevel+1));
+
+ aStyleName = aBuf.makeStringAndClear();
+ }
+
+ return aStyleName;
+}
+
+bool XclTools::IsBuiltInStyleName( const OUString& rStyleName, sal_uInt8* pnStyleId, sal_Int32* pnNextChar )
+{
+ // "Default" becomes "Normal"
+ if (rStyleName == ScResId(STR_STYLENAME_STANDARD))
+ {
+ if( pnStyleId ) *pnStyleId = EXC_STYLE_NORMAL;
+ if( pnNextChar ) *pnNextChar = rStyleName.getLength();
+ return true;
+ }
+
+ // try the other built-in styles
+ sal_uInt8 nFoundId = 0;
+ sal_Int32 nNextChar = 0;
+
+ sal_Int32 nPrefixLen = 0;
+ if( rStyleName.startsWithIgnoreAsciiCase( maStyleNamePrefix1 ) )
+ nPrefixLen = strlen(maStyleNamePrefix1);
+ else if( rStyleName.startsWithIgnoreAsciiCase( maStyleNamePrefix2 ) )
+ nPrefixLen = strlen(maStyleNamePrefix2);
+ if( nPrefixLen > 0 )
+ {
+ for( sal_uInt8 nId = 0; nId < SAL_N_ELEMENTS( ppcStyleNames ); ++nId )
+ {
+ if( nId != EXC_STYLE_NORMAL )
+ {
+ OUString aShortName = OUString::createFromAscii(ppcStyleNames[nId]);
+ if( rStyleName.matchIgnoreAsciiCase( aShortName, nPrefixLen ) &&
+ (nNextChar < nPrefixLen + aShortName.getLength()))
+ {
+ nFoundId = nId;
+ nNextChar = nPrefixLen + aShortName.getLength();
+ }
+ }
+ }
+ }
+
+ if( nNextChar > 0 )
+ {
+ if( pnStyleId ) *pnStyleId = nFoundId;
+ if( pnNextChar ) *pnNextChar = nNextChar;
+ return true;
+ }
+
+ if( pnStyleId ) *pnStyleId = EXC_STYLE_USERDEF;
+ if( pnNextChar ) *pnNextChar = 0;
+ return nPrefixLen > 0; // also return true for unknown built-in styles
+}
+
+bool XclTools::GetBuiltInStyleId( sal_uInt8& rnStyleId, sal_uInt8& rnLevel, const OUString& rStyleName )
+{
+ sal_uInt8 nStyleId;
+ sal_Int32 nNextChar;
+ if( IsBuiltInStyleName( rStyleName, &nStyleId, &nNextChar ) && (nStyleId != EXC_STYLE_USERDEF) )
+ {
+ if( (nStyleId == EXC_STYLE_ROWLEVEL) || (nStyleId == EXC_STYLE_COLLEVEL) )
+ {
+ std::u16string_view aLevel = rStyleName.subView(nNextChar);
+ sal_Int32 nLevel = o3tl::toInt32(aLevel);
+ if (std::u16string_view(OUString::number(nLevel)) == aLevel
+ && nLevel > 0 && nLevel <= EXC_STYLE_LEVELCOUNT)
+ {
+ rnStyleId = nStyleId;
+ rnLevel = static_cast< sal_uInt8 >( nLevel - 1 );
+ return true;
+ }
+ }
+ else if( rStyleName.getLength() == nNextChar )
+ {
+ rnStyleId = nStyleId;
+ rnLevel = EXC_STYLE_NOLEVEL;
+ return true;
+ }
+ }
+
+ rnStyleId = EXC_STYLE_USERDEF;
+ rnLevel = EXC_STYLE_NOLEVEL;
+ return false;
+}
+
+// conditional formatting style names
+
+const char maCFStyleNamePrefix1[] = "Excel_CondFormat_"; /// Prefix for cond. formatting style names.
+const char maCFStyleNamePrefix2[] = "ConditionalStyle_"; /// Prefix for cond. formatting style names from OOX filter.
+const char maCFStyleNamePrefix3[] = "ExtConditionalStyle_";
+
+OUString XclTools::GetCondFormatStyleName( SCTAB nScTab, sal_Int32 nFormat, sal_uInt16 nCondition )
+{
+ return maCFStyleNamePrefix1 +
+ OUString::number(static_cast<sal_Int32>(nScTab+1)) +
+ "_" +
+ OUString::number(static_cast<sal_Int32>(nFormat+1)) +
+ "_" +
+ OUString::number(static_cast<sal_Int32>(nCondition+1));
+}
+
+bool XclTools::IsCondFormatStyleName( const OUString& rStyleName )
+{
+ if( rStyleName.startsWithIgnoreAsciiCase( maCFStyleNamePrefix1 ) )
+ return true;
+
+ if( rStyleName.startsWithIgnoreAsciiCase( maCFStyleNamePrefix2 ) )
+ return true;
+
+ if (rStyleName.startsWithIgnoreAsciiCase(maCFStyleNamePrefix3))
+ return true;
+
+ return false;
+}
+
+// stream handling
+
+void XclTools::SkipSubStream( XclImpStream& rStrm )
+{
+ bool bLoop = true;
+ while( bLoop && rStrm.StartNextRecord() )
+ {
+ sal_uInt16 nRecId = rStrm.GetRecId();
+ bLoop = nRecId != EXC_ID_EOF;
+ if( (nRecId == EXC_ID2_BOF) || (nRecId == EXC_ID3_BOF) || (nRecId == EXC_ID4_BOF) || (nRecId == EXC_ID5_BOF) )
+ SkipSubStream( rStrm );
+ }
+}
+
+// Basic macro names
+
+const char maSbMacroPrefix[] = "vnd.sun.star.script:"; /// Prefix for StarBasic macros.
+const char maSbMacroSuffix[] = "?language=Basic&location=document"; /// Suffix for StarBasic macros.
+
+OUString XclTools::GetSbMacroUrl( const OUString& rMacroName, SfxObjectShell* pDocShell )
+{
+ OSL_ENSURE( !rMacroName.isEmpty(), "XclTools::GetSbMacroUrl - macro name is empty" );
+ ::ooo::vba::MacroResolvedInfo aMacroInfo = ::ooo::vba::resolveVBAMacro( pDocShell, rMacroName );
+ if( aMacroInfo.mbFound )
+ return ::ooo::vba::makeMacroURL( aMacroInfo.msResolvedMacro );
+ return OUString();
+}
+
+OUString XclTools::GetXclMacroName( const OUString& rSbMacroUrl )
+{
+ sal_Int32 nSbMacroUrlLen = rSbMacroUrl.getLength();
+ sal_Int32 nMacroNameLen = nSbMacroUrlLen - strlen(maSbMacroPrefix) - strlen(maSbMacroSuffix);
+ if( (nMacroNameLen > 0) && rSbMacroUrl.startsWithIgnoreAsciiCase( maSbMacroPrefix ) &&
+ rSbMacroUrl.endsWithIgnoreAsciiCase( maSbMacroSuffix ) )
+ {
+ sal_Int32 nPrjDot = rSbMacroUrl.indexOf( '.', strlen(maSbMacroPrefix) ) + 1;
+ return rSbMacroUrl.copy( nPrjDot, nSbMacroUrlLen - nPrjDot - strlen(maSbMacroSuffix) );
+ }
+ return OUString();
+}
+
+// read/write colors
+
+XclImpStream& operator>>( XclImpStream& rStrm, Color& rColor )
+{
+ sal_uInt8 nR = rStrm.ReaduInt8();
+ sal_uInt8 nG = rStrm.ReaduInt8();
+ sal_uInt8 nB = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );//nD
+ rColor = Color( nR, nG, nB );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const Color& rColor )
+{
+ return rStrm << rColor.GetRed() << rColor.GetGreen() << rColor.GetBlue() << sal_uInt8( 0 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xltracer.cxx b/sc/source/filter/excel/xltracer.cxx
new file mode 100644
index 000000000..c6931dbf0
--- /dev/null
+++ b/sc/source/filter/excel/xltracer.cxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xltracer.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <address.hxx>
+
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::beans::PropertyValue;
+
+XclTracer::XclTracer(std::u16string_view /*rDocUrl*/)
+ : mbEnabled(false)
+ , maFirstTimes(eTraceLength, true)
+{
+}
+
+XclTracer::~XclTracer() {}
+
+void XclTracer::ProcessTraceOnce(XclTracerId eProblem)
+{
+ if (mbEnabled && maFirstTimes[eProblem])
+ {
+ maFirstTimes[eProblem] = false;
+ }
+}
+
+void XclTracer::TraceInvalidAddress(const ScAddress& rPos, const ScAddress& rMaxPos)
+{
+ TraceInvalidRow(rPos.Row(), rMaxPos.Row());
+ TraceInvalidTab(rPos.Tab(), rMaxPos.Tab());
+}
+
+void XclTracer::TraceInvalidRow(sal_uInt32 nRow, sal_uInt32 nMaxRow)
+{
+ if (nRow > nMaxRow)
+ ProcessTraceOnce(eRowLimitExceeded);
+}
+
+void XclTracer::TraceInvalidTab(SCTAB nTab, SCTAB nMaxTab)
+{
+ if (nTab > nMaxTab)
+ ProcessTraceOnce(eTabLimitExceeded);
+}
+
+void XclTracer::TracePrintRange() { ProcessTraceOnce(ePrintRange); }
+
+void XclTracer::TraceDates(sal_uInt16 nNumFmt)
+{
+ // Short Date = 14 and Short Date+Time = 22
+ if (nNumFmt == 14 || nNumFmt == 22)
+ ProcessTraceOnce(eShortDate);
+}
+
+void XclTracer::TraceBorderLineStyle(bool bBorderLineStyle)
+{
+ if (bBorderLineStyle)
+ ProcessTraceOnce(eBorderLineStyle);
+}
+
+void XclTracer::TraceFillPattern(bool bFillPattern)
+{
+ if (bFillPattern)
+ ProcessTraceOnce(eFillPattern);
+}
+
+void XclTracer::TraceFormulaMissingArg()
+{
+ // missing parameter in Formula record
+ ProcessTraceOnce(eFormulaMissingArg);
+}
+
+void XclTracer::TracePivotDataSource(bool bExternal)
+{
+ if (bExternal)
+ ProcessTraceOnce(ePivotDataSource);
+}
+
+void XclTracer::TracePivotChartExists()
+{
+ // Pivot Charts not currently displayed.
+ ProcessTraceOnce(ePivotChartExists);
+}
+
+void XclTracer::TraceChartUnKnownType() { ProcessTraceOnce(eChartUnKnownType); }
+
+void XclTracer::TraceChartOnlySheet() { ProcessTraceOnce(eChartOnlySheet); }
+
+void XclTracer::TraceChartDataTable()
+{
+ // Data table is not supported.
+ ProcessTraceOnce(eChartDataTable);
+}
+
+void XclTracer::TraceChartLegendPosition()
+{
+ // If position is set to "not docked or inside the plot area" then
+ // we cannot guarantee the legend position.
+ ProcessTraceOnce(eChartLegendPosition);
+}
+
+void XclTracer::TraceUnsupportedObjects()
+{
+ // Called from Excel 5.0 - limited Graphical object support.
+ ProcessTraceOnce(eUnsupportedObject);
+}
+
+void XclTracer::TraceObjectNotPrintable() { ProcessTraceOnce(eObjectNotPrintable); }
+
+void XclTracer::TraceDVType(bool bType)
+{
+ // Custom types work if 'Data->validity dialog' is not OKed.
+ if (bType)
+ ProcessTraceOnce(eDVType);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlview.cxx b/sc/source/filter/excel/xlview.cxx
new file mode 100644
index 000000000..b5768e2df
--- /dev/null
+++ b/sc/source/filter/excel/xlview.cxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlview.hxx>
+#include <osl/diagnose.h>
+
+// Structs ====================================================================
+
+XclDocViewData::XclDocViewData() :
+ mnWinX( 0 ),
+ mnWinY( 0 ),
+ mnWinWidth( 0 ),
+ mnWinHeight( 0 ),
+ mnFlags( EXC_WIN1_HOR_SCROLLBAR | EXC_WIN1_VER_SCROLLBAR | EXC_WIN1_TABBAR ),
+ mnDisplXclTab( 0 ),
+ mnFirstVisXclTab( 0 ),
+ mnXclSelectCnt( 1 ),
+ mnTabBarWidth( 600 )
+{
+}
+
+XclTabViewData::XclTabViewData() :
+ maFirstXclPos( ScAddress::UNINITIALIZED ),
+ maSecondXclPos( ScAddress::UNINITIALIZED )
+{
+ SetDefaults();
+}
+
+XclTabViewData::~XclTabViewData()
+{
+}
+
+void XclTabViewData::SetDefaults()
+{
+ maSelMap.clear();
+ maGridColor = COL_AUTO;
+ maFirstXclPos.Set( 0, 0 );
+ maSecondXclPos.Set( 0, 0 );
+ mnSplitX = mnSplitY = 0;
+ mnNormalZoom = EXC_WIN2_NORMALZOOM_DEF;
+ mnPageZoom = EXC_WIN2_PAGEZOOM_DEF;
+ mnCurrentZoom = 0; // default to mnNormalZoom or mnPageZoom
+ mnActivePane = EXC_PANE_TOPLEFT;
+ mbSelected = mbDisplayed = false;
+ mbMirrored = false;
+ mbFrozenPanes = false;
+ mbPageMode = false;
+ mbDefGridColor = true;
+ mbShowFormulas = false;
+ mbShowGrid = mbShowHeadings = mbShowZeros = mbShowOutline = true;
+ maTabBgColor = COL_AUTO;
+ mnTabBgColorId = 0;
+}
+
+bool XclTabViewData::IsSplit() const
+{
+ return (mnSplitX > 0) || (mnSplitY > 0);
+}
+
+bool XclTabViewData::HasPane( sal_uInt8 nPaneId ) const
+{
+ switch( nPaneId )
+ {
+ case EXC_PANE_BOTTOMRIGHT: return (mnSplitX > 0) && (mnSplitY > 0);
+ case EXC_PANE_TOPRIGHT: return mnSplitX > 0;
+ case EXC_PANE_BOTTOMLEFT: return mnSplitY > 0;
+ case EXC_PANE_TOPLEFT: return true;
+ }
+ OSL_FAIL( "XclExpPane::HasPane - wrong pane ID" );
+ return false;
+}
+
+const XclSelectionData* XclTabViewData::GetSelectionData( sal_uInt8 nPane ) const
+{
+ XclSelectionMap::const_iterator aIt = maSelMap.find( nPane );
+ return (aIt == maSelMap.end()) ? nullptr : aIt->second.get();
+}
+
+XclSelectionData& XclTabViewData::CreateSelectionData( sal_uInt8 nPane )
+{
+ XclSelectionDataRef& rxSelData = maSelMap[ nPane ];
+ if( !rxSelData )
+ rxSelData = std::make_shared<XclSelectionData>();
+ return *rxSelData;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */