/* -*- 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 <officecfg/Office/Common.hxx>
#include <unotools/moduleoptions.hxx>
#include <comphelper/configuration.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 <docsh.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::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;
}
const tools::Rectangle& XclImpDrawObjBase::GetDffRect() const
{
return maDffRect;
}
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;
maDffRect = rDffObjData.aChildAnchor;
}
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();
}
rtl::Reference<SdrObject> XclImpDrawObjBase::CreateSdrObject( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect, bool bIsDff ) const
{
rtl::Reference<SdrObject> 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 OUString sPropertyName(u"ControlTypeinMSO"_ustr);
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;
ScDocShell* 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;
}
rtl::Reference<SdrObject> 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();
}
rtl::Reference<SdrObject> XclImpGroupObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& /*rAnchorRect*/ ) const
{
rtl::Reference<SdrObjGroup> 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 );
}
rtl::Reference<SdrObject> 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;
}
rtl::Reference<SdrObject> 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
tools::Long nWidth = static_cast< tools::Long >( 125 * fArrowWidth );
if( bLineStart )
{
xSdrObj->SetMergedItem( XLineStartItem( OUString(), basegfx::B2DPolyPolygon(aArrowPoly) ) );
xSdrObj->SetMergedItem( XLineStartWidthItem( nWidth ) );
xSdrObj->SetMergedItem( XLineStartCenterItem( false ) );
}
if( bLineEnd )
{
xSdrObj->SetMergedItem( XLineEndItem( OUString(), basegfx::B2DPolyPolygon(aArrowPoly) ) );
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 );
}
rtl::Reference<SdrObject> XclImpRectObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
{
rtl::Reference<SdrObject> xSdrObj(
new SdrRectObj(
*GetDoc().GetDrawLayer(),
rAnchorRect));
ConvertRectStyle( *xSdrObj );
rDffConv.Progress();
return xSdrObj;
}
XclImpOvalObj::XclImpOvalObj( const XclImpRoot& rRoot ) :
XclImpRectObj( rRoot )
{
}
rtl::Reference<SdrObject> XclImpOvalObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
{
rtl::Reference<SdrObject> 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 );
}
rtl::Reference<SdrObject> 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;
rtl::Reference<SdrObject> 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
rtl::Reference<SdrObject> XclImpPolygonObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
{
rtl::Reference<SdrObject> 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 =
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 );
}
rtl::Reference<SdrObject> XclImpTextObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
{
rtl::Reference<SdrObjCustomShape> xSdrObj(
new SdrObjCustomShape(
*GetDoc().GetDrawLayer()));
xSdrObj->NbcSetSnapRect( rAnchorRect );
OUString aRectType = u"rectangle"_ustr;
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 = DynCastSdrTextObj( &rSdrObj ) )
{
if( maTextData.mxString )
{
if( maTextData.mxString->IsRich() )
{
if (maTextData.mxString->GetText().getLength() > 1024 && comphelper::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 = css::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;
}
rtl::Reference<SdrObject> XclImpChartObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
{
rtl::Reference<SdrObject> xSdrObj;
ScDocShell* 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 =
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(),
OUString(), *pOutlinerObj,
rSdrObj.GetLogicRect(),
::get_flag( mnNoteFlags, EXC_NOTE_VISIBLE ) );
}
}
XclImpControlHelper::XclImpControlHelper( const XclImpRoot& rRoot, XclCtrlBindMode eBindMode ) :
mrRoot( rRoot ),
meBindMode( eBindMode )
{
}
XclImpControlHelper::~XclImpControlHelper()
{
}
rtl::Reference<SdrObject> XclImpControlHelper::CreateSdrObjectFromShape(
const Reference< XShape >& rxShape, const tools::Rectangle& rAnchorRect ) const
{
mxShape = rxShape;
rtl::Reference<SdrObject> 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
ScDocShell* pDocShell = mrRoot.GetDocShell();
if(!pDocShell)
return;
ScModelObj* pModelObj = pDocShell->GetModel();
if( !pModelObj )
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(
pModelObj->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( pModelObj->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( u"Name"_ustr, rDrawObj.GetObjName() );
// control visible and printable?
aPropSet.SetBoolProperty( u"EnableVisible"_ustr, rDrawObj.IsVisible() );
aPropSet.SetBoolProperty( u"Printable"_ustr, 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 );
}
void XclImpControlHelper::SetStringProperty(const OUString& sName, const OUString& sVal)
{
Reference<XControlModel> xCtrlModel = XclControlHelper::GetControlModel(mxShape);
if (!xCtrlModel.is())
return;
ScfPropertySet aProps(xCtrlModel);
aProps.SetStringProperty(sName, sVal);
}
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( u"Label"_ustr, 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( u"Description"_ustr, Any(aLabel) );
}catch( ... )
{
SAL_WARN("sc.filter", "Can't set a default text for TBX Control ");
}
}
ConvertFont( rPropSet );
}
rtl::Reference<SdrObject> XclImpTbxObjBase::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
{
rtl::Reference<SdrObject> 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( u"Align"_ustr, nHorAlign );
// vertical text alignment
namespace csss = css::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( u"VerticalAlign"_ustr, eVerAlign );
// always wrap text automatically
rPropSet.SetBoolProperty( u"MultiLine"_ustr, true );
// default button
bool bDefButton = ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_DEFAULT );
rPropSet.SetBoolProperty( u"DefaultButton"_ustr, bDefButton );
// button type (flags cannot be combined in OOo)
namespace cssa = css::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( u"PushButtonType"_ustr, sal_Int16( eButtonType ) );
}
OUString XclImpButtonObj::DoGetServiceName() const
{
return u"com.sun.star.form.component.CommandButton"_ustr;
}
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( u"TriState"_ustr, nApiState == 2 );
rPropSet.SetProperty( u"DefaultState"_ustr, nApiState );
// box style
namespace AwtVisualEffect = css::awt::VisualEffect;
sal_Int16 nEffect = ::get_flagvalue( mnCheckBoxFlags, EXC_OBJ_CHECKBOX_FLAT, AwtVisualEffect::FLAT, AwtVisualEffect::LOOK3D );
rPropSet.SetProperty( u"VisualEffect"_ustr, nEffect );
// do not wrap text automatically
rPropSet.SetBoolProperty( u"MultiLine"_ustr, false );
// #i40279# always centered vertically
namespace csss = css::style;
rPropSet.SetProperty( u"VerticalAlign"_ustr, csss::VerticalAlignment_MIDDLE );
// background color
if( maFillData.IsFilled() )
{
sal_Int32 nColor = static_cast< sal_Int32 >( GetSolidFillColor( maFillData ) );
rPropSet.SetProperty( u"BackgroundColor"_ustr, nColor );
}
}
OUString XclImpCheckBoxObj::DoGetServiceName() const
{
return u"com.sun.star.form.component.CheckBox"_ustr;
}
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( u"GroupName"_ustr, sGroupName );
aProps.SetStringProperty( u"RefValue"_ustr, 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 u"com.sun.star.form.component.RadioButton"_ustr;
}
XclTbxEventType XclImpOptionButtonObj::DoGetEventType() const
{
return EXC_TBX_EVENT_ACTION;
}
bool XclImpOptionButtonObj::IsInGroup() const
{
return mnNextInGroup;
}
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( u"Align"_ustr, sal_Int16( 0 ) );
namespace csss = css::style;
rPropSet.SetProperty( u"VerticalAlign"_ustr, csss::VerticalAlignment_TOP );
// always wrap text automatically
rPropSet.SetBoolProperty( u"MultiLine"_ustr, true );
}
OUString XclImpLabelObj::DoGetServiceName() const
{
return u"com.sun.star.form.component.FixedText"_ustr;
}
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 u"com.sun.star.form.component.GroupBox"_ustr;
}
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 u"com.sun.star.form.component.GroupBox"_ustr;
}
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( u"DefaultValue"_ustr, aText.toDouble() );
rPropSet.SetBoolProperty( u"Spin"_ustr, mnScrollBar != 0 );
}
else
{
rPropSet.SetProperty( u"DefaultText"_ustr, aText );
rPropSet.SetBoolProperty( u"MultiLine"_ustr, mnMultiLine != 0 );
rPropSet.SetBoolProperty( u"VScroll"_ustr, mnScrollBar != 0 );
}
}
ConvertFont( rPropSet );
}
OUString XclImpEditObj::DoGetServiceName() const
{
return IsNumeric() ?
u"com.sun.star.form.component.NumericField"_ustr :
u"com.sun.star.form.component.TextField"_ustr;
}
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( u"Border"_ustr, css::awt::VisualEffect::NONE );
rPropSet.SetProperty< sal_Int32 >( u"DefaultSpinValue"_ustr, mnValue );
rPropSet.SetProperty< sal_Int32 >( u"SpinValueMin"_ustr, mnMin );
rPropSet.SetProperty< sal_Int32 >( u"SpinValueMax"_ustr, mnMax );
rPropSet.SetProperty< sal_Int32 >( u"SpinIncrement"_ustr, mnStep );
// Excel spin buttons always vertical
rPropSet.SetProperty( u"Orientation"_ustr, css::awt::ScrollBarOrientation::VERTICAL );
}
OUString XclImpSpinButtonObj::DoGetServiceName() const
{
return u"com.sun.star.form.component.SpinButton"_ustr;
}
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( u"Border"_ustr, css::awt::VisualEffect::NONE );
rPropSet.SetProperty< sal_Int32 >( u"DefaultScrollValue"_ustr, mnValue );
rPropSet.SetProperty< sal_Int32 >( u"ScrollValueMin"_ustr, mnMin );
rPropSet.SetProperty< sal_Int32 >( u"ScrollValueMax"_ustr, mnMax );
rPropSet.SetProperty< sal_Int32 >( u"LineIncrement"_ustr, mnStep );
rPropSet.SetProperty< sal_Int32 >( u"BlockIncrement"_ustr, mnPageStep );
rPropSet.SetProperty( u"VisibleSize"_ustr, ::std::min< sal_Int32 >( mnPageStep, 1 ) );
namespace AwtScrollOrient = css::awt::ScrollBarOrientation;
sal_Int32 nApiOrient = ::get_flagvalue( mnOrient, EXC_OBJ_SCROLLBAR_HOR, AwtScrollOrient::HORIZONTAL, AwtScrollOrient::VERTICAL );
rPropSet.SetProperty( u"Orientation"_ustr, nApiOrient );
}
OUString XclImpScrollBarObj::DoGetServiceName() const
{
return u"com.sun.star.form.component.ScrollBar"_ustr;
}
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 = css::awt::VisualEffect;
sal_Int16 nApiBorder = ::get_flagvalue( mnListFlags, EXC_OBJ_LISTBOX_FLAT, AwtVisualEffect::FLAT, AwtVisualEffect::LOOK3D );
rPropSet.SetProperty( u"Border"_ustr, 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( u"MultiSelection"_ustr, 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( u"DefaultSelection"_ustr, aSelSeq );
}
}
OUString XclImpListBoxObj::DoGetServiceName() const
{
return u"com.sun.star.form.component.ListBox"_ustr;
}
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( u"Dropdown"_ustr, true );
// dropdown line count
rPropSet.SetProperty( u"LineCount"_ustr, mnLineCount );
if( GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX )
{
// text of editable combobox
if( maTextData.mxString )
rPropSet.SetStringProperty( u"DefaultText"_ustr, 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( u"DefaultSelection"_ustr, aSelSeq );
}
}
}
OUString XclImpDropDownObj::DoGetServiceName() const
{
return (GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX) ?
u"com.sun.star.form.component.ComboBox"_ustr :
u"com.sun.star.form.component.ListBox"_ustr;
}
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 );
}
}
rtl::Reference<SdrObject> XclImpPictureObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
{
// try to create an OLE object or form control
rtl::Reference<SdrObject> 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 =
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 OUString gaStdFormName( u"Standard"_ustr ); /// 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)
{
if( officecfg::Office::Common::Filter::Microsoft::Import::MathTypeToMath::get() )
mnOleImpFlags |= OLE_MATHTYPE_2_STARMATH;
if( officecfg::Office::Common::Filter::Microsoft::Import::WinWordToWriter::get() )
mnOleImpFlags |= OLE_WINWORD_2_STARWRITER;
if( officecfg::Office::Common::Filter::Microsoft::Import::PowerPointToImpress::get() )
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
rtl::Reference<SdrObject> xSdrObj( rDrawObj.CreateSdrObject( *this, aAnchorRect, false ) );
if( xSdrObj )
rDrawObj.PreProcessSdrObject( *this, *xSdrObj );
// call InsertSdrObject() also, if SdrObject is missing
InsertSdrObject( rObjList, rDrawObj, xSdrObj.get() );
}
}
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;
}
rtl::Reference<SdrObject> XclImpDffConverter::CreateSdrObject( const XclImpTbxObjBase& rTbxObj, const tools::Rectangle& rAnchorRect )
{
rtl::Reference<SdrObject> 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;
}
rtl::Reference<SdrObject> XclImpDffConverter::CreateSdrObject( const XclImpPictureObj& rPicObj, const tools::Rectangle& rAnchorRect )
{
rtl::Reference<SdrObject> 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( u"Name"_ustr, rPicObj.GetObjName() );
InsertControl( xFComp, aSz,&xShape,true);
xSdrObj = rPicObj.CreateSdrObjectFromShape( xShape, rAnchorRect );
}
}
}
catch( const Exception& )
{
}
}
else
{
SfxObjectShell* pDocShell = GetDocShell();
rtl::Reference<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 = css::embed::Aspects;
sal_Int64 nAspects = rPicObj.IsSymbol() ? cssea::MSOLE_ICON : cssea::MSOLE_CONTENT;
xSdrObj =
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 {}
};
}
rtl::Reference<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. */
rtl::Reference<SdrObject> 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
rtl::Reference<SdrObject> 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.clear();
}
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;
}
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. */
rtl::Reference<SdrObject> 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.get();
}
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(), u"com.sun.star.drawing.ControlShape"_ustr ), 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 = std::move(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. */
rtl::Reference<SdrObject> xSdrObj( ImportObj( rDffStrm, aDrawObjClientData, aDummy, aDummy, /*nCalledByGroup*/0, /*pShapeId*/nullptr ) );
if (aDrawObjClientData.m_pTopLevelObj && xSdrObj )
InsertSdrObject( GetConvData().mrSdrPage, *aDrawObjClientData.m_pTopLevelObj, xSdrObj.get() );
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. */
rtl::Reference<SdrObject> xSdrObj( pSdrObj );
if( xSdrObj && rDrawObj.IsInsertSdrObj() )
{
rObjList.NbcInsertObject( xSdrObj.get() );
// 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, u"com.sun.star.form.component.Form"_ustr ), 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::ApplyGroupBoxes()
{
// sorted: smallest to largest - looking for smallest contained-in GroupBox
// multimap: allows duplicate key values - may have identical areas.
std::multimap<double, XclImpDrawObjRef> aGroupBoxAreaMap;
for (auto& rGroupBox : maObjMapId)
{
if (rGroupBox.second->GetObjType() != EXC_OBJTYPE_GROUPBOX)
continue;
const tools::Rectangle& rRect = rGroupBox.second->GetDffRect();
const double fArea = double(rRect.GetWidth()) * rRect.GetHeight();
aGroupBoxAreaMap.insert(std::pair<double, XclImpDrawObjRef>(fArea, rGroupBox.second));
}
for (auto& rGroupedObj : maObjMapId)
{
auto pRadioButton = dynamic_cast<XclImpOptionButtonObj*>(rGroupedObj.second.get());
if (!pRadioButton || pRadioButton->IsInGroup())
continue;
OUString sGroupName(u"autoGroup_"_ustr);
for (auto& rGroupBox : aGroupBoxAreaMap)
{
assert(pRadioButton->GetTab() == rGroupBox.second->GetTab() && "impossible right?");
if (!rGroupBox.second->GetDffRect().Contains(pRadioButton->GetDffRect()))
continue;
sGroupName = rGroupBox.second->GetObjName();
if (sGroupName.isEmpty())
sGroupName += "autoGroup_" + OUString::number(rGroupBox.second->GetObjId());
// I ASSUME the smallest box wins in MS Word. (otherwise first? last?)
break;
}
pRadioButton->SetStringProperty(u"GroupName"_ustr, sGroupName);
}
}
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 );
// assign groups based on being contained in the same GroupBox/sheet
ApplyGroupBoxes();
// 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
{
OUString aDefName;
DefObjNameMap::const_iterator aIt = maDefObjNames.find( rDrawObj.GetObjType() );
if( aIt != maDefObjNames.end() )
aDefName = aIt->second;
return aDefName + " " + OUString::number(static_cast<sal_Int32>(rDrawObj.GetObjId()));
}
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: */
↑ V1048 The 'nHorAlign' variable was assigned the same value.