diff options
Diffstat (limited to 'sc/source/filter/xcl97/xcl97rec.cxx')
-rw-r--r-- | sc/source/filter/xcl97/xcl97rec.cxx | 2026 |
1 files changed, 2026 insertions, 0 deletions
diff --git a/sc/source/filter/xcl97/xcl97rec.cxx b/sc/source/filter/xcl97/xcl97rec.cxx new file mode 100644 index 000000000..d4524b22e --- /dev/null +++ b/sc/source/filter/xcl97/xcl97rec.cxx @@ -0,0 +1,2026 @@ +/* -*- 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 <svx/sdtaitm.hxx> +#include <svx/svdotext.hxx> +#include <editeng/editobj.hxx> +#include <svx/svdoole2.hxx> +#include <sot/storage.hxx> +#include <svl/itemset.hxx> +#include <svx/svdocapt.hxx> +#include <svx/unoapi.hxx> +#include <editeng/writingmodeitem.hxx> +#include <tools/urlobj.hxx> + +#include <rtl/math.hxx> +#include <rtl/uuid.h> +#include <sal/log.hxx> +#include <drwlayer.hxx> + +#include <root.hxx> +#include <xcl97rec.hxx> +#include <xcl97esc.hxx> +#include <xeescher.hxx> +#include <xehelper.hxx> +#include <xelink.hxx> +#include <xlcontent.hxx> + +#include <unotools/fltrcfg.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/eeitem.hxx> +#include <filter/msfilter/msoleexp.hxx> + +#include <unotools/localedatawrapper.hxx> + +#include <stdio.h> + +#include <document.hxx> +#include <rangelst.hxx> +#include <docoptio.hxx> +#include <tabprotection.hxx> + +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> + +#include <sax/fastattribs.hxx> +#include <oox/token/tokens.hxx> +#include <oox/token/namespaces.hxx> +#include <oox/token/relationship.hxx> +#include <oox/export/shapes.hxx> +#include <oox/export/utils.hxx> +#include <oox/export/vmlexport.hxx> +#include <detfunc.hxx> + +#include <memory> + +using namespace ::com::sun::star; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::drawing::XShape; +using ::oox::drawingml::ShapeExport; +using ::oox::vml::VMLExport; +using namespace oox; + +XclExpObjList::XclExpObjList( const XclExpRoot& rRoot, XclEscherEx& rEscherEx ) : + XclExpRoot( rRoot ), + mnScTab( rRoot.GetCurrScTab() ), + mrEscherEx( rEscherEx ) +{ + pMsodrawingPerSheet.reset( new XclExpMsoDrawing( rEscherEx ) ); + // open the DGCONTAINER and the patriarch group shape + mrEscherEx.OpenContainer( ESCHER_DgContainer ); + tools::Rectangle aRect( 0, 0, 0, 0 ); + mrEscherEx.EnterGroup( &aRect ); + mrEscherEx.UpdateDffFragmentEnd(); +} + +XclExpObjList::~XclExpObjList() +{ + maObjs.clear(); + pMsodrawingPerSheet.reset(); + pSolverContainer.reset(); +} + +sal_uInt16 XclExpObjList::Add( std::unique_ptr<XclObj> pObj ) +{ + OSL_ENSURE( maObjs.size() < 0xFFFF, "XclExpObjList::Add: too much for Xcl" ); + + size_t nSize = maObjs.size(); + + if ( nSize < 0xFFFF ) + { + pObj->SetId( nSize+1 ); + pObj->SetTab( mnScTab ); + maObjs.push_back(std::move(pObj)); + ++nSize; + } + else + { + nSize = 0; + } + + return nSize; +} + +std::unique_ptr<XclObj> XclExpObjList::pop_back () +{ + auto ret = std::move(maObjs.back()); + maObjs.pop_back(); + return ret; +} + +void XclExpObjList::EndSheet() +{ + // Is there still something in the stream? -> The solver container + if( mrEscherEx.HasPendingDffData() ) + pSolverContainer.reset( new XclExpMsoDrawing( mrEscherEx ) ); + + // close the DGCONTAINER created by XclExpObjList ctor MSODRAWING + mrEscherEx.CloseContainer(); +} + +void XclExpObjList::Save( XclExpStream& rStrm ) +{ + //! Escher must be written, even if there are no objects + pMsodrawingPerSheet->Save( rStrm ); + + for ( const auto& rxObj : maObjs ) + rxObj->Save( rStrm ); + + if( pSolverContainer ) + pSolverContainer->Save( rStrm ); +} + +namespace { + +bool IsFormControlObject( const XclObj *rObj ) +{ + switch( rObj->GetObjType() ) + { + case EXC_OBJTYPE_CHECKBOX: + case EXC_OBJTYPE_BUTTON: + return true; + default: + return false; + } +} + +bool IsVmlObject( const XclObj *rObj ) +{ + switch( rObj->GetObjType() ) + { + case EXC_OBJTYPE_NOTE: + return true; + default: + return false; + } +} + +sal_Int32 GetVmlObjectCount( XclExpObjList& rList ) +{ + return static_cast<sal_Int32>(std::count_if(rList.begin(), rList.end(), + [](const std::unique_ptr<XclObj>& rxObj) { return IsVmlObject( rxObj.get() ) || IsFormControlObject( rxObj.get() ); })); +} + +bool IsValidObject( const XclObj& rObj ) +{ + if (rObj.GetObjType() == EXC_OBJTYPE_CHART) + { + // Chart object. Make sure it's a valid chart object. We skip + // invalid chart objects from exporting to prevent Excel from + // complaining on load. + + const XclExpChartObj& rChartObj = static_cast<const XclExpChartObj&>(rObj); + uno::Reference<chart2::XChartDocument> xChartDoc(rChartObj.GetChartDoc(), uno::UNO_QUERY); + if (!xChartDoc.is()) + return false; + + uno::Reference<chart2::XDiagram> xDiagram = xChartDoc->getFirstDiagram(); + if (!xDiagram.is()) + return false; + + uno::Reference<chart2::XCoordinateSystemContainer> xCooSysContainer(xDiagram, uno::UNO_QUERY); + if (!xCooSysContainer.is()) + return false; + + const uno::Sequence<uno::Reference<chart2::XCoordinateSystem>> xCooSysSeq = xCooSysContainer->getCoordinateSystems(); + for (const auto& rCooSys : xCooSysSeq) + { + Reference<chart2::XChartTypeContainer> xChartTypeCont(rCooSys, uno::UNO_QUERY); + if (!xChartTypeCont.is()) + return false; + + uno::Sequence<uno::Reference<chart2::XChartType>> xChartTypeSeq = xChartTypeCont->getChartTypes(); + if (!xChartTypeSeq.hasElements()) + // No chart type. Not good. + return false; + } + } + + return true; +} + +void SaveDrawingMLObjects( XclExpObjList& rList, XclExpXmlStream& rStrm ) +{ + std::vector<XclObj*> aList; + // do not add objects to the list that are in the group, + // because the group already contains them. For this, count + // the next skipped objects, i.e. objects of a group, + // including objects of its subgroups + size_t nSkipObj = 0; + for (const auto& rxObj : rList) + { + // FIXME: Can DrawingML objects be grouped with VML or not valid objects? + if (IsVmlObject(rxObj.get()) || !IsValidObject(*rxObj)) + continue; + + if (nSkipObj == 0) + aList.push_back(rxObj.get()); + else + --nSkipObj; + + XclObjAny* pObj = nullptr; + if (rxObj->GetObjType() == 0) // group (it can be a subgroup) + pObj = dynamic_cast<XclObjAny*>(rxObj.get()); + if (pObj) + { + css::uno::Reference<css::drawing::XShapes> xShapes(pObj->GetShape(), UNO_QUERY); + if (xShapes) + { + // skip (also) the objects of this group + nSkipObj += xShapes->getCount(); + } + } + } + + if (aList.empty()) + return; + + sal_Int32 nDrawing = drawingml::DrawingML::getNewDrawingUniqueId(); + OUString sId; + sax_fastparser::FSHelperPtr pDrawing = rStrm.CreateOutputStream( + XclXmlUtils::GetStreamName( "xl/", "drawings/drawing", nDrawing ), + XclXmlUtils::GetStreamName( "../", "drawings/drawing", nDrawing ), + rStrm.GetCurrentStream()->getOutputStream(), + "application/vnd.openxmlformats-officedocument.drawing+xml", + oox::getRelationship(Relationship::DRAWING), + &sId ); + + rStrm.GetCurrentStream()->singleElement(XML_drawing, FSNS(XML_r, XML_id), sId.toUtf8()); + + rStrm.PushStream( pDrawing ); + pDrawing->startElement( FSNS( XML_xdr, XML_wsDr ), + FSNS(XML_xmlns, XML_xdr), rStrm.getNamespaceURL(OOX_NS(dmlSpreadDr)).toUtf8(), + FSNS(XML_xmlns, XML_a), rStrm.getNamespaceURL(OOX_NS(dml)).toUtf8(), + FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8() ); + + sal_Int32 nShapeId = 1000; // unique id of the shape inside one worksheet (not the whole document) + for (const auto& rpObj : aList) + { + // validate shapeId + if ( IsFormControlObject( rpObj ) ) + { + XclExpTbxControlObj* pXclExpTbxControlObj = dynamic_cast<XclExpTbxControlObj*>(rpObj); + if (pXclExpTbxControlObj) + { + pXclExpTbxControlObj->setShapeId(++nShapeId); + } + } + + rpObj->SaveXml(rStrm); + } + + pDrawing->endElement( FSNS( XML_xdr, XML_wsDr ) ); + + rStrm.PopStream(); +} + +void SaveFormControlObjects(XclExpObjList& rList, XclExpXmlStream& rStrm) +{ + bool hasControls = false; + for (const auto& rxObj : rList) + { + if (IsFormControlObject(rxObj.get())) + { + hasControls = true; + break; + } + } + + if (!hasControls) + { + return; + } + + sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); + + rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent), + FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)).toUtf8()); + rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x14"); + rWorksheet->startElement(XML_controls); + + for (const auto& rxObj : rList) + { + if (IsFormControlObject(rxObj.get())) + { + XclExpTbxControlObj* pXclExpTbxControlObj = dynamic_cast<XclExpTbxControlObj*>(rxObj.get()); + if (pXclExpTbxControlObj) + { + const OUString aIdFormControlPr = pXclExpTbxControlObj->SaveControlPropertiesXml(rStrm); + pXclExpTbxControlObj->SaveSheetXml(rStrm, aIdFormControlPr); + } + } + } + + rWorksheet->endElement(XML_controls); + rWorksheet->endElement(FSNS(XML_mc, XML_Choice)); + rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent)); +} + +void SaveVmlObjects( XclExpObjList& rList, XclExpXmlStream& rStrm ) +{ + if( GetVmlObjectCount( rList ) == 0 ) + return; + + sal_Int32 nDrawing = drawingml::DrawingML::getNewVMLUniqueId(); + OUString sId; + sax_fastparser::FSHelperPtr pVmlDrawing = rStrm.CreateOutputStream( + XclXmlUtils::GetStreamName( "xl/", "drawings/vmlDrawing", nDrawing ), + XclXmlUtils::GetStreamName( "../", "drawings/vmlDrawing", nDrawing ), + rStrm.GetCurrentStream()->getOutputStream(), + "application/vnd.openxmlformats-officedocument.vmlDrawing", + oox::getRelationship(Relationship::VMLDRAWING), + &sId ); + + rStrm.GetCurrentStream()->singleElement(XML_legacyDrawing, FSNS(XML_r, XML_id), sId.toUtf8()); + + rStrm.PushStream( pVmlDrawing ); + pVmlDrawing->write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"); + pVmlDrawing->startElement( XML_xml, + FSNS(XML_xmlns, XML_v), rStrm.getNamespaceURL(OOX_NS(vml)).toUtf8(), + FSNS(XML_xmlns, XML_o), rStrm.getNamespaceURL(OOX_NS(vmlOffice)).toUtf8(), + FSNS(XML_xmlns, XML_x), rStrm.getNamespaceURL(OOX_NS(vmlExcel)).toUtf8(), + FSNS(XML_xmlns, XML_w10), rStrm.getNamespaceURL(OOX_NS(vmlWord)).toUtf8() ); + + for ( const auto& rxObj : rList ) + { + if (IsFormControlObject(rxObj.get())) + { + auto pFormControlObject = dynamic_cast<XclExpTbxControlObj*>(rxObj.get()); + if (pFormControlObject) + { + pFormControlObject->SaveVml(rStrm); + } + } + + if( !IsVmlObject( rxObj.get() ) ) + continue; + rxObj->SaveXml( rStrm ); + } + + pVmlDrawing->endElement( XML_xml ); + + rStrm.PopStream(); +} + +} + +void XclExpObjList::SaveXml( XclExpXmlStream& rStrm ) +{ + if( pSolverContainer ) + pSolverContainer->SaveXml( rStrm ); + + if( maObjs.empty()) + return; + + SaveDrawingMLObjects( *this, rStrm ); + SaveVmlObjects( *this, rStrm ); + SaveFormControlObjects( *this, rStrm ); +} + +// --- class XclObj -------------------------------------------------- + +XclObj::XclObj( XclExpObjectManager& rObjMgr, sal_uInt16 nObjType, bool bOwnEscher ) : + XclExpRecord( EXC_ID_OBJ, 26 ), + mrEscherEx( rObjMgr.GetEscherEx() ), + mnObjType( nObjType ), + nObjId(0), + nGrbit( 0x6011 ), // AutoLine, AutoFill, Printable, Locked + mnScTab(0), + bFirstOnSheet( !rObjMgr.HasObj() ), + mbOwnEscher( bOwnEscher ) +{ + //! first object continues the first MSODRAWING record + if ( bFirstOnSheet ) + pMsodrawing = rObjMgr.GetMsodrawingPerSheet(); + else + pMsodrawing = new XclExpMsoDrawing( mrEscherEx ); +} + +XclObj::~XclObj() +{ + if ( !bFirstOnSheet ) + delete pMsodrawing; + pClientTextbox.reset(); + pTxo.reset(); +} + +void XclObj::ImplWriteAnchor( const SdrObject* pSdrObj, const tools::Rectangle* pChildAnchor ) +{ + if( pChildAnchor ) + { + mrEscherEx.AddChildAnchor( *pChildAnchor ); + } + else if( pSdrObj ) + { + std::unique_ptr< XclExpDffAnchorBase > xDffAnchor( mrEscherEx.CreateDffAnchor( *pSdrObj ) ); + xDffAnchor->WriteDffData( mrEscherEx ); + } +} + +void XclObj::SetEscherShapeType( sal_uInt16 nType ) +{ +//ToDo: what about the other defined or... types? + switch ( nType ) + { + case ESCHER_ShpInst_Line : + mnObjType = EXC_OBJTYPE_LINE; + break; + case ESCHER_ShpInst_Rectangle : + case ESCHER_ShpInst_RoundRectangle : + mnObjType = EXC_OBJTYPE_RECTANGLE; + break; + case ESCHER_ShpInst_Ellipse : + mnObjType = EXC_OBJTYPE_OVAL; + break; + case ESCHER_ShpInst_Arc : + mnObjType = EXC_OBJTYPE_ARC; + break; + case ESCHER_ShpInst_TextBox : + mnObjType = EXC_OBJTYPE_TEXT; + break; + case ESCHER_ShpInst_PictureFrame : + mnObjType = EXC_OBJTYPE_PICTURE; + break; + default: + mnObjType = EXC_OBJTYPE_DRAWING; + } +} + +void XclObj::SetText( const XclExpRoot& rRoot, const SdrTextObj& rObj ) +{ + OSL_ENSURE( !pClientTextbox, "XclObj::SetText: already set" ); + if ( !pClientTextbox ) + { + mrEscherEx.UpdateDffFragmentEnd(); + pClientTextbox.reset( new XclExpMsoDrawing( mrEscherEx ) ); + mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox ); // TXO record + mrEscherEx.UpdateDffFragmentEnd(); + pTxo.reset( new XclTxo( rRoot, rObj ) ); + } +} + +void XclObj::WriteBody( XclExpStream& rStrm ) +{ + OSL_ENSURE( mnObjType != EXC_OBJTYPE_UNKNOWN, "XclObj::WriteBody - unknown type" ); + + // create a substream to be able to create subrecords + SvMemoryStream aMemStrm; + std::optional< XclExpStream > pXclStrm( std::in_place, aMemStrm, rStrm.GetRoot() ); + + // write the ftCmo subrecord + pXclStrm->StartRecord( EXC_ID_OBJCMO, 18 ); + *pXclStrm << mnObjType << nObjId << nGrbit; + pXclStrm->WriteZeroBytes( 12 ); + pXclStrm->EndRecord(); + + // write other subrecords + WriteSubRecs( *pXclStrm ); + + // write the ftEnd subrecord + pXclStrm->StartRecord( EXC_ID_OBJEND, 0 ); + pXclStrm->EndRecord(); + + // copy the data to the OBJ record + pXclStrm.reset(); + aMemStrm.Seek( 0 ); + rStrm.CopyFromStream( aMemStrm ); +} + +void XclObj::Save( XclExpStream& rStrm ) +{ + // MSODRAWING record (msofbtSpContainer) + if ( !bFirstOnSheet ) + pMsodrawing->Save( rStrm ); + + // OBJ + XclExpRecord::Save( rStrm ); + + // second MSODRAWING record and TXO and CONTINUE records + SaveTextRecs( rStrm ); +} + +void XclObj::WriteSubRecs( XclExpStream& rStrm ) +{ + if( mnObjType != EXC_OBJTYPE_NOTE ) + return; + + // FtNts subrecord + AddRecSize( 26 ); + // ft, cb + rStrm << EXC_ID_OBJNTS << sal_uInt16(0x0016); + sal_uInt8 aGUID[16]; + rtl_createUuid( aGUID, nullptr, false ); + // guid + rStrm.SetSliceSize( 16 ); + for( int i = 0; i < 16; i++ ) + rStrm << aGUID[i]; + rStrm.SetSliceSize( 0 ); + // fSharedNote + rStrm << sal_uInt16(0); + // unused + rStrm.WriteZeroBytes( 4 ); +} + +void XclObj::SaveTextRecs( XclExpStream& rStrm ) +{ + // MSODRAWING record (msofbtClientTextbox) + if ( pClientTextbox ) + pClientTextbox->Save( rStrm ); + // TXO and CONTINUE records + if ( pTxo ) + pTxo->Save( rStrm ); +} + +// --- class XclObjComment ------------------------------------------ + +// tdf#118662 static helper to allow single function access as friend in SdrCaptionObj +void setSuppressGetBitmapFromXclObjComment(SdrCaptionObj* pSdrCaptionObj, bool bValue) +{ + if(nullptr != pSdrCaptionObj) + { + pSdrCaptionObj->setSuppressGetBitmap(bValue); + } +} + +XclObjComment::XclObjComment( XclExpObjectManager& rObjMgr, const tools::Rectangle& rRect, const EditTextObject& rEditObj, SdrCaptionObj* pCaption, bool bVisible, const ScAddress& rAddress, const tools::Rectangle &rFrom, const tools::Rectangle &rTo ) : + XclObj( rObjMgr, EXC_OBJTYPE_NOTE, true ) + , maScPos( rAddress ) + , mpCaption( pCaption ) + , mbVisible( bVisible ) + , maFrom ( rFrom ) + , maTo ( rTo ) +{ + // tdf#118662 due to no longer cloning the SdrCaptionObj an old 'hack' using the + // fact that no Graphics gets created when a SdrObject is not inserted in a SdrPage + // does not work anymore. In SvxShape::GetBitmap that info was used, and here the + // SdrCaptionObj was cloned for the only reason to have one not added to a SdrPage. + // To emulate old behaviour, use a boolean flag at the SdrCaptionObj. + setSuppressGetBitmapFromXclObjComment(mpCaption, true); + + ProcessEscherObj( rObjMgr.GetRoot(), rRect, pCaption, bVisible); + // TXO + pTxo .reset(new XclTxo( rObjMgr.GetRoot(), rEditObj, pCaption )); +} + +static void lcl_FillProps( EscherPropertyContainer& rPropOpt, SdrObject* pCaption, bool bVisible ) +{ + if( pCaption ) + { + Reference< XShape > aXShape = GetXShapeForSdrObject( pCaption ); + Reference< XPropertySet > aXPropSet( aXShape, UNO_QUERY ); + if( aXPropSet.is() ) + { + rPropOpt.CreateFillProperties( aXPropSet, true); + + rPropOpt.AddOpt( ESCHER_Prop_lTxid, 0 ); // undocumented + rPropOpt.AddOpt( 0x0158, 0x00000000 ); // undocumented + + sal_uInt32 nValue = 0; + if( !rPropOpt.GetOpt( ESCHER_Prop_FitTextToShape, nValue ) ) + rPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); // bool field + + // Maybe the colour is the same as the 'ToolTip' System colour, but the tooltip + // colour shouldn't have influence on the fill colour of the exported shape + if( !rPropOpt.GetOpt( ESCHER_Prop_fillColor, nValue ) ) + rPropOpt.AddOpt( ESCHER_Prop_fillColor, 0x08000050 ); + if( !rPropOpt.GetOpt( ESCHER_Prop_fillBackColor, nValue ) ) + rPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x08000050 ); + if( !rPropOpt.GetOpt( ESCHER_Prop_fNoFillHitTest, nValue ) ) + rPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00110010 ); // bool field + if( !rPropOpt.GetOpt( ESCHER_Prop_shadowColor, nValue ) ) + rPropOpt.AddOpt( ESCHER_Prop_shadowColor, 0x00000000 ); + if( !rPropOpt.GetOpt( ESCHER_Prop_fshadowObscured, nValue ) ) // bool field + rPropOpt.AddOpt( ESCHER_Prop_fshadowObscured, 0x00030003 ); // bool field + } + } + + sal_uInt32 nFlags = 0x000A0000; + ::set_flag( nFlags, sal_uInt32(2), !bVisible ); + rPropOpt.AddOpt( ESCHER_Prop_fPrint, nFlags ); // bool field +} + +void XclObjComment::ProcessEscherObj( const XclExpRoot& rRoot, const tools::Rectangle& rRect, SdrObject* pCaption, const bool bVisible ) +{ + EscherPropertyContainer aPropOpt; + + lcl_FillProps( aPropOpt, pCaption, bVisible ); + + nGrbit = 0; // all off: AutoLine, AutoFill, Printable, Locked + mrEscherEx.OpenContainer( ESCHER_SpContainer ); + mrEscherEx.AddShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty ); + aPropOpt.Commit( mrEscherEx.GetStream() ); + + XclExpDffNoteAnchor( rRoot, rRect ).WriteDffData( mrEscherEx ); + + mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record + mrEscherEx.UpdateDffFragmentEnd(); + + //! Be sure to construct the MSODRAWING ClientTextbox record _after_ the + //! base OBJ's MSODRAWING record Escher data is completed. + pClientTextbox.reset( new XclExpMsoDrawing( mrEscherEx ) ); + mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox ); // TXO record + mrEscherEx.UpdateDffFragmentEnd(); + mrEscherEx.CloseContainer(); // ESCHER_SpContainer +} + +XclObjComment::~XclObjComment() +{ + // tdf#118662 reset flag + setSuppressGetBitmapFromXclObjComment(mpCaption, false); +} + +void XclObjComment::Save( XclExpStream& rStrm ) +{ + // content of this record + XclObj::Save( rStrm ); +} + +namespace { + +class VmlCommentExporter : public VMLExport +{ + ScAddress maScPos; + SdrCaptionObj* mpCaption; + bool mbVisible; + tools::Rectangle maFrom; + tools::Rectangle maTo; + +public: + VmlCommentExporter ( const sax_fastparser::FSHelperPtr& p, const ScAddress& aScPos, SdrCaptionObj* pCaption, bool bVisible, const tools::Rectangle &aFrom, const tools::Rectangle &aTo ); +protected: + virtual void Commit( EscherPropertyContainer& rProps, const tools::Rectangle& rRect ) override; + using VMLExport::StartShape; + virtual sal_Int32 StartShape() override; + using VMLExport::EndShape; + virtual void EndShape( sal_Int32 nShapeElement ) override; +}; + +} + +VmlCommentExporter::VmlCommentExporter( const sax_fastparser::FSHelperPtr& p, const ScAddress& aScPos, SdrCaptionObj* pCaption, + bool bVisible, const tools::Rectangle &aFrom, const tools::Rectangle &aTo ) + : VMLExport( p ) + , maScPos( aScPos ) + , mpCaption( pCaption ) + , mbVisible( bVisible ) + , maFrom ( aFrom ) + , maTo ( aTo ) +{ +} + +void VmlCommentExporter::Commit( EscherPropertyContainer& rProps, const tools::Rectangle& rRect ) +{ + lcl_FillProps( rProps, mpCaption, mbVisible ); + rProps.AddOpt( ESCHER_Prop_fHidden, sal_uInt32(mbVisible) ); // bool field + + // shadow property value for comment ( set in lcl_FillProps [*] ) has been + // overwritten by new value ( 0x20000 ) in the generic part of the export + // ( see EscherPropertyContainer::CreateShadowProperties ) + // Safer option here is to just force the needed value here for oox vml + // export alone ( and avoid potential problems with binary export ) + // #TODO investigate value of ESCHER_Prop_fshadowObscured generally + // in binary export ( if indeed this value is good for binary export ) + // we can change the heuristics and/or initialisation path and get + // rid of line below. + // [*] lcl_FillProps seems to be called twice when exporting to xlsx + // once from XclObjComment::ProcessEscherObj #TODO look into that also + rProps.AddOpt( ESCHER_Prop_fshadowObscured, 0x00030003 ); // force value for comments + + VMLExport::Commit( rProps, rRect ); +} + +sal_Int32 VmlCommentExporter::StartShape() +{ + AddShapeAttribute( XML_type, "#_x0000_t202" ); + + sal_Int32 nId = VMLExport::StartShape(); + + return nId; +} + +static const char* lcl_GetHorizAlignFromItemSetChar(const SfxItemSet& rItemSet) +{ + switch (rItemSet.Get(EE_PARA_JUST).GetAdjust()) + { + case SvxAdjust::Center: + return "Center"; + case SvxAdjust::Right: + return "Right"; + case SvxAdjust::Block: + return "Justify"; + default: + return "Left"; + } +} + +static const char* lcl_GetVertAlignFromItemSetChar( const SfxItemSet& rItemSet ) +{ + switch( rItemSet.Get( SDRATTR_TEXT_VERTADJUST ).GetValue() ) + { + case SDRTEXTVERTADJUST_CENTER: + return "Center"; + case SDRTEXTVERTADJUST_BOTTOM: + return "Bottom"; + case SDRTEXTVERTADJUST_BLOCK: + return "Justify"; + case SDRTEXTVERTADJUST_TOP: + default: + return "Top"; + } +} + +void VmlCommentExporter::EndShape( sal_Int32 nShapeElement ) +{ + char pAnchor[100]; + sax_fastparser::FSHelperPtr pVmlDrawing = GetFS(); + snprintf( pAnchor, 100, "%" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64, + sal_Int64(maFrom.Left()), sal_Int64(maFrom.Top()), sal_Int64(maFrom.Right()), sal_Int64(maFrom.Bottom()), + sal_Int64(maTo.Left()), sal_Int64(maTo.Top()), sal_Int64(maTo.Right()), sal_Int64(maTo.Bottom()) ); + + // Getting comment text alignments + const char* pVertAlign = lcl_GetVertAlignFromItemSetChar(mpCaption->GetMergedItemSet()); + const char* pHorizAlign = lcl_GetHorizAlignFromItemSetChar(mpCaption->GetMergedItemSet()); + + pVmlDrawing->startElement(FSNS(XML_x, XML_ClientData), XML_ObjectType, "Note"); + pVmlDrawing->singleElement(FSNS(XML_x, XML_MoveWithCells)); + pVmlDrawing->singleElement(FSNS(XML_x, XML_SizeWithCells)); + XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_Anchor ), pAnchor ); + XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_AutoFill ), "False" ); + XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_TextVAlign ), pVertAlign ); + XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_TextHAlign ), pHorizAlign ); + XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_Row ), maScPos.Row() ); + XclXmlUtils::WriteElement( pVmlDrawing, FSNS(XML_x, XML_Column), sal_Int32(maScPos.Col())); + if(mbVisible) + pVmlDrawing->singleElement(FSNS(XML_x, XML_Visible)); + pVmlDrawing->endElement( FSNS( XML_x, XML_ClientData ) ); + + VMLExport::EndShape( nShapeElement ); +} + +void XclObjComment::SaveXml( XclExpXmlStream& rStrm ) +{ + VmlCommentExporter aCommentExporter( rStrm.GetCurrentStream(), maScPos, mpCaption, mbVisible, maFrom, maTo ); + aCommentExporter.AddSdrObject( *mpCaption ); +} + +// --- class XclObjDropDown ------------------------------------------ + +XclObjDropDown::XclObjDropDown( XclExpObjectManager& rObjMgr, const ScAddress& rPos, bool bFilt ) : + XclObj( rObjMgr, EXC_OBJTYPE_DROPDOWN, true ), + bIsFiltered( bFilt ) +{ + SetLocked( true ); + SetPrintable( false ); + SetAutoFill( true ); + SetAutoLine( false ); + nGrbit |= 0x0100; // undocumented + mrEscherEx.OpenContainer( ESCHER_SpContainer ); + mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01040104 ); // bool field + aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); // bool field + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00010000 ); // bool field + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field + aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x000A0000 ); // bool field + aPropOpt.Commit( mrEscherEx.GetStream() ); + + XclExpDffDropDownAnchor( rObjMgr.GetRoot(), rPos ).WriteDffData( mrEscherEx ); + + mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record + mrEscherEx.UpdateDffFragmentEnd(); + mrEscherEx.CloseContainer(); // ESCHER_SpContainer + + // old size + ftSbs + ftLbsData + AddRecSize( 24 + 20 ); +} + +XclObjDropDown::~XclObjDropDown() +{ +} + +void XclObjDropDown::WriteSubRecs( XclExpStream& rStrm ) +{ + // ftSbs subrecord - Scroll bars (dummy) + rStrm.StartRecord( EXC_ID_OBJSBS, 20 ); + rStrm.WriteZeroBytes( 20 ); + rStrm.EndRecord(); + + // ftLbsData subrecord - Listbox data + sal_uInt16 nDropDownFlags = 0; + ::insert_value( nDropDownFlags, EXC_OBJ_DROPDOWN_SIMPLE, 0, 2 ); + ::set_flag( nDropDownFlags, EXC_OBJ_DROPDOWN_FILTERED, bIsFiltered ); + rStrm.StartRecord( EXC_ID_OBJLBSDATA, 16 ); + rStrm << sal_uInt32(0) << sal_uInt16(0) << sal_uInt16(0x0301) << sal_uInt16(0) + << nDropDownFlags << sal_uInt16( 20 ) << sal_uInt16( 130 ); + rStrm.EndRecord(); +} + +// --- class XclTxo -------------------------------------------------- + +static sal_uInt8 lcl_GetHorAlignFromItemSet( const SfxItemSet& rItemSet ) +{ + sal_uInt8 nHorAlign = EXC_OBJ_HOR_LEFT; + + switch( rItemSet.Get( EE_PARA_JUST ).GetAdjust() ) + { + case SvxAdjust::Left: nHorAlign = EXC_OBJ_HOR_LEFT; break; + case SvxAdjust::Center: nHorAlign = EXC_OBJ_HOR_CENTER; break; + case SvxAdjust::Right: nHorAlign = EXC_OBJ_HOR_RIGHT; break; + case SvxAdjust::Block: nHorAlign = EXC_OBJ_HOR_JUSTIFY; break; + default:; + } + return nHorAlign; +} + +static sal_uInt8 lcl_GetVerAlignFromItemSet( const SfxItemSet& rItemSet ) +{ + sal_uInt8 nVerAlign = EXC_OBJ_VER_TOP; + + switch( rItemSet.Get( SDRATTR_TEXT_VERTADJUST ).GetValue() ) + { + case SDRTEXTVERTADJUST_TOP: nVerAlign = EXC_OBJ_VER_TOP; break; + case SDRTEXTVERTADJUST_CENTER: nVerAlign = EXC_OBJ_VER_CENTER; break; + case SDRTEXTVERTADJUST_BOTTOM: nVerAlign = EXC_OBJ_VER_BOTTOM; break; + case SDRTEXTVERTADJUST_BLOCK: nVerAlign = EXC_OBJ_VER_JUSTIFY; break; + default:; + } + return nVerAlign; +} + +XclTxo::XclTxo( const OUString& rString, sal_uInt16 nFontIx ) : + mpString( std::make_shared<XclExpString>( rString ) ), + mnRotation( EXC_OBJ_ORIENT_NONE ), + mnHorAlign( EXC_OBJ_HOR_LEFT ), + mnVerAlign( EXC_OBJ_VER_TOP ) +{ + if( mpString->Len() ) + { + // If there is text, Excel *needs* the 2nd CONTINUE record with at least two format runs + mpString->AppendFormat( 0, nFontIx ); + mpString->AppendFormat( mpString->Len(), EXC_FONT_APP ); + } +} + +XclTxo::XclTxo( const XclExpRoot& rRoot, const SdrTextObj& rTextObj ) : + mpString( XclExpStringHelper::CreateString( rRoot, rTextObj ) ), + mnRotation( EXC_OBJ_ORIENT_NONE ), + mnHorAlign( EXC_OBJ_HOR_LEFT ), + mnVerAlign( EXC_OBJ_VER_TOP ) +{ + // additional alignment and orientation items + const SfxItemSet& rItemSet = rTextObj.GetMergedItemSet(); + + // horizontal alignment + SetHorAlign( lcl_GetHorAlignFromItemSet( rItemSet ) ); + + // vertical alignment + SetVerAlign( lcl_GetVerAlignFromItemSet( rItemSet ) ); + + // rotation + Degree100 nAngle = rTextObj.GetRotateAngle(); + if( (4500_deg100 < nAngle) && (nAngle < 13500_deg100) ) + mnRotation = EXC_OBJ_ORIENT_90CCW; + else if( (22500_deg100 < nAngle) && (nAngle < 31500_deg100) ) + mnRotation = EXC_OBJ_ORIENT_90CW; + else + mnRotation = EXC_OBJ_ORIENT_NONE; +} + +XclTxo::XclTxo( const XclExpRoot& rRoot, const EditTextObject& rEditObj, SdrObject* pCaption ) : + mpString( XclExpStringHelper::CreateString( rRoot, rEditObj ) ), + mnRotation( EXC_OBJ_ORIENT_NONE ), + mnHorAlign( EXC_OBJ_HOR_LEFT ), + mnVerAlign( EXC_OBJ_VER_TOP ) +{ + if(!pCaption) + return; + + // Excel has one alignment per NoteObject while Calc supports + // one alignment per paragraph - use the first paragraph + // alignment (if set) as our overall alignment. + OUString aParaText( rEditObj.GetText( 0 ) ); + if( !aParaText.isEmpty() ) + { + const SfxItemSet& aSet( rEditObj.GetParaAttribs( 0)); + if( const SvxAdjustItem* pItem = aSet.GetItemIfSet( EE_PARA_JUST ) ) + { + SvxAdjust eEEAlign = pItem->GetAdjust(); + pCaption->SetMergedItem( SvxAdjustItem( eEEAlign, EE_PARA_JUST ) ); + } + } + const SfxItemSet& rItemSet = pCaption->GetMergedItemSet(); + + // horizontal alignment + SetHorAlign( lcl_GetHorAlignFromItemSet( rItemSet ) ); + + // vertical alignment + SetVerAlign( lcl_GetVerAlignFromItemSet( rItemSet ) ); + + // orientation alignment + const SvxWritingModeItem& rItem = rItemSet.Get( SDRATTR_TEXTDIRECTION ); + if( rItem.GetValue() == css::text::WritingMode_TB_RL ) + mnRotation = EXC_OBJ_ORIENT_90CW; +} + +void XclTxo::SaveCont( XclExpStream& rStrm ) +{ + OSL_ENSURE( mpString, "XclTxo::SaveCont - missing string" ); + + // #i96858# do not save existing string formatting if text is empty + sal_uInt16 nRunLen = mpString->IsEmpty() ? 0 : (8 * mpString->GetFormatsCount()); + // alignment + sal_uInt16 nFlags = 0; + ::insert_value( nFlags, mnHorAlign, 1, 3 ); + ::insert_value( nFlags, mnVerAlign, 4, 3 ); + + rStrm << nFlags << mnRotation; + rStrm.WriteZeroBytes( 6 ); + rStrm << mpString->Len() << nRunLen << sal_uInt32( 0 ); +} + +void XclTxo::Save( XclExpStream& rStrm ) +{ + // Write the TXO part + ExcRecord::Save( rStrm ); + + // CONTINUE records are only written if there is some text + if( mpString->IsEmpty() ) + return; + + // CONTINUE for character array + rStrm.StartRecord( EXC_ID_CONT, mpString->GetBufferSize() + 1 ); + rStrm << static_cast< sal_uInt8 >( mpString->GetFlagField() & EXC_STRF_16BIT ); // only Unicode flag + mpString->WriteBuffer( rStrm ); + rStrm.EndRecord(); + + // CONTINUE for formatting runs + rStrm.StartRecord( EXC_ID_CONT, 8 * mpString->GetFormatsCount() ); + const XclFormatRunVec& rFormats = mpString->GetFormats(); + for( const auto& rFormat : rFormats ) + rStrm << rFormat.mnChar << rFormat.mnFontIdx << sal_uInt32( 0 ); + rStrm.EndRecord(); +} + +sal_uInt16 XclTxo::GetNum() const +{ + return EXC_ID_TXO; +} + +std::size_t XclTxo::GetLen() const +{ + return 18; +} + +// --- class XclObjOle ------------------------------------------- + +XclObjOle::XclObjOle( XclExpObjectManager& rObjMgr, const SdrObject& rObj ) : + XclObj( rObjMgr, EXC_OBJTYPE_PICTURE ), + rOleObj( rObj ), + pRootStorage( rObjMgr.GetRoot().GetRootStorage().get() ) +{ +} + +XclObjOle::~XclObjOle() +{ +} + +void XclObjOle::WriteSubRecs( XclExpStream& rStrm ) +{ + // write only as embedded, not linked + OUString aStorageName( "MBD" ); + char aBuf[ sizeof(sal_uInt32) * 2 + 1 ]; + // FIXME Eeek! Is this just a way to get a unique id? + sal_uInt32 nPictureId = sal_uInt32(reinterpret_cast<sal_uIntPtr>(this) >> 2); + sprintf( aBuf, "%08X", static_cast< unsigned int >( nPictureId ) ); + aStorageName += OUString::createFromAscii(aBuf); + tools::SvRef<SotStorage> xOleStg = pRootStorage->OpenSotStorage( aStorageName ); + if( !xOleStg.is() ) + return; + + uno::Reference < embed::XEmbeddedObject > xObj( static_cast<const SdrOle2Obj&>(rOleObj).GetObjRef() ); + if ( !xObj.is() ) + return; + + // set version to "old" version, because it must be + // saved in MS notation. + sal_uInt32 nFl = 0; + const SvtFilterOptions& rFltOpts = SvtFilterOptions::Get(); + if( rFltOpts.IsMath2MathType() ) + nFl |= OLE_STARMATH_2_MATHTYPE; + + if( rFltOpts.IsWriter2WinWord() ) + nFl |= OLE_STARWRITER_2_WINWORD; + + if( rFltOpts.IsCalc2Excel() ) + nFl |= OLE_STARCALC_2_EXCEL; + + if( rFltOpts.IsImpress2PowerPoint() ) + nFl |= OLE_STARIMPRESS_2_POWERPOINT; + + SvxMSExportOLEObjects aOLEExpFilt( nFl ); + aOLEExpFilt.ExportOLEObject( xObj, *xOleStg ); + + // OBJCF subrecord, undocumented as usual + rStrm.StartRecord( EXC_ID_OBJCF, 2 ); + rStrm << sal_uInt16(0x0002); + rStrm.EndRecord(); + + // OBJFLAGS subrecord, undocumented as usual + rStrm.StartRecord( EXC_ID_OBJFLAGS, 2 ); + sal_uInt16 nFlags = EXC_OBJ_PIC_MANUALSIZE; + ::set_flag( nFlags, EXC_OBJ_PIC_SYMBOL, static_cast<const SdrOle2Obj&>(rOleObj).GetAspect() == embed::Aspects::MSOLE_ICON ); + rStrm << nFlags; + rStrm.EndRecord(); + + // OBJPICTFMLA subrecord, undocumented as usual + XclExpString aName( xOleStg->GetUserName() ); + sal_uInt16 nPadLen = static_cast<sal_uInt16>(aName.GetSize() & 0x01); + sal_uInt16 nFmlaLen = static_cast< sal_uInt16 >( 12 + aName.GetSize() + nPadLen ); + sal_uInt16 nSubRecLen = nFmlaLen + 6; + + rStrm.StartRecord( EXC_ID_OBJPICTFMLA, nSubRecLen ); + rStrm << nFmlaLen + << sal_uInt16( 5 ) << sal_uInt32( 0 ) << sal_uInt8( 2 ) + << sal_uInt32( 0 ) << sal_uInt8( 3 ) + << aName; + if( nPadLen ) + rStrm << sal_uInt8( 0 ); // pad byte + rStrm << nPictureId; + rStrm.EndRecord(); +} + +void XclObjOle::Save( XclExpStream& rStrm ) +{ + // content of this record + XclObj::Save( rStrm ); +} + +// --- class XclObjAny ------------------------------------------- + +XclObjAny::XclObjAny( XclExpObjectManager& rObjMgr, const Reference< XShape >& rShape, ScDocument* pDoc ) + : XclObj( rObjMgr, EXC_OBJTYPE_UNKNOWN ) + , mxShape( rShape ) + , mpDoc(pDoc) +{ +} + +XclObjAny::~XclObjAny() +{ +} + +void XclObjAny::WriteSubRecs( XclExpStream& rStrm ) +{ + if( mnObjType == EXC_OBJTYPE_GROUP ) + // ftGmo subrecord + rStrm << EXC_ID_OBJGMO << sal_uInt16(2) << sal_uInt16(0); +} + +void XclObjAny::Save( XclExpStream& rStrm ) +{ + if( mnObjType == EXC_OBJTYPE_GROUP ) + // old size + ftGmo + AddRecSize( 6 ); + + // content of this record + XclObj::Save( rStrm ); +} + +// --- class ExcBof8_Base -------------------------------------------- + +ExcBof8_Base::ExcBof8_Base() +{ + nVers = 0x0600; + nRupBuild = 0x0dbb; + nRupYear = 0x07cc; +} + +void XclObjAny::WriteFromTo( XclExpXmlStream& rStrm, const Reference< XShape >& rShape, SCTAB nTab ) +{ + sax_fastparser::FSHelperPtr pDrawing = rStrm.GetCurrentStream(); + + awt::Point aTopLeft = rShape->getPosition(); + awt::Size aSize = rShape->getSize(); + + // There are a few cases where we must adjust these values + // Do not adjust objects, which have rotation incorporated into their points + // but report a rotation angle nevertheless. + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rShape); + if (pObj && pObj->GetObjIdentifier() != SdrObjKind::Line && pObj->GetObjIdentifier() != SdrObjKind::PolyLine + && pObj->GetObjIdentifier() != SdrObjKind::PathLine && pObj->GetObjIdentifier() != SdrObjKind::FreehandLine + && pObj->GetObjIdentifier() != SdrObjKind::PathPolyLine) + { + Degree100 nRotation = NormAngle36000(pObj->GetRotateAngle()); + if (nRotation) + { + sal_Int16 nHalfWidth = aSize.Width / 2; + sal_Int16 nHalfHeight = aSize.Height / 2; + + // Center of bounding box of the rotated shape + const auto aSnapRectCenter(pObj->GetSnapRect().Center()); + aTopLeft.X = aSnapRectCenter.X() - nHalfWidth; + aTopLeft.Y = aSnapRectCenter.Y() - nHalfHeight; + + // MSO changes the anchor positions at these angles and that does an extra 90 degrees + // rotation on our shapes, so we output it in such position that MSO + // can draw this shape correctly. + if ((nRotation > 4500_deg100 && nRotation <= 13500_deg100) || (nRotation > 22500_deg100 && nRotation <= 31500_deg100)) + { + aTopLeft.X = aTopLeft.X - nHalfHeight + nHalfWidth; + aTopLeft.Y = aTopLeft.Y - nHalfWidth + nHalfHeight; + + std::swap(aSize.Width, aSize.Height); + } + } + } + + tools::Rectangle aLocation( aTopLeft.X, aTopLeft.Y, aTopLeft.X + aSize.Width, aTopLeft.Y + aSize.Height ); + ScRange aRange = rStrm.GetRoot().GetDoc().GetRange( nTab, aLocation ); + tools::Rectangle aRangeRect = rStrm.GetRoot().GetDoc().GetMMRect( aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col()-1, aRange.aEnd.Row()-1, + nTab ); + + pDrawing->startElement(FSNS(XML_xdr, XML_from)); + XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_col ), static_cast<sal_Int32>(aRange.aStart.Col()) ); + XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_colOff ), + oox::drawingml::convertHmmToEmu( aLocation.Left() - aRangeRect.Left() ) ); + XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_row ), static_cast<sal_Int32>(aRange.aStart.Row()) ); + XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_rowOff ), + oox::drawingml::convertHmmToEmu( aLocation.Top() - aRangeRect.Top() ) ); + pDrawing->endElement( FSNS( XML_xdr, XML_from ) ); + + pDrawing->startElement(FSNS(XML_xdr, XML_to)); + XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_col ), static_cast<sal_Int32>(aRange.aEnd.Col()) ); + XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_colOff ), + oox::drawingml::convertHmmToEmu( aLocation.Right() - aRangeRect.Right() ) ); + XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_row ), static_cast<sal_Int32>(aRange.aEnd.Row()) ); + XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_rowOff ), + oox::drawingml::convertHmmToEmu( aLocation.Bottom() - aRangeRect.Bottom() ) ); + pDrawing->endElement( FSNS( XML_xdr, XML_to ) ); +} + +void XclObjAny::WriteFromTo( XclExpXmlStream& rStrm, const XclObjAny& rObj ) +{ + WriteFromTo( rStrm, rObj.GetShape(), rObj.GetTab() ); +} + +static const char* +GetEditAs( const XclObjAny& rObj ) +{ + if( const SdrObject* pShape = EscherEx::GetSdrObject( rObj.GetShape() ) ) + { + switch( ScDrawLayer::GetAnchorType( *pShape ) ) + { + case SCA_CELL: + return "oneCell"; + case SCA_CELL_RESIZE: + return "twoCell"; + default: + case SCA_PAGE: + break; // absolute + } + } + return "absolute"; +} + +namespace { + +ScRefFlags parseRange(const OUString& rString, ScRange& rRange, const ScDocument& rDoc) +{ + // start with the address convention set in the document + formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention(); + ScRefFlags nResult = rRange.Parse(rString, rDoc, eConv); + if ( nResult & ScRefFlags::VALID ) + return nResult; + + // try the default calc address convention + nResult = rRange.Parse(rString, rDoc); + if ( nResult & ScRefFlags::VALID ) + return nResult; + + // try excel a1 + nResult = rRange.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_A1); + if ( nResult & ScRefFlags::VALID ) + return nResult; + + // try r1c1 + return rRange.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_R1C1); +} + +ScRefFlags parseAddress(const OUString& rString, ScAddress& rAddress, const ScDocument& rDoc) +{ + // start with the address convention set in the document + formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention(); + ScRefFlags nResult = rAddress.Parse(rString, rDoc, eConv); + if ( nResult & ScRefFlags::VALID ) + return nResult; + + // try the default calc address convention + nResult = rAddress.Parse(rString, rDoc); + if ( nResult & ScRefFlags::VALID ) + return nResult; + + // try excel a1 + nResult = rAddress.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_A1); + if ( nResult & ScRefFlags::VALID ) + return nResult; + + // try r1c1 + return rAddress.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_R1C1); +} + +void transformURL(const OUString& rOldURL, OUString& rNewURL, const ScDocument& rDoc) +{ + if (rOldURL.startsWith("#")) + { + // URL has to be decoded for escaped characters (%20) + OUString aURL = INetURLObject::decode( rOldURL, + INetURLObject::DecodeMechanism::WithCharset ); + OUString aAddressString = aURL.copy(1); + + ScRange aRange; + ScRefFlags nResult = parseRange(aAddressString, aRange, rDoc); + if ( nResult & ScRefFlags::VALID ) + { + OUString aString = aRange.Format(rDoc, nResult, formula::FormulaGrammar::CONV_XL_OOX); + rNewURL = "#" + aString; + return; + } + else + { + ScAddress aAddress; + nResult = parseAddress(aAddressString, aAddress, rDoc); + if( nResult & ScRefFlags::VALID ) + { + OUString aString = aAddress.Format(nResult, &rDoc, formula::FormulaGrammar::CONV_XL_OOX); + rNewURL = "#" + aString; + return; + } + } + } + + rNewURL = rOldURL; +} + +} + +ScURLTransformer::ScURLTransformer(ScDocument& rDoc) + : mrDoc(rDoc) +{ +} + +OUString ScURLTransformer::getTransformedString(const OUString& rURL) const +{ + OUString aNewURL; + transformURL(rURL, aNewURL, mrDoc); + return aNewURL; +} + +bool ScURLTransformer::isExternalURL(const OUString& rURL) const +{ + return !rURL.startsWith("#"); +} + +void XclObjAny::SaveXml( XclExpXmlStream& rStrm ) +{ + // Do not output any of the detective shapes and validation circles. + SdrObject* pObject = SdrObject::getSdrObjectFromXShape(mxShape); + if (pObject) + { + ScDocument& rDoc = rStrm.GetRoot().GetDoc(); + ScDetectiveFunc aDetFunc(rDoc, mnScTab); + ScAddress aPosition; + ScRange aSourceRange; + bool bRedLine; + ScDetectiveObjType eObjType + = aDetFunc.GetDetectiveObjectType(pObject, mnScTab, aPosition, aSourceRange, bRedLine); + + if (eObjType != SC_DETOBJ_NONE) + return; + } + + sax_fastparser::FSHelperPtr pDrawing = rStrm.GetCurrentStream(); + + ShapeExport aDML(XML_xdr, pDrawing, nullptr, &rStrm, drawingml::DOCUMENT_XLSX); + auto pURLTransformer = std::make_shared<ScURLTransformer>(*mpDoc); + aDML.SetURLTranslator(pURLTransformer); + + pDrawing->startElement( FSNS( XML_xdr, XML_twoCellAnchor ), // OOXTODO: oneCellAnchor, absoluteAnchor + XML_editAs, GetEditAs( *this ) ); + Reference< XPropertySet > xPropSet( mxShape, UNO_QUERY ); + if (xPropSet.is()) + { + WriteFromTo( rStrm, *this ); + aDML.WriteShape( mxShape ); + } + + pDrawing->singleElement( FSNS( XML_xdr, XML_clientData) + // OOXTODO: XML_fLocksWithSheet + // OOXTODO: XML_fPrintsWithSheet + ); + pDrawing->endElement( FSNS( XML_xdr, XML_twoCellAnchor ) ); +} + +void ExcBof8_Base::SaveCont( XclExpStream& rStrm ) +{ + rStrm.DisableEncryption(); + rStrm << nVers << nDocType << nRupBuild << nRupYear + << sal_uInt32(0)/*nFileHistory*/ + << sal_uInt32(0x06) /*nLowestBiffVer = Biff8*/; +} + +sal_uInt16 ExcBof8_Base::GetNum() const +{ + return 0x0809; +} + +std::size_t ExcBof8_Base::GetLen() const +{ + return 16; +} + +// --- class ExcBof8 ------------------------------------------------- + +ExcBof8::ExcBof8() +{ + nDocType = 0x0010; +} + +// --- class ExcBofW8 ------------------------------------------------ + +ExcBofW8::ExcBofW8() +{ + nDocType = 0x0005; +} + +// --- class ExcBundlesheet8 ----------------------------------------- + +ExcBundlesheet8::ExcBundlesheet8( const RootData& rRootData, SCTAB _nTab ) : + ExcBundlesheetBase( rRootData, static_cast<sal_uInt16>(_nTab) ), + sUnicodeName( rRootData.pER->GetTabInfo().GetScTabName( _nTab ) ) +{ +} + +ExcBundlesheet8::ExcBundlesheet8( const OUString& rString ) : + sUnicodeName( rString ) +{ +} + +void ExcBundlesheet8::SaveCont( XclExpStream& rStrm ) +{ + m_nOwnPos = rStrm.GetSvStreamPos(); + // write dummy position, real position comes later + rStrm.DisableEncryption(); + rStrm << sal_uInt32(0); + rStrm.EnableEncryption(); + rStrm << nGrbit << GetName(); +} + +std::size_t ExcBundlesheet8::GetLen() const +{ // Text max 255 chars + return 8 + GetName().GetBufferSize(); +} + +void ExcBundlesheet8::SaveXml( XclExpXmlStream& rStrm ) +{ + OUString sId; + rStrm.CreateOutputStream( + XclXmlUtils::GetStreamName( "xl/", "worksheets/sheet", nTab+1), + XclXmlUtils::GetStreamName( nullptr, "worksheets/sheet", nTab+1), + rStrm.GetCurrentStream()->getOutputStream(), + "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml", + oox::getRelationship(Relationship::WORKSHEET), + &sId ); + + rStrm.GetCurrentStream()->singleElement( XML_sheet, + XML_name, sUnicodeName.toUtf8(), + XML_sheetId, OString::number( nTab+1 ), + XML_state, nGrbit == 0x0000 ? "visible" : "hidden", + FSNS( XML_r, XML_id ), sId.toUtf8() ); +} + +// --- class XclObproj ----------------------------------------------- + +sal_uInt16 XclObproj::GetNum() const +{ + return 0x00D3; +} + +std::size_t XclObproj::GetLen() const +{ + return 0; +} + +// ---- class XclCodename -------------------------------------------- + +XclCodename::XclCodename( const OUString& r ) : aName( r ) +{ +} + +void XclCodename::SaveCont( XclExpStream& rStrm ) +{ + rStrm << aName; +} + +sal_uInt16 XclCodename::GetNum() const +{ + return 0x01BA; +} + +std::size_t XclCodename::GetLen() const +{ + return aName.GetSize(); +} + +// ---- Scenarios ---------------------------------------------------- + +ExcEScenarioCell::ExcEScenarioCell( sal_uInt16 nC, sal_uInt16 nR, const OUString& rTxt ) : + nCol( nC ), + nRow( nR ), + sText( rTxt, XclStrFlags::NONE, 255 ) +{ +} + +void ExcEScenarioCell::WriteAddress( XclExpStream& rStrm ) const +{ + rStrm << nRow << nCol; +} + +void ExcEScenarioCell::WriteText( XclExpStream& rStrm ) const +{ + rStrm << sText; +} + +void ExcEScenarioCell::SaveXml( XclExpXmlStream& rStrm ) const +{ + rStrm.GetCurrentStream()->singleElement( XML_inputCells, + // OOXTODO: XML_deleted, + // OOXTODO: XML_numFmtId, + XML_r, XclXmlUtils::ToOString( rStrm.GetRoot().GetDoc(), ScAddress( nCol, nRow, 0 ) ), + // OOXTODO: XML_undone, + XML_val, XclXmlUtils::ToOString( sText ) ); +} + +ExcEScenario::ExcEScenario( const XclExpRoot& rRoot, SCTAB nTab ) +{ + OUString sTmpName; + OUString sTmpComm; + OUString aTmp; + Color aDummyCol; + ScScenarioFlags nFlags; + + ScDocument& rDoc = rRoot.GetDoc(); + rDoc.GetName(nTab, aTmp); + sTmpName = aTmp; + sName.Assign( sTmpName, XclStrFlags::EightBitLength ); + nRecLen = 8 + sName.GetBufferSize(); + + rDoc.GetScenarioData( nTab, aTmp, aDummyCol, nFlags ); + sTmpComm = aTmp; + sComment.Assign( sTmpComm, XclStrFlags::NONE, 255 ); + if( sComment.Len() ) + nRecLen += sComment.GetSize(); + bProtected = (nFlags & ScScenarioFlags::Protected) != ScScenarioFlags::NONE; + + sUserName.Assign( rRoot.GetUserName(), XclStrFlags::NONE, 255 ); + nRecLen += sUserName.GetSize(); + + const ScRangeList* pRList = rDoc.GetScenarioRanges( nTab ); + if( !pRList ) + return; + + bool bContLoop = true; + SCROW nRow; + SCCOL nCol; + OUString sText; + double fVal; + + for( size_t nRange = 0; (nRange < pRList->size()) && bContLoop; nRange++ ) + { + const ScRange & rRange = (*pRList)[nRange]; + for( nRow = rRange.aStart.Row(); (nRow <= rRange.aEnd.Row()) && bContLoop; nRow++ ) + for( nCol = rRange.aStart.Col(); (nCol <= rRange.aEnd.Col()) && bContLoop; nCol++ ) + { + if( rDoc.HasValueData( nCol, nRow, nTab ) ) + { + fVal = rDoc.GetValue( nCol, nRow, nTab ); + sText = ::rtl::math::doubleToUString( fVal, + rtl_math_StringFormat_Automatic, + rtl_math_DecimalPlaces_Max, + ScGlobal::getLocaleData().getNumDecimalSep()[0], + true ); + } + else + sText = rDoc.GetString(nCol, nRow, nTab); + bContLoop = Append( static_cast<sal_uInt16>(nCol), + static_cast<sal_uInt16>(nRow), sText ); + } + } +} + +bool ExcEScenario::Append( sal_uInt16 nCol, sal_uInt16 nRow, const OUString& rTxt ) +{ + if( aCells.size() == EXC_SCEN_MAXCELL ) + return false; + + ExcEScenarioCell aCell(nCol, nRow, rTxt); + aCells.push_back(aCell); + nRecLen += 6 + aCell.GetStringBytes(); // 4 bytes address, 2 bytes ifmt + return true; +} + +void ExcEScenario::SaveCont( XclExpStream& rStrm ) +{ + sal_uInt16 count = aCells.size(); + + rStrm << count // number of cells + << sal_uInt8(bProtected) // fProtection + << sal_uInt8(0) // fHidden + << static_cast<sal_uInt8>(sName.Len()) // length of scen name + << static_cast<sal_uInt8>(sComment.Len()) // length of comment + << static_cast<sal_uInt8>(sUserName.Len()); // length of user name + sName.WriteFlagField( rStrm ); + sName.WriteBuffer( rStrm ); + + rStrm << sUserName; + + if( sComment.Len() ) + rStrm << sComment; + + for( const auto& rCell : aCells ) + rCell.WriteAddress( rStrm ); // pos of cell + for( const auto& rCell : aCells ) + rCell.WriteText( rStrm ); // string content + rStrm.SetSliceSize( 2 ); + rStrm.WriteZeroBytes( 2 * count ); // date format +} + +sal_uInt16 ExcEScenario::GetNum() const +{ + return 0x00AF; +} + +std::size_t ExcEScenario::GetLen() const +{ + return nRecLen; +} + +void ExcEScenario::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream(); + rWorkbook->startElement( XML_scenario, + XML_name, XclXmlUtils::ToOString( sName ).getStr(), + XML_locked, ToPsz( bProtected ), + // OOXTODO: XML_hidden, + XML_count, OString::number( aCells.size() ).getStr(), + XML_user, XESTRING_TO_PSZ( sUserName ), + XML_comment, XESTRING_TO_PSZ( sComment ) ); + + for( const auto& rCell : aCells ) + rCell.SaveXml( rStrm ); + + rWorkbook->endElement( XML_scenario ); +} + +ExcEScenarioManager::ExcEScenarioManager( const XclExpRoot& rRoot, SCTAB nTab ) : + nActive( 0 ) +{ + ScDocument& rDoc = rRoot.GetDoc(); + if( rDoc.IsScenario( nTab ) ) + return; + + SCTAB nFirstTab = nTab + 1; + SCTAB nNewTab = nFirstTab; + + while( rDoc.IsScenario( nNewTab ) ) + { + aScenes.emplace_back( rRoot, nNewTab ); + + if( rDoc.IsActiveScenario( nNewTab ) ) + nActive = static_cast<sal_uInt16>(nNewTab - nFirstTab); + nNewTab++; + } +} + +ExcEScenarioManager::~ExcEScenarioManager() +{ +} + +void ExcEScenarioManager::SaveCont( XclExpStream& rStrm ) +{ + rStrm << static_cast<sal_uInt16>(aScenes.size()) // number of scenarios + << nActive // active scen + << nActive // last displayed + << sal_uInt16(0); // reference areas +} + +void ExcEScenarioManager::Save( XclExpStream& rStrm ) +{ + if( !aScenes.empty() ) + ExcRecord::Save( rStrm ); + + for( ExcEScenario& rScenario : aScenes ) + rScenario.Save( rStrm ); +} + +void ExcEScenarioManager::SaveXml( XclExpXmlStream& rStrm ) +{ + if( aScenes.empty() ) + return; + + sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream(); + rWorkbook->startElement( XML_scenarios, + XML_current, OString::number( nActive ), + XML_show, OString::number( nActive ) + // OOXTODO: XML_sqref + ); + + for( ExcEScenario& rScenario : aScenes ) + rScenario.SaveXml( rStrm ); + + rWorkbook->endElement( XML_scenarios ); +} + +sal_uInt16 ExcEScenarioManager::GetNum() const +{ + return 0x00AE; +} + +std::size_t ExcEScenarioManager::GetLen() const +{ + return 8; +} + +namespace { + +struct XclExpTabProtectOption +{ + ScTableProtection::Option eOption; + sal_uInt16 nMask; +}; + +} + +XclExpSheetProtectOptions::XclExpSheetProtectOptions( const XclExpRoot& rRoot, SCTAB nTab ) : + XclExpRecord( 0x0867, 23 ) +{ + static const XclExpTabProtectOption aTable[] = + { + { ScTableProtection::OBJECTS, 0x0001 }, + { ScTableProtection::SCENARIOS, 0x0002 }, + { ScTableProtection::FORMAT_CELLS, 0x0004 }, + { ScTableProtection::FORMAT_COLUMNS, 0x0008 }, + { ScTableProtection::FORMAT_ROWS, 0x0010 }, + { ScTableProtection::INSERT_COLUMNS, 0x0020 }, + { ScTableProtection::INSERT_ROWS, 0x0040 }, + { ScTableProtection::INSERT_HYPERLINKS, 0x0080 }, + + { ScTableProtection::DELETE_COLUMNS, 0x0100 }, + { ScTableProtection::DELETE_ROWS, 0x0200 }, + { ScTableProtection::SELECT_LOCKED_CELLS, 0x0400 }, + { ScTableProtection::SORT, 0x0800 }, + { ScTableProtection::AUTOFILTER, 0x1000 }, + { ScTableProtection::PIVOT_TABLES, 0x2000 }, + { ScTableProtection::SELECT_UNLOCKED_CELLS, 0x4000 }, + + { ScTableProtection::NONE, 0x0000 } + }; + + mnOptions = 0x0000; + const ScTableProtection* pProtect = rRoot.GetDoc().GetTabProtection(nTab); + if (!pProtect) + return; + + for (int i = 0; aTable[i].nMask != 0x0000; ++i) + { + if ( pProtect->isOptionEnabled(aTable[i].eOption) ) + mnOptions |= aTable[i].nMask; + } +} + +void XclExpSheetProtectOptions::WriteBody( XclExpStream& rStrm ) +{ + sal_uInt16 nBytes = 0x0867; + rStrm << nBytes; + + for (int i = 0; i < 9; ++i) + rStrm << static_cast<unsigned char>(0); + + nBytes = 0x0200; + rStrm << nBytes; + nBytes = 0x0100; + rStrm << nBytes; + nBytes = 0xFFFF; + rStrm << nBytes << nBytes; + + rStrm << mnOptions; + nBytes = 0; + rStrm << nBytes; +} + +XclExpSheetEnhancedProtection::XclExpSheetEnhancedProtection( const XclExpRoot& rRoot, + const ScEnhancedProtection & rProt ) : + XclExpRecord( 0x0868 ), + mrRoot( rRoot ), + maEnhancedProtection( rProt ) +{ +} + +void XclExpSheetEnhancedProtection::WriteBody( XclExpStream& rStrm ) +{ + sal_uInt16 const nRecordType = 0x0868; + rStrm << nRecordType; // frtHeader rt + rStrm.WriteZeroBytesToRecord(10); // frtHeader unused + rStrm << EXC_ISFPROTECTION; // isf + rStrm.WriteZeroBytesToRecord(5); // reserved1 (1 bytes) and reserved2 (4 bytes) + + XclRangeList aRefs; + if (maEnhancedProtection.maRangeList.is()) + mrRoot.GetAddressConverter().ConvertRangeList( aRefs, *maEnhancedProtection.maRangeList, false); + sal_uInt16 nCref = ulimit_cast<sal_uInt16>(aRefs.size()); + rStrm << nCref; // cref + rStrm.WriteZeroBytesToRecord(6); // cbFeatData if EXC_ISFFEC2 (4 bytes) and reserved3 (2 bytes) + aRefs.Write( rStrm, true, nCref); // refs + + // FeatProtection structure + rStrm << maEnhancedProtection.mnAreserved; // 1 bit A and 31 bits reserved + rStrm << maEnhancedProtection.mnPasswordVerifier; // wPassword + rStrm << XclExpString( maEnhancedProtection.maTitle); // stTitle + bool bSDContainer = ((maEnhancedProtection.mnAreserved & 0x00000001) == 0x00000001); + sal_uInt32 nCbSD = maEnhancedProtection.maSecurityDescriptor.size(); + SAL_WARN_IF( bSDContainer && nCbSD < 20, "sc.filter", + "XclExpSheetEnhancedProtection A flag indicates container but cbSD < 20"); + SAL_WARN_IF( !bSDContainer && nCbSD > 0, "sc.filter", + "XclExpSheetEnhancedProtection A flag indicates no container but cbSD > 0"); + if (bSDContainer) + { + rStrm << nCbSD; + rStrm.Write( &maEnhancedProtection.maSecurityDescriptor.front(), nCbSD); + } +} + +void XclCalccount::SaveCont( XclExpStream& rStrm ) +{ + rStrm << nCount; +} + +XclCalccount::XclCalccount( const ScDocument& rDoc ) +{ + nCount = rDoc.GetDocOptions().GetIterCount(); +} + +sal_uInt16 XclCalccount::GetNum() const +{ + return 0x000C; +} + +std::size_t XclCalccount::GetLen() const +{ + return 2; +} + +void XclCalccount::SaveXml( XclExpXmlStream& rStrm ) +{ + rStrm.WriteAttributes(XML_iterateCount, OUString::number(nCount)); +} + +void XclIteration::SaveCont( XclExpStream& rStrm ) +{ + rStrm << nIter; +} + +XclIteration::XclIteration( const ScDocument& rDoc ) +{ + nIter = rDoc.GetDocOptions().IsIter()? 1 : 0; +} + +sal_uInt16 XclIteration::GetNum() const +{ + return 0x0011; +} + +std::size_t XclIteration::GetLen() const +{ + return 2; +} + +void XclIteration::SaveXml( XclExpXmlStream& rStrm ) +{ + rStrm.WriteAttributes(XML_iterate, ToPsz(nIter == 1)); +} + +void XclDelta::SaveCont( XclExpStream& rStrm ) +{ + rStrm << fDelta; +} + +XclDelta::XclDelta( const ScDocument& rDoc ) +{ + fDelta = rDoc.GetDocOptions().GetIterEps(); +} + +sal_uInt16 XclDelta::GetNum() const +{ + return 0x0010; +} + +std::size_t XclDelta::GetLen() const +{ + return 8; +} + +void XclDelta::SaveXml( XclExpXmlStream& rStrm ) +{ + rStrm.WriteAttributes(XML_iterateDelta, OUString::number(fDelta)); +} + +XclExpFileEncryption::XclExpFileEncryption( const XclExpRoot& rRoot ) : + XclExpRecord(0x002F, 54), + mrRoot(rRoot) +{ +} + +XclExpFileEncryption::~XclExpFileEncryption() +{ +} + +void XclExpFileEncryption::WriteBody( XclExpStream& rStrm ) +{ + // 0x0000 - neither standard nor strong encryption + // 0x0001 - standard or strong encryption + rStrm << static_cast<sal_uInt16>(0x0001); + + // 0x0000 - non standard encryption + // 0x0001 - standard encryption + sal_uInt16 nStdEnc = 0x0001; + rStrm << nStdEnc << nStdEnc; + + sal_uInt8 pnDocId[16]; + sal_uInt8 pnSalt[16]; + sal_uInt8 pnSaltHash[16]; + XclExpEncrypterRef xEnc = std::make_shared<XclExpBiff8Encrypter>(mrRoot); + xEnc->GetDocId(pnDocId); + xEnc->GetSalt(pnSalt); + xEnc->GetSaltDigest(pnSaltHash); + + rStrm.Write(pnDocId, 16); + rStrm.Write(pnSalt, 16); + rStrm.Write(pnSaltHash, 16); + + rStrm.SetEncrypter(xEnc); +} + +XclExpInterfaceHdr::XclExpInterfaceHdr( sal_uInt16 nCodePage ) : + XclExpUInt16Record( EXC_ID_INTERFACEHDR, nCodePage ) +{ +} + +void XclExpInterfaceHdr::WriteBody( XclExpStream& rStrm ) +{ + rStrm.DisableEncryption(); + rStrm << GetValue(); +} + +XclExpInterfaceEnd::XclExpInterfaceEnd() : + XclExpRecord(0x00E2, 0) {} + +XclExpInterfaceEnd::~XclExpInterfaceEnd() {} + +void XclExpInterfaceEnd::WriteBody( XclExpStream& rStrm ) +{ + // Don't forget to re-enable encryption. + rStrm.EnableEncryption(); +} + +XclExpWriteAccess::XclExpWriteAccess() : + XclExpRecord(0x005C, 112) +{ +} + +XclExpWriteAccess::~XclExpWriteAccess() +{ +} + +void XclExpWriteAccess::WriteBody( XclExpStream& rStrm ) +{ + static const sal_uInt8 aData[] = { + 0x04, 0x00, 0x00, 'C', 'a', 'l', 'c', 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, 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, 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, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }; + + for (std::size_t i = 0; i < sizeof(aData); ++i) + rStrm << aData[i]; +} + +XclExpFileSharing::XclExpFileSharing( const XclExpRoot& rRoot, sal_uInt16 nPasswordHash, bool bRecommendReadOnly ) : + XclExpRecord( EXC_ID_FILESHARING ), + mnPasswordHash( nPasswordHash ), + mbRecommendReadOnly( bRecommendReadOnly ) +{ + if( rRoot.GetBiff() <= EXC_BIFF5 ) + maUserName.AssignByte( rRoot.GetUserName(), rRoot.GetTextEncoding(), XclStrFlags::EightBitLength ); + else + maUserName.Assign( rRoot.GetUserName() ); +} + +void XclExpFileSharing::Save( XclExpStream& rStrm ) +{ + if( (mnPasswordHash != 0) || mbRecommendReadOnly ) + XclExpRecord::Save( rStrm ); +} + +void XclExpFileSharing::WriteBody( XclExpStream& rStrm ) +{ + rStrm << sal_uInt16( mbRecommendReadOnly ? 1 : 0 ) << mnPasswordHash << maUserName; +} + +XclExpProt4Rev::XclExpProt4Rev() : + XclExpRecord(0x01AF, 2) +{ +} + +XclExpProt4Rev::~XclExpProt4Rev() +{ +} + +void XclExpProt4Rev::WriteBody( XclExpStream& rStrm ) +{ + rStrm << static_cast<sal_uInt16>(0x0000); +} + +XclExpProt4RevPass::XclExpProt4RevPass() : + XclExpRecord(0x01BC, 2) +{ +} + +XclExpProt4RevPass::~XclExpProt4RevPass() +{ +} + +void XclExpProt4RevPass::WriteBody( XclExpStream& rStrm ) +{ + rStrm << static_cast<sal_uInt16>(0x0000); +} + +const sal_uInt8 nDataRecalcId[] = { + 0xC1, 0x01, 0x00, 0x00, 0x54, 0x8D, 0x01, 0x00 +}; + +XclExpRecalcId::XclExpRecalcId() : + XclExpDummyRecord(0x01C1, nDataRecalcId, sizeof(nDataRecalcId)) +{ +} + +const sal_uInt8 nDataBookExt[] = { + 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02 +}; + +XclExpBookExt::XclExpBookExt() : + XclExpDummyRecord(0x0863, nDataBookExt, sizeof(nDataBookExt)) +{ +} + +XclRefmode::XclRefmode( const ScDocument& rDoc ) : + XclExpBoolRecord( 0x000F, rDoc.GetAddressConvention() != formula::FormulaGrammar::CONV_XL_R1C1 ) +{ +} + +void XclRefmode::SaveXml( XclExpXmlStream& rStrm ) +{ + rStrm.WriteAttributes(XML_refMode, GetBool() ? "A1" : "R1C1"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |