diff options
Diffstat (limited to 'sc/source/filter/excel/xiescher.cxx')
-rw-r--r-- | sc/source/filter/excel/xiescher.cxx | 4453 |
1 files changed, 4453 insertions, 0 deletions
diff --git a/sc/source/filter/excel/xiescher.cxx b/sc/source/filter/excel/xiescher.cxx new file mode 100644 index 000000000..1de9da95d --- /dev/null +++ b/sc/source/filter/excel/xiescher.cxx @@ -0,0 +1,4453 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <xiescher.hxx> + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/awt/PushButtonType.hpp> +#include <com/sun/star/awt/ScrollBarOrientation.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/style/VerticalAlignment.hpp> +#include <com/sun/star/drawing/XControlShape.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <com/sun/star/form/XFormsSupplier.hpp> +#include <com/sun/star/form/binding/XBindableValue.hpp> +#include <com/sun/star/form/binding/XValueBinding.hpp> +#include <com/sun/star/form/binding/XListEntrySink.hpp> +#include <com/sun/star/form/binding/XListEntrySource.hpp> +#include <com/sun/star/script/ScriptEventDescriptor.hpp> +#include <com/sun/star/script/XEventAttacherManager.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XModel.hpp> + +#include <sfx2/objsh.hxx> +#include <unotools/moduleoptions.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/fltrcfg.hxx> +#include <vcl/dibtools.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/outdev.hxx> +#include <vcl/wmf.hxx> +#include <comphelper/classids.hxx> +#include <comphelper/documentinfo.hxx> +#include <o3tl/safeint.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <sal/log.hxx> + +#include <svx/svdopath.hxx> +#include <svx/svdocirc.hxx> +#include <svx/svdoedge.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdoashp.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdouno.hxx> +#include <svx/svdpage.hxx> +#include <editeng/editobj.hxx> +#include <editeng/outliner.hxx> +#include <editeng/outlobj.hxx> +#include <svx/svditer.hxx> +#include <editeng/writingmodeitem.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xlnedcit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xlnedwit.hxx> +#include <svx/xlnstcit.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnstwit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/sdasitm.hxx> +#include <svx/sdshcitm.hxx> +#include <svx/sdshitm.hxx> +#include <svx/sdsxyitm.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/sdtditm.hxx> + +#include <editeng/eeitem.hxx> +#include <svx/xflclit.hxx> +#include <sal/macros.h> +#include <editeng/adjustitem.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlinjoit.hxx> +#include <svx/xlntrit.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xbitmap.hxx> +#include <svtools/embedhlp.hxx> +#include <sot/storage.hxx> + +#include <document.hxx> +#include <drwlayer.hxx> +#include <userdat.hxx> +#include <unonames.hxx> +#include <convuno.hxx> +#include <postit.hxx> +#include <globstr.hrc> +#include <scresid.hxx> + +#include <fprogressbar.hxx> +#include <xltracer.hxx> +#include <xistream.hxx> +#include <xihelper.hxx> +#include <xiformula.hxx> +#include <xilink.hxx> +#include <xistyle.hxx> +#include <xipage.hxx> +#include <xichart.hxx> +#include <xicontent.hxx> +#include <scextopt.hxx> + +#include <namebuff.hxx> +#include <sfx2/docfile.hxx> +#include <memory> +#include <numeric> +#include <string_view> +#include <utility> + +using namespace com::sun::star; +using ::com::sun::star::uno::Any; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::UNO_SET_THROW; +using ::com::sun::star::beans::NamedValue; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::container::XIndexContainer; +using ::com::sun::star::container::XNameContainer; +using ::com::sun::star::frame::XModel; +using ::com::sun::star::awt::XControlModel; +using ::com::sun::star::embed::XEmbeddedObject; +using ::com::sun::star::embed::XEmbedPersist; +using ::com::sun::star::drawing::XControlShape; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::form::XFormComponent; +using ::com::sun::star::form::XFormsSupplier; +using ::com::sun::star::form::binding::XBindableValue; +using ::com::sun::star::form::binding::XValueBinding; +using ::com::sun::star::form::binding::XListEntrySink; +using ::com::sun::star::form::binding::XListEntrySource; +using ::com::sun::star::script::ScriptEventDescriptor; +using ::com::sun::star::script::XEventAttacherManager; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; + +// Drawing objects ============================================================ + +XclImpDrawObjBase::XclImpDrawObjBase( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + mnObjId( EXC_OBJ_INVALID_ID ), + mnTab( 0 ), + mnObjType( EXC_OBJTYPE_UNKNOWN ), + mnDffShapeId( 0 ), + mnDffFlags( ShapeFlag::NONE ), + mbHasAnchor( false ), + mbHidden( false ), + mbVisible( true ), + mbPrintable( true ), + mbAreaObj( false ), + mbAutoMargin( true ), + mbSimpleMacro( true ), + mbProcessSdr( true ), + mbInsertSdr( true ), + mbCustomDff( false ), + mbNotifyMacroEventRead( false ) +{ +} + +XclImpDrawObjBase::~XclImpDrawObjBase() +{ +} + +XclImpDrawObjRef XclImpDrawObjBase::ReadObj3( const XclImpRoot& rRoot, XclImpStream& rStrm ) +{ + XclImpDrawObjRef xDrawObj; + + if( rStrm.GetRecLeft() >= 30 ) + { + sal_uInt16 nObjType; + rStrm.Ignore( 4 ); + nObjType = rStrm.ReaduInt16(); + switch( nObjType ) + { + case EXC_OBJTYPE_GROUP: xDrawObj= std::make_shared<XclImpGroupObj>( rRoot ); break; + case EXC_OBJTYPE_LINE: xDrawObj= std::make_shared<XclImpLineObj>( rRoot ); break; + case EXC_OBJTYPE_RECTANGLE: xDrawObj= std::make_shared<XclImpRectObj>( rRoot ); break; + case EXC_OBJTYPE_OVAL: xDrawObj= std::make_shared<XclImpOvalObj>( rRoot ); break; + case EXC_OBJTYPE_ARC: xDrawObj= std::make_shared<XclImpArcObj>( rRoot ); break; + case EXC_OBJTYPE_CHART: xDrawObj= std::make_shared<XclImpChartObj>( rRoot ); break; + case EXC_OBJTYPE_TEXT: xDrawObj= std::make_shared<XclImpTextObj>( rRoot ); break; + case EXC_OBJTYPE_BUTTON: xDrawObj= std::make_shared<XclImpButtonObj>( rRoot ); break; + case EXC_OBJTYPE_PICTURE: xDrawObj= std::make_shared<XclImpPictureObj>( rRoot ); break; + default: + SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj3 - unknown object type 0x" << std::hex << nObjType ); + rRoot.GetTracer().TraceUnsupportedObjects(); + } + } + + if (!xDrawObj) + { + xDrawObj = std::make_shared<XclImpPhObj>(rRoot); + } + + xDrawObj->mnTab = rRoot.GetCurrScTab(); + xDrawObj->ImplReadObj3( rStrm ); + return xDrawObj; +} + +XclImpDrawObjRef XclImpDrawObjBase::ReadObj4( const XclImpRoot& rRoot, XclImpStream& rStrm ) +{ + XclImpDrawObjRef xDrawObj; + + if( rStrm.GetRecLeft() >= 30 ) + { + sal_uInt16 nObjType; + rStrm.Ignore( 4 ); + nObjType = rStrm.ReaduInt16(); + switch( nObjType ) + { + case EXC_OBJTYPE_GROUP: xDrawObj = std::make_shared<XclImpGroupObj>( rRoot ); break; + case EXC_OBJTYPE_LINE: xDrawObj = std::make_shared<XclImpLineObj>( rRoot ); break; + case EXC_OBJTYPE_RECTANGLE: xDrawObj = std::make_shared<XclImpRectObj>( rRoot ); break; + case EXC_OBJTYPE_OVAL: xDrawObj = std::make_shared<XclImpOvalObj>( rRoot ); break; + case EXC_OBJTYPE_ARC: xDrawObj = std::make_shared<XclImpArcObj>( rRoot ); break; + case EXC_OBJTYPE_CHART: xDrawObj = std::make_shared<XclImpChartObj>( rRoot ); break; + case EXC_OBJTYPE_TEXT: xDrawObj = std::make_shared<XclImpTextObj>( rRoot ); break; + case EXC_OBJTYPE_BUTTON: xDrawObj = std::make_shared<XclImpButtonObj>( rRoot ); break; + case EXC_OBJTYPE_PICTURE: xDrawObj = std::make_shared<XclImpPictureObj>( rRoot ); break; + case EXC_OBJTYPE_POLYGON: xDrawObj = std::make_shared<XclImpPolygonObj>( rRoot ); break; + default: + SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj4 - unknown object type 0x" << std::hex << nObjType ); + rRoot.GetTracer().TraceUnsupportedObjects(); + } + } + + if (!xDrawObj) + { + xDrawObj = std::make_shared<XclImpPhObj>(rRoot); + } + + xDrawObj->mnTab = rRoot.GetCurrScTab(); + xDrawObj->ImplReadObj4( rStrm ); + return xDrawObj; +} + +XclImpDrawObjRef XclImpDrawObjBase::ReadObj5( const XclImpRoot& rRoot, XclImpStream& rStrm ) +{ + XclImpDrawObjRef xDrawObj; + + if( rStrm.GetRecLeft() >= 34 ) + { + sal_uInt16 nObjType(EXC_OBJTYPE_UNKNOWN); + rStrm.Ignore( 4 ); + nObjType = rStrm.ReaduInt16(); + switch( nObjType ) + { + case EXC_OBJTYPE_GROUP: xDrawObj = std::make_shared<XclImpGroupObj>( rRoot ); break; + case EXC_OBJTYPE_LINE: xDrawObj = std::make_shared<XclImpLineObj>( rRoot ); break; + case EXC_OBJTYPE_RECTANGLE: xDrawObj = std::make_shared<XclImpRectObj>( rRoot ); break; + case EXC_OBJTYPE_OVAL: xDrawObj = std::make_shared<XclImpOvalObj>( rRoot ); break; + case EXC_OBJTYPE_ARC: xDrawObj = std::make_shared<XclImpArcObj>( rRoot ); break; + case EXC_OBJTYPE_CHART: xDrawObj = std::make_shared<XclImpChartObj>( rRoot ); break; + case EXC_OBJTYPE_TEXT: xDrawObj = std::make_shared<XclImpTextObj>( rRoot ); break; + case EXC_OBJTYPE_BUTTON: xDrawObj = std::make_shared<XclImpButtonObj>( rRoot ); break; + case EXC_OBJTYPE_PICTURE: xDrawObj = std::make_shared<XclImpPictureObj>( rRoot ); break; + case EXC_OBJTYPE_POLYGON: xDrawObj = std::make_shared<XclImpPolygonObj>( rRoot ); break; + case EXC_OBJTYPE_CHECKBOX: xDrawObj = std::make_shared<XclImpCheckBoxObj>( rRoot ); break; + case EXC_OBJTYPE_OPTIONBUTTON: xDrawObj = std::make_shared<XclImpOptionButtonObj>( rRoot ); break; + case EXC_OBJTYPE_EDIT: xDrawObj = std::make_shared<XclImpEditObj>( rRoot ); break; + case EXC_OBJTYPE_LABEL: xDrawObj = std::make_shared<XclImpLabelObj>( rRoot ); break; + case EXC_OBJTYPE_DIALOG: xDrawObj = std::make_shared<XclImpDialogObj>( rRoot ); break; + case EXC_OBJTYPE_SPIN: xDrawObj = std::make_shared<XclImpSpinButtonObj>( rRoot ); break; + case EXC_OBJTYPE_SCROLLBAR: xDrawObj = std::make_shared<XclImpScrollBarObj>( rRoot ); break; + case EXC_OBJTYPE_LISTBOX: xDrawObj = std::make_shared<XclImpListBoxObj>( rRoot ); break; + case EXC_OBJTYPE_GROUPBOX: xDrawObj = std::make_shared<XclImpGroupBoxObj>( rRoot ); break; + case EXC_OBJTYPE_DROPDOWN: xDrawObj = std::make_shared<XclImpDropDownObj>( rRoot ); break; + default: + SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj5 - unknown object type 0x" << std::hex << nObjType ); + rRoot.GetTracer().TraceUnsupportedObjects(); + xDrawObj = std::make_shared<XclImpPhObj>( rRoot ); + } + } + + OSL_ENSURE(xDrawObj, "object import failed"); + + if (xDrawObj) + { + xDrawObj->mnTab = rRoot.GetCurrScTab(); + xDrawObj->ImplReadObj5( rStrm ); + } + return xDrawObj; +} + +XclImpDrawObjRef XclImpDrawObjBase::ReadObj8( const XclImpRoot& rRoot, XclImpStream& rStrm ) +{ + XclImpDrawObjRef xDrawObj; + + if( rStrm.GetRecLeft() >= 10 ) + { + sal_uInt16 nSubRecId(0), nSubRecSize(0), nObjType(0); + nSubRecId = rStrm.ReaduInt16(); + nSubRecSize = rStrm.ReaduInt16(); + nObjType = rStrm.ReaduInt16(); + OSL_ENSURE( nSubRecId == EXC_ID_OBJCMO, "XclImpDrawObjBase::ReadObj8 - OBJCMO subrecord expected" ); + if( (nSubRecId == EXC_ID_OBJCMO) && (nSubRecSize >= 6) ) + { + switch( nObjType ) + { + // in BIFF8, all simple objects support text + case EXC_OBJTYPE_LINE: + case EXC_OBJTYPE_ARC: + xDrawObj = std::make_shared<XclImpTextObj>( rRoot ); + // lines and arcs may be 2-dimensional + xDrawObj->SetAreaObj( false ); + break; + + // in BIFF8, all simple objects support text + case EXC_OBJTYPE_RECTANGLE: + case EXC_OBJTYPE_OVAL: + case EXC_OBJTYPE_POLYGON: + case EXC_OBJTYPE_DRAWING: + case EXC_OBJTYPE_TEXT: + xDrawObj = std::make_shared<XclImpTextObj>( rRoot ); + break; + + case EXC_OBJTYPE_GROUP: xDrawObj = std::make_shared<XclImpGroupObj>( rRoot ); break; + case EXC_OBJTYPE_CHART: xDrawObj = std::make_shared<XclImpChartObj>( rRoot ); break; + case EXC_OBJTYPE_BUTTON: xDrawObj = std::make_shared<XclImpButtonObj>( rRoot ); break; + case EXC_OBJTYPE_PICTURE: xDrawObj = std::make_shared<XclImpPictureObj>( rRoot ); break; + case EXC_OBJTYPE_CHECKBOX: xDrawObj = std::make_shared<XclImpCheckBoxObj>( rRoot ); break; + case EXC_OBJTYPE_OPTIONBUTTON: xDrawObj = std::make_shared<XclImpOptionButtonObj>( rRoot ); break; + case EXC_OBJTYPE_EDIT: xDrawObj = std::make_shared<XclImpEditObj>( rRoot ); break; + case EXC_OBJTYPE_LABEL: xDrawObj = std::make_shared<XclImpLabelObj>( rRoot ); break; + case EXC_OBJTYPE_DIALOG: xDrawObj = std::make_shared<XclImpDialogObj>( rRoot ); break; + case EXC_OBJTYPE_SPIN: xDrawObj = std::make_shared<XclImpSpinButtonObj>( rRoot ); break; + case EXC_OBJTYPE_SCROLLBAR: xDrawObj = std::make_shared<XclImpScrollBarObj>( rRoot ); break; + case EXC_OBJTYPE_LISTBOX: xDrawObj = std::make_shared<XclImpListBoxObj>( rRoot ); break; + case EXC_OBJTYPE_GROUPBOX: xDrawObj = std::make_shared<XclImpGroupBoxObj>( rRoot ); break; + case EXC_OBJTYPE_DROPDOWN: xDrawObj = std::make_shared<XclImpDropDownObj>( rRoot ); break; + case EXC_OBJTYPE_NOTE: xDrawObj = std::make_shared<XclImpNoteObj>( rRoot ); break; + + default: + SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj8 - unknown object type 0x" << std::hex << nObjType ); + rRoot.GetTracer().TraceUnsupportedObjects(); + } + } + } + + if (!xDrawObj) //ensure placeholder for unknown or broken records + { + SAL_WARN( "sc.filter", "XclImpDrawObjBase::ReadObj8 import failed, substituting placeholder"); + xDrawObj = std::make_shared<XclImpPhObj>( rRoot ); + } + + xDrawObj->mnTab = rRoot.GetCurrScTab(); + xDrawObj->ImplReadObj8( rStrm ); + return xDrawObj; +} + +void XclImpDrawObjBase::SetAnchor( const XclObjAnchor& rAnchor ) +{ + maAnchor = rAnchor; + mbHasAnchor = true; +} + +void XclImpDrawObjBase::SetDffData( + const DffObjData& rDffObjData, const OUString& rObjName, const OUString& rHyperlink, + bool bVisible, bool bAutoMargin ) +{ + mnDffShapeId = rDffObjData.nShapeId; + mnDffFlags = rDffObjData.nSpFlags; + maObjName = rObjName; + maHyperlink = rHyperlink; + mbVisible = bVisible; + mbAutoMargin = bAutoMargin; +} + +OUString XclImpDrawObjBase::GetObjName() const +{ + /* #i51348# Always return a non-empty name. Create English + default names depending on the object type. This is not implemented as + virtual functions in derived classes, as class type and object type may + not match. */ + return maObjName.isEmpty() ? GetObjectManager().GetDefaultObjName(*this) : maObjName; +} + +const XclObjAnchor* XclImpDrawObjBase::GetAnchor() const +{ + return mbHasAnchor ? &maAnchor : nullptr; +} + +bool XclImpDrawObjBase::IsValidSize( const tools::Rectangle& rAnchorRect ) const +{ + // XclObjAnchor rounds up the width, width of 3 is the result of an Excel width of 0 + return mbAreaObj ? + ((rAnchorRect.GetWidth() > 3) && (rAnchorRect.GetHeight() > 1)) : + ((rAnchorRect.GetWidth() > 3) || (rAnchorRect.GetHeight() > 1)); +} + +ScRange XclImpDrawObjBase::GetUsedArea( SCTAB nScTab ) const +{ + ScRange aScUsedArea( ScAddress::INITIALIZE_INVALID ); + // #i44077# object inserted -> update used area for OLE object import + if( mbHasAnchor && GetAddressConverter().ConvertRange( aScUsedArea, maAnchor, nScTab, nScTab, false ) ) + { + // reduce range, if object ends directly on borders between two columns or rows + if( (maAnchor.mnRX == 0) && (aScUsedArea.aStart.Col() < aScUsedArea.aEnd.Col()) ) + aScUsedArea.aEnd.IncCol( -1 ); + if( (maAnchor.mnBY == 0) && (aScUsedArea.aStart.Row() < aScUsedArea.aEnd.Row()) ) + aScUsedArea.aEnd.IncRow( -1 ); + } + return aScUsedArea; +} + +std::size_t XclImpDrawObjBase::GetProgressSize() const +{ + return DoGetProgressSize(); +} + +SdrObjectUniquePtr XclImpDrawObjBase::CreateSdrObject( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect, bool bIsDff ) const +{ + SdrObjectUniquePtr xSdrObj; + if( bIsDff && !mbCustomDff ) + { + rDffConv.Progress( GetProgressSize() ); + } + else + { + xSdrObj = DoCreateSdrObj( rDffConv, rAnchorRect ); + + //added for exporting OCX control + /* mnObjType value set should be as below table: + 0x0000 Group 0x0001 Line + 0x0002 Rectangle 0x0003 Oval + 0x0004 Arc 0x0005 Chart + 0x0006 Text 0x0009 Polygon + +-----------------------------------------------------+ + OCX ==>| 0x0008 Picture | + +-----------------------------------------------------+ + | 0x0007 Button | + | 0x000B Checkbox 0x000C Radio button | + | 0x000D Edit box 0x000E Label | + TBX ==> | 0x000F Dialog box 0x0010 Spin control | + | 0x0011 Scrollbar 0x0012 List | + | 0x0013 Group box 0x0014 Dropdown list | + +-----------------------------------------------------+ + 0x0019 Note 0x001E OfficeArt object + */ + if( xSdrObj && xSdrObj->IsUnoObj() && + ( (mnObjType < 25 && mnObjType > 10) || mnObjType == 7 || mnObjType == 8 ) ) + { + SdrUnoObj* pSdrUnoObj = dynamic_cast< SdrUnoObj* >( xSdrObj.get() ); + if( pSdrUnoObj != nullptr ) + { + const Reference< XControlModel >& xCtrlModel = pSdrUnoObj->GetUnoControlModel(); + Reference< XPropertySet > xPropSet(xCtrlModel,UNO_QUERY); + static constexpr OUStringLiteral sPropertyName(u"ControlTypeinMSO"); + + enum { eCreateFromOffice = 0, eCreateFromMSTBXControl, eCreateFromMSOCXControl }; + + if( mnObjType == 7 || (mnObjType < 25 && mnObjType > 10) )//TBX + { + try + { + //Need summary type for export. Detail type(checkbox, button ...) has been contained by mnObjType + const sal_Int16 nTBXControlType = eCreateFromMSTBXControl ; + xPropSet->setPropertyValue(sPropertyName, Any(nTBXControlType)); + } + catch(const Exception&) + { + SAL_WARN("sc.filter", "XclImpDrawObjBase::CreateSdrObject, this control can't be set the property ControlTypeinMSO!"); + } + } + if( mnObjType == 8 )//OCX + { + //Need summary type for export + static constexpr OUStringLiteral sObjIdPropertyName(u"ObjIDinMSO"); + const XclImpPictureObj* const pObj = dynamic_cast< const XclImpPictureObj* const >(this); + if( pObj != nullptr && pObj->IsOcxControl() ) + { + try + { + const sal_Int16 nOCXControlType = eCreateFromMSOCXControl; + xPropSet->setPropertyValue(sPropertyName, Any(nOCXControlType)); + //Detail type(checkbox, button ...) + xPropSet->setPropertyValue(sObjIdPropertyName, Any(sal_uInt16(mnObjId))); + } + catch(const Exception&) + { + SAL_WARN("sc.filter", "XclImpDrawObjBase::CreateSdrObject, this control can't be set the property ObjIDinMSO!"); + } + } + } + + } + } + } + return xSdrObj; +} + +void XclImpDrawObjBase::NotifyMacroEventRead() +{ + if (mbNotifyMacroEventRead) + return; + SfxObjectShell* pDocShell = GetDocShell(); + if (!pDocShell) + return; + comphelper::DocumentInfo::notifyMacroEventRead(pDocShell->GetModel()); + mbNotifyMacroEventRead = true; +} + +void XclImpDrawObjBase::PreProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) +{ + // default: front layer, derived classes may have to set other layer in DoPreProcessSdrObj() + rSdrObj.NbcSetLayer( SC_LAYER_FRONT ); + + // set object name (GetObjName() will always return a non-empty name) + rSdrObj.SetName( GetObjName() ); + + // #i39167# full width for all objects regardless of horizontal alignment + rSdrObj.SetMergedItem( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_BLOCK ) ); + + // automatic text margin + if( mbAutoMargin ) + { + sal_Int32 nMargin = rDffConv.GetDefaultTextMargin(); + rSdrObj.SetMergedItem( makeSdrTextLeftDistItem( nMargin ) ); + rSdrObj.SetMergedItem( makeSdrTextRightDistItem( nMargin ) ); + rSdrObj.SetMergedItem( makeSdrTextUpperDistItem( nMargin ) ); + rSdrObj.SetMergedItem( makeSdrTextLowerDistItem( nMargin ) ); + } + + // macro and hyperlink + // removed oracle/sun check for mbSimpleMacro ( no idea what its for ) + if (!maMacroName.isEmpty()) + { + if( ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( &rSdrObj, true ) ) + { + OUString sMacro = XclTools::GetSbMacroUrl(maMacroName, GetDocShell()); + if (!sMacro.isEmpty()) + NotifyMacroEventRead(); + pInfo->SetMacro(sMacro); + } + } + if (!maHyperlink.isEmpty()) + rSdrObj.setHyperlink(maHyperlink); + + // call virtual function for object type specific processing + DoPreProcessSdrObj( rDffConv, rSdrObj ); +} + +void XclImpDrawObjBase::PostProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const +{ + // call virtual function for object type specific processing + DoPostProcessSdrObj( rDffConv, rSdrObj ); +} + +// protected ------------------------------------------------------------------ + +void XclImpDrawObjBase::ReadName5( XclImpStream& rStrm, sal_uInt16 nNameLen ) +{ + maObjName.clear(); + if( nNameLen > 0 ) + { + // name length field is repeated before the name + maObjName = rStrm.ReadByteString( false ); + // skip padding byte for word boundaries + if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 ); + } +} + +void XclImpDrawObjBase::ReadMacro3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + maMacroName.clear(); + rStrm.Ignore( nMacroSize ); + // skip padding byte for word boundaries, not contained in nMacroSize + if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 ); +} + +void XclImpDrawObjBase::ReadMacro4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + maMacroName.clear(); + rStrm.Ignore( nMacroSize ); +} + +void XclImpDrawObjBase::ReadMacro5( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + maMacroName.clear(); + rStrm.Ignore( nMacroSize ); +} + +void XclImpDrawObjBase::ReadMacro8( XclImpStream& rStrm ) +{ + maMacroName.clear(); + if( rStrm.GetRecLeft() <= 6 ) + return; + + // macro is stored in a tNameXR token containing a link to a defined name + sal_uInt16 nFmlaSize; + nFmlaSize = rStrm.ReaduInt16(); + rStrm.Ignore( 4 ); + OSL_ENSURE( nFmlaSize == 7, "XclImpDrawObjBase::ReadMacro - unexpected formula size" ); + if( nFmlaSize == 7 ) + { + sal_uInt8 nTokenId; + sal_uInt16 nExtSheet, nExtName; + nTokenId = rStrm.ReaduInt8(); + nExtSheet = rStrm.ReaduInt16(); + nExtName = rStrm.ReaduInt16(); + OSL_ENSURE( nTokenId == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ), + "XclImpDrawObjBase::ReadMacro - tNameXR token expected" ); + if( nTokenId == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ) ) + maMacroName = GetLinkManager().GetMacroName( nExtSheet, nExtName ); + } +} + +void XclImpDrawObjBase::ConvertLineStyle( SdrObject& rSdrObj, const XclObjLineData& rLineData ) const +{ + if( rLineData.IsAuto() ) + { + XclObjLineData aAutoData; + aAutoData.mnAuto = 0; + ConvertLineStyle( rSdrObj, aAutoData ); + } + else + { + tools::Long nLineWidth = 35 * ::std::min( rLineData.mnWidth, EXC_OBJ_LINE_THICK ); + rSdrObj.SetMergedItem( XLineWidthItem( nLineWidth ) ); + rSdrObj.SetMergedItem( XLineColorItem( OUString(), GetPalette().GetColor( rLineData.mnColorIdx ) ) ); + rSdrObj.SetMergedItem( XLineJointItem( css::drawing::LineJoint_MITER ) ); + + sal_uLong nDotLen = ::std::max< sal_uLong >( 70 * rLineData.mnWidth, 35 ); + sal_uLong nDashLen = 3 * nDotLen; + sal_uLong nDist = 2 * nDotLen; + + switch( rLineData.mnStyle ) + { + default: + case EXC_OBJ_LINE_SOLID: + rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) ); + break; + case EXC_OBJ_LINE_DASH: + rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) ); + rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 0, nDotLen, 1, nDashLen, nDist ) ) ); + break; + case EXC_OBJ_LINE_DOT: + rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) ); + rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 1, nDotLen, 0, nDashLen, nDist ) ) ); + break; + case EXC_OBJ_LINE_DASHDOT: + rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) ); + rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 1, nDotLen, 1, nDashLen, nDist ) ) ); + break; + case EXC_OBJ_LINE_DASHDOTDOT: + rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) ); + rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 2, nDotLen, 1, nDashLen, nDist ) ) ); + break; + case EXC_OBJ_LINE_MEDTRANS: + rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) ); + rSdrObj.SetMergedItem( XLineTransparenceItem( 50 ) ); + break; + case EXC_OBJ_LINE_DARKTRANS: + rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) ); + rSdrObj.SetMergedItem( XLineTransparenceItem( 25 ) ); + break; + case EXC_OBJ_LINE_LIGHTTRANS: + rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) ); + rSdrObj.SetMergedItem( XLineTransparenceItem( 75 ) ); + break; + case EXC_OBJ_LINE_NONE: + rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_NONE ) ); + break; + } + } +} + +void XclImpDrawObjBase::ConvertFillStyle( SdrObject& rSdrObj, const XclObjFillData& rFillData ) const +{ + if( rFillData.IsAuto() ) + { + XclObjFillData aAutoData; + aAutoData.mnAuto = 0; + ConvertFillStyle( rSdrObj, aAutoData ); + } + else if( rFillData.mnPattern == EXC_PATT_NONE ) + { + rSdrObj.SetMergedItem( XFillStyleItem( drawing::FillStyle_NONE ) ); + } + else + { + Color aPattColor = GetPalette().GetColor( rFillData.mnPattColorIdx ); + Color aBackColor = GetPalette().GetColor( rFillData.mnBackColorIdx ); + if( (rFillData.mnPattern == EXC_PATT_SOLID) || (aPattColor == aBackColor) ) + { + rSdrObj.SetMergedItem( XFillStyleItem( drawing::FillStyle_SOLID ) ); + rSdrObj.SetMergedItem( XFillColorItem( OUString(), aPattColor ) ); + } + else + { + static const sal_uInt8 sppnPatterns[][ 8 ] = + { + { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 }, + { 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD }, + { 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22 }, + { 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00 }, + { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC }, + { 0x33, 0x66, 0xCC, 0x99, 0x33, 0x66, 0xCC, 0x99 }, + { 0xCC, 0x66, 0x33, 0x99, 0xCC, 0x66, 0x33, 0x99 }, + { 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33 }, + { 0xCC, 0xFF, 0x33, 0xFF, 0xCC, 0xFF, 0x33, 0xFF }, + { 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, + { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 }, + { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 }, + { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 }, + { 0xFF, 0x11, 0x11, 0x11, 0xFF, 0x11, 0x11, 0x11 }, + { 0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11 }, + { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 }, + { 0x80, 0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x00 } + }; + const sal_uInt8* const pnPattern = sppnPatterns[std::min<size_t>(rFillData.mnPattern - 2, SAL_N_ELEMENTS(sppnPatterns) - 1)]; + // create 2-colored 8x8 DIB + SvMemoryStream aMemStrm; + aMemStrm.WriteUInt32( 12 ).WriteInt16( 8 ).WriteInt16( 8 ).WriteUInt16( 1 ).WriteUInt16( 1 ); + aMemStrm.WriteUChar( 0xFF ).WriteUChar( 0xFF ).WriteUChar( 0xFF ); + aMemStrm.WriteUChar( 0x00 ).WriteUChar( 0x00 ).WriteUChar( 0x00 ); + for( size_t nIdx = 0; nIdx < 8; ++nIdx ) + aMemStrm.WriteUInt32( pnPattern[ nIdx ] ); // 32-bit little-endian + aMemStrm.Seek( STREAM_SEEK_TO_BEGIN ); + Bitmap aBitmap; + (void)ReadDIB(aBitmap, aMemStrm, false); + + XOBitmap aXOBitmap(( BitmapEx(aBitmap) )); + aXOBitmap.Bitmap2Array(); + if( aXOBitmap.GetBackgroundColor() == COL_BLACK ) + ::std::swap( aPattColor, aBackColor ); + aXOBitmap.SetPixelColor( aPattColor ); + aXOBitmap.SetBackgroundColor( aBackColor ); + aXOBitmap.Array2Bitmap(); + aBitmap = aXOBitmap.GetBitmap().GetBitmap(); + + rSdrObj.SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP)); + rSdrObj.SetMergedItem(XFillBitmapItem(OUString(), Graphic(BitmapEx(aBitmap)))); + } + } +} + +void XclImpDrawObjBase::ConvertFrameStyle( SdrObject& rSdrObj, sal_uInt16 nFrameFlags ) const +{ + if( ::get_flag( nFrameFlags, EXC_OBJ_FRAME_SHADOW ) ) + { + rSdrObj.SetMergedItem( makeSdrShadowItem( true ) ); + rSdrObj.SetMergedItem( makeSdrShadowXDistItem( 35 ) ); + rSdrObj.SetMergedItem( makeSdrShadowYDistItem( 35 ) ); + rSdrObj.SetMergedItem( makeSdrShadowColorItem( GetPalette().GetColor( EXC_COLOR_WINDOWTEXT ) ) ); + } +} + +Color XclImpDrawObjBase::GetSolidLineColor( const XclObjLineData& rLineData ) const +{ + Color aColor( COL_TRANSPARENT ); + if( rLineData.IsAuto() ) + { + XclObjLineData aAutoData; + aAutoData.mnAuto = 0; + aColor = GetSolidLineColor( aAutoData ); + } + else if( rLineData.mnStyle != EXC_OBJ_LINE_NONE ) + { + aColor = GetPalette().GetColor( rLineData.mnColorIdx ); + } + return aColor; +} + +Color XclImpDrawObjBase::GetSolidFillColor( const XclObjFillData& rFillData ) const +{ + Color aColor( COL_TRANSPARENT ); + if( rFillData.IsAuto() ) + { + XclObjFillData aAutoData; + aAutoData.mnAuto = 0; + aColor = GetSolidFillColor( aAutoData ); + } + else if( rFillData.mnPattern != EXC_PATT_NONE ) + { + Color aPattColor = GetPalette().GetColor( rFillData.mnPattColorIdx ); + Color aBackColor = GetPalette().GetColor( rFillData.mnBackColorIdx ); + aColor = XclTools::GetPatternColor( aPattColor, aBackColor, rFillData.mnPattern ); + } + return aColor; +} + +void XclImpDrawObjBase::DoReadObj3( XclImpStream&, sal_uInt16 ) +{ +} + +void XclImpDrawObjBase::DoReadObj4( XclImpStream&, sal_uInt16 ) +{ +} + +void XclImpDrawObjBase::DoReadObj5( XclImpStream&, sal_uInt16, sal_uInt16 ) +{ +} + +void XclImpDrawObjBase::DoReadObj8SubRec( XclImpStream&, sal_uInt16, sal_uInt16 ) +{ +} + +std::size_t XclImpDrawObjBase::DoGetProgressSize() const +{ + return 1; +} + +SdrObjectUniquePtr XclImpDrawObjBase::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& ) const +{ + rDffConv.Progress( GetProgressSize() ); + return nullptr; +} + +void XclImpDrawObjBase::DoPreProcessSdrObj( XclImpDffConverter&, SdrObject& ) const +{ + // trace if object is not printable + if( !IsPrintable() ) + GetTracer().TraceObjectNotPrintable(); +} + +void XclImpDrawObjBase::DoPostProcessSdrObj( XclImpDffConverter&, SdrObject& ) const +{ +} + +void XclImpDrawObjBase::ImplReadObj3( XclImpStream& rStrm ) +{ + // back to offset 4 (ignore object count field) + rStrm.Seek( 4 ); + + sal_uInt16 nObjFlags, nMacroSize; + mnObjType = rStrm.ReaduInt16(); + mnObjId = rStrm.ReaduInt16(); + nObjFlags = rStrm.ReaduInt16(); + rStrm >> maAnchor; + nMacroSize = rStrm.ReaduInt16(); + rStrm.Ignore( 2 ); + + mbHasAnchor = true; + mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN ); + mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE ); + DoReadObj3( rStrm, nMacroSize ); +} + +void XclImpDrawObjBase::ImplReadObj4( XclImpStream& rStrm ) +{ + // back to offset 4 (ignore object count field) + rStrm.Seek( 4 ); + + sal_uInt16 nObjFlags, nMacroSize; + mnObjType = rStrm.ReaduInt16(); + mnObjId = rStrm.ReaduInt16(); + nObjFlags = rStrm.ReaduInt16(); + rStrm >> maAnchor; + nMacroSize = rStrm.ReaduInt16(); + rStrm.Ignore( 2 ); + + mbHasAnchor = true; + mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN ); + mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE ); + mbPrintable = ::get_flag( nObjFlags, EXC_OBJ_PRINTABLE ); + DoReadObj4( rStrm, nMacroSize ); +} + +void XclImpDrawObjBase::ImplReadObj5( XclImpStream& rStrm ) +{ + // back to offset 4 (ignore object count field) + rStrm.Seek( 4 ); + + sal_uInt16 nObjFlags, nMacroSize, nNameLen; + mnObjType = rStrm.ReaduInt16(); + mnObjId = rStrm.ReaduInt16(); + nObjFlags = rStrm.ReaduInt16(); + rStrm >> maAnchor; + nMacroSize = rStrm.ReaduInt16(); + rStrm.Ignore( 2 ); + nNameLen = rStrm.ReaduInt16(); + rStrm.Ignore( 2 ); + + mbHasAnchor = true; + mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN ); + mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE ); + mbPrintable = ::get_flag( nObjFlags, EXC_OBJ_PRINTABLE ); + DoReadObj5( rStrm, nNameLen, nMacroSize ); +} + +void XclImpDrawObjBase::ImplReadObj8( XclImpStream& rStrm ) +{ + // back to beginning + rStrm.Seek( EXC_REC_SEEK_TO_BEGIN ); + + bool bLoop = true; + while (bLoop) + { + if (rStrm.GetRecLeft() < 4) + break; + + sal_uInt16 nSubRecId = rStrm.ReaduInt16(); + sal_uInt16 nSubRecSize = rStrm.ReaduInt16(); + rStrm.PushPosition(); + // sometimes the last subrecord has an invalid length (OBJLBSDATA) -> min() + nSubRecSize = static_cast< sal_uInt16 >( ::std::min< std::size_t >( nSubRecSize, rStrm.GetRecLeft() ) ); + + switch( nSubRecId ) + { + case EXC_ID_OBJCMO: + OSL_ENSURE( rStrm.GetRecPos() == 4, "XclImpDrawObjBase::ImplReadObj8 - unexpected OBJCMO subrecord" ); + if( (rStrm.GetRecPos() == 4) && (nSubRecSize >= 6) ) + { + sal_uInt16 nObjFlags; + mnObjType = rStrm.ReaduInt16(); + mnObjId = rStrm.ReaduInt16( ); + nObjFlags = rStrm.ReaduInt16( ); + mbPrintable = ::get_flag( nObjFlags, EXC_OBJCMO_PRINTABLE ); + } + break; + case EXC_ID_OBJMACRO: + ReadMacro8( rStrm ); + break; + case EXC_ID_OBJEND: + bLoop = false; + break; + default: + DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } + + rStrm.PopPosition(); + rStrm.Ignore( nSubRecSize ); + } + + /* Call DoReadObj8SubRec() with EXC_ID_OBJEND for further stream + processing (e.g. charts), even if the OBJEND subrecord is missing. */ + DoReadObj8SubRec( rStrm, EXC_ID_OBJEND, 0 ); + + /* Pictures that Excel reads from BIFF5 and writes to BIFF8 still have the + IMGDATA record following the OBJ record (but they use the image data + stored in DFF). The IMGDATA record may be continued by several CONTINUE + records. But the last CONTINUE record may be in fact an MSODRAWING + record that contains the DFF data of the next drawing object! So we + have to skip just enough CONTINUE records to look at the next + MSODRAWING/CONTINUE record. */ + if( !((rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord()) ) + return; + + rStrm.Ignore( 4 ); + sal_uInt32 nDataSize = rStrm.ReaduInt32(); + nDataSize -= rStrm.GetRecLeft(); + // skip following CONTINUE records until IMGDATA ends + while (true) + { + if (!nDataSize) + break; + if (rStrm.GetNextRecId() != EXC_ID_CONT) + break; + if (!rStrm.StartNextRecord()) + break; + OSL_ENSURE( nDataSize >= rStrm.GetRecLeft(), "XclImpDrawObjBase::ImplReadObj8 - CONTINUE too long" ); + nDataSize -= ::std::min< sal_uInt32 >( rStrm.GetRecLeft(), nDataSize ); + } + OSL_ENSURE( nDataSize == 0, "XclImpDrawObjBase::ImplReadObj8 - missing CONTINUE records" ); + // next record may be MSODRAWING or CONTINUE or anything else +} + +void XclImpDrawObjVector::InsertGrouped( XclImpDrawObjRef const & xDrawObj ) +{ + if( !mObjs.empty() ) + if( XclImpGroupObj* pGroupObj = dynamic_cast< XclImpGroupObj* >( mObjs.back().get() ) ) + if( pGroupObj->TryInsert( xDrawObj ) ) + return; + mObjs.push_back( xDrawObj ); +} + +std::size_t XclImpDrawObjVector::GetProgressSize() const +{ + return std::accumulate(mObjs.begin(), mObjs.end(), std::size_t(0), + [](const std::size_t& rSum, const XclImpDrawObjRef& rxObj) { return rSum + rxObj->GetProgressSize(); }); +} + +XclImpPhObj::XclImpPhObj( const XclImpRoot& rRoot ) : + XclImpDrawObjBase( rRoot ) +{ + SetProcessSdrObj( false ); +} + +XclImpGroupObj::XclImpGroupObj( const XclImpRoot& rRoot ) : + XclImpDrawObjBase( rRoot ), + mnFirstUngrouped( 0 ) +{ +} + +bool XclImpGroupObj::TryInsert( XclImpDrawObjRef const & xDrawObj ) +{ + if( xDrawObj->GetObjId() == mnFirstUngrouped ) + return false; + // insert into own list or into nested group + maChildren.InsertGrouped( xDrawObj ); + return true; +} + +void XclImpGroupObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + rStrm.Ignore( 4 ); + mnFirstUngrouped = rStrm.ReaduInt16(); + rStrm.Ignore( 16 ); + ReadMacro3( rStrm, nMacroSize ); +} + +void XclImpGroupObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + rStrm.Ignore( 4 ); + mnFirstUngrouped = rStrm.ReaduInt16(); + rStrm.Ignore( 16 ); + ReadMacro4( rStrm, nMacroSize ); +} + +void XclImpGroupObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + rStrm.Ignore( 4 ); + mnFirstUngrouped = rStrm.ReaduInt16(); + rStrm.Ignore( 16 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); +} + +std::size_t XclImpGroupObj::DoGetProgressSize() const +{ + return XclImpDrawObjBase::DoGetProgressSize() + maChildren.GetProgressSize(); +} + +SdrObjectUniquePtr XclImpGroupObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& /*rAnchorRect*/ ) const +{ + std::unique_ptr<SdrObjGroup, SdrObjectFreeOp> xSdrObj( + new SdrObjGroup( + *GetDoc().GetDrawLayer())); + // child objects in BIFF2-BIFF5 have absolute size, not needed to pass own anchor rectangle + SdrObjList& rObjList = *xSdrObj->GetSubList(); // SdrObjGroup always returns existing sublist + for( const auto& rxChild : maChildren ) + rDffConv.ProcessObject( rObjList, *rxChild ); + rDffConv.Progress(); + return xSdrObj; +} + +XclImpLineObj::XclImpLineObj( const XclImpRoot& rRoot ) : + XclImpDrawObjBase( rRoot ), + mnArrows( 0 ), + mnStartPoint( EXC_OBJ_LINE_TL ) +{ + SetAreaObj( false ); +} + +void XclImpLineObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + rStrm >> maLineData; + mnArrows = rStrm.ReaduInt16(); + mnStartPoint = rStrm.ReaduInt8(); + rStrm.Ignore( 1 ); + ReadMacro3( rStrm, nMacroSize ); +} + +void XclImpLineObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + rStrm >> maLineData; + mnArrows = rStrm.ReaduInt16(); + mnStartPoint = rStrm.ReaduInt8(); + rStrm.Ignore( 1 ); + ReadMacro4( rStrm, nMacroSize ); +} + +void XclImpLineObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + rStrm >> maLineData; + mnArrows = rStrm.ReaduInt16(); + mnStartPoint = rStrm.ReaduInt8(); + rStrm.Ignore( 1 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); +} + +SdrObjectUniquePtr XclImpLineObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const +{ + ::basegfx::B2DPolygon aB2DPolygon; + switch( mnStartPoint ) + { + default: + case EXC_OBJ_LINE_TL: + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Top() ) ); + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Bottom() ) ); + break; + case EXC_OBJ_LINE_TR: + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Top() ) ); + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Bottom() ) ); + break; + case EXC_OBJ_LINE_BR: + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Bottom() ) ); + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Top() ) ); + break; + case EXC_OBJ_LINE_BL: + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Bottom() ) ); + aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Top() ) ); + break; + } + SdrObjectUniquePtr xSdrObj( + new SdrPathObj( + *GetDoc().GetDrawLayer(), + SdrObjKind::Line, + ::basegfx::B2DPolyPolygon(aB2DPolygon))); + ConvertLineStyle( *xSdrObj, maLineData ); + + // line ends + sal_uInt8 nArrowType = ::extract_value< sal_uInt8 >( mnArrows, 0, 4 ); + bool bLineStart = false; + bool bLineEnd = false; + bool bFilled = false; + switch( nArrowType ) + { + case EXC_OBJ_ARROW_OPEN: bLineStart = false; bLineEnd = true; bFilled = false; break; + case EXC_OBJ_ARROW_OPENBOTH: bLineStart = true; bLineEnd = true; bFilled = false; break; + case EXC_OBJ_ARROW_FILLED: bLineStart = false; bLineEnd = true; bFilled = true; break; + case EXC_OBJ_ARROW_FILLEDBOTH: bLineStart = true; bLineEnd = true; bFilled = true; break; + } + if( bLineStart || bLineEnd ) + { + sal_uInt8 nArrowWidth = ::extract_value< sal_uInt8 >( mnArrows, 4, 4 ); + double fArrowWidth = 3.0; + switch( nArrowWidth ) + { + case EXC_OBJ_ARROW_NARROW: fArrowWidth = 2.0; break; + case EXC_OBJ_ARROW_MEDIUM: fArrowWidth = 3.0; break; + case EXC_OBJ_ARROW_WIDE: fArrowWidth = 5.0; break; + } + + sal_uInt8 nArrowLength = ::extract_value< sal_uInt8 >( mnArrows, 8, 4 ); + double fArrowLength = 3.0; + switch( nArrowLength ) + { + case EXC_OBJ_ARROW_NARROW: fArrowLength = 2.5; break; + case EXC_OBJ_ARROW_MEDIUM: fArrowLength = 3.5; break; + case EXC_OBJ_ARROW_WIDE: fArrowLength = 6.0; break; + } + + ::basegfx::B2DPolygon aArrowPoly; +#define EXC_ARROW_POINT( x, y ) ::basegfx::B2DPoint( fArrowWidth * (x), fArrowLength * (y) ) + if( bFilled ) + { + aArrowPoly.append( EXC_ARROW_POINT( 0, 100 ) ); + aArrowPoly.append( EXC_ARROW_POINT( 50, 0 ) ); + aArrowPoly.append( EXC_ARROW_POINT( 100, 100 ) ); + } + else + { + sal_uInt8 nLineWidth = ::limit_cast< sal_uInt8 >( maLineData.mnWidth, EXC_OBJ_LINE_THIN, EXC_OBJ_LINE_THICK ); + aArrowPoly.append( EXC_ARROW_POINT( 50, 0 ) ); + aArrowPoly.append( EXC_ARROW_POINT( 100, 100 - 3 * nLineWidth ) ); + aArrowPoly.append( EXC_ARROW_POINT( 100 - 5 * nLineWidth, 100 ) ); + aArrowPoly.append( EXC_ARROW_POINT( 50, 12 * nLineWidth ) ); + aArrowPoly.append( EXC_ARROW_POINT( 5 * nLineWidth, 100 ) ); + aArrowPoly.append( EXC_ARROW_POINT( 0, 100 - 3 * nLineWidth ) ); + } +#undef EXC_ARROW_POINT + + ::basegfx::B2DPolyPolygon aArrowPolyPoly( aArrowPoly ); + tools::Long nWidth = static_cast< tools::Long >( 125 * fArrowWidth ); + if( bLineStart ) + { + xSdrObj->SetMergedItem( XLineStartItem( OUString(), aArrowPolyPoly ) ); + xSdrObj->SetMergedItem( XLineStartWidthItem( nWidth ) ); + xSdrObj->SetMergedItem( XLineStartCenterItem( false ) ); + } + if( bLineEnd ) + { + xSdrObj->SetMergedItem( XLineEndItem( OUString(), aArrowPolyPoly ) ); + xSdrObj->SetMergedItem( XLineEndWidthItem( nWidth ) ); + xSdrObj->SetMergedItem( XLineEndCenterItem( false ) ); + } + } + rDffConv.Progress(); + return xSdrObj; +} + +XclImpRectObj::XclImpRectObj( const XclImpRoot& rRoot ) : + XclImpDrawObjBase( rRoot ), + mnFrameFlags( 0 ) +{ + SetAreaObj( true ); +} + +void XclImpRectObj::ReadFrameData( XclImpStream& rStrm ) +{ + rStrm >> maFillData >> maLineData; + mnFrameFlags = rStrm.ReaduInt16(); +} + +void XclImpRectObj::ConvertRectStyle( SdrObject& rSdrObj ) const +{ + ConvertLineStyle( rSdrObj, maLineData ); + ConvertFillStyle( rSdrObj, maFillData ); + ConvertFrameStyle( rSdrObj, mnFrameFlags ); +} + +void XclImpRectObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + ReadMacro3( rStrm, nMacroSize ); +} + +void XclImpRectObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + ReadMacro4( rStrm, nMacroSize ); +} + +void XclImpRectObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); +} + +SdrObjectUniquePtr XclImpRectObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const +{ + SdrObjectUniquePtr xSdrObj( + new SdrRectObj( + *GetDoc().GetDrawLayer(), + rAnchorRect)); + ConvertRectStyle( *xSdrObj ); + rDffConv.Progress(); + return xSdrObj; +} + +XclImpOvalObj::XclImpOvalObj( const XclImpRoot& rRoot ) : + XclImpRectObj( rRoot ) +{ +} + +SdrObjectUniquePtr XclImpOvalObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const +{ + SdrObjectUniquePtr xSdrObj( + new SdrCircObj( + *GetDoc().GetDrawLayer(), + SdrCircKind::Full, + rAnchorRect)); + ConvertRectStyle( *xSdrObj ); + rDffConv.Progress(); + return xSdrObj; +} + +XclImpArcObj::XclImpArcObj( const XclImpRoot& rRoot ) : + XclImpDrawObjBase( rRoot ), + mnQuadrant( EXC_OBJ_ARC_TR ) +{ + SetAreaObj( false ); // arc may be 2-dimensional +} + +void XclImpArcObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + rStrm >> maFillData >> maLineData; + mnQuadrant = rStrm.ReaduInt8(); + rStrm.Ignore( 1 ); + ReadMacro3( rStrm, nMacroSize ); +} + +void XclImpArcObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + rStrm >> maFillData >> maLineData; + mnQuadrant = rStrm.ReaduInt8(); + rStrm.Ignore( 1 ); + ReadMacro4( rStrm, nMacroSize ); +} + +void XclImpArcObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + rStrm >> maFillData >> maLineData; + mnQuadrant = rStrm.ReaduInt8(); + rStrm.Ignore( 1 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); +} + +SdrObjectUniquePtr XclImpArcObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const +{ + tools::Rectangle aNewRect = rAnchorRect; + Degree100 nStartAngle; + Degree100 nEndAngle; + switch( mnQuadrant ) + { + default: + case EXC_OBJ_ARC_TR: + nStartAngle = 0_deg100; + nEndAngle = 9000_deg100; + aNewRect.AdjustLeft( -(rAnchorRect.GetWidth()) ); + aNewRect.AdjustBottom(rAnchorRect.GetHeight() ); + break; + case EXC_OBJ_ARC_TL: + nStartAngle = 9000_deg100; + nEndAngle = 18000_deg100; + aNewRect.AdjustRight(rAnchorRect.GetWidth() ); + aNewRect.AdjustBottom(rAnchorRect.GetHeight() ); + break; + case EXC_OBJ_ARC_BL: + nStartAngle = 18000_deg100; + nEndAngle = 27000_deg100; + aNewRect.AdjustRight(rAnchorRect.GetWidth() ); + aNewRect.AdjustTop( -(rAnchorRect.GetHeight()) ); + break; + case EXC_OBJ_ARC_BR: + nStartAngle = 27000_deg100; + nEndAngle = 0_deg100; + aNewRect.AdjustLeft( -(rAnchorRect.GetWidth()) ); + aNewRect.AdjustTop( -(rAnchorRect.GetHeight()) ); + break; + } + SdrCircKind eObjKind = maFillData.IsFilled() ? SdrCircKind::Section : SdrCircKind::Arc; + SdrObjectUniquePtr xSdrObj( + new SdrCircObj( + *GetDoc().GetDrawLayer(), + eObjKind, + aNewRect, + nStartAngle, + nEndAngle)); + ConvertFillStyle( *xSdrObj, maFillData ); + ConvertLineStyle( *xSdrObj, maLineData ); + rDffConv.Progress(); + return xSdrObj; +} + +XclImpPolygonObj::XclImpPolygonObj( const XclImpRoot& rRoot ) : + XclImpRectObj( rRoot ), + mnPolyFlags( 0 ), + mnPointCount( 0 ) +{ + SetAreaObj( false ); // polygon may be 2-dimensional +} + +void XclImpPolygonObj::ReadCoordList( XclImpStream& rStrm ) +{ + if( (rStrm.GetNextRecId() == EXC_ID_COORDLIST) && rStrm.StartNextRecord() ) + { + OSL_ENSURE( rStrm.GetRecLeft() / 4 == mnPointCount, "XclImpPolygonObj::ReadCoordList - wrong polygon point count" ); + while (true) + { + if (rStrm.GetRecLeft() < 4) + break; + sal_uInt16 nX = rStrm.ReaduInt16(); + sal_uInt16 nY = rStrm.ReaduInt16(); + maCoords.emplace_back( nX, nY ); + } + } +} + +void XclImpPolygonObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + mnPolyFlags = rStrm.ReaduInt16(); + rStrm.Ignore( 10 ); + mnPointCount = rStrm.ReaduInt16(); + rStrm.Ignore( 8 ); + ReadMacro4( rStrm, nMacroSize ); + ReadCoordList( rStrm ); +} + +void XclImpPolygonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + mnPolyFlags = rStrm.ReaduInt16(); + rStrm.Ignore( 10 ); + mnPointCount = rStrm.ReaduInt16(); + rStrm.Ignore( 8 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); + ReadCoordList( rStrm ); +} + +namespace { + +::basegfx::B2DPoint lclGetPolyPoint( const tools::Rectangle& rAnchorRect, const Point& rPoint ) +{ + return ::basegfx::B2DPoint( + rAnchorRect.Left() + static_cast< sal_Int32 >( ::std::min< double >( rPoint.X(), 16384.0 ) / 16384.0 * rAnchorRect.GetWidth() + 0.5 ), + rAnchorRect.Top() + static_cast< sal_Int32 >( ::std::min< double >( rPoint.Y(), 16384.0 ) / 16384.0 * rAnchorRect.GetHeight() + 0.5 ) ); +} + +} // namespace + +SdrObjectUniquePtr XclImpPolygonObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const +{ + SdrObjectUniquePtr xSdrObj; + if( maCoords.size() >= 2 ) + { + // create the polygon + ::basegfx::B2DPolygon aB2DPolygon; + for( const auto& rCoord : maCoords ) + aB2DPolygon.append( lclGetPolyPoint( rAnchorRect, rCoord ) ); + // close polygon if specified + if( ::get_flag( mnPolyFlags, EXC_OBJ_POLY_CLOSED ) && (maCoords.front() != maCoords.back()) ) + aB2DPolygon.append( lclGetPolyPoint( rAnchorRect, maCoords.front() ) ); + // create the SdrObject + SdrObjKind eObjKind = maFillData.IsFilled() ? SdrObjKind::PathPoly : SdrObjKind::PathPolyLine; + xSdrObj.reset( + new SdrPathObj( + *GetDoc().GetDrawLayer(), + eObjKind, + ::basegfx::B2DPolyPolygon(aB2DPolygon))); + ConvertRectStyle( *xSdrObj ); + } + rDffConv.Progress(); + return xSdrObj; +} + +void XclImpObjTextData::ReadByteString( XclImpStream& rStrm ) +{ + mxString.reset(); + if( maData.mnTextLen > 0 ) + { + mxString = std::make_shared<XclImpString>( rStrm.ReadRawByteString( maData.mnTextLen ) ); + // skip padding byte for word boundaries + if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 ); + } +} + +void XclImpObjTextData::ReadFormats( XclImpStream& rStrm ) +{ + if( mxString ) + mxString->ReadObjFormats( rStrm, maData.mnFormatSize ); + else + rStrm.Ignore( maData.mnFormatSize ); +} + +XclImpTextObj::XclImpTextObj( const XclImpRoot& rRoot ) : + XclImpRectObj( rRoot ) +{ +} + +void XclImpTextObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + maTextData.maData.ReadObj3( rStrm ); + ReadMacro3( rStrm, nMacroSize ); + maTextData.ReadByteString( rStrm ); + maTextData.ReadFormats( rStrm ); +} + +void XclImpTextObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + maTextData.maData.ReadObj3( rStrm ); + ReadMacro4( rStrm, nMacroSize ); + maTextData.ReadByteString( rStrm ); + maTextData.ReadFormats( rStrm ); +} + +void XclImpTextObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + ReadFrameData( rStrm ); + maTextData.maData.ReadObj5( rStrm ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); + maTextData.ReadByteString( rStrm ); + rStrm.Ignore( maTextData.maData.mnLinkSize ); // ignore text link formula + maTextData.ReadFormats( rStrm ); +} + +SdrObjectUniquePtr XclImpTextObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const +{ + std::unique_ptr<SdrObjCustomShape, SdrObjectFreeOp> xSdrObj( + new SdrObjCustomShape( + *GetDoc().GetDrawLayer())); + xSdrObj->NbcSetSnapRect( rAnchorRect ); + OUString aRectType = "rectangle"; + xSdrObj->MergeDefaultAttributes( &aRectType ); + ConvertRectStyle( *xSdrObj ); + bool bAutoSize = ::get_flag( maTextData.maData.mnFlags, EXC_OBJ_TEXT_AUTOSIZE ); + xSdrObj->SetMergedItem( makeSdrTextAutoGrowWidthItem( bAutoSize ) ); + xSdrObj->SetMergedItem( makeSdrTextAutoGrowHeightItem( bAutoSize ) ); + xSdrObj->SetMergedItem( makeSdrTextWordWrapItem( true ) ); + rDffConv.Progress(); + return xSdrObj; +} + +void XclImpTextObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const +{ + // set text data + if( SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( &rSdrObj ) ) + { + if( maTextData.mxString ) + { + if( maTextData.mxString->IsRich() ) + { + if (maTextData.mxString->GetText().getLength() > 1024 && utl::ConfigManager::IsFuzzing()) + { + SAL_WARN("sc.filter", "truncating slow long rich text for fuzzing performance"); + maTextData.mxString->SetText(maTextData.mxString->GetText().copy(0, 1024)); + } + + // rich text + std::unique_ptr< EditTextObject > xEditObj( + XclImpStringHelper::CreateTextObject( GetRoot(), *maTextData.mxString ) ); + OutlinerParaObject aOutlineObj(std::move(xEditObj)); + aOutlineObj.SetOutlinerMode( OutlinerMode::TextObject ); + pTextObj->NbcSetOutlinerParaObject( std::move(aOutlineObj) ); + } + else + { + // plain text + pTextObj->NbcSetText( maTextData.mxString->GetText() ); + } + + /* #i96858# Do not apply any formatting if there is no text. + SdrObjCustomShape::SetVerticalWriting (initiated from + SetMergedItem) calls SdrTextObj::ForceOutlinerParaObject which + ensures that we can erroneously write a ClientTextbox record + (with no content) while exporting to XLS, which can cause a + corrupted exported document. */ + + SvxAdjust eHorAlign = SvxAdjust::Left; + SdrTextVertAdjust eVerAlign = SDRTEXTVERTADJUST_TOP; + + // orientation (this is only a fake, drawing does not support real text orientation) + namespace csst = ::com::sun::star::text; + csst::WritingMode eWriteMode = csst::WritingMode_LR_TB; + switch( maTextData.maData.mnOrient ) + { + default: + case EXC_OBJ_ORIENT_NONE: + { + eWriteMode = csst::WritingMode_LR_TB; + switch( maTextData.maData.GetHorAlign() ) + { + case EXC_OBJ_HOR_LEFT: eHorAlign = SvxAdjust::Left; break; + case EXC_OBJ_HOR_CENTER: eHorAlign = SvxAdjust::Center; break; + case EXC_OBJ_HOR_RIGHT: eHorAlign = SvxAdjust::Right; break; + case EXC_OBJ_HOR_JUSTIFY: eHorAlign = SvxAdjust::Block; break; + } + switch( maTextData.maData.GetVerAlign() ) + { + case EXC_OBJ_VER_TOP: eVerAlign = SDRTEXTVERTADJUST_TOP; break; + case EXC_OBJ_VER_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break; + case EXC_OBJ_VER_BOTTOM: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break; + case EXC_OBJ_VER_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break; + } + } + break; + + case EXC_OBJ_ORIENT_90CCW: + { + if( SdrObjCustomShape* pObjCustomShape = dynamic_cast< SdrObjCustomShape* >( &rSdrObj ) ) + { + css::beans::PropertyValue aTextRotateAngle; + aTextRotateAngle.Name = "TextRotateAngle"; + aTextRotateAngle.Value <<= 180.0; + SdrCustomShapeGeometryItem aGeometryItem(pObjCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY )); + aGeometryItem.SetPropertyValue( aTextRotateAngle ); + pObjCustomShape->SetMergedItem( aGeometryItem ); + } + eWriteMode = csst::WritingMode_TB_RL; + switch( maTextData.maData.GetHorAlign() ) + { + case EXC_OBJ_HOR_LEFT: eVerAlign = SDRTEXTVERTADJUST_TOP; break; + case EXC_OBJ_HOR_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break; + case EXC_OBJ_HOR_RIGHT: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break; + case EXC_OBJ_HOR_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break; + } + MSO_Anchor eTextAnchor = static_cast<MSO_Anchor>(rDffConv.GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop )); + switch( eTextAnchor ) + { + case mso_anchorTopCentered : + case mso_anchorMiddleCentered : + case mso_anchorBottomCentered : + { + eHorAlign = SvxAdjust::Center; + } + break; + + default: + { + switch( maTextData.maData.GetVerAlign() ) + { + case EXC_OBJ_VER_TOP: eHorAlign = SvxAdjust::Right; break; + case EXC_OBJ_VER_CENTER: eHorAlign = SvxAdjust::Center; break; + case EXC_OBJ_VER_BOTTOM: eHorAlign = SvxAdjust::Left; break; + case EXC_OBJ_VER_JUSTIFY: eHorAlign = SvxAdjust::Block; break; + } + } + } + } + break; + + case EXC_OBJ_ORIENT_STACKED: + { + // sj: STACKED is not supported, maybe it can be optimized here a bit + [[fallthrough]]; + } + case EXC_OBJ_ORIENT_90CW: + { + eWriteMode = csst::WritingMode_TB_RL; + switch( maTextData.maData.GetHorAlign() ) + { + case EXC_OBJ_HOR_LEFT: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break; + case EXC_OBJ_HOR_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break; + case EXC_OBJ_HOR_RIGHT: eVerAlign = SDRTEXTVERTADJUST_TOP; break; + case EXC_OBJ_HOR_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break; + } + MSO_Anchor eTextAnchor = static_cast<MSO_Anchor>(rDffConv.GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop )); + switch ( eTextAnchor ) + { + case mso_anchorTopCentered : + case mso_anchorMiddleCentered : + case mso_anchorBottomCentered : + { + eHorAlign = SvxAdjust::Center; + } + break; + + default: + { + switch( maTextData.maData.GetVerAlign() ) + { + case EXC_OBJ_VER_TOP: eHorAlign = SvxAdjust::Left; break; + case EXC_OBJ_VER_CENTER: eHorAlign = SvxAdjust::Center; break; + case EXC_OBJ_VER_BOTTOM: eHorAlign = SvxAdjust::Right; break; + case EXC_OBJ_VER_JUSTIFY: eHorAlign = SvxAdjust::Block; break; + } + } + } + } + break; + } + rSdrObj.SetMergedItem( SvxAdjustItem( eHorAlign, EE_PARA_JUST ) ); + rSdrObj.SetMergedItem( SdrTextVertAdjustItem( eVerAlign ) ); + rSdrObj.SetMergedItem( SvxWritingModeItem( eWriteMode, SDRATTR_TEXTDIRECTION ) ); + } + } + // base class processing + XclImpRectObj::DoPreProcessSdrObj( rDffConv, rSdrObj ); +} + +XclImpChartObj::XclImpChartObj( const XclImpRoot& rRoot, bool bOwnTab ) : + XclImpRectObj( rRoot ), + mbOwnTab( bOwnTab ) +{ + SetSimpleMacro( false ); + SetCustomDffObj( true ); +} + +void XclImpChartObj::ReadChartSubStream( XclImpStream& rStrm ) +{ + /* If chart is read from a chartsheet (mbOwnTab == true), the BOF record + has already been read. If chart is embedded as object, the next record + has to be the BOF record. */ + if( mbOwnTab ) + { + /* #i109800# The input stream may point somewhere inside the chart + substream and not exactly to the leading BOF record. To read this + record correctly in the following, the stream has to rewind it, so + that the next call to StartNextRecord() will find it correctly. */ + if( rStrm.GetRecId() != EXC_ID5_BOF ) + rStrm.RewindRecord(); + } + else + { + if( (rStrm.GetNextRecId() == EXC_ID5_BOF) && rStrm.StartNextRecord() ) + { + sal_uInt16 nBofType; + rStrm.Seek( 2 ); + nBofType = rStrm.ReaduInt16(); + SAL_WARN_IF( nBofType != EXC_BOF_CHART, "sc.filter", "XclImpChartObj::ReadChartSubStream - no chart BOF record" ); + } + else + { + SAL_INFO("sc.filter", "XclImpChartObj::ReadChartSubStream - missing chart substream"); + return; + } + } + + // read chart, even if BOF record contains wrong substream identifier + mxChart = std::make_shared<XclImpChart>( GetRoot(), mbOwnTab ); + mxChart->ReadChartSubStream( rStrm ); + if( mbOwnTab ) + FinalizeTabChart(); +} + +void XclImpChartObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + // read OBJ record and the following chart substream + ReadFrameData( rStrm ); + rStrm.Ignore( 18 ); + ReadMacro3( rStrm, nMacroSize ); + // set frame format from OBJ record, it is used if chart itself is transparent + if( mxChart ) + mxChart->UpdateObjFrame( maLineData, maFillData ); +} + +void XclImpChartObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + // read OBJ record and the following chart substream + ReadFrameData( rStrm ); + rStrm.Ignore( 18 ); + ReadMacro4( rStrm, nMacroSize ); + // set frame format from OBJ record, it is used if chart itself is transparent + if( mxChart ) + mxChart->UpdateObjFrame( maLineData, maFillData ); +} + +void XclImpChartObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + // read OBJ record and the following chart substream + ReadFrameData( rStrm ); + rStrm.Ignore( 18 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); + ReadChartSubStream( rStrm ); + // set frame format from OBJ record, it is used if chart itself is transparent + if( mxChart ) + mxChart->UpdateObjFrame( maLineData, maFillData ); +} + +void XclImpChartObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 /*nSubRecSize*/ ) +{ + // read the following chart substream + if( nSubRecId == EXC_ID_OBJEND ) + { + // enable CONTINUE handling for the entire chart substream + rStrm.ResetRecord( true ); + ReadChartSubStream( rStrm ); + /* disable CONTINUE handling again to be able to read + following CONTINUE records as MSODRAWING records. */ + rStrm.ResetRecord( false ); + } +} + +std::size_t XclImpChartObj::DoGetProgressSize() const +{ + return mxChart ? mxChart->GetProgressSize() : 1; +} + +SdrObjectUniquePtr XclImpChartObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const +{ + SdrObjectUniquePtr xSdrObj; + SfxObjectShell* pDocShell = GetDocShell(); + if( rDffConv.SupportsOleObjects() && SvtModuleOptions().IsChart() && pDocShell && mxChart && !mxChart->IsPivotChart() ) + { + // create embedded chart object + OUString aEmbObjName; + OUString sBaseURL(GetRoot().GetMedium().GetBaseURL()); + Reference< XEmbeddedObject > xEmbObj = pDocShell->GetEmbeddedObjectContainer(). + CreateEmbeddedObject( SvGlobalName( SO3_SCH_CLASSID ).GetByteSequence(), aEmbObjName, &sBaseURL ); + + if (!xEmbObj) + return xSdrObj; + + /* Set the size to the embedded object, this prevents that font sizes + of text objects are changed in the chart when the object is + inserted into the draw page. */ + sal_Int64 nAspect = css::embed::Aspects::MSOLE_CONTENT; + MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xEmbObj->getMapUnit( nAspect ) ); + Size aSize( OutputDevice::LogicToLogic( rAnchorRect.GetSize(), MapMode( MapUnit::Map100thMM ), MapMode( aUnit ) ) ); + css::awt::Size aAwtSize( aSize.Width(), aSize.Height() ); + xEmbObj->setVisualAreaSize( nAspect, aAwtSize ); + + // #i121334# This call will change the chart's default background fill from white to transparent. + // Add here again if this is wanted (see task description for details) + // ChartHelper::AdaptDefaultsForChart( xEmbObj ); + + // create the container OLE object + xSdrObj.reset( + new SdrOle2Obj( + *GetDoc().GetDrawLayer(), + svt::EmbeddedObjectRef(xEmbObj, nAspect), + aEmbObjName, + rAnchorRect)); + } + + return xSdrObj; +} + +void XclImpChartObj::DoPostProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const +{ + const SdrOle2Obj* pSdrOleObj = dynamic_cast< const SdrOle2Obj* >( &rSdrObj ); + if( !(mxChart && pSdrOleObj) ) + return; + + const Reference< XEmbeddedObject >& xEmbObj = pSdrOleObj->GetObjRef(); + if( xEmbObj.is() && ::svt::EmbeddedObjectRef::TryRunningState( xEmbObj ) ) try + { + Reference< XEmbedPersist > xPersist( xEmbObj, UNO_QUERY_THROW ); + Reference< XModel > xModel( xEmbObj->getComponent(), UNO_QUERY_THROW ); + mxChart->Convert( xModel, rDffConv, xPersist->getEntryName(), rSdrObj.GetLogicRect() ); + } + catch( const Exception& ) + { + } +} + +void XclImpChartObj::FinalizeTabChart() +{ + /* #i44077# Calculate and store DFF anchor for sheet charts. + Needed to get used area if this chart is inserted as OLE object. */ + OSL_ENSURE( mbOwnTab, "XclImpChartObj::FinalizeTabChart - not allowed for embedded chart objects" ); + + // set uninitialized page to landscape + if( !GetPageSettings().GetPageData().mbValid ) + GetPageSettings().SetPaperSize( EXC_PAPERSIZE_DEFAULT, false ); + + // calculate size of the chart object + const XclPageData& rPageData = GetPageSettings().GetPageData(); + Size aPaperSize = rPageData.GetScPaperSize(); + + tools::Long nWidth = XclTools::GetHmmFromTwips( aPaperSize.Width() ); + tools::Long nHeight = XclTools::GetHmmFromTwips( aPaperSize.Height() ); + + // subtract page margins, give some more extra space + nWidth -= o3tl::saturating_add(XclTools::GetHmmFromInch(rPageData.mfLeftMargin + rPageData.mfRightMargin), static_cast<sal_Int32>(2000)); + nHeight -= o3tl::saturating_add(XclTools::GetHmmFromInch(rPageData.mfTopMargin + rPageData.mfBottomMargin), static_cast<sal_Int32>(1000)); + + // print column/row headers? + if( rPageData.mbPrintHeadings ) + { + nWidth -= 2000; + nHeight -= 1000; + } + + // create the object anchor + XclObjAnchor aAnchor; + aAnchor.SetRect( GetRoot(), GetCurrScTab(), tools::Rectangle( 1000, 500, nWidth, nHeight ), MapUnit::Map100thMM ); + SetAnchor( aAnchor ); +} + +XclImpNoteObj::XclImpNoteObj( const XclImpRoot& rRoot ) : + XclImpTextObj( rRoot ), + maScPos( ScAddress::INITIALIZE_INVALID ), + mnNoteFlags( 0 ) +{ + SetSimpleMacro( false ); + // caption object will be created manually + SetInsertSdrObj( false ); +} + +void XclImpNoteObj::SetNoteData( const ScAddress& rScPos, sal_uInt16 nNoteFlags ) +{ + maScPos = rScPos; + mnNoteFlags = nNoteFlags; +} + +void XclImpNoteObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const +{ + // create formatted text + XclImpTextObj::DoPreProcessSdrObj( rDffConv, rSdrObj ); + OutlinerParaObject* pOutlinerObj = rSdrObj.GetOutlinerParaObject(); + if( maScPos.IsValid() && pOutlinerObj ) + { + // create cell note with all data from drawing object + ScNoteUtil::CreateNoteFromObjectData( + GetDoc(), maScPos, + rSdrObj.GetMergedItemSet().CloneAsValue(), // new object on heap expected + *pOutlinerObj, + rSdrObj.GetLogicRect(), + ::get_flag( mnNoteFlags, EXC_NOTE_VISIBLE ) ); + } +} + +XclImpControlHelper::XclImpControlHelper( const XclImpRoot& rRoot, XclCtrlBindMode eBindMode ) : + mrRoot( rRoot ), + meBindMode( eBindMode ) +{ +} + +XclImpControlHelper::~XclImpControlHelper() +{ +} + +SdrObjectUniquePtr XclImpControlHelper::CreateSdrObjectFromShape( + const Reference< XShape >& rxShape, const tools::Rectangle& rAnchorRect ) const +{ + mxShape = rxShape; + SdrObjectUniquePtr xSdrObj( SdrObject::getSdrObjectFromXShape( rxShape ) ); + if( xSdrObj ) + { + xSdrObj->NbcSetSnapRect( rAnchorRect ); + // #i30543# insert into control layer + xSdrObj->NbcSetLayer( SC_LAYER_CONTROLS ); + } + return xSdrObj; +} + +void XclImpControlHelper::ApplySheetLinkProps() const +{ + + Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( mxShape ); + if( !xCtrlModel.is() ) + return; + + // sheet links + SfxObjectShell* pDocShell = mrRoot.GetDocShell(); + if(!pDocShell) + return; + + Reference< XMultiServiceFactory > xFactory( pDocShell->GetModel(), UNO_QUERY ); + if( !xFactory.is() ) + return; + + // cell link + if( mxCellLink ) try + { + Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY_THROW ); + + // create argument sequence for createInstanceWithArguments() + CellAddress aApiAddress; + ScUnoConversion::FillApiAddress( aApiAddress, *mxCellLink ); + + NamedValue aValue; + aValue.Name = SC_UNONAME_BOUNDCELL; + aValue.Value <<= aApiAddress; + + Sequence< Any > aArgs{ Any(aValue) }; + + // create the CellValueBinding instance and set at the control model + OUString aServiceName; + switch( meBindMode ) + { + case EXC_CTRL_BINDCONTENT: aServiceName = SC_SERVICENAME_VALBIND; break; + case EXC_CTRL_BINDPOSITION: aServiceName = SC_SERVICENAME_LISTCELLBIND; break; + } + Reference< XValueBinding > xBinding( + xFactory->createInstanceWithArguments( aServiceName, aArgs ), UNO_QUERY_THROW ); + xBindable->setValueBinding( xBinding ); + } + catch( const Exception& ) + { + } + + // source range + if( !mxSrcRange ) + return; + + try + { + Reference< XListEntrySink > xEntrySink( xCtrlModel, UNO_QUERY_THROW ); + + // create argument sequence for createInstanceWithArguments() + CellRangeAddress aApiRange; + ScUnoConversion::FillApiRange( aApiRange, *mxSrcRange ); + + NamedValue aValue; + aValue.Name = SC_UNONAME_CELLRANGE; + aValue.Value <<= aApiRange; + + Sequence< Any > aArgs{ Any(aValue) }; + + // create the EntrySource instance and set at the control model + Reference< XListEntrySource > xEntrySource( xFactory->createInstanceWithArguments( + SC_SERVICENAME_LISTSOURCE, aArgs ), UNO_QUERY_THROW ); + xEntrySink->setListEntrySource( xEntrySource ); + } + catch( const Exception& ) + { + } +} + +void XclImpControlHelper::ProcessControl( const XclImpDrawObjBase& rDrawObj ) const +{ + Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( mxShape ); + if( !xCtrlModel.is() ) + return; + + ApplySheetLinkProps(); + + ScfPropertySet aPropSet( xCtrlModel ); + + // #i51348# set object name at control model + aPropSet.SetStringProperty( "Name", rDrawObj.GetObjName() ); + + // control visible and printable? + aPropSet.SetBoolProperty( "EnableVisible", rDrawObj.IsVisible() ); + aPropSet.SetBoolProperty( "Printable", rDrawObj.IsPrintable() ); + + // virtual call for type specific processing + DoProcessControl( aPropSet ); +} + +void XclImpControlHelper::ReadCellLinkFormula( XclImpStream& rStrm, bool bWithBoundSize ) +{ + ScRangeList aScRanges; + ReadRangeList( aScRanges, rStrm, bWithBoundSize ); + // Use first cell of first range + if ( !aScRanges.empty() ) + { + const ScRange & rScRange = aScRanges.front(); + mxCellLink = std::make_shared<ScAddress>( rScRange.aStart ); + } +} + +void XclImpControlHelper::ReadSourceRangeFormula( XclImpStream& rStrm, bool bWithBoundSize ) +{ + ScRangeList aScRanges; + ReadRangeList( aScRanges, rStrm, bWithBoundSize ); + // Use first range + if ( !aScRanges.empty() ) + { + const ScRange & rScRange = aScRanges.front(); + mxSrcRange = std::make_shared<ScRange>( rScRange ); + } +} + +void XclImpControlHelper::DoProcessControl( ScfPropertySet& ) const +{ +} + +void XclImpControlHelper::ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm ) +{ + XclTokenArray aXclTokArr; + sal_uInt16 nSize = XclTokenArray::ReadSize(rStrm); + rStrm.Ignore( 4 ); + aXclTokArr.ReadArray(nSize, rStrm); + mrRoot.GetFormulaCompiler().CreateRangeList( rScRanges, EXC_FMLATYPE_CONTROL, aXclTokArr, rStrm ); +} + +void XclImpControlHelper::ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm, bool bWithBoundSize ) +{ + if( bWithBoundSize ) + { + sal_uInt16 nSize; + nSize = rStrm.ReaduInt16(); + if( nSize > 0 ) + { + rStrm.PushPosition(); + ReadRangeList( rScRanges, rStrm ); + rStrm.PopPosition(); + rStrm.Ignore( nSize ); + } + } + else + { + ReadRangeList( rScRanges, rStrm ); + } +} + +XclImpTbxObjBase::XclImpTbxObjBase( const XclImpRoot& rRoot ) : + XclImpTextObj( rRoot ), + XclImpControlHelper( rRoot, EXC_CTRL_BINDPOSITION ) +{ + SetSimpleMacro( false ); + SetCustomDffObj( true ); +} + +namespace { + +void lclExtractColor( sal_uInt8& rnColorIdx, const DffPropSet& rDffPropSet, sal_uInt32 nPropId ) +{ + if( rDffPropSet.IsProperty( nPropId ) ) + { + sal_uInt32 nColor = rDffPropSet.GetPropertyValue( nPropId, 0 ); + if( (nColor & 0xFF000000) == 0x08000000 ) + rnColorIdx = ::extract_value< sal_uInt8 >( nColor, 0, 8 ); + } +} + +} // namespace + +void XclImpTbxObjBase::SetDffProperties( const DffPropSet& rDffPropSet ) +{ + maFillData.mnPattern = rDffPropSet.GetPropertyBool( DFF_Prop_fFilled ) ? EXC_PATT_SOLID : EXC_PATT_NONE; + lclExtractColor( maFillData.mnBackColorIdx, rDffPropSet, DFF_Prop_fillBackColor ); + lclExtractColor( maFillData.mnPattColorIdx, rDffPropSet, DFF_Prop_fillColor ); + ::set_flag( maFillData.mnAuto, EXC_OBJ_LINE_AUTO, false ); + + maLineData.mnStyle = rDffPropSet.GetPropertyBool( DFF_Prop_fLine ) ? EXC_OBJ_LINE_SOLID : EXC_OBJ_LINE_NONE; + lclExtractColor( maLineData.mnColorIdx, rDffPropSet, DFF_Prop_lineColor ); + ::set_flag( maLineData.mnAuto, EXC_OBJ_FILL_AUTO, false ); +} + +bool XclImpTbxObjBase::FillMacroDescriptor( ScriptEventDescriptor& rDescriptor ) const +{ + return XclControlHelper::FillMacroDescriptor( rDescriptor, DoGetEventType(), GetMacroName(), GetDocShell() ); +} + +void XclImpTbxObjBase::ConvertFont( ScfPropertySet& rPropSet ) const +{ + if( maTextData.mxString ) + { + const XclFormatRunVec& rFormatRuns = maTextData.mxString->GetFormats(); + if( rFormatRuns.empty() ) + GetFontBuffer().WriteDefaultCtrlFontProperties( rPropSet ); + else + GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL, rFormatRuns.front().mnFontIdx ); + } +} + +void XclImpTbxObjBase::ConvertLabel( ScfPropertySet& rPropSet ) const +{ + if( maTextData.mxString ) + { + OUString aLabel = maTextData.mxString->GetText(); + if( maTextData.maData.mnShortcut > 0 ) + { + sal_Int32 nPos = aLabel.indexOf( static_cast< sal_Unicode >( maTextData.maData.mnShortcut ) ); + if( nPos != -1 ) + aLabel = aLabel.replaceAt( nPos, 0, u"~" ); + } + rPropSet.SetStringProperty( "Label", aLabel ); + + //Excel Alt text <==> Aoo description + //For TBX control, if user does not operate alt text, alt text will be set label text as default value in Excel. + //In this case, DFF_Prop_wzDescription will not be set in excel file. + //So In the end of SvxMSDffManager::ImportShape, description will not be set. But actually in excel, + //the alt text is the label value. So here set description as label text first which is called before ImportShape. + Reference< css::beans::XPropertySet > xPropset( mxShape, UNO_QUERY ); + try{ + if(xPropset.is()) + xPropset->setPropertyValue( "Description", Any(aLabel) ); + }catch( ... ) + { + SAL_WARN("sc.filter", "Can't set a default text for TBX Control "); + } + } + ConvertFont( rPropSet ); +} + +SdrObjectUniquePtr XclImpTbxObjBase::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const +{ + SdrObjectUniquePtr xSdrObj( rDffConv.CreateSdrObject( *this, rAnchorRect ) ); + rDffConv.Progress(); + return xSdrObj; +} + +void XclImpTbxObjBase::DoPreProcessSdrObj( XclImpDffConverter& /*rDffConv*/, SdrObject& /*rSdrObj*/ ) const +{ + // do not call DoPreProcessSdrObj() from base class (to skip text processing) + ProcessControl( *this ); +} + +XclImpButtonObj::XclImpButtonObj( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ) +{ +} + +void XclImpButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // label and text formatting + ConvertLabel( rPropSet ); + + /* Horizontal text alignment. For unknown reason, the property type is a + simple sal_Int16 and not a com.sun.star.style.HorizontalAlignment. */ + sal_Int16 nHorAlign = 1; + switch( maTextData.maData.GetHorAlign() ) + { + case EXC_OBJ_HOR_LEFT: nHorAlign = 0; break; + case EXC_OBJ_HOR_CENTER: nHorAlign = 1; break; + case EXC_OBJ_HOR_RIGHT: nHorAlign = 2; break; + } + rPropSet.SetProperty( "Align", nHorAlign ); + + // vertical text alignment + namespace csss = ::com::sun::star::style; + csss::VerticalAlignment eVerAlign = csss::VerticalAlignment_MIDDLE; + switch( maTextData.maData.GetVerAlign() ) + { + case EXC_OBJ_VER_TOP: eVerAlign = csss::VerticalAlignment_TOP; break; + case EXC_OBJ_VER_CENTER: eVerAlign = csss::VerticalAlignment_MIDDLE; break; + case EXC_OBJ_VER_BOTTOM: eVerAlign = csss::VerticalAlignment_BOTTOM; break; + } + rPropSet.SetProperty( "VerticalAlign", eVerAlign ); + + // always wrap text automatically + rPropSet.SetBoolProperty( "MultiLine", true ); + + // default button + bool bDefButton = ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_DEFAULT ); + rPropSet.SetBoolProperty( "DefaultButton", bDefButton ); + + // button type (flags cannot be combined in OOo) + namespace cssa = ::com::sun::star::awt; + cssa::PushButtonType eButtonType = cssa::PushButtonType_STANDARD; + if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_CLOSE ) ) + eButtonType = cssa::PushButtonType_OK; + else if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_CANCEL ) ) + eButtonType = cssa::PushButtonType_CANCEL; + else if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_HELP ) ) + eButtonType = cssa::PushButtonType_HELP; + // property type is short, not enum + rPropSet.SetProperty( "PushButtonType", sal_Int16( eButtonType ) ); +} + +OUString XclImpButtonObj::DoGetServiceName() const +{ + return "com.sun.star.form.component.CommandButton"; +} + +XclTbxEventType XclImpButtonObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_ACTION; +} + +XclImpCheckBoxObj::XclImpCheckBoxObj( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ), + mnState( EXC_OBJ_CHECKBOX_UNCHECKED ), + mnCheckBoxFlags( 0 ) +{ +} + +void XclImpCheckBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + rStrm.Ignore( 10 ); + maTextData.maData.mnFlags = rStrm.ReaduInt16(); + rStrm.Ignore( 20 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused + ReadCellLinkFormula( rStrm, true ); + maTextData.maData.mnTextLen = rStrm.ReaduInt16(); + maTextData.ReadByteString( rStrm ); + mnState = rStrm.ReaduInt16(); + maTextData.maData.mnShortcut = rStrm.ReaduInt16(); + maTextData.maData.mnShortcutEA = rStrm.ReaduInt16(); + mnCheckBoxFlags = rStrm.ReaduInt16(); +} + +void XclImpCheckBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJCBLS: + // do not read EXC_ID_OBJCBLSDATA, not written by OOo Excel export + mnState = rStrm.ReaduInt16(); + rStrm.Ignore( 4 ); + maTextData.maData.mnShortcut = rStrm.ReaduInt16(); + maTextData.maData.mnShortcutEA = rStrm.ReaduInt16(); + mnCheckBoxFlags = rStrm.ReaduInt16(); + break; + case EXC_ID_OBJCBLSFMLA: + ReadCellLinkFormula( rStrm, false ); + break; + default: + XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +void XclImpCheckBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // label and text formatting + ConvertLabel( rPropSet ); + + // state + bool bSupportsTristate = GetObjType() == EXC_OBJTYPE_CHECKBOX; + sal_Int16 nApiState = 0; + switch( mnState ) + { + case EXC_OBJ_CHECKBOX_UNCHECKED: nApiState = 0; break; + case EXC_OBJ_CHECKBOX_CHECKED: nApiState = 1; break; + case EXC_OBJ_CHECKBOX_TRISTATE: nApiState = bSupportsTristate ? 2 : 1; break; + } + if( bSupportsTristate ) + rPropSet.SetBoolProperty( "TriState", nApiState == 2 ); + rPropSet.SetProperty( "DefaultState", nApiState ); + + // box style + namespace AwtVisualEffect = ::com::sun::star::awt::VisualEffect; + sal_Int16 nEffect = ::get_flagvalue( mnCheckBoxFlags, EXC_OBJ_CHECKBOX_FLAT, AwtVisualEffect::FLAT, AwtVisualEffect::LOOK3D ); + rPropSet.SetProperty( "VisualEffect", nEffect ); + + // do not wrap text automatically + rPropSet.SetBoolProperty( "MultiLine", false ); + + // #i40279# always centered vertically + namespace csss = ::com::sun::star::style; + rPropSet.SetProperty( "VerticalAlign", csss::VerticalAlignment_MIDDLE ); + + // background color + if( maFillData.IsFilled() ) + { + sal_Int32 nColor = static_cast< sal_Int32 >( GetSolidFillColor( maFillData ) ); + rPropSet.SetProperty( "BackgroundColor", nColor ); + } +} + +OUString XclImpCheckBoxObj::DoGetServiceName() const +{ + return "com.sun.star.form.component.CheckBox"; +} + +XclTbxEventType XclImpCheckBoxObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_ACTION; +} + +XclImpOptionButtonObj::XclImpOptionButtonObj( const XclImpRoot& rRoot ) : + XclImpCheckBoxObj( rRoot ), + mnNextInGroup( 0 ), + mnFirstInGroup( 1 ) +{ +} + +void XclImpOptionButtonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + rStrm.Ignore( 10 ); + maTextData.maData.mnFlags = rStrm.ReaduInt16(); + rStrm.Ignore( 32 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused + ReadCellLinkFormula( rStrm, true ); + maTextData.maData.mnTextLen = rStrm.ReaduInt16(); + maTextData.ReadByteString( rStrm ); + mnState = rStrm.ReaduInt16(); + maTextData.maData.mnShortcut = rStrm.ReaduInt16(); + maTextData.maData.mnShortcutEA = rStrm.ReaduInt16(); + mnCheckBoxFlags = rStrm.ReaduInt16(); + mnNextInGroup = rStrm.ReaduInt16(); + mnFirstInGroup = rStrm.ReaduInt16(); +} + +void XclImpOptionButtonObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJRBODATA: + mnNextInGroup = rStrm.ReaduInt16(); + mnFirstInGroup = rStrm.ReaduInt16(); + break; + default: + XclImpCheckBoxObj::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +void XclImpOptionButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + XclImpCheckBoxObj::DoProcessControl( rPropSet ); + // TODO: grouping + XclImpOptionButtonObj* pTbxObj = dynamic_cast< XclImpOptionButtonObj* >( GetObjectManager().GetSheetDrawing( GetTab() ).FindDrawObj( mnNextInGroup ).get() ); + if ( pTbxObj && pTbxObj->mnFirstInGroup ) + { + // Group has terminated + // traverse each RadioButton in group and + // a) apply the groupname + // b) propagate the linked cell from the lead radiobutton + // c) apply the correct Ref value + XclImpOptionButtonObj* pLeader = pTbxObj; + + sal_Int32 nRefVal = 1; + do + { + + Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( pTbxObj->mxShape ); + if ( xCtrlModel.is() ) + { + ScfPropertySet aProps( xCtrlModel ); + OUString sGroupName = OUString::number( pLeader->GetDffShapeId() ); + + aProps.SetStringProperty( "GroupName", sGroupName ); + aProps.SetStringProperty( "RefValue", OUString::number( nRefVal++ ) ); + if ( pLeader->HasCellLink() && !pTbxObj->HasCellLink() ) + { + // propagate cell link info + pTbxObj->mxCellLink = std::make_shared<ScAddress>( *pLeader->mxCellLink ); + pTbxObj->ApplySheetLinkProps(); + } + pTbxObj = dynamic_cast< XclImpOptionButtonObj* >( GetObjectManager().GetSheetDrawing( GetTab() ).FindDrawObj( pTbxObj->mnNextInGroup ).get() ); + } + else + pTbxObj = nullptr; + } while ( pTbxObj && ( pTbxObj->mnFirstInGroup != 1 ) ); + } + else + { + // not the leader? try and find it + } +} + +OUString XclImpOptionButtonObj::DoGetServiceName() const +{ + return "com.sun.star.form.component.RadioButton"; +} + +XclTbxEventType XclImpOptionButtonObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_ACTION; +} + +XclImpLabelObj::XclImpLabelObj( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ) +{ +} + +void XclImpLabelObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // label and text formatting + ConvertLabel( rPropSet ); + + // text alignment (always top/left aligned) + rPropSet.SetProperty( "Align", sal_Int16( 0 ) ); + namespace csss = ::com::sun::star::style; + rPropSet.SetProperty( "VerticalAlign", csss::VerticalAlignment_TOP ); + + // always wrap text automatically + rPropSet.SetBoolProperty( "MultiLine", true ); +} + +OUString XclImpLabelObj::DoGetServiceName() const +{ + return "com.sun.star.form.component.FixedText"; +} + +XclTbxEventType XclImpLabelObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_MOUSE; +} + +XclImpGroupBoxObj::XclImpGroupBoxObj( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ), + mnGroupBoxFlags( 0 ) +{ +} + +void XclImpGroupBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + rStrm.Ignore( 10 ); + maTextData.maData.mnFlags = rStrm.ReaduInt16(); + rStrm.Ignore( 26 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused + maTextData.maData.mnTextLen = rStrm.ReaduInt16(); + maTextData.ReadByteString( rStrm ); + maTextData.maData.mnShortcut = rStrm.ReaduInt16(); + maTextData.maData.mnShortcutEA = rStrm.ReaduInt16( ); + mnGroupBoxFlags = rStrm.ReaduInt16(); +} + +void XclImpGroupBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJGBODATA: + maTextData.maData.mnShortcut = rStrm.ReaduInt16(); + maTextData.maData.mnShortcutEA = rStrm.ReaduInt16(); + mnGroupBoxFlags = rStrm.ReaduInt16(); + break; + default: + XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +void XclImpGroupBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // label and text formatting + ConvertLabel( rPropSet ); +} + +OUString XclImpGroupBoxObj::DoGetServiceName() const +{ + return "com.sun.star.form.component.GroupBox"; +} + +XclTbxEventType XclImpGroupBoxObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_MOUSE; +} + +XclImpDialogObj::XclImpDialogObj( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ) +{ +} + +void XclImpDialogObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // label and text formatting + ConvertLabel( rPropSet ); +} + +OUString XclImpDialogObj::DoGetServiceName() const +{ + // dialog frame faked by a groupbox + return "com.sun.star.form.component.GroupBox"; +} + +XclTbxEventType XclImpDialogObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_MOUSE; +} + +XclImpEditObj::XclImpEditObj( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ), + mnContentType( EXC_OBJ_EDIT_TEXT ), + mnMultiLine( 0 ), + mnScrollBar( 0 ), + mnListBoxObjId( 0 ) +{ +} + +bool XclImpEditObj::IsNumeric() const +{ + return (mnContentType == EXC_OBJ_EDIT_INTEGER) || (mnContentType == EXC_OBJ_EDIT_DOUBLE); +} + +void XclImpEditObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + rStrm.Ignore( 10 ); + maTextData.maData.mnFlags = rStrm.ReaduInt16(); + rStrm.Ignore( 14 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused + maTextData.maData.mnTextLen = rStrm.ReaduInt16(); + maTextData.ReadByteString( rStrm ); + mnContentType = rStrm.ReaduInt16(); + mnMultiLine = rStrm.ReaduInt16(); + mnScrollBar = rStrm.ReaduInt16(); + mnListBoxObjId = rStrm.ReaduInt16(); +} + +void XclImpEditObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJEDODATA: + mnContentType = rStrm.ReaduInt16(); + mnMultiLine = rStrm.ReaduInt16(); + mnScrollBar = rStrm.ReaduInt16(); + mnListBoxObjId = rStrm.ReaduInt16(); + break; + default: + XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +void XclImpEditObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + if( maTextData.mxString ) + { + OUString aText = maTextData.mxString->GetText(); + if( IsNumeric() ) + { + // TODO: OUString::toDouble() does not handle local decimal separator + rPropSet.SetProperty( "DefaultValue", aText.toDouble() ); + rPropSet.SetBoolProperty( "Spin", mnScrollBar != 0 ); + } + else + { + rPropSet.SetProperty( "DefaultText", aText ); + rPropSet.SetBoolProperty( "MultiLine", mnMultiLine != 0 ); + rPropSet.SetBoolProperty( "VScroll", mnScrollBar != 0 ); + } + } + ConvertFont( rPropSet ); +} + +OUString XclImpEditObj::DoGetServiceName() const +{ + return IsNumeric() ? + OUString( "com.sun.star.form.component.NumericField" ) : + OUString( "com.sun.star.form.component.TextField" ); +} + +XclTbxEventType XclImpEditObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_TEXT; +} + +XclImpTbxObjScrollableBase::XclImpTbxObjScrollableBase( const XclImpRoot& rRoot ) : + XclImpTbxObjBase( rRoot ), + mnValue( 0 ), + mnMin( 0 ), + mnMax( 100 ), + mnStep( 1 ), + mnPageStep( 10 ), + mnOrient( 0 ), + mnThumbWidth( 1 ), + mnScrollFlags( 0 ) +{ +} + +void XclImpTbxObjScrollableBase::ReadSbs( XclImpStream& rStrm ) +{ + rStrm.Ignore( 4 ); + mnValue = rStrm.ReaduInt16(); + mnMin = rStrm.ReaduInt16(); + mnMax = rStrm.ReaduInt16(); + mnStep = rStrm.ReaduInt16(); + mnPageStep = rStrm.ReaduInt16(); + mnOrient = rStrm.ReaduInt16(); + mnThumbWidth = rStrm.ReaduInt16(); + mnScrollFlags = rStrm.ReaduInt16(); +} + +void XclImpTbxObjScrollableBase::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJSBS: + ReadSbs( rStrm ); + break; + case EXC_ID_OBJSBSFMLA: + ReadCellLinkFormula( rStrm, false ); + break; + default: + XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +XclImpSpinButtonObj::XclImpSpinButtonObj( const XclImpRoot& rRoot ) : + XclImpTbxObjScrollableBase( rRoot ) +{ +} + +void XclImpSpinButtonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + ReadSbs( rStrm ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused + ReadCellLinkFormula( rStrm, true ); +} + +void XclImpSpinButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // Calc's "Border" property is not the 3D/flat style effect in Excel (#i34712#) + rPropSet.SetProperty( "Border", css::awt::VisualEffect::NONE ); + rPropSet.SetProperty< sal_Int32 >( "DefaultSpinValue", mnValue ); + rPropSet.SetProperty< sal_Int32 >( "SpinValueMin", mnMin ); + rPropSet.SetProperty< sal_Int32 >( "SpinValueMax", mnMax ); + rPropSet.SetProperty< sal_Int32 >( "SpinIncrement", mnStep ); + + // Excel spin buttons always vertical + rPropSet.SetProperty( "Orientation", css::awt::ScrollBarOrientation::VERTICAL ); +} + +OUString XclImpSpinButtonObj::DoGetServiceName() const +{ + return "com.sun.star.form.component.SpinButton"; +} + +XclTbxEventType XclImpSpinButtonObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_VALUE; +} + +XclImpScrollBarObj::XclImpScrollBarObj( const XclImpRoot& rRoot ) : + XclImpTbxObjScrollableBase( rRoot ) +{ +} + +void XclImpScrollBarObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + ReadSbs( rStrm ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused + ReadCellLinkFormula( rStrm, true ); +} + +void XclImpScrollBarObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // Calc's "Border" property is not the 3D/flat style effect in Excel (#i34712#) + rPropSet.SetProperty( "Border", css::awt::VisualEffect::NONE ); + rPropSet.SetProperty< sal_Int32 >( "DefaultScrollValue", mnValue ); + rPropSet.SetProperty< sal_Int32 >( "ScrollValueMin", mnMin ); + rPropSet.SetProperty< sal_Int32 >( "ScrollValueMax", mnMax ); + rPropSet.SetProperty< sal_Int32 >( "LineIncrement", mnStep ); + rPropSet.SetProperty< sal_Int32 >( "BlockIncrement", mnPageStep ); + rPropSet.SetProperty( "VisibleSize", ::std::min< sal_Int32 >( mnPageStep, 1 ) ); + + namespace AwtScrollOrient = ::com::sun::star::awt::ScrollBarOrientation; + sal_Int32 nApiOrient = ::get_flagvalue( mnOrient, EXC_OBJ_SCROLLBAR_HOR, AwtScrollOrient::HORIZONTAL, AwtScrollOrient::VERTICAL ); + rPropSet.SetProperty( "Orientation", nApiOrient ); +} + +OUString XclImpScrollBarObj::DoGetServiceName() const +{ + return "com.sun.star.form.component.ScrollBar"; +} + +XclTbxEventType XclImpScrollBarObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_VALUE; +} + +XclImpTbxObjListBase::XclImpTbxObjListBase( const XclImpRoot& rRoot ) : + XclImpTbxObjScrollableBase( rRoot ), + mnEntryCount( 0 ), + mnSelEntry( 0 ), + mnListFlags( 0 ), + mnEditObjId( 0 ), + mbHasDefFontIdx( false ) +{ +} + +void XclImpTbxObjListBase::ReadLbsData( XclImpStream& rStrm ) +{ + ReadSourceRangeFormula( rStrm, true ); + mnEntryCount = rStrm.ReaduInt16(); + mnSelEntry = rStrm.ReaduInt16(); + mnListFlags = rStrm.ReaduInt16(); + mnEditObjId = rStrm.ReaduInt16(); +} + +void XclImpTbxObjListBase::SetBoxFormatting( ScfPropertySet& rPropSet ) const +{ + // border style + namespace AwtVisualEffect = ::com::sun::star::awt::VisualEffect; + sal_Int16 nApiBorder = ::get_flagvalue( mnListFlags, EXC_OBJ_LISTBOX_FLAT, AwtVisualEffect::FLAT, AwtVisualEffect::LOOK3D ); + rPropSet.SetProperty( "Border", nApiBorder ); + + // font formatting + if( mbHasDefFontIdx ) + GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL, maTextData.maData.mnDefFontIdx ); + else + GetFontBuffer().WriteDefaultCtrlFontProperties( rPropSet ); +} + +XclImpListBoxObj::XclImpListBoxObj( const XclImpRoot& rRoot ) : + XclImpTbxObjListBase( rRoot ) +{ +} + +void XclImpListBoxObj::ReadFullLbsData( XclImpStream& rStrm, std::size_t nRecLeft ) +{ + std::size_t nRecEnd = rStrm.GetRecPos() + nRecLeft; + ReadLbsData( rStrm ); + OSL_ENSURE( (rStrm.GetRecPos() == nRecEnd) || (rStrm.GetRecPos() + mnEntryCount == nRecEnd), + "XclImpListBoxObj::ReadFullLbsData - invalid size of OBJLBSDATA record" ); + while (rStrm.IsValid()) + { + if (rStrm.GetRecPos() >= nRecEnd) + break; + maSelection.push_back( rStrm.ReaduInt8() ); + } +} + +void XclImpListBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + ReadSbs( rStrm ); + rStrm.Ignore( 18 ); + maTextData.maData.mnDefFontIdx = rStrm.ReaduInt16(); + rStrm.Ignore( 4 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused + ReadCellLinkFormula( rStrm, true ); + ReadFullLbsData( rStrm, rStrm.GetRecLeft() ); + mbHasDefFontIdx = true; +} + +void XclImpListBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJLBSDATA: + ReadFullLbsData( rStrm, nSubRecSize ); + break; + default: + XclImpTbxObjListBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +void XclImpListBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // listbox formatting + SetBoxFormatting( rPropSet ); + + // selection type + sal_uInt8 nSelType = ::extract_value< sal_uInt8 >( mnListFlags, 4, 2 ); + bool bMultiSel = nSelType != EXC_OBJ_LISTBOX_SINGLE; + rPropSet.SetBoolProperty( "MultiSelection", bMultiSel ); + + // selection (do not set, if listbox is linked to a cell) + if( HasCellLink() ) + return; + + ScfInt16Vec aSelVec; + + // multi selection: API expects sequence of list entry indexes + if( bMultiSel ) + { + sal_Int16 nIndex = 0; + for( const auto& rItem : maSelection ) + { + if( rItem != 0 ) + aSelVec.push_back( nIndex ); + ++nIndex; + } + } + // single selection: mnSelEntry is one-based, API expects zero-based + else if( mnSelEntry > 0 ) + aSelVec.push_back( static_cast< sal_Int16 >( mnSelEntry - 1 ) ); + + if( !aSelVec.empty() ) + { + Sequence<sal_Int16> aSelSeq(aSelVec.data(), static_cast<sal_Int32>(aSelVec.size())); + rPropSet.SetProperty( "DefaultSelection", aSelSeq ); + } +} + +OUString XclImpListBoxObj::DoGetServiceName() const +{ + return "com.sun.star.form.component.ListBox"; +} + +XclTbxEventType XclImpListBoxObj::DoGetEventType() const +{ + return EXC_TBX_EVENT_CHANGE; +} + +XclImpDropDownObj::XclImpDropDownObj( const XclImpRoot& rRoot ) : + XclImpTbxObjListBase( rRoot ), + mnLeft( 0 ), + mnTop( 0 ), + mnRight( 0 ), + mnBottom( 0 ), + mnDropDownFlags( 0 ), + mnLineCount( 0 ), + mnMinWidth( 0 ) +{ +} + +sal_uInt16 XclImpDropDownObj::GetDropDownType() const +{ + return ::extract_value< sal_uInt8 >( mnDropDownFlags, 0, 2 ); +} + +void XclImpDropDownObj::ReadFullLbsData( XclImpStream& rStrm ) +{ + ReadLbsData( rStrm ); + mnDropDownFlags = rStrm.ReaduInt16(); + mnLineCount = rStrm.ReaduInt16(); + mnMinWidth = rStrm.ReaduInt16(); + maTextData.maData.mnTextLen = rStrm.ReaduInt16(); + maTextData.ReadByteString( rStrm ); + // dropdowns of auto-filters have 'simple' style, they don't have a text area + if( GetDropDownType() == EXC_OBJ_DROPDOWN_SIMPLE ) + SetProcessSdrObj( false ); +} + +void XclImpDropDownObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ ) +{ + ReadFrameData( rStrm ); + ReadSbs( rStrm ); + rStrm.Ignore( 18 ); + maTextData.maData.mnDefFontIdx = rStrm.ReaduInt16(); + rStrm.Ignore( 14 ); + mnLeft = rStrm.ReaduInt16(); + mnTop = rStrm.ReaduInt16(); + mnRight = rStrm.ReaduInt16(); + mnBottom = rStrm.ReaduInt16(); + rStrm.Ignore( 4 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused + ReadCellLinkFormula( rStrm, true ); + ReadFullLbsData( rStrm ); + mbHasDefFontIdx = true; +} + +void XclImpDropDownObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJLBSDATA: + ReadFullLbsData( rStrm ); + break; + default: + XclImpTbxObjListBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +void XclImpDropDownObj::DoProcessControl( ScfPropertySet& rPropSet ) const +{ + // dropdown listbox formatting + SetBoxFormatting( rPropSet ); + // enable dropdown button + rPropSet.SetBoolProperty( "Dropdown", true ); + // dropdown line count + rPropSet.SetProperty( "LineCount", mnLineCount ); + + if( GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX ) + { + // text of editable combobox + if( maTextData.mxString ) + rPropSet.SetStringProperty( "DefaultText", maTextData.mxString->GetText() ); + } + else + { + // selection (do not set, if dropdown is linked to a cell) + if( !HasCellLink() && (mnSelEntry > 0) ) + { + Sequence< sal_Int16 > aSelSeq{ o3tl::narrowing<sal_Int16>(mnSelEntry - 1) }; + rPropSet.SetProperty( "DefaultSelection", aSelSeq ); + } + } +} + +OUString XclImpDropDownObj::DoGetServiceName() const +{ + return (GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX) ? + OUString( "com.sun.star.form.component.ComboBox" ) : + OUString( "com.sun.star.form.component.ListBox" ); +} + +XclTbxEventType XclImpDropDownObj::DoGetEventType() const +{ + return (GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX) ? EXC_TBX_EVENT_TEXT : EXC_TBX_EVENT_CHANGE; +} + +XclImpPictureObj::XclImpPictureObj( const XclImpRoot& rRoot ) : + XclImpRectObj( rRoot ), + XclImpControlHelper( rRoot, EXC_CTRL_BINDCONTENT ), + mnStorageId( 0 ), + mnCtlsStrmPos( 0 ), + mnCtlsStrmSize( 0 ), + mbEmbedded( false ), + mbLinked( false ), + mbSymbol( false ), + mbControl( false ), + mbUseCtlsStrm( false ) +{ + SetAreaObj( true ); + SetSimpleMacro( true ); + SetCustomDffObj( true ); +} + +OUString XclImpPictureObj::GetOleStorageName() const +{ + OUStringBuffer aStrgName; + if( (mbEmbedded || mbLinked) && !mbControl && (mnStorageId > 0) ) + { + aStrgName = mbEmbedded ? std::u16string_view(u"" EXC_STORAGE_OLE_EMBEDDED) : std::u16string_view(u"" EXC_STORAGE_OLE_LINKED); + static const char spcHexChars[] = "0123456789ABCDEF"; + for( sal_uInt8 nIndex = 32; nIndex > 0; nIndex -= 4 ) + aStrgName.append(OUStringChar( spcHexChars[ ::extract_value< sal_uInt8 >( mnStorageId, nIndex - 4, 4 ) ] )); + } + return aStrgName.makeStringAndClear(); +} + +void XclImpPictureObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + sal_uInt16 nLinkSize; + ReadFrameData( rStrm ); + rStrm.Ignore( 6 ); + nLinkSize = rStrm.ReaduInt16(); + rStrm.Ignore( 2 ); + ReadFlags3( rStrm ); + ReadMacro3( rStrm, nMacroSize ); + ReadPictFmla( rStrm, nLinkSize ); + + if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() ) + maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm ); +} + +void XclImpPictureObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) +{ + sal_uInt16 nLinkSize; + ReadFrameData( rStrm ); + rStrm.Ignore( 6 ); + nLinkSize = rStrm.ReaduInt16(); + rStrm.Ignore( 2 ); + ReadFlags3( rStrm ); + ReadMacro4( rStrm, nMacroSize ); + ReadPictFmla( rStrm, nLinkSize ); + + if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() ) + maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm ); +} + +void XclImpPictureObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) +{ + sal_uInt16 nLinkSize; + ReadFrameData( rStrm ); + rStrm.Ignore( 6 ); + nLinkSize = rStrm.ReaduInt16(); + rStrm.Ignore( 2 ); + ReadFlags3( rStrm ); + rStrm.Ignore( 4 ); + ReadName5( rStrm, nNameLen ); + ReadMacro5( rStrm, nMacroSize ); + ReadPictFmla( rStrm, nLinkSize ); + + if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() ) + { + // page background is stored as hidden picture with name "__BkgndObj" + if ( IsHidden() && (GetObjName() == "__BkgndObj") ) + GetPageSettings().ReadImgData( rStrm ); + else + maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm ); + } +} + +void XclImpPictureObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) +{ + switch( nSubRecId ) + { + case EXC_ID_OBJFLAGS: + ReadFlags8( rStrm ); + break; + case EXC_ID_OBJPICTFMLA: + ReadPictFmla( rStrm, rStrm.ReaduInt16() ); + break; + default: + XclImpDrawObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize ); + } +} + +SdrObjectUniquePtr XclImpPictureObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const +{ + // try to create an OLE object or form control + SdrObjectUniquePtr xSdrObj( rDffConv.CreateSdrObject( *this, rAnchorRect ) ); + + // insert a graphic replacement for unsupported ole object ( if none already + // exists ) Hmm ok, it's possibly that there has been some imported + // graphic at a base level but unlikely, normally controls have a valid + // preview in the IMGDATA record ( see below ) + // It might be possible to push such an imported graphic up to this + // XclImpPictureObj instance but there are so many layers of indirection I + // don't see an easy way. This way at least ensures that we can + // avoid a 'blank' shape that can result from a failed control import + if ( !xSdrObj && IsOcxControl() && maGraphic.GetType() == GraphicType::NONE ) + { + const_cast< XclImpPictureObj* >( this )->maGraphic = + SdrOle2Obj::GetEmptyOLEReplacementGraphic(); + } + // no OLE - create a plain picture from IMGDATA record data + if( !xSdrObj && (maGraphic.GetType() != GraphicType::NONE) ) + { + xSdrObj.reset( + new SdrGrafObj( + *GetDoc().GetDrawLayer(), + maGraphic, + rAnchorRect)); + ConvertRectStyle( *xSdrObj ); + } + + rDffConv.Progress(); + return xSdrObj; +} + +OUString XclImpPictureObj::GetObjName() const +{ + if( IsOcxControl() ) + { + OUString sName( GetObjectManager().GetOleNameOverride( GetTab(), GetObjId() ) ); + if (!sName.isEmpty()) + return sName; + } + return XclImpDrawObjBase::GetObjName(); +} + +void XclImpPictureObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const +{ + if( IsOcxControl() ) + { + // do not call XclImpRectObj::DoPreProcessSdrObj(), it would trace missing "printable" feature + ProcessControl( *this ); + } + else if( mbEmbedded || mbLinked ) + { + // trace missing "printable" feature + XclImpRectObj::DoPreProcessSdrObj( rDffConv, rSdrObj ); + + SfxObjectShell* pDocShell = GetDocShell(); + SdrOle2Obj* pOleSdrObj = dynamic_cast< SdrOle2Obj* >( &rSdrObj ); + if( pOleSdrObj && pDocShell ) + { + comphelper::EmbeddedObjectContainer& rEmbObjCont = pDocShell->GetEmbeddedObjectContainer(); + Reference< XEmbeddedObject > xEmbObj = pOleSdrObj->GetObjRef(); + OUString aOldName( pOleSdrObj->GetPersistName() ); + + /* The object persistence should be already in the storage, but + the object still might not be inserted into the container. */ + if( rEmbObjCont.HasEmbeddedObject( aOldName ) ) + { + if( !rEmbObjCont.HasEmbeddedObject( xEmbObj ) ) + // filter code is allowed to call the following method + rEmbObjCont.AddEmbeddedObject( xEmbObj, aOldName ); + } + else + { + /* If the object is still not in container it must be inserted + there, the name must be generated in this case. */ + OUString aNewName; + rEmbObjCont.InsertEmbeddedObject( xEmbObj, aNewName ); + if( aOldName != aNewName ) + // SetPersistName, not SetName + pOleSdrObj->SetPersistName( aNewName ); + } + } + } +} + +void XclImpPictureObj::ReadFlags3( XclImpStream& rStrm ) +{ + sal_uInt16 nFlags; + nFlags = rStrm.ReaduInt16(); + mbSymbol = ::get_flag( nFlags, EXC_OBJ_PIC_SYMBOL ); +} + +void XclImpPictureObj::ReadFlags8( XclImpStream& rStrm ) +{ + sal_uInt16 nFlags; + nFlags = rStrm.ReaduInt16(); + mbSymbol = ::get_flag( nFlags, EXC_OBJ_PIC_SYMBOL ); + mbControl = ::get_flag( nFlags, EXC_OBJ_PIC_CONTROL ); + mbUseCtlsStrm = ::get_flag( nFlags, EXC_OBJ_PIC_CTLSSTREAM ); + OSL_ENSURE( mbControl || !mbUseCtlsStrm, "XclImpPictureObj::ReadFlags8 - CTLS stream for controls only" ); + SetProcessSdrObj( mbControl || !mbUseCtlsStrm ); +} + +void XclImpPictureObj::ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nLinkSize ) +{ + std::size_t nLinkEnd = rStrm.GetRecPos() + nLinkSize; + if( nLinkSize >= 6 ) + { + sal_uInt16 nFmlaSize; + nFmlaSize = rStrm.ReaduInt16(); + OSL_ENSURE( nFmlaSize > 0, "XclImpPictureObj::ReadPictFmla - missing link formula" ); + // BIFF3/BIFF4 do not support storages, nothing to do here + if( (nFmlaSize > 0) && (GetBiff() >= EXC_BIFF5) ) + { + rStrm.Ignore( 4 ); + sal_uInt8 nToken; + nToken = rStrm.ReaduInt8(); + + // different processing for linked vs. embedded OLE objects + if( nToken == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ) ) + { + mbLinked = true; + switch( GetBiff() ) + { + case EXC_BIFF5: + { + sal_Int16 nRefIdx; + sal_uInt16 nNameIdx; + nRefIdx = rStrm.ReadInt16(); + rStrm.Ignore( 8 ); + nNameIdx = rStrm.ReaduInt16(); + rStrm.Ignore( 12 ); + const ExtName* pExtName = GetOldRoot().pExtNameBuff->GetNameByIndex( nRefIdx, nNameIdx ); + if( pExtName && pExtName->IsOLE() ) + mnStorageId = pExtName->nStorageId; + } + break; + case EXC_BIFF8: + { + sal_uInt16 nXti, nExtName; + nXti = rStrm.ReaduInt16(); + nExtName = rStrm.ReaduInt16(); + const XclImpExtName* pExtName = GetLinkManager().GetExternName( nXti, nExtName ); + if( pExtName && (pExtName->GetType() == xlExtOLE) ) + mnStorageId = pExtName->GetStorageId(); + } + break; + default: + DBG_ERROR_BIFF(); + } + } + else if( nToken == XclTokenArrayHelper::GetTokenId( EXC_TOKID_TBL, EXC_TOKCLASS_NONE ) ) + { + mbEmbedded = true; + OSL_ENSURE( nFmlaSize == 5, "XclImpPictureObj::ReadPictFmla - unexpected formula size" ); + rStrm.Ignore( nFmlaSize - 1 ); // token ID already read + if( nFmlaSize & 1 ) + rStrm.Ignore( 1 ); // padding byte + + // a class name may follow inside the picture link + if( rStrm.GetRecPos() + 2 <= nLinkEnd ) + { + sal_uInt16 nLen = rStrm.ReaduInt16(); + if( nLen > 0 ) + maClassName = (GetBiff() == EXC_BIFF8) ? rStrm.ReadUniString( nLen ) : rStrm.ReadRawByteString( nLen ); + } + } + // else: ignore other formulas, e.g. pictures linked to cell ranges + } + } + + // seek behind picture link data + rStrm.Seek( nLinkEnd ); + + // read additional data for embedded OLE objects following the picture link + if( IsOcxControl() ) + { + // #i26521# form controls to be ignored + if( maClassName == "Forms.HTML:Hidden.1" ) + { + SetProcessSdrObj( false ); + return; + } + + if( rStrm.GetRecLeft() <= 8 ) return; + + // position and size of control data in 'Ctls' stream + mnCtlsStrmPos = static_cast< std::size_t >( rStrm.ReaduInt32() ); + mnCtlsStrmSize = static_cast< std::size_t >( rStrm.ReaduInt32() ); + + if( rStrm.GetRecLeft() <= 8 ) return; + + // additional string (16-bit characters), e.g. for progress bar control + sal_uInt32 nAddStrSize; + nAddStrSize = rStrm.ReaduInt32(); + OSL_ENSURE( rStrm.GetRecLeft() >= nAddStrSize + 4, "XclImpPictureObj::ReadPictFmla - missing data" ); + if( rStrm.GetRecLeft() >= nAddStrSize + 4 ) + { + rStrm.Ignore( nAddStrSize ); + // cell link and source range + ReadCellLinkFormula( rStrm, true ); + ReadSourceRangeFormula( rStrm, true ); + } + } + else if( mbEmbedded && (rStrm.GetRecLeft() >= 4) ) + { + mnStorageId = rStrm.ReaduInt32(); + } +} + +// DFF stream conversion ====================================================== + +void XclImpSolverContainer::InsertSdrObjectInfo( SdrObject& rSdrObj, sal_uInt32 nDffShapeId, ShapeFlag nDffFlags ) +{ + if( nDffShapeId > 0 ) + { + maSdrInfoMap[ nDffShapeId ].Set( &rSdrObj, nDffFlags ); + maSdrObjMap[ &rSdrObj ] = nDffShapeId; + } +} + +void XclImpSolverContainer::RemoveSdrObjectInfo( SdrObject& rSdrObj ) +{ + // remove info of passed object from the maps + XclImpSdrObjMap::iterator aIt = maSdrObjMap.find( &rSdrObj ); + if( aIt != maSdrObjMap.end() ) + { + maSdrInfoMap.erase( aIt->second ); + maSdrObjMap.erase( aIt ); + } + + // remove info of all child objects of a group object + if( SdrObjGroup* pGroupObj = dynamic_cast< SdrObjGroup* >( &rSdrObj ) ) + { + if( SdrObjList* pSubList = pGroupObj->GetSubList() ) + { + // iterate flat over the list because this function already works recursively + SdrObjListIter aObjIt( pSubList, SdrIterMode::Flat ); + for( SdrObject* pChildObj = aObjIt.Next(); pChildObj; pChildObj = aObjIt.Next() ) + RemoveSdrObjectInfo( *pChildObj ); + } + } +} + +void XclImpSolverContainer::UpdateConnectorRules() +{ + for (auto const & pRule : aCList) + { + UpdateConnection( pRule->nShapeA, pRule->pAObj, &pRule->nSpFlagsA ); + UpdateConnection( pRule->nShapeB, pRule->pBObj, &pRule->nSpFlagsB ); + UpdateConnection( pRule->nShapeC, pRule->pCObj ); + } +} + +void XclImpSolverContainer::RemoveConnectorRules() +{ + aCList.clear(); + maSdrInfoMap.clear(); + maSdrObjMap.clear(); +} + +void XclImpSolverContainer::UpdateConnection( sal_uInt32 nDffShapeId, SdrObject*& rpSdrObj, ShapeFlag* pnDffFlags ) +{ + XclImpSdrInfoMap::const_iterator aIt = maSdrInfoMap.find( nDffShapeId ); + if( aIt != maSdrInfoMap.end() ) + { + rpSdrObj = aIt->second.mpSdrObj; + if( pnDffFlags ) + *pnDffFlags = aIt->second.mnDffFlags; + } +} + +XclImpSimpleDffConverter::XclImpSimpleDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm ) : + SvxMSDffManager( rDffStrm, rRoot.GetBasePath(), 0, nullptr, rRoot.GetDoc().GetDrawLayer(), 1440, COL_DEFAULT, nullptr ), + XclImpRoot( rRoot ) +{ + SetSvxMSDffSettings( SVXMSDFF_SETTINGS_CROP_BITMAPS | SVXMSDFF_SETTINGS_IMPORT_EXCEL ); +} + +XclImpSimpleDffConverter::~XclImpSimpleDffConverter() +{ +} + +bool XclImpSimpleDffConverter::GetColorFromPalette( sal_uInt16 nIndex, Color& rColor ) const +{ + Color nColor = GetPalette().GetColor( nIndex ); + + if( nColor == COL_AUTO ) + return false; + + rColor = nColor; + return true; +} + +XclImpDffConverter::XclImpDffConvData::XclImpDffConvData( + XclImpDrawing& rDrawing, SdrModel& rSdrModel, SdrPage& rSdrPage ) : + mrDrawing( rDrawing ), + mrSdrModel( rSdrModel ), + mrSdrPage( rSdrPage ), + mnLastCtrlIndex( -1 ), + mbHasCtrlForm( false ) +{ +} + +constexpr OUStringLiteral gaStdFormName( u"Standard" ); /// Standard name of control forms. + +XclImpDffConverter::XclImpDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm ) : + XclImpSimpleDffConverter( rRoot, rDffStrm ), + oox::ole::MSConvertOCXControls( rRoot.GetDocShell()->GetModel() ), + mnOleImpFlags( 0 ), + mbNotifyMacroEventRead(false) +{ + const SvtFilterOptions& rFilterOpt = SvtFilterOptions::Get(); + if( rFilterOpt.IsMathType2Math() ) + mnOleImpFlags |= OLE_MATHTYPE_2_STARMATH; + if( rFilterOpt.IsWinWord2Writer() ) + mnOleImpFlags |= OLE_WINWORD_2_STARWRITER; + if( rFilterOpt.IsPowerPoint2Impress() ) + mnOleImpFlags |= OLE_POWERPOINT_2_STARIMPRESS; + + // try to open the 'Ctls' storage stream containing OCX control properties + mxCtlsStrm = OpenStream( EXC_STREAM_CTLS ); + + // default text margin (convert EMU to drawing layer units) + mnDefTextMargin = EXC_OBJ_TEXT_MARGIN; + ScaleEmu( mnDefTextMargin ); +} + +XclImpDffConverter::~XclImpDffConverter() +{ +} + +OUString XclImpObjectManager::GetOleNameOverride( SCTAB nTab, sal_uInt16 nObjId ) +{ + OUString sOleName; + OUString sCodeName = GetExtDocOptions().GetCodeName( nTab ); + + if (mxOleCtrlNameOverride.is() && mxOleCtrlNameOverride->hasByName(sCodeName)) + { + Reference< XIndexContainer > xIdToOleName; + mxOleCtrlNameOverride->getByName( sCodeName ) >>= xIdToOleName; + xIdToOleName->getByIndex( nObjId ) >>= sOleName; + } + + return sOleName; +} + +void XclImpDffConverter::StartProgressBar( std::size_t nProgressSize ) +{ + mxProgress = std::make_shared<ScfProgressBar>( GetDocShell(), STR_PROGRESS_CALCULATING ); + mxProgress->AddSegment( nProgressSize ); + mxProgress->Activate(); +} + +void XclImpDffConverter::Progress( std::size_t nDelta ) +{ + OSL_ENSURE( mxProgress, "XclImpDffConverter::Progress - invalid call, no progress bar" ); + mxProgress->Progress( nDelta ); +} + +void XclImpDffConverter::InitializeDrawing( XclImpDrawing& rDrawing, SdrModel& rSdrModel, SdrPage& rSdrPage ) +{ + XclImpDffConvDataRef xConvData = std::make_shared<XclImpDffConvData>( rDrawing, rSdrModel, rSdrPage ); + maDataStack.push_back( xConvData ); + SetModel( &xConvData->mrSdrModel, 1440 ); +} + +void XclImpDffConverter::ProcessObject( SdrObjList& rObjList, XclImpDrawObjBase& rDrawObj ) +{ + if( !rDrawObj.IsProcessSdrObj() ) + return; + + const XclObjAnchor* pAnchor = rDrawObj.GetAnchor(); + if(!pAnchor) + return; + + tools::Rectangle aAnchorRect = GetConvData().mrDrawing.CalcAnchorRect( *pAnchor, false ); + if( rDrawObj.IsValidSize( aAnchorRect ) ) + { + // CreateSdrObject() recursively creates embedded child objects + SdrObjectUniquePtr xSdrObj( rDrawObj.CreateSdrObject( *this, aAnchorRect, false ) ); + if( xSdrObj ) + rDrawObj.PreProcessSdrObject( *this, *xSdrObj ); + // call InsertSdrObject() also, if SdrObject is missing + InsertSdrObject( rObjList, rDrawObj, xSdrObj.release() ); + } +} + +void XclImpDffConverter::ProcessDrawing( const XclImpDrawObjVector& rDrawObjs ) +{ + SdrPage& rSdrPage = GetConvData().mrSdrPage; + for( const auto& rxDrawObj : rDrawObjs ) + ProcessObject( rSdrPage, *rxDrawObj ); +} + +void XclImpDffConverter::ProcessDrawing( SvStream& rDffStrm ) +{ + if( rDffStrm.TellEnd() > 0 ) + { + rDffStrm.Seek( STREAM_SEEK_TO_BEGIN ); + DffRecordHeader aHeader; + ReadDffRecordHeader( rDffStrm, aHeader ); + OSL_ENSURE( aHeader.nRecType == DFF_msofbtDgContainer, "XclImpDffConverter::ProcessDrawing - unexpected record" ); + if( aHeader.nRecType == DFF_msofbtDgContainer ) + ProcessDgContainer( rDffStrm, aHeader ); + } +} + +void XclImpDffConverter::FinalizeDrawing() +{ + OSL_ENSURE( !maDataStack.empty(), "XclImpDffConverter::FinalizeDrawing - no drawing manager on stack" ); + maDataStack.pop_back(); + // restore previous model at core DFF converter + if( !maDataStack.empty() ) + SetModel( &maDataStack.back()->mrSdrModel, 1440 ); +} + +void XclImpDffConverter::NotifyMacroEventRead() +{ + if (mbNotifyMacroEventRead) + return; + comphelper::DocumentInfo::notifyMacroEventRead(mxModel); + mbNotifyMacroEventRead = true; +} + +SdrObjectUniquePtr XclImpDffConverter::CreateSdrObject( const XclImpTbxObjBase& rTbxObj, const tools::Rectangle& rAnchorRect ) +{ + SdrObjectUniquePtr xSdrObj; + + OUString aServiceName = rTbxObj.GetServiceName(); + if( SupportsOleObjects() && !aServiceName.isEmpty() ) try + { + // create the form control from scratch + Reference< XFormComponent > xFormComp( ScfApiHelper::CreateInstance( GetDocShell(), aServiceName ), UNO_QUERY_THROW ); + // set controls form, needed in virtual function InsertControl() + InitControlForm(); + // try to insert the control into the form + css::awt::Size aDummySize; + Reference< XShape > xShape; + XclImpDffConvData& rConvData = GetConvData(); + if( rConvData.mxCtrlForm.is() && InsertControl( xFormComp, aDummySize, &xShape, true ) ) + { + xSdrObj = rTbxObj.CreateSdrObjectFromShape( xShape, rAnchorRect ); + // try to attach a macro to the control + ScriptEventDescriptor aDescriptor; + if( (rConvData.mnLastCtrlIndex >= 0) && rTbxObj.FillMacroDescriptor( aDescriptor ) ) + { + NotifyMacroEventRead(); + Reference< XEventAttacherManager > xEventMgr( rConvData.mxCtrlForm, UNO_QUERY_THROW ); + xEventMgr->registerScriptEvent( rConvData.mnLastCtrlIndex, aDescriptor ); + } + } + } + catch( const Exception& ) + { + } + + return xSdrObj; +} + +SdrObjectUniquePtr XclImpDffConverter::CreateSdrObject( const XclImpPictureObj& rPicObj, const tools::Rectangle& rAnchorRect ) +{ + SdrObjectUniquePtr xSdrObj; + + if( SupportsOleObjects() ) + { + if( rPicObj.IsOcxControl() ) + { + if( mxCtlsStrm.is() ) try + { + /* set controls form, needed in virtual function InsertControl() + called from ReadOCXExcelKludgeStream() */ + InitControlForm(); + + // read from mxCtlsStrm into xShape, insert the control model into the form + Reference< XShape > xShape; + if( GetConvData().mxCtrlForm.is() ) + { + Reference< XFormComponent > xFComp; + ReadOCXCtlsStream( mxCtlsStrm, xFComp, rPicObj.GetCtlsStreamPos(), rPicObj.GetCtlsStreamSize() ); + // recreate the method formerly known as ReadOCXExcelKludgeStream() + if ( xFComp.is() ) + { + css::awt::Size aSz; // not used in import + ScfPropertySet aPropSet( xFComp ); + aPropSet.SetStringProperty( "Name", rPicObj.GetObjName() ); + InsertControl( xFComp, aSz,&xShape,true); + xSdrObj = rPicObj.CreateSdrObjectFromShape( xShape, rAnchorRect ); + } + } + } + catch( const Exception& ) + { + } + } + else + { + SfxObjectShell* pDocShell = GetDocShell(); + tools::SvRef<SotStorage> xSrcStrg = GetRootStorage(); + OUString aStrgName = rPicObj.GetOleStorageName(); + if( pDocShell && xSrcStrg.is() && (!aStrgName.isEmpty()) ) + { + // first try to resolve graphic from DFF storage + Graphic aGraphic; + tools::Rectangle aVisArea; + if( !GetBLIP( GetPropertyValue( DFF_Prop_pib, 0 ), aGraphic, &aVisArea ) ) + { + // if not found, use graphic from object (imported from IMGDATA record) + aGraphic = rPicObj.GetGraphic(); + } + if( aGraphic.GetType() != GraphicType::NONE ) + { + ErrCode nError = ERRCODE_NONE; + namespace cssea = ::com::sun::star::embed::Aspects; + sal_Int64 nAspects = rPicObj.IsSymbol() ? cssea::MSOLE_ICON : cssea::MSOLE_CONTENT; + xSdrObj.reset( + CreateSdrOLEFromStorage( + GetConvData().mrSdrModel, + aStrgName, + xSrcStrg, + pDocShell->GetStorage(), + aGraphic, + rAnchorRect, + aVisArea, + nullptr, + nError, + mnOleImpFlags, + nAspects, + GetRoot().GetMedium().GetBaseURL())); + } + } + } + } + + return xSdrObj; +} + +bool XclImpDffConverter::SupportsOleObjects() const +{ + return GetConvData().mrDrawing.SupportsOleObjects(); +} + +// virtual functions ---------------------------------------------------------- + +void XclImpDffConverter::ProcessClientAnchor2( SvStream& rDffStrm, + DffRecordHeader& rHeader, DffObjData& rObjData ) +{ + // find the OBJ record data related to the processed shape + XclImpDffConvData& rConvData = GetConvData(); + XclImpDrawObjBase* pDrawObj = rConvData.mrDrawing.FindDrawObj( rObjData.rSpHd ).get(); + if(!pDrawObj) + return; + + OSL_ENSURE( rHeader.nRecType == DFF_msofbtClientAnchor, "XclImpDffConverter::ProcessClientAnchor2 - no client anchor record" ); + XclObjAnchor aAnchor; + rHeader.SeekToContent( rDffStrm ); + sal_uInt8 nFlags(0); + rDffStrm.ReadUChar( nFlags ); + rDffStrm.SeekRel( 1 ); // flags + rDffStrm >> aAnchor; // anchor format equal to BIFF5 OBJ records + + if (!rDffStrm.good()) + { + SAL_WARN("sc.filter", "ProcessClientAnchor2 short read"); + return; + } + + pDrawObj->SetAnchor( aAnchor ); + rObjData.aChildAnchor = rConvData.mrDrawing.CalcAnchorRect( aAnchor, true ); + rObjData.bChildAnchor = true; + // page anchoring is the best approximation we have if mbMove + // is set + rObjData.bPageAnchor = ( nFlags & 0x1 ); +} + +namespace { + +struct XclImpDrawObjClientData : public SvxMSDffClientData +{ + const XclImpDrawObjBase* m_pTopLevelObj; + + XclImpDrawObjClientData() + : m_pTopLevelObj(nullptr) + { + } + virtual void NotifyFreeObj(SdrObject*) override {} +}; + +} + +SdrObject* XclImpDffConverter::ProcessObj( SvStream& rDffStrm, DffObjData& rDffObjData, + SvxMSDffClientData& rClientData, tools::Rectangle& /*rTextRect*/, SdrObject* pOldSdrObj ) +{ + XclImpDffConvData& rConvData = GetConvData(); + + /* pOldSdrObj passes a generated SdrObject. This function owns this object + and can modify it. The function has either to return it back to caller + or to delete it by itself. */ + SdrObjectUniquePtr xSdrObj( pOldSdrObj ); + + // find the OBJ record data related to the processed shape + XclImpDrawObjRef xDrawObj = rConvData.mrDrawing.FindDrawObj( rDffObjData.rSpHd ); + const tools::Rectangle& rAnchorRect = rDffObjData.aChildAnchor; + + // Do not process the global page group shape + bool bGlobalPageGroup( rDffObjData.nSpFlags & ShapeFlag::Patriarch ); + if( !xDrawObj || !xDrawObj->IsProcessSdrObj() || bGlobalPageGroup ) + return nullptr; // simply return, xSdrObj will be destroyed + + /* Pass pointer to top-level object back to caller. If the processed + object is embedded in a group, the pointer is already set to the + top-level parent object. */ + XclImpDrawObjClientData& rDrawObjClientData = static_cast<XclImpDrawObjClientData&>(rClientData); + const bool bIsTopLevel = !rDrawObjClientData.m_pTopLevelObj; + if (bIsTopLevel ) + rDrawObjClientData.m_pTopLevelObj = xDrawObj.get(); + + // connectors don't have to be area objects + if( dynamic_cast< SdrEdgeObj* >( xSdrObj.get() ) ) + xDrawObj->SetAreaObj( false ); + + /* Check for valid size for all objects. Needed to ignore lots of invisible + phantom objects from deleted rows or columns (for performance reasons). + #i30816# Include objects embedded in groups. + #i58780# Ignore group shapes, size is not initialized. */ + bool bEmbeddedGroup = !bIsTopLevel && dynamic_cast< SdrObjGroup* >( xSdrObj.get() ); + if( !bEmbeddedGroup && !xDrawObj->IsValidSize( rAnchorRect ) ) + return nullptr; // simply return, xSdrObj will be destroyed + + // set shape information from DFF stream + OUString aObjName = GetPropertyString( DFF_Prop_wzName, rDffStrm ); + OUString aHyperlink = ReadHlinkProperty( rDffStrm ); + bool bVisible = !GetPropertyBool( DFF_Prop_fHidden ); + bool bAutoMargin = GetPropertyBool( DFF_Prop_AutoTextMargin ); + xDrawObj->SetDffData( rDffObjData, aObjName, aHyperlink, bVisible, bAutoMargin ); + + /* Connect textbox data (string, alignment, text orientation) to object. + don't ask for a text-ID, DFF export doesn't set one. */ + if( XclImpTextObj* pTextObj = dynamic_cast< XclImpTextObj* >( xDrawObj.get() ) ) + if( const XclImpObjTextData* pTextData = rConvData.mrDrawing.FindTextData( rDffObjData.rSpHd ) ) + pTextObj->SetTextData( *pTextData ); + + // copy line and fill formatting of TBX form controls from DFF properties + if( XclImpTbxObjBase* pTbxObj = dynamic_cast< XclImpTbxObjBase* >( xDrawObj.get() ) ) + pTbxObj->SetDffProperties( *this ); + + // try to create a custom SdrObject that overwrites the passed object + SdrObjectUniquePtr xNewSdrObj( xDrawObj->CreateSdrObject( *this, rAnchorRect, true ) ); + if( xNewSdrObj ) + xSdrObj = std::move( xNewSdrObj ); + + // process the SdrObject + if( xSdrObj ) + { + // filled without color -> set system window color + if( GetPropertyBool( DFF_Prop_fFilled ) && !IsProperty( DFF_Prop_fillColor ) ) + xSdrObj->SetMergedItem( XFillColorItem( OUString(), GetPalette().GetColor( EXC_COLOR_WINDOWBACK ) ) ); + + // additional processing on the SdrObject + xDrawObj->PreProcessSdrObject( *this, *xSdrObj ); + + /* If the SdrObject will not be inserted into the draw page, delete it + here. Happens e.g. for notes: The PreProcessSdrObject() call above + has inserted the note into the document, and the SdrObject is not + needed anymore. */ + if( !xDrawObj->IsInsertSdrObj() ) + xSdrObj.reset(); + } + + if( xSdrObj ) + { + /* Store the relation between shape ID and SdrObject for connectors. + Must be done here (and not in InsertSdrObject() function), + otherwise all SdrObjects embedded in groups would be lost. */ + rConvData.maSolverCont.InsertSdrObjectInfo( *xSdrObj, xDrawObj->GetDffShapeId(), xDrawObj->GetDffFlags() ); + + /* If the drawing object is embedded in a group object, call + PostProcessSdrObject() here. For top-level objects this will be + done automatically in InsertSdrObject() but grouped shapes are + inserted into their groups somewhere in the SvxMSDffManager base + class without chance of notification. Unfortunately, now this is + called before the object is really inserted into its group object, + but that should not have any effect for grouped objects. */ + if( !bIsTopLevel ) + xDrawObj->PostProcessSdrObject( *this, *xSdrObj ); + } + + return xSdrObj.release(); +} + +SdrObject* XclImpDffConverter::FinalizeObj(DffObjData& rDffObjData, SdrObject* pOldSdrObj ) +{ + XclImpDffConvData& rConvData = GetConvData(); + + /* pOldSdrObj passes a generated SdrObject. This function owns this object + and can modify it. The function has either to return it back to caller + or to delete it by itself. */ + SdrObjectUniquePtr xSdrObj( pOldSdrObj ); + + // find the OBJ record data related to the processed shape + XclImpDrawObjRef xDrawObj = rConvData.mrDrawing.FindDrawObj( rDffObjData.rSpHd ); + + if( xSdrObj && xDrawObj ) + { + // cell anchoring + if ( !rDffObjData.bPageAnchor ) + ScDrawLayer::SetCellAnchoredFromPosition( *xSdrObj, GetDoc(), xDrawObj->GetTab(), false ); + } + + return xSdrObj.release(); +} + +bool XclImpDffConverter::InsertControl( const Reference< XFormComponent >& rxFormComp, + const css::awt::Size& /*rSize*/, Reference< XShape >* pxShape, + bool /*bFloatingCtrl*/ ) +{ + if( GetDocShell() ) try + { + XclImpDffConvData& rConvData = GetConvData(); + Reference< XIndexContainer > xFormIC( rConvData.mxCtrlForm, UNO_QUERY_THROW ); + Reference< XControlModel > xCtrlModel( rxFormComp, UNO_QUERY_THROW ); + + // create the control shape + Reference< XShape > xShape( ScfApiHelper::CreateInstance( GetDocShell(), "com.sun.star.drawing.ControlShape" ), UNO_QUERY_THROW ); + Reference< XControlShape > xCtrlShape( xShape, UNO_QUERY_THROW ); + + // insert the new control into the form + sal_Int32 nNewIndex = xFormIC->getCount(); + xFormIC->insertByIndex( nNewIndex, Any( rxFormComp ) ); + // on success: store new index of the control for later use (macro events) + rConvData.mnLastCtrlIndex = nNewIndex; + + // set control model at control shape and pass back shape to caller + xCtrlShape->setControl( xCtrlModel ); + if( pxShape ) *pxShape = xShape; + return true; + } + catch( const Exception& ) + { + OSL_FAIL( "XclImpDffConverter::InsertControl - cannot create form control" ); + } + + return false; +} + +// private -------------------------------------------------------------------- + +XclImpDffConverter::XclImpDffConvData& XclImpDffConverter::GetConvData() +{ + OSL_ENSURE( !maDataStack.empty(), "XclImpDffConverter::GetConvData - no drawing manager on stack" ); + return *maDataStack.back(); +} + +const XclImpDffConverter::XclImpDffConvData& XclImpDffConverter::GetConvData() const +{ + OSL_ENSURE( !maDataStack.empty(), "XclImpDffConverter::GetConvData - no drawing manager on stack" ); + return *maDataStack.back(); +} + +OUString XclImpDffConverter::ReadHlinkProperty( SvStream& rDffStrm ) const +{ + /* Reads hyperlink data from a complex DFF property. Contents of this + property are equal to the HLINK record, import of this record is + implemented in class XclImpHyperlink. This function has to create an + instance of the XclImpStream class to be able to reuse the + functionality of XclImpHyperlink. */ + OUString aString; + sal_uInt32 nBufferSize = GetPropertyValue( DFF_Prop_pihlShape, 0 ); + if( (0 < nBufferSize) && (nBufferSize <= 0xFFFF) && SeekToContent( DFF_Prop_pihlShape, rDffStrm ) ) + { + // create a faked BIFF record that can be read by XclImpStream class + SvMemoryStream aMemStream; + aMemStream.WriteUInt16( 0 ).WriteUInt16( nBufferSize ); + + // copy from DFF stream to memory stream + ::std::vector< sal_uInt8 > aBuffer( nBufferSize ); + sal_uInt8* pnData = aBuffer.data(); + if (rDffStrm.ReadBytes(pnData, nBufferSize) == nBufferSize) + { + aMemStream.WriteBytes(pnData, nBufferSize); + + // create BIFF import stream to be able to use XclImpHyperlink class + XclImpStream aXclStrm( aMemStream, GetRoot() ); + if( aXclStrm.StartNextRecord() ) + aString = XclImpHyperlink::ReadEmbeddedData( aXclStrm ); + } + } + return aString; +} + +bool XclImpDffConverter::ProcessDgContainer( SvStream& rDffStrm, const DffRecordHeader& rDgHeader ) +{ + std::size_t nEndPos = rDgHeader.GetRecEndFilePos(); + bool isBreak(false); + while (!isBreak && rDffStrm.good() && rDffStrm.Tell() < nEndPos) + { + DffRecordHeader aHeader; + ReadDffRecordHeader( rDffStrm, aHeader ); + switch( aHeader.nRecType ) + { + case DFF_msofbtSolverContainer: + isBreak = !ProcessSolverContainer( rDffStrm, aHeader ); + break; + case DFF_msofbtSpgrContainer: + isBreak = !ProcessShGrContainer( rDffStrm, aHeader ); + break; + default: + isBreak = !aHeader.SeekToEndOfRecord( rDffStrm ); + } + } + // seek to end of drawing page container + isBreak = !rDgHeader.SeekToEndOfRecord( rDffStrm ); + + // #i12638# #i37900# connector rules + XclImpSolverContainer& rSolverCont = GetConvData().maSolverCont; + rSolverCont.UpdateConnectorRules(); + SolveSolver( rSolverCont ); + rSolverCont.RemoveConnectorRules(); + return !isBreak; +} + +bool XclImpDffConverter::ProcessShGrContainer( SvStream& rDffStrm, const DffRecordHeader& rShGrHeader ) +{ + std::size_t nEndPos = rShGrHeader.GetRecEndFilePos(); + bool isBreak(false); + while (!isBreak && rDffStrm.good() && rDffStrm.Tell() < nEndPos) + { + DffRecordHeader aHeader; + ReadDffRecordHeader( rDffStrm, aHeader ); + switch( aHeader.nRecType ) + { + case DFF_msofbtSpgrContainer: + case DFF_msofbtSpContainer: + isBreak = !ProcessShContainer( rDffStrm, aHeader ); + break; + default: + isBreak = !aHeader.SeekToEndOfRecord( rDffStrm ); + } + } + // seek to end of shape group container + return rShGrHeader.SeekToEndOfRecord( rDffStrm ) && !isBreak; +} + +bool XclImpDffConverter::ProcessSolverContainer( SvStream& rDffStrm, const DffRecordHeader& rSolverHeader ) +{ + // solver container wants to read the solver container header again + rSolverHeader.SeekToBegOfRecord( rDffStrm ); + // read the entire solver container + ReadSvxMSDffSolverContainer( rDffStrm, GetConvData().maSolverCont ); + // seek to end of solver container + return rSolverHeader.SeekToEndOfRecord( rDffStrm ); +} + +bool XclImpDffConverter::ProcessShContainer( SvStream& rDffStrm, const DffRecordHeader& rShHeader ) +{ + rShHeader.SeekToBegOfRecord( rDffStrm ); + tools::Rectangle aDummy; + XclImpDrawObjClientData aDrawObjClientData; + /* The call to ImportObj() creates and returns a new SdrObject for the + processed shape. We take ownership of the returned object here. If the + shape is a group object, all embedded objects are created recursively, + and the returned group object contains them all. ImportObj() calls the + virtual functions ProcessClientAnchor2() and ProcessObj() and writes + the pointer to the related draw object data (OBJ record) into aDrawObjClientData. */ + SdrObjectUniquePtr xSdrObj( ImportObj( rDffStrm, aDrawObjClientData, aDummy, aDummy, /*nCalledByGroup*/0, /*pShapeId*/nullptr ) ); + if (aDrawObjClientData.m_pTopLevelObj && xSdrObj ) + InsertSdrObject( GetConvData().mrSdrPage, *aDrawObjClientData.m_pTopLevelObj, xSdrObj.release() ); + return rShHeader.SeekToEndOfRecord( rDffStrm ); +} + +void XclImpDffConverter::InsertSdrObject( SdrObjList& rObjList, const XclImpDrawObjBase& rDrawObj, SdrObject* pSdrObj ) +{ + XclImpDffConvData& rConvData = GetConvData(); + /* Take ownership of the passed object. If insertion fails (e.g. rDrawObj + states to skip insertion), the object is automatically deleted. */ + SdrObjectUniquePtr xSdrObj( pSdrObj ); + if( xSdrObj && rDrawObj.IsInsertSdrObj() ) + { + rObjList.NbcInsertObject( xSdrObj.release() ); + // callback to drawing manager for e.g. tracking of used sheet area + rConvData.mrDrawing.OnObjectInserted( rDrawObj ); + // callback to drawing object for post processing (use pSdrObj, xSdrObj already released) + rDrawObj.PostProcessSdrObject( *this, *pSdrObj ); + } + /* SdrObject still here? Insertion failed, remove data from shape ID map. + The SdrObject will be destructed then. */ + if( xSdrObj ) + rConvData.maSolverCont.RemoveSdrObjectInfo( *xSdrObj ); +} + +void XclImpDffConverter::InitControlForm() +{ + XclImpDffConvData& rConvData = GetConvData(); + if( rConvData.mbHasCtrlForm ) + return; + + rConvData.mbHasCtrlForm = true; + if( !SupportsOleObjects() ) + return; + + try + { + Reference< XFormsSupplier > xFormsSupplier( rConvData.mrSdrPage.getUnoPage(), UNO_QUERY_THROW ); + Reference< XNameContainer > xFormsNC( xFormsSupplier->getForms(), UNO_SET_THROW ); + // find or create the Standard form used to insert the imported controls + if( xFormsNC->hasByName( gaStdFormName ) ) + { + xFormsNC->getByName( gaStdFormName ) >>= rConvData.mxCtrlForm; + } + else if( SfxObjectShell* pDocShell = GetDocShell() ) + { + rConvData.mxCtrlForm.set( ScfApiHelper::CreateInstance( pDocShell, "com.sun.star.form.component.Form" ), UNO_QUERY_THROW ); + xFormsNC->insertByName( gaStdFormName, Any( rConvData.mxCtrlForm ) ); + } + } + catch( const Exception& ) + { + } +} + +// Drawing manager ============================================================ + +XclImpDrawing::XclImpDrawing( const XclImpRoot& rRoot, bool bOleObjects ) : + XclImpRoot( rRoot ), + mbOleObjs( bOleObjects ) +{ +} + +XclImpDrawing::~XclImpDrawing() +{ +} + +Graphic XclImpDrawing::ReadImgData( const XclImpRoot& rRoot, XclImpStream& rStrm ) +{ + Graphic aGraphic; + sal_uInt16 nFormat = rStrm.ReaduInt16(); + rStrm.Ignore( 2 );//nEnv + sal_uInt32 nDataSize = rStrm.ReaduInt32(); + if( nDataSize <= rStrm.GetRecLeft() ) + { + switch( nFormat ) + { + case EXC_IMGDATA_WMF: ReadWmf( aGraphic, rStrm ); break; + case EXC_IMGDATA_BMP: ReadBmp( aGraphic, rRoot, rStrm ); break; + default: OSL_FAIL( "XclImpDrawing::ReadImgData - unknown image format" ); + } + } + return aGraphic; +} + +void XclImpDrawing::ReadObj( XclImpStream& rStrm ) +{ + XclImpDrawObjRef xDrawObj; + + /* #i61786# In BIFF8 streams, OBJ records may occur without MSODRAWING + records. In this case, the OBJ records are in BIFF5 format. Do a sanity + check here that there is no DFF data loaded before. */ + OSL_ENSURE( maDffStrm.Tell() == 0, "XclImpDrawing::ReadObj - unexpected DFF stream data, OBJ will be ignored" ); + if( maDffStrm.Tell() == 0 ) switch( GetBiff() ) + { + case EXC_BIFF3: + xDrawObj = XclImpDrawObjBase::ReadObj3( GetRoot(), rStrm ); + break; + case EXC_BIFF4: + xDrawObj = XclImpDrawObjBase::ReadObj4( GetRoot(), rStrm ); + break; + case EXC_BIFF5: + case EXC_BIFF8: + xDrawObj = XclImpDrawObjBase::ReadObj5( GetRoot(), rStrm ); + break; + default: + DBG_ERROR_BIFF(); + } + + if( xDrawObj ) + { + // insert into maRawObjs or into the last open group object + maRawObjs.InsertGrouped( xDrawObj ); + // to be able to find objects by ID + maObjMapId[ xDrawObj->GetObjId() ] = xDrawObj; + } +} + +void XclImpDrawing::ReadMsoDrawing( XclImpStream& rStrm ) +{ + OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 ); + // disable internal CONTINUE handling + rStrm.ResetRecord( false ); + // read leading MSODRAWING record + ReadDffRecord( rStrm ); + + // read following drawing records, but do not start following unrelated record + bool bLoop = true; + while( bLoop ) switch( rStrm.GetNextRecId() ) + { + case EXC_ID_MSODRAWING: + case EXC_ID_MSODRAWINGSEL: + case EXC_ID_CONT: + rStrm.StartNextRecord(); + ReadDffRecord( rStrm ); + break; + case EXC_ID_OBJ: + rStrm.StartNextRecord(); + ReadObj8( rStrm ); + break; + case EXC_ID_TXO: + rStrm.StartNextRecord(); + ReadTxo( rStrm ); + break; + default: + bLoop = false; + } + + // re-enable internal CONTINUE handling + rStrm.ResetRecord( true ); +} + +XclImpDrawObjRef XclImpDrawing::FindDrawObj( const DffRecordHeader& rHeader ) const +{ + /* maObjMap stores objects by position of the client data (OBJ record) in + the DFF stream, which is always behind shape start position of the + passed header. The function upper_bound() finds the first element in + the map whose key is greater than the start position of the header. Its + end position is used to test whether the found object is really related + to the shape. */ + XclImpDrawObjRef xDrawObj; + XclImpObjMap::const_iterator aIt = maObjMap.upper_bound( rHeader.GetRecBegFilePos() ); + if( (aIt != maObjMap.end()) && (aIt->first <= rHeader.GetRecEndFilePos()) ) + xDrawObj = aIt->second; + return xDrawObj; +} + +XclImpDrawObjRef XclImpDrawing::FindDrawObj( sal_uInt16 nObjId ) const +{ + XclImpDrawObjRef xDrawObj; + XclImpObjMapById::const_iterator aIt = maObjMapId.find( nObjId ); + if( aIt != maObjMapId.end() ) + xDrawObj = aIt->second; + return xDrawObj; +} + +const XclImpObjTextData* XclImpDrawing::FindTextData( const DffRecordHeader& rHeader ) const +{ + /* maTextMap stores textbox data by position of the client data (TXO + record) in the DFF stream, which is always behind shape start position + of the passed header. The function upper_bound() finds the first + element in the map whose key is greater than the start position of the + header. Its end position is used to test whether the found object is + really related to the shape. */ + XclImpObjTextMap::const_iterator aIt = maTextMap.upper_bound( rHeader.GetRecBegFilePos() ); + if( (aIt != maTextMap.end()) && (aIt->first <= rHeader.GetRecEndFilePos()) ) + return aIt->second.get(); + return nullptr; +} + +void XclImpDrawing::SetSkipObj( sal_uInt16 nObjId ) +{ + maSkipObjs.push_back( nObjId ); +} + +std::size_t XclImpDrawing::GetProgressSize() const +{ + return std::accumulate(maObjMap.begin(), maObjMap.end(), maRawObjs.GetProgressSize(), + [](const std::size_t& rSum, const XclImpObjMap::value_type& rEntry) { return rSum + rEntry.second->GetProgressSize(); }); +} + +void XclImpDrawing::ImplConvertObjects( XclImpDffConverter& rDffConv, SdrModel& rSdrModel, SdrPage& rSdrPage ) +{ + //rhbz#636521, disable undo during conversion. faster, smaller and stops + //temp objects being inserted into the undo list + bool bOrigUndoStatus = rSdrModel.IsUndoEnabled(); + rSdrModel.EnableUndo(false); + // register this drawing manager at the passed (global) DFF manager + rDffConv.InitializeDrawing( *this, rSdrModel, rSdrPage ); + // process list of objects to be skipped + for( const auto& rSkipObj : maSkipObjs ) + if( XclImpDrawObjBase* pDrawObj = FindDrawObj( rSkipObj ).get() ) + pDrawObj->SetProcessSdrObj( false ); + // process drawing objects without DFF data + rDffConv.ProcessDrawing( maRawObjs ); + // process all objects in the DFF stream + rDffConv.ProcessDrawing( maDffStrm ); + // unregister this drawing manager at the passed (global) DFF manager + rDffConv.FinalizeDrawing(); + rSdrModel.EnableUndo(bOrigUndoStatus); +} + +// protected ------------------------------------------------------------------ + +void XclImpDrawing::AppendRawObject( const XclImpDrawObjRef& rxDrawObj ) +{ + OSL_ENSURE( rxDrawObj, "XclImpDrawing::AppendRawObject - unexpected empty reference" ); + maRawObjs.push_back( rxDrawObj ); +} + +// private -------------------------------------------------------------------- + +void XclImpDrawing::ReadWmf( Graphic& rGraphic, XclImpStream& rStrm ) // static helper +{ + // extract graphic data from IMGDATA and following CONTINUE records + rStrm.Ignore( 8 ); + SvMemoryStream aMemStrm; + rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() ); + aMemStrm.Seek( STREAM_SEEK_TO_BEGIN ); + // import the graphic from memory stream + GDIMetaFile aGDIMetaFile; + if( ::ReadWindowMetafile( aMemStrm, aGDIMetaFile ) ) + rGraphic = aGDIMetaFile; +} + +void XclImpDrawing::ReadBmp( Graphic& rGraphic, const XclImpRoot& rRoot, XclImpStream& rStrm ) // static helper +{ + // extract graphic data from IMGDATA and following CONTINUE records + SvMemoryStream aMemStrm; + + /* Excel 3 and 4 seem to write broken BMP data. Usually they write a + DIBCOREHEADER (12 bytes) containing width, height, planes = 1, and + pixel depth = 32 bit. After that, 3 unused bytes are added before the + actual pixel data. This does even confuse Excel 5 and later, which + cannot read the image data correctly. */ + if( rRoot.GetBiff() <= EXC_BIFF4 ) + { + rStrm.PushPosition(); + sal_uInt32 nHdrSize; + sal_uInt16 nWidth, nHeight, nPlanes, nDepth; + nHdrSize = rStrm.ReaduInt32(); + nWidth = rStrm.ReaduInt16(); + nHeight = rStrm.ReaduInt16(); + nPlanes = rStrm.ReaduInt16(); + nDepth = rStrm.ReaduInt16(); + if( (nHdrSize == 12) && (nPlanes == 1) && (nDepth == 32) ) + { + rStrm.Ignore( 3 ); + aMemStrm.SetEndian( SvStreamEndian::LITTLE ); + aMemStrm.WriteUInt32( nHdrSize ).WriteUInt16( nWidth ).WriteUInt16( nHeight ).WriteUInt16( nPlanes ).WriteUInt16( nDepth ); + rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() ); + } + rStrm.PopPosition(); + } + + // no special handling above -> just copy the remaining record data + if( aMemStrm.Tell() == 0 ) + rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() ); + + // import the graphic from memory stream + aMemStrm.Seek( STREAM_SEEK_TO_BEGIN ); + Bitmap aBitmap; + if( ReadDIB(aBitmap, aMemStrm, false) ) // read DIB without file header + rGraphic = BitmapEx(aBitmap); +} + +void XclImpDrawing::ReadDffRecord( XclImpStream& rStrm ) +{ + maDffStrm.Seek( STREAM_SEEK_TO_END ); + rStrm.CopyRecordToStream( maDffStrm ); +} + +void XclImpDrawing::ReadObj8( XclImpStream& rStrm ) +{ + XclImpDrawObjRef xDrawObj = XclImpDrawObjBase::ReadObj8( GetRoot(), rStrm ); + // store the new object in the internal containers + maObjMap[ maDffStrm.Tell() ] = xDrawObj; + maObjMapId[ xDrawObj->GetObjId() ] = xDrawObj; +} + +void XclImpDrawing::ReadTxo( XclImpStream& rStrm ) +{ + XclImpObjTextRef xTextData = std::make_shared<XclImpObjTextData>(); + maTextMap[ maDffStrm.Tell() ] = xTextData; + + // 1) read the TXO record + xTextData->maData.ReadTxo8( rStrm ); + + // 2) first CONTINUE with string + xTextData->mxString.reset(); + bool bValid = true; + if( xTextData->maData.mnTextLen > 0 ) + { + bValid = (rStrm.GetNextRecId() == EXC_ID_CONT) && rStrm.StartNextRecord(); + OSL_ENSURE( bValid, "XclImpDrawing::ReadTxo - missing CONTINUE record" ); + if( bValid ) + xTextData->mxString = std::make_shared<XclImpString>( rStrm.ReadUniString( xTextData->maData.mnTextLen ) ); + } + + // 3) second CONTINUE with formatting runs + if( xTextData->maData.mnFormatSize > 0 ) + { + bValid = (rStrm.GetNextRecId() == EXC_ID_CONT) && rStrm.StartNextRecord(); + OSL_ENSURE( bValid, "XclImpDrawing::ReadTxo - missing CONTINUE record" ); + if( bValid ) + xTextData->ReadFormats( rStrm ); + } +} + +XclImpSheetDrawing::XclImpSheetDrawing( const XclImpRoot& rRoot, SCTAB nScTab ) : + XclImpDrawing( rRoot, true ), + maScUsedArea( ScAddress::INITIALIZE_INVALID ) +{ + maScUsedArea.aStart.SetTab( nScTab ); + maScUsedArea.aEnd.SetTab( nScTab ); +} + +void XclImpSheetDrawing::ReadNote( XclImpStream& rStrm ) +{ + switch( GetBiff() ) + { + case EXC_BIFF2: + case EXC_BIFF3: + case EXC_BIFF4: + case EXC_BIFF5: + ReadNote3( rStrm ); + break; + case EXC_BIFF8: + ReadNote8( rStrm ); + break; + default: + DBG_ERROR_BIFF(); + } +} + +void XclImpSheetDrawing::ReadTabChart( XclImpStream& rStrm ) +{ + OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF5 ); + auto xChartObj = std::make_shared<XclImpChartObj>( GetRoot(), true ); + xChartObj->ReadChartSubStream( rStrm ); + // insert the chart as raw object without connected DFF data + AppendRawObject( xChartObj ); +} + +void XclImpSheetDrawing::ConvertObjects( XclImpDffConverter& rDffConv ) +{ + if( SdrModel* pSdrModel = GetDoc().GetDrawLayer() ) + if( SdrPage* pSdrPage = GetSdrPage( maScUsedArea.aStart.Tab() ) ) + ImplConvertObjects( rDffConv, *pSdrModel, *pSdrPage ); +} + +tools::Rectangle XclImpSheetDrawing::CalcAnchorRect( const XclObjAnchor& rAnchor, bool /*bDffAnchor*/ ) const +{ + return rAnchor.GetRect( GetRoot(), maScUsedArea.aStart.Tab(), MapUnit::Map100thMM ); +} + +void XclImpSheetDrawing::OnObjectInserted( const XclImpDrawObjBase& rDrawObj ) +{ + ScRange aScObjArea = rDrawObj.GetUsedArea( maScUsedArea.aStart.Tab() ); + if( aScObjArea.IsValid() ) + maScUsedArea.ExtendTo( aScObjArea ); +} + +// private -------------------------------------------------------------------- + +void XclImpSheetDrawing::ReadNote3( XclImpStream& rStrm ) +{ + XclAddress aXclPos; + rStrm >> aXclPos; + sal_uInt16 nTotalLen = rStrm.ReaduInt16(); + + ScAddress aScNotePos( ScAddress::UNINITIALIZED ); + if( !GetAddressConverter().ConvertAddress( aScNotePos, aXclPos, maScUsedArea.aStart.Tab(), true ) ) + return; + + sal_uInt16 nPartLen = ::std::min( nTotalLen, static_cast< sal_uInt16 >( rStrm.GetRecLeft() ) ); + OUStringBuffer aNoteText(rStrm.ReadRawByteString( nPartLen )); + nTotalLen = nTotalLen - nPartLen; + while (true) + { + if (!nTotalLen) + break; + if (rStrm.GetNextRecId() != EXC_ID_NOTE) + break; + if (!rStrm.StartNextRecord()) + break; + rStrm >> aXclPos; + nPartLen = rStrm.ReaduInt16(); + OSL_ENSURE( aXclPos.mnRow == 0xFFFF, "XclImpObjectManager::ReadNote3 - missing continuation NOTE record" ); + if( aXclPos.mnRow == 0xFFFF ) + { + OSL_ENSURE( nPartLen <= nTotalLen, "XclImpObjectManager::ReadNote3 - string too long" ); + aNoteText.append(rStrm.ReadRawByteString( nPartLen )); + nTotalLen = nTotalLen - ::std::min( nTotalLen, nPartLen ); + } + else + { + // seems to be a new note, record already started -> load the note + rStrm.Seek( EXC_REC_SEEK_TO_BEGIN ); + ReadNote( rStrm ); + nTotalLen = 0; + } + } + ScNoteUtil::CreateNoteFromString( GetDoc(), aScNotePos, aNoteText.makeStringAndClear(), false, false ); +} + +void XclImpSheetDrawing::ReadNote8( XclImpStream& rStrm ) +{ + XclAddress aXclPos; + sal_uInt16 nFlags, nObjId; + rStrm >> aXclPos; + nFlags = rStrm.ReaduInt16(); + nObjId = rStrm.ReaduInt16(); + + ScAddress aScNotePos( ScAddress::UNINITIALIZED ); + if( GetAddressConverter().ConvertAddress( aScNotePos, aXclPos, maScUsedArea.aStart.Tab(), true ) ) + if( nObjId != EXC_OBJ_INVALID_ID ) + if( XclImpNoteObj* pNoteObj = dynamic_cast< XclImpNoteObj* >( FindDrawObj( nObjId ).get() ) ) + pNoteObj->SetNoteData( aScNotePos, nFlags ); +} + +// The object manager ========================================================= + +XclImpObjectManager::XclImpObjectManager( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ + maDefObjNames[ EXC_OBJTYPE_GROUP ] = "Group"; + maDefObjNames[ EXC_OBJTYPE_LINE ] = ScResId( STR_SHAPE_LINE ); + maDefObjNames[ EXC_OBJTYPE_RECTANGLE ] = ScResId( STR_SHAPE_RECTANGLE ); + maDefObjNames[ EXC_OBJTYPE_OVAL ] = ScResId( STR_SHAPE_OVAL ); + maDefObjNames[ EXC_OBJTYPE_ARC ] = "Arc"; + maDefObjNames[ EXC_OBJTYPE_CHART ] = "Chart"; + maDefObjNames[ EXC_OBJTYPE_TEXT ] = "Text"; + maDefObjNames[ EXC_OBJTYPE_BUTTON ] = ScResId( STR_FORM_BUTTON ); + maDefObjNames[ EXC_OBJTYPE_PICTURE ] = "Picture"; + maDefObjNames[ EXC_OBJTYPE_POLYGON ] = "Freeform"; + maDefObjNames[ EXC_OBJTYPE_CHECKBOX ] = ScResId( STR_FORM_CHECKBOX ); + maDefObjNames[ EXC_OBJTYPE_OPTIONBUTTON ] = ScResId( STR_FORM_OPTIONBUTTON ); + maDefObjNames[ EXC_OBJTYPE_EDIT ] = "Edit Box"; + maDefObjNames[ EXC_OBJTYPE_LABEL ] = ScResId( STR_FORM_LABEL ); + maDefObjNames[ EXC_OBJTYPE_DIALOG ] = "Dialog Frame"; + maDefObjNames[ EXC_OBJTYPE_SPIN ] = ScResId( STR_FORM_SPINNER ); + maDefObjNames[ EXC_OBJTYPE_SCROLLBAR ] = ScResId( STR_FORM_SCROLLBAR ); + maDefObjNames[ EXC_OBJTYPE_LISTBOX ] = ScResId( STR_FORM_LISTBOX ); + maDefObjNames[ EXC_OBJTYPE_GROUPBOX ] = ScResId( STR_FORM_GROUPBOX ); + maDefObjNames[ EXC_OBJTYPE_DROPDOWN ] = ScResId( STR_FORM_DROPDOWN ); + maDefObjNames[ EXC_OBJTYPE_NOTE ] = "Comment"; + maDefObjNames[ EXC_OBJTYPE_DRAWING ] = ScResId( STR_SHAPE_AUTOSHAPE ); +} + +XclImpObjectManager::~XclImpObjectManager() +{ +} + +void XclImpObjectManager::ReadMsoDrawingGroup( XclImpStream& rStrm ) +{ + OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 ); + // Excel continues this record with MSODRAWINGGROUP and CONTINUE records, hmm. + rStrm.ResetRecord( true, EXC_ID_MSODRAWINGGROUP ); + maDggStrm.Seek( STREAM_SEEK_TO_END ); + rStrm.CopyRecordToStream( maDggStrm ); +} + +XclImpSheetDrawing& XclImpObjectManager::GetSheetDrawing( SCTAB nScTab ) +{ + XclImpSheetDrawingRef& rxDrawing = maSheetDrawings[ nScTab ]; + if( !rxDrawing ) + rxDrawing = std::make_shared<XclImpSheetDrawing>( GetRoot(), nScTab ); + return *rxDrawing; +} + +void XclImpObjectManager::ConvertObjects() +{ + // do nothing if the document does not contain a drawing layer + if( !GetDoc().GetDrawLayer() ) + return; + + // get total progress bar size for all sheet drawing managers + std::size_t nProgressSize = std::accumulate(maSheetDrawings.begin(), maSheetDrawings.end(), std::size_t(0), + [](const std::size_t& rSum, const XclImpSheetDrawingMap::value_type& rEntry) { return rSum + rEntry.second->GetProgressSize(); }); + // nothing to do if progress bar is zero (no objects present) + if( nProgressSize == 0 ) + return; + + XclImpDffConverter aDffConv( GetRoot(), maDggStrm ); + aDffConv.StartProgressBar( nProgressSize ); + for( auto& rEntry : maSheetDrawings ) + rEntry.second->ConvertObjects( aDffConv ); + + // #i112436# don't call ScChartListenerCollection::SetDirty here, + // instead use InterpretDirtyCells in ScDocument::CalcAfterLoad. +} + +OUString XclImpObjectManager::GetDefaultObjName( const XclImpDrawObjBase& rDrawObj ) const +{ + OUStringBuffer aDefName; + DefObjNameMap::const_iterator aIt = maDefObjNames.find( rDrawObj.GetObjType() ); + if( aIt != maDefObjNames.end() ) + aDefName.append(aIt->second); + return aDefName.append(' ').append(static_cast<sal_Int32>(rDrawObj.GetObjId())).makeStringAndClear(); +} + +ScRange XclImpObjectManager::GetUsedArea( SCTAB nScTab ) const +{ + XclImpSheetDrawingMap::const_iterator aIt = maSheetDrawings.find( nScTab ); + if( aIt != maSheetDrawings.end() ) + return aIt->second->GetUsedArea(); + return ScRange( ScAddress::INITIALIZE_INVALID ); +} + +// DFF property set helper ==================================================== + +XclImpDffPropSet::XclImpDffPropSet( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + maDffConv( rRoot, maDummyStrm ) +{ +} + +void XclImpDffPropSet::Read( XclImpStream& rStrm ) +{ + sal_uInt32 nPropSetSize; + + rStrm.PushPosition(); + rStrm.Ignore( 4 ); + nPropSetSize = rStrm.ReaduInt32(); + rStrm.PopPosition(); + + mxMemStrm.reset( new SvMemoryStream ); + rStrm.CopyToStream( *mxMemStrm, 8 + nPropSetSize ); + mxMemStrm->Seek( STREAM_SEEK_TO_BEGIN ); + maDffConv.ReadPropSet( *mxMemStrm, nullptr ); +} + +sal_uInt32 XclImpDffPropSet::GetPropertyValue( sal_uInt16 nPropId ) const +{ + return maDffConv.GetPropertyValue( nPropId, 0 ); +} + +void XclImpDffPropSet::FillToItemSet( SfxItemSet& rItemSet ) const +{ + if( mxMemStrm ) + maDffConv.ApplyAttributes( *mxMemStrm, rItemSet ); +} + +XclImpStream& operator>>( XclImpStream& rStrm, XclImpDffPropSet& rPropSet ) +{ + rPropSet.Read( rStrm ); + return rStrm; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |