From d3483180e684ab8272b30676a395a53c9c7c6ad2 Mon Sep 17 00:00:00 2001 From: Eike Rathke Date: Tue, 5 Aug 2014 22:51:01 +0200 Subject: write OOXML externalReferences, externalLinks, fdo#45286 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This for the first time writes external references (hopefully) correctly and adds the necessary relationship streams and the externalLink streams with sheetData. At least Excel 2013 loaded the result without complaining, so do we. (cherry picked from commit 8c23a767d926d8d08213f5e2f8e81775c653cbd7) Conflicts: sc/source/filter/excel/xelink.cxx do not write empty externalReferences element, fdo#45286 follow-up OOXML validation spewed - (xl/workbook.xml:2 col:700) cvc-complex-type.2.4.b: The content of element ´externalReferences´ is not complete. One of ´{"http://schemas.openxmlformats.org/spreadsheetml/2006/main":externalReference}´ is expected. (cherry picked from commit 8c51532289db36337c4984baec1bf5a7ca9e40af) really do not write empty externalReferences element, fdo#45286 follow-up Xti records exist also for internal sheet references in BIFF. It is not enough to check for their presence, at least one EXC_SBTYPE_EXTERN must be present as SUPBOOK. (cherry picked from commit e04a4d2d2c18134556bb4e001d497ca7d66c6828) Change-Id: I3d615490a60c5420ae13c0bfc6297642d86a07b9 Reviewed-on: https://gerrit.libreoffice.org/10872 Reviewed-by: Markus Mohrhard Tested-by: Markus Mohrhard --- sc/inc/compiler.hxx | 7 +- sc/source/core/tool/compiler.cxx | 90 ++++++-- sc/source/core/tool/token.cxx | 7 +- sc/source/filter/excel/excdoc.cxx | 1 + sc/source/filter/excel/ooxml-export-TODO.txt | 2 - sc/source/filter/excel/xecontent.cxx | 2 +- sc/source/filter/excel/xelink.cxx | 313 +++++++++++++++++++++++++-- sc/source/filter/inc/xecontent.hxx | 7 +- sc/source/filter/inc/xelink.hxx | 3 + 9 files changed, 387 insertions(+), 45 deletions(-) (limited to 'sc') diff --git a/sc/inc/compiler.hxx b/sc/inc/compiler.hxx index 61008358b235..18d788aab3c4 100644 --- a/sc/inc/compiler.hxx +++ b/sc/inc/compiler.hxx @@ -256,15 +256,16 @@ public: const ::com::sun::star::uno::Sequence< com::sun::star::sheet::ExternalLinkInfo>* pExternalLinks ) const = 0; - virtual OUString makeExternalNameStr( const OUString& rFile, const OUString& rName ) const = 0; + virtual OUString makeExternalNameStr( sal_uInt16 nFileId, const OUString& rFile, + const OUString& rName ) const = 0; virtual void makeExternalRefStr( - OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName, + OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& rFileName, const OUString& rTabName, const ScSingleRefData& rRef ) const = 0; virtual void makeExternalRefStr( OUStringBuffer& rBuffer, const ScAddress& rPos, - const OUString& rFileName, const std::vector& rTabNames, + sal_uInt16 nFileId, const OUString& rFileName, const std::vector& rTabNames, const OUString& rTabName, const ScComplexRefData& rRef ) const = 0; enum SpecialSymbolType diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx index 8031f8eef87c..bfd8802edef7 100644 --- a/sc/source/core/tool/compiler.cxx +++ b/sc/source/core/tool/compiler.cxx @@ -823,7 +823,8 @@ struct ConventionOOO_A1 : public Convention_A1 return lcl_parseExternalName(rSymbol, rFile, rName, '#', pDoc, pExternalLinks); } - virtual OUString makeExternalNameStr( const OUString& rFile, const OUString& rName ) const SAL_OVERRIDE + virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile, + const OUString& rName ) const SAL_OVERRIDE { return lcl_makeExternalNameStr( rFile, rName, '#', false); } @@ -874,7 +875,7 @@ struct ConventionOOO_A1 : public Convention_A1 } virtual void makeExternalRefStr( - OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName, + OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, const OUString& rTabName, const ScSingleRefData& rRef ) const SAL_OVERRIDE { makeExternalRefStrImpl(rBuffer, rPos, rFileName, rTabName, rRef, false); @@ -921,7 +922,7 @@ struct ConventionOOO_A1 : public Convention_A1 } virtual void makeExternalRefStr( - OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName, + OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, const std::vector& rTabNames, const OUString& rTabName, const ScComplexRefData& rRef ) const SAL_OVERRIDE { @@ -966,20 +967,21 @@ struct ConventionOOO_A1_ODF : public ConventionOOO_A1 rBuffer.append(']'); } - virtual OUString makeExternalNameStr( const OUString& rFile, const OUString& rName ) const SAL_OVERRIDE + virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile, + const OUString& rName ) const SAL_OVERRIDE { return lcl_makeExternalNameStr( rFile, rName, '#', true); } virtual void makeExternalRefStr( - OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName, + OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, const OUString& rTabName, const ScSingleRefData& rRef ) const SAL_OVERRIDE { makeExternalRefStrImpl(rBuffer, rPos, rFileName, rTabName, rRef, true); } virtual void makeExternalRefStr( - OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName, + OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, const std::vector& rTabNames, const OUString& rTabName, const ScComplexRefData& rRef ) const SAL_OVERRIDE { @@ -1264,13 +1266,14 @@ struct ConventionXL_A1 : public Convention_A1, public ConventionXL return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks); } - virtual OUString makeExternalNameStr( const OUString& rFile, const OUString& rName ) const SAL_OVERRIDE + virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile, + const OUString& rName ) const SAL_OVERRIDE { return ConventionXL::makeExternalNameStr(rFile, rName); } virtual void makeExternalRefStr( - OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName, + OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, const OUString& rTabName, const ScSingleRefData& rRef ) const SAL_OVERRIDE { // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1 @@ -1287,7 +1290,7 @@ struct ConventionXL_A1 : public Convention_A1, public ConventionXL } virtual void makeExternalRefStr( - OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName, + OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, const std::vector& rTabNames, const OUString& rTabName, const ScComplexRefData& rRef ) const SAL_OVERRIDE { @@ -1309,6 +1312,57 @@ struct ConventionXL_A1 : public Convention_A1, public ConventionXL struct ConventionXL_OOX : public ConventionXL_A1 { ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { } + + virtual OUString makeExternalNameStr( sal_uInt16 nFileId, const OUString& /*rFile*/, + const OUString& rName ) const SAL_OVERRIDE + { + // [N]!DefinedName is a workbook global name. + return OUString( "[" + OUString::number(nFileId+1) + "]!" + rName ); + + /* TODO: add support for sheet local names, would be + * [N]'Sheet Name'!DefinedName + * Similar to makeExternalRefStr() but with DefinedName instead of + * CellStr. */ + } + + virtual void makeExternalRefStr( + OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/, + const OUString& rTabName, const ScSingleRefData& rRef ) const SAL_OVERRIDE + { + // [N]'Sheet Name'!$A$1 + // Where N is a 1-based positive integer number of a file name in OOXML + // xl/externalLinks/externalLinkN.xml + + ConventionXL_OOX::makeExternalDocStr(rBuffer, nFileId); + ScRangeStringConverter::AppendTableName(rBuffer, rTabName); + rBuffer.append('!'); + + makeSingleCellStr(rBuffer, rRef, rRef.toAbs(rPos)); + } + + virtual void makeExternalRefStr( + OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/, + const std::vector& rTabNames, const OUString& rTabName, + const ScComplexRefData& rRef ) const SAL_OVERRIDE + { + ScRange aAbsRef = rRef.toAbs(rPos); + + ConventionXL_OOX::makeExternalDocStr(rBuffer, nFileId); + ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef); + rBuffer.append('!'); + + makeSingleCellStr(rBuffer, rRef.Ref1, aAbsRef.aStart); + if (aAbsRef.aStart != aAbsRef.aEnd) + { + rBuffer.append(':'); + makeSingleCellStr(rBuffer, rRef.Ref2, aAbsRef.aEnd); + } + } + + static void makeExternalDocStr( OUStringBuffer& rBuffer, sal_uInt16 nFileId ) + { + rBuffer.append('[').append( OUString::number( nFileId+1)).append(']'); + } }; static void @@ -1438,13 +1492,14 @@ struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks); } - virtual OUString makeExternalNameStr( const OUString& rFile, const OUString& rName ) const SAL_OVERRIDE + virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile, + const OUString& rName ) const SAL_OVERRIDE { return ConventionXL::makeExternalNameStr(rFile, rName); } virtual void makeExternalRefStr( - OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName, + OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, const OUString& rTabName, const ScSingleRefData& rRef ) const SAL_OVERRIDE { // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1 @@ -1463,7 +1518,7 @@ struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL } virtual void makeExternalRefStr( - OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName, + OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, const std::vector& rTabNames, const OUString& rTabName, const ScComplexRefData& rRef ) const SAL_OVERRIDE { @@ -4082,30 +4137,31 @@ bool ScCompiler::IsCharFlagAllConventions( void ScCompiler::CreateStringFromExternal(OUStringBuffer& rBuffer, FormulaToken* pTokenP) const { FormulaToken* t = pTokenP; + sal_uInt16 nFileId = t->GetIndex(); ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); - const OUString* pFileName = pRefMgr->getExternalFileName(t->GetIndex()); + const OUString* pFileName = pRefMgr->getExternalFileName(nFileId); if (!pFileName) return; switch (t->GetType()) { case svExternalName: - rBuffer.append(pConv->makeExternalNameStr(*pFileName, t->GetString().getString())); + rBuffer.append(pConv->makeExternalNameStr( nFileId, *pFileName, t->GetString().getString())); break; case svExternalSingleRef: pConv->makeExternalRefStr( - rBuffer, GetPos(), *pFileName, t->GetString().getString(), + rBuffer, GetPos(), nFileId, *pFileName, t->GetString().getString(), static_cast(t)->GetSingleRef()); break; case svExternalDoubleRef: { vector aTabNames; - pRefMgr->getAllCachedTableNames(t->GetIndex(), aTabNames); + pRefMgr->getAllCachedTableNames(nFileId, aTabNames); if (aTabNames.empty()) return; pConv->makeExternalRefStr( - rBuffer, GetPos(), *pFileName, aTabNames, t->GetString().getString(), + rBuffer, GetPos(), nFileId, *pFileName, aTabNames, t->GetString().getString(), static_cast(t)->GetDoubleRef()); } break; diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx index 4deb0babb413..8263380518d7 100644 --- a/sc/source/core/tool/token.cxx +++ b/sc/source/core/tool/token.cxx @@ -3715,11 +3715,11 @@ void appendTokenByType( sc::TokenStringContext& rCxt, OUStringBuffer& rBuf, cons switch (rToken.GetType()) { case svExternalName: - rBuf.append(rCxt.mpRefConv->makeExternalNameStr(aFileName, aTabName)); + rBuf.append(rCxt.mpRefConv->makeExternalNameStr(nFileId, aFileName, aTabName)); break; case svExternalSingleRef: rCxt.mpRefConv->makeExternalRefStr( - rBuf, rPos, aFileName, aTabName, static_cast(rToken).GetSingleRef()); + rBuf, rPos, nFileId, aFileName, aTabName, static_cast(rToken).GetSingleRef()); break; case svExternalDoubleRef: { @@ -3730,7 +3730,8 @@ void appendTokenByType( sc::TokenStringContext& rCxt, OUStringBuffer& rBuf, cons return; rCxt.mpRefConv->makeExternalRefStr( - rBuf, rPos, aFileName, it->second, aTabName, static_cast(rToken).GetDoubleRef()); + rBuf, rPos, nFileId, aFileName, it->second, aTabName, + static_cast(rToken).GetDoubleRef()); } break; default: diff --git a/sc/source/filter/excel/excdoc.cxx b/sc/source/filter/excel/excdoc.cxx index 40ed956b624e..d5bc941e7154 100644 --- a/sc/source/filter/excel/excdoc.cxx +++ b/sc/source/filter/excel/excdoc.cxx @@ -377,6 +377,7 @@ void ExcTable::FillAsHeader( ExcBoundsheetList& rBoundsheetList ) // 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 ) ); diff --git a/sc/source/filter/excel/ooxml-export-TODO.txt b/sc/source/filter/excel/ooxml-export-TODO.txt index 9926cfdabfbd..c72b956623bb 100644 --- a/sc/source/filter/excel/ooxml-export-TODO.txt +++ b/sc/source/filter/excel/ooxml-export-TODO.txt @@ -30,8 +30,6 @@ Elements: - Workbook (§3.2): - customWorkbookViews (§3.2.3) - ext (§3.2.7) - - externalReference (§3.2.8) - - externalReferences (§3.2.9) - extLst (§3.2.10) - fileRecoveryPr (§3.2.11) [ CRASHRECERR? 865h ] - fileSharing (§3.2.12) [ FILESHARING 5Bh ] diff --git a/sc/source/filter/excel/xecontent.cxx b/sc/source/filter/excel/xecontent.cxx index ab47be4d5cce..f0e8a079e502 100644 --- a/sc/source/filter/excel/xecontent.cxx +++ b/sc/source/filter/excel/xecontent.cxx @@ -444,7 +444,7 @@ XclExpHyperlink::~XclExpHyperlink() } OUString XclExpHyperlink::BuildFileName( - sal_uInt16& rnLevel, bool& rbRel, const OUString& rUrl, const XclExpRoot& rRoot ) const + sal_uInt16& rnLevel, bool& rbRel, const OUString& rUrl, const XclExpRoot& rRoot ) { OUString aDosName( INetURLObject( rUrl ).getFSysPath( INetURLObject::FSYS_DOS ) ); rnLevel = 0; diff --git a/sc/source/filter/excel/xelink.cxx b/sc/source/filter/excel/xelink.cxx index 944a0723cf9b..234e65b4c225 100644 --- a/sc/source/filter/excel/xelink.cxx +++ b/sc/source/filter/excel/xelink.cxx @@ -27,6 +27,7 @@ #include "scextopt.hxx" #include "externalrefmgr.hxx" #include "tokenarray.hxx" +#include "xecontent.hxx" #include #include @@ -36,6 +37,7 @@ using ::std::find_if; using ::std::vector; using ::com::sun::star::uno::Any; +using namespace oox; // *** Helper classes *** @@ -160,6 +162,9 @@ public: /** 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 ) SAL_OVERRIDE; + private: virtual void WriteBody( XclExpStream& rStrm ) SAL_OVERRIDE; @@ -177,6 +182,8 @@ private: SCROW mnScRow; /// Row index of the external cells. }; +namespace { class XclExpCrnList; } + /** Represents the record XCT which is the header record of a CRN record list. */ class XclExpXct : public XclExpRecordBase, protected XclExpRoot @@ -198,12 +205,18 @@ public: /** Writes the XCT and all CRN records. */ virtual void Save( XclExpStream& rStrm ) SAL_OVERRIDE; + /** Writes the sheetDataSet and child elements. */ + virtual void SaveXml( XclExpXmlStream& rStrm ) SAL_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 ======================= @@ -224,8 +237,10 @@ public: protected: /** Creates and returns the list of EXTERNNAME records. */ XclExpExtNameBuffer& GetExtNameBuffer(); - /** Creates and returns the list of EXTERNNAME records. */ + /** Writes the list of EXTERNNAME records. */ void WriteExtNameBuffer( XclExpStream& rStrm ); + /** Writes the list of externalName elements. */ + void WriteExtNameBufferXml( XclExpXmlStream& rStrm ); private: typedef boost::shared_ptr< XclExpExtNameBuffer > XclExpExtNameBfrRef; @@ -309,9 +324,21 @@ public: sal_uInt16 InsertExtName( const OUString& rName, const ScExternalRefCache::TokenArrayRef pArray ); + /** 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 ) SAL_OVERRIDE; + /** Writes the externalBook and all child elements. */ + virtual void SaveXml( XclExpXmlStream& rStrm ) SAL_OVERRIDE; + private: /** Returns the sheet name inside of this SUPBOOK. */ const XclExpString* GetTabName( sal_uInt16 nSBTab ) const; @@ -329,6 +356,7 @@ private: 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 ================================================= @@ -403,6 +431,12 @@ public: /** Writes all SUPBOOK records with their sub records. */ virtual void Save( XclExpStream& rStrm ) SAL_OVERRIDE; + /** Writes all externalBook elements with their child elements to OOXML. */ + virtual void SaveXml( XclExpXmlStream& rStrm ) SAL_OVERRIDE; + + /** Whether we need to write externalReferences or not. */ + bool HasExternalReferences() const; + struct XclExpSBIndex { sal_uInt16 mnSupbook; /// SUPBOOK index for an Excel sheet. @@ -486,6 +520,9 @@ public: /** 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 ); }; @@ -530,6 +567,8 @@ public: virtual void Save( XclExpStream& rStrm ) SAL_OVERRIDE; + virtual void SaveXml( XclExpXmlStream& rStrm ) SAL_OVERRIDE; + private: typedef XclExpRecordList< XclExpExternSheet > XclExpExtSheetList; typedef XclExpExtSheetList::RecordRefType XclExpExtSheetRef; @@ -558,7 +597,7 @@ private: XclExpCodeMap maCodeMap; /// Maps special external codes to EXTERNSHEET records. }; -/** Implementation of the link manager for BIFF8. */ +/** Implementation of the link manager for BIFF8 and OOXML. */ class XclExpLinkManagerImpl8 : public XclExpLinkManagerImpl { public: @@ -597,6 +636,8 @@ public: virtual void Save( XclExpStream& rStrm ) SAL_OVERRIDE; + virtual void SaveXml( XclExpXmlStream& rStrm ) SAL_OVERRIDE; + private: /** Searches for or inserts a new XTI structure. @return The 0-based list index of the XTI structure. */ @@ -1152,6 +1193,69 @@ void XclExpCrn::WriteEmpty( XclExpStream& rStrm ) rStrm.WriteZeroBytes( 8 ); } +void XclExpCrn::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr pFS = rStrm.GetCurrentStream(); + + pFS->startElement( XML_row, + XML_r, OString::number( mnScRow + 1 ).getStr(), + FSEND); + + ScAddress aAdr( mnScCol, mnScRow, 0); // Tab number doesn't matter + for( CachedValues::iterator aIt = maValues.begin(), aEnd = maValues.end(); aIt != aEnd; ++aIt, aAdr.IncCol() ) + { + if( aIt->has< double >() ) + { + double fVal = aIt->get< double >(); + if (rtl::math::isFinite( fVal)) + { + // t='n' is omitted + pFS->startElement( XML_cell, + XML_r, XclXmlUtils::ToOString( aAdr), + FSEND); + pFS->startElement( XML_v, FSEND ); + pFS->write( fVal ); + } + else + { + pFS->startElement( XML_cell, + XML_r, XclXmlUtils::ToOString( aAdr), + XML_t, "e", + FSEND); + pFS->startElement( XML_v, FSEND ); + pFS->write( "#VALUE!" ); // OOXTODO: support other error values + } + } + else if( aIt->has< OUString >() ) + { + pFS->startElement( XML_cell, + XML_r, XclXmlUtils::ToOString( aAdr), + XML_t, "str", + FSEND); + pFS->startElement( XML_v, FSEND ); + pFS->write( aIt->get< OUString >() ); + } + else if( aIt->has< bool >() ) + { + pFS->startElement( XML_cell, + XML_r, XclXmlUtils::ToOString( aAdr), + XML_t, "b", + FSEND); + pFS->startElement( XML_v, FSEND ); + pFS->write( aIt->get< bool >() ? "1" : "0" ); + } + // OOXTODO: error type cell t='e' + else + { + // Empty/blank cell not stored, only aAdr is incremented. + } + pFS->endElement( XML_v ); + pFS->endElement( XML_cell); + } + + pFS->endElement( XML_row); +} + // Cached cells of a sheet ==================================================== XclExpXct::XclExpXct( const XclExpRoot& rRoot, const OUString& rTabName, @@ -1211,10 +1315,10 @@ bool XclExpCrnList::InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue ) } // namespace -void XclExpXct::Save( XclExpStream& rStrm ) +bool XclExpXct::BuildCrnList( XclExpCrnList& rCrnRecs ) { if( !mxCacheTable ) - return; + 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 @@ -1222,24 +1326,23 @@ void XclExpXct::Save( XclExpStream& rStrm ) formulas. */ ::std::pair< SCROW, SCROW > aRowRange = mxCacheTable->getRowRange(); if( aRowRange.first >= aRowRange.second ) - return; + 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; + 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; + 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 strem back after writing the CRN records is not an option). */ - XclExpCrnList aCrnRecs; SvNumberFormatter& rFormatter = GetFormatter(); bool bValid = true; for( SCROW nScRow = nScRow1; bValid && (nScRow <= nScRow2); ++nScRow ) @@ -1256,13 +1359,13 @@ void XclExpXct::Save( XclExpStream& rStrm ) { case svDouble: bValid = (rFormatter.GetType( nScNumFmt ) == NUMBERFORMAT_LOGICAL) ? - aCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetDouble() != 0 ) ) : - aCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetDouble() ) ); + 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 = aCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetString().getString() ) ); + bValid = rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetString().getString() ) ); break; default: break; @@ -1270,6 +1373,14 @@ void XclExpXct::Save( XclExpStream& rStrm ) } } } + 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 ); @@ -1278,6 +1389,22 @@ void XclExpXct::Save( XclExpStream& rStrm ) aCrnRecs.Save( rStrm ); } +void XclExpXct::SaveXml( XclExpXmlStream& rStrm ) +{ + XclExpCrnList aCrnRecs; + if (!BuildCrnList( aCrnRecs)) + return; + + sax_fastparser::FSHelperPtr pFS = rStrm.GetCurrentStream(); + + pFS->startElement( XML_sheetData, + XML_sheetId, OString::number( mnSBTab).getStr(), + FSEND); + // 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 ) : @@ -1299,6 +1426,12 @@ void XclExpExternSheetBase::WriteExtNameBuffer( XclExpStream& rStrm ) 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 ) : @@ -1349,14 +1482,16 @@ void XclExpExternSheet::WriteBody( XclExpStream& rStrm ) XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, sal_uInt16 nXclTabCount ) : XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ), meType( EXC_SBTYPE_SELF ), - mnXclTabCount( nXclTabCount ) + mnXclTabCount( nXclTabCount ), + mnFileId( 0 ) { } XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot ) : XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ), meType( EXC_SBTYPE_ADDIN ), - mnXclTabCount( 1 ) + mnXclTabCount( 1 ), + mnFileId( 0 ) { } @@ -1365,7 +1500,8 @@ XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl, Xcl maUrl( rUrl ), maUrlEncoded( rUrl ), meType( EXC_SBTYPE_EUROTOOL ), - mnXclTabCount( 0 ) + mnXclTabCount( 0 ), + mnFileId( 0 ) { SetRecSize( 2 + maUrlEncoded.GetSize() ); } @@ -1376,13 +1512,15 @@ XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl ) : maUrl( rUrl ), maUrlEncoded( XclExpUrlHelper::EncodeUrl( rRoot, rUrl ) ), meType( EXC_SBTYPE_EXTERN ), - mnXclTabCount( 0 ) + 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 ); for( ScfStringVec::const_iterator aBeg = aTabNames.begin(), aIt = aBeg, aEnd = aTabNames.end(); aIt != aEnd; ++aIt ) @@ -1395,7 +1533,8 @@ XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rApplic, maDdeTopic( rTopic ), maUrlEncoded( XclExpUrlHelper::EncodeDde( rApplic, rTopic ) ), meType( EXC_SBTYPE_SPECIAL ), - mnXclTabCount( 0 ) + mnXclTabCount( 0 ), + mnFileId( 0 ) { SetRecSize( 2 + maUrlEncoded.GetSize() ); } @@ -1486,6 +1625,21 @@ sal_uInt16 XclExpSupbook::InsertExtName( const OUString& rName, const ScExternal return GetExtNameBuffer().InsertExtName(*this, rName, pArray); } +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 @@ -1496,6 +1650,48 @@ void XclExpSupbook::Save( XclExpStream& rStrm ) 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; + OUString sId = rStrm.addRelation( pExternalLink->getOutputStream(), + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLinkPath", + XclExpHyperlink::BuildFileName( nLevel, bRel, maUrl, GetRoot()), + true ); + + pExternalLink->startElement( XML_externalLink, + XML_xmlns, "http://schemas.openxmlformats.org/spreadsheetml/2006/main", + FSEND); + + pExternalLink->startElement( XML_externalBook, + FSNS(XML_xmlns, XML_r), "http://schemas.openxmlformats.org/officeDocument/2006/relationships", + FSNS(XML_r, XML_id), XclXmlUtils::ToOString( sId ).getStr(), + FSEND); + + pExternalLink->startElement( XML_sheetNames, FSEND); + for (size_t nPos = 0, nSize = maXctList.GetSize(); nPos < nSize; ++nPos) + { + pExternalLink->singleElement( XML_sheetName, + XML_val, XclXmlUtils::ToOString( maXctList.GetRecord( nPos )->GetTabName()).getStr(), + FSEND); + } + pExternalLink->endElement( XML_sheetNames); + + pExternalLink->startElement( XML_sheetDataSet, FSEND); + + // sheetData elements + maXctList.SaveXml( rStrm ); + // externalName elements + WriteExtNameBufferXml( 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 ); @@ -1855,6 +2051,58 @@ void XclExpSupbookBuffer::Save( XclExpStream& rStrm ) maSupbookList.Save( rStrm ); } +void XclExpSupbookBuffer::SaveXml( XclExpXmlStream& rStrm ) +{ + ::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() != EXC_SBTYPE_EXTERN) + continue; // handle only external reference (for now?) + + sal_uInt16 nId = xRef->GetFileId(); + const OUString& rUrl = xRef->GetUrl(); + ::std::pair< ::std::map< sal_uInt16, OUString >::iterator, bool > xInsert( + aMap.insert( ::std::make_pair( nId, rUrl))); + if (!xInsert.second) + { + SAL_WARN( "sc.filter", "XclExpSupbookBuffer::SaveXml: file ID already used: " << nId << + " wanted for " << rUrl << " and is " << (*xInsert.first).second << + (rUrl == (*xInsert.first).second ? " multiple Supbook not supported" : "")); + continue; + } + + OUString sId; + sax_fastparser::FSHelperPtr pExternalLink = rStrm.CreateOutputStream( + XclXmlUtils::GetStreamName( "xl/", "externalLinks/externalLink", nId), + XclXmlUtils::GetStreamName( NULL, "externalLinks/externalLink", nId), + 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 ), XclXmlUtils::ToOString( sId ).getStr(), + FSEND ); + + // 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() == EXC_SBTYPE_EXTERN) + return true; + } + return false; +} + bool XclExpSupbookBuffer::GetSupbookUrl( XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex, const OUString& rUrl ) const { @@ -1998,6 +2246,11 @@ void XclExpLinkManagerImpl5::Save( XclExpStream& rStrm ) } } +void XclExpLinkManagerImpl5::SaveXml( XclExpXmlStream& /*rStrm*/ ) +{ + // not applicable +} + sal_uInt16 XclExpLinkManagerImpl5::GetExtSheetCount() const { return static_cast< sal_uInt16 >( maExtSheetList.GetSize() ); @@ -2216,6 +2469,29 @@ void XclExpLinkManagerImpl8::Save( XclExpStream& rStrm ) } } +void XclExpLinkManagerImpl8::SaveXml( XclExpXmlStream& rStrm ) +{ + if (maSBBuffer.HasExternalReferences()) + { + sax_fastparser::FSHelperPtr pWorkbook = rStrm.GetCurrentStream(); + pWorkbook->startElement( XML_externalReferences, FSEND); + + // externalLink, externalBook, sheetNames, sheetDataSet, externalName + maSBBuffer.SaveXml( rStrm ); + + pWorkbook->endElement( XML_externalReferences); + } + + // TODO: equivalent for EXTERNSHEET in OOXML? +#if 0 + if( !maXtiVec.empty() ) + { + for( XclExpXtiVec::const_iterator aIt = maXtiVec.begin(), aEnd = maXtiVec.end(); aIt != aEnd; ++aIt ) + aIt->SaveXml( rStrm ); + } +#endif +} + sal_uInt16 XclExpLinkManagerImpl8::InsertXti( const XclExpXti& rXti ) { for( XclExpXtiVec::const_iterator aIt = maXtiVec.begin(), aEnd = maXtiVec.end(); aIt != aEnd; ++aIt ) @@ -2322,4 +2598,9 @@ 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/inc/xecontent.hxx b/sc/source/filter/inc/xecontent.hxx index 0a6705c31f60..214229397860 100644 --- a/sc/source/filter/inc/xecontent.hxx +++ b/sc/source/filter/inc/xecontent.hxx @@ -110,13 +110,14 @@ public: virtual void WriteEmbeddedData( XclExpStream& rStrm ); void SetDisplay( bool bDisplay ) { mbSetDisplay = bDisplay; } -private: + /** Builds file name from the passed file URL. Tries to convert to relative file name. @param rnLevel (out-param) The parent directory level. @param rbRel (out-param) true = path is relative. */ - OUString BuildFileName( + static OUString BuildFileName( sal_uInt16& rnLevel, bool& rbRel, - const OUString& rUrl, const XclExpRoot& rRoot ) const; + const OUString& rUrl, const XclExpRoot& rRoot ); +private: /** Writes the body of the HLINK record. */ virtual void WriteBody( XclExpStream& rStrm ) SAL_OVERRIDE; diff --git a/sc/source/filter/inc/xelink.hxx b/sc/source/filter/inc/xelink.hxx index 444192e820fe..89d8b2f17303 100644 --- a/sc/source/filter/inc/xelink.hxx +++ b/sc/source/filter/inc/xelink.hxx @@ -200,6 +200,9 @@ public: /** Writes the entire Link table. */ virtual void Save( XclExpStream& rStrm ) SAL_OVERRIDE; + /** Writes the entire Link table to OOXML. */ + virtual void SaveXml( XclExpXmlStream& rStrm ) SAL_OVERRIDE; + private: typedef boost::shared_ptr< XclExpLinkManagerImpl > XclExpLinkMgrImplPtr; XclExpLinkMgrImplPtr mxImpl; -- cgit v1.2.3