1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <tools/debug.hxx>
21 #include <tools/diagnose_ex.h>
22 #include <sal/log.hxx>
23 #include <comphelper/attributelist.hxx>
24 
25 #include <com/sun/star/beans/XPropertySet.hpp>
26 #include <com/sun/star/text/PositionLayoutDir.hpp>
27 #include <com/sun/star/drawing/XShapes3.hpp>
28 
29 #include <utility>
30 #include <xmloff/unointerfacetouniqueidentifiermapper.hxx>
31 
32 #include <xmloff/shapeimport.hxx>
33 #include <xmloff/xmlstyle.hxx>
34 #include <xmloff/xmltkmap.hxx>
35 #include <xmloff/xmlnamespace.hxx>
36 #include <xmloff/xmltoken.hxx>
37 #include <xmloff/table/XMLTableImport.hxx>
38 #include <xmloff/attrlist.hxx>
39 #include "eventimp.hxx"
40 #include "ximpshap.hxx"
41 #include "sdpropls.hxx"
42 #include <xmloff/xmlprmap.hxx>
43 #include "ximp3dscene.hxx"
44 #include "ximp3dobject.hxx"
45 #include "ximpgrp.hxx"
46 #include "ximplink.hxx"
47 
48 #include <map>
49 #include <string_view>
50 #include <vector>
51 
52 namespace {
53 
54 class ShapeGroupContext;
55 
56 }
57 
58 using namespace ::std;
59 using namespace ::com::sun::star;
60 using namespace ::xmloff::token;
61 
62 namespace {
63 
64 struct ConnectionHint
65 {
66     css::uno::Reference< css::drawing::XShape > mxConnector;
67     bool      bStart;
68     OUString  aDestShapeId;
69     sal_Int32 nDestGlueId;
70 };
71 
72 struct XShapeCompareHelper
73 {
operator ()__anon61edc7f20211::XShapeCompareHelper74   bool operator()(const css::uno::Reference < css::drawing::XShape >& x1,
75                   const css::uno::Reference < css::drawing::XShape >& x2 ) const
76   {
77     return x1.get() < x2.get();
78   }
79 };
80 
81 }
82 
83 /** this map store all glue point id mappings for shapes that had user defined glue points. This
84     is needed because on insertion the glue points will get a new and unique id */
85 typedef std::map<sal_Int32,sal_Int32> GluePointIdMap;
86 typedef std::map< css::uno::Reference < css::drawing::XShape >, GluePointIdMap, XShapeCompareHelper > ShapeGluePointsMap;
87 
88 /** this struct is created for each startPage() call and stores information that is needed during
89     import of shapes for one page. Since pages could be nested ( notes pages inside impress ) there
90     is a pointer so one can build up a stack of this structs */
91 struct XMLShapeImportPageContextImpl
92 {
93     ShapeGluePointsMap      maShapeGluePointsMap;
94 
95     uno::Reference < drawing::XShapes > mxShapes;
96 
97     std::shared_ptr<XMLShapeImportPageContextImpl> mpNext;
98 };
99 
100 /** this class is to enable adding members to the XMLShapeImportHelper without getting incompatible */
101 struct XMLShapeImportHelperImpl
102 {
103     // context for sorting shapes
104     std::shared_ptr<ShapeGroupContext> mpGroupContext;
105 
106     std::vector<ConnectionHint> maConnections;
107 
108     // #88546# possibility to switch progress bar handling on/off
109     bool                        mbHandleProgressBar;
110 
111     // stores the capability of the current model to create presentation shapes
112     bool                        mbIsPresentationShapesSupported;
113 };
114 
115 constexpr OUStringLiteral gsStartShape(u"StartShape");
116 constexpr OUStringLiteral gsEndShape(u"EndShape");
117 constexpr OUStringLiteral gsStartGluePointIndex(u"StartGluePointIndex");
118 constexpr OUStringLiteral gsEndGluePointIndex(u"EndGluePointIndex");
119 
XMLShapeImportHelper(SvXMLImport & rImporter,const uno::Reference<frame::XModel> & rModel,SvXMLImportPropertyMapper * pExtMapper)120 XMLShapeImportHelper::XMLShapeImportHelper(
121         SvXMLImport& rImporter,
122         const uno::Reference< frame::XModel>& rModel,
123         SvXMLImportPropertyMapper *pExtMapper )
124 :   mpImpl( new XMLShapeImportHelperImpl ),
125     mrImporter( rImporter )
126 {
127     mpImpl->mpGroupContext = nullptr;
128 
129     // #88546# init to sal_False
130     mpImpl->mbHandleProgressBar = false;
131 
132     mpSdPropHdlFactory = new XMLSdPropHdlFactory( rModel, rImporter );
133 
134     // construct PropertySetMapper
135     rtl::Reference < XMLPropertySetMapper > xMapper = new XMLShapePropertySetMapper(mpSdPropHdlFactory, false);
136     mpPropertySetMapper = new SvXMLImportPropertyMapper( xMapper, rImporter );
137 
138     if( pExtMapper )
139     {
140         rtl::Reference < SvXMLImportPropertyMapper > xExtMapper( pExtMapper );
141         mpPropertySetMapper->ChainImportMapper( xExtMapper );
142     }
143 
144     // chain text attributes
145     mpPropertySetMapper->ChainImportMapper(XMLTextImportHelper::CreateParaExtPropMapper(rImporter));
146     mpPropertySetMapper->ChainImportMapper(XMLTextImportHelper::CreateParaDefaultExtPropMapper(rImporter));
147 
148     // construct PresPagePropsMapper
149     xMapper = new XMLPropertySetMapper(aXMLSDPresPageProps, mpSdPropHdlFactory, false);
150     mpPresPagePropsMapper = new SvXMLImportPropertyMapper( xMapper, rImporter );
151 
152     uno::Reference< lang::XServiceInfo > xInfo( rImporter.GetModel(), uno::UNO_QUERY );
153     mpImpl->mbIsPresentationShapesSupported = xInfo.is() && xInfo->supportsService( "com.sun.star.presentation.PresentationDocument" );
154 }
155 
~XMLShapeImportHelper()156 XMLShapeImportHelper::~XMLShapeImportHelper()
157 {
158     SAL_WARN_IF( !mpImpl->maConnections.empty(), "xmloff", "XMLShapeImportHelper::restoreConnections() was not called!" );
159 
160     // cleanup factory, decrease refcount. Should lead to destruction.
161     mpSdPropHdlFactory.clear();
162 
163     // cleanup mapper, decrease refcount. Should lead to destruction.
164     mpPropertySetMapper.clear();
165 
166     // cleanup presPage mapper, decrease refcount. Should lead to destruction.
167     mpPresPagePropsMapper.clear();
168 
169     // Styles or AutoStyles context?
170     if(mxStylesContext.is())
171         mxStylesContext->dispose();
172 
173     if(mxAutoStylesContext.is())
174         mxAutoStylesContext->dispose();
175 }
176 
177 
Create3DSceneChildContext(SvXMLImport & rImport,sal_Int32 nElement,const uno::Reference<xml::sax::XFastAttributeList> & xAttrList,uno::Reference<drawing::XShapes> const & rShapes)178 SvXMLShapeContext* XMLShapeImportHelper::Create3DSceneChildContext(
179     SvXMLImport& rImport,
180     sal_Int32 nElement,
181     const uno::Reference< xml::sax::XFastAttributeList>& xAttrList,
182     uno::Reference< drawing::XShapes > const & rShapes)
183 {
184     SdXMLShapeContext *pContext = nullptr;
185 
186     if(rShapes.is())
187     {
188         switch(nElement)
189         {
190             case XML_ELEMENT(DR3D, XML_SCENE):
191             {
192                 // dr3d:3dscene inside dr3d:3dscene context
193                 pContext = new SdXML3DSceneShapeContext( rImport, xAttrList, rShapes, false);
194                 break;
195             }
196             case XML_ELEMENT(DR3D, XML_CUBE):
197             {
198                 // dr3d:3dcube inside dr3d:3dscene context
199                 pContext = new SdXML3DCubeObjectShapeContext( rImport, xAttrList, rShapes);
200                 break;
201             }
202             case XML_ELEMENT(DR3D, XML_SPHERE):
203             {
204                 // dr3d:3dsphere inside dr3d:3dscene context
205                 pContext = new SdXML3DSphereObjectShapeContext( rImport, xAttrList, rShapes);
206                 break;
207             }
208             case XML_ELEMENT(DR3D, XML_ROTATE):
209             {
210                 // dr3d:3dlathe inside dr3d:3dscene context
211                 pContext = new SdXML3DLatheObjectShapeContext( rImport, xAttrList, rShapes);
212                 break;
213             }
214             case XML_ELEMENT(DR3D, XML_EXTRUDE):
215             {
216                 // dr3d:3dextrude inside dr3d:3dscene context
217                 pContext = new SdXML3DExtrudeObjectShapeContext( rImport, xAttrList, rShapes);
218                 break;
219             }
220             default:
221                 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
222         }
223     }
224 
225     if (!pContext)
226         return nullptr;
227 
228     // now parse the attribute list and call the child context for each unknown attribute
229     for(auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
230     {
231         if (!pContext->processAttribute( aIter ))
232             XMLOFF_WARN_UNKNOWN("xmloff", aIter);
233 
234     }
235 
236     return pContext;
237 }
238 
SetStylesContext(SvXMLStylesContext * pNew)239 void XMLShapeImportHelper::SetStylesContext(SvXMLStylesContext* pNew)
240 {
241     mxStylesContext.set(pNew);
242 }
243 
SetAutoStylesContext(SvXMLStylesContext * pNew)244 void XMLShapeImportHelper::SetAutoStylesContext(SvXMLStylesContext* pNew)
245 {
246     mxAutoStylesContext.set(pNew);
247 }
248 
CreateGroupChildContext(SvXMLImport & rImport,sal_Int32 nElement,const uno::Reference<xml::sax::XFastAttributeList> & xAttrList,uno::Reference<drawing::XShapes> const & rShapes,bool bTemporaryShape)249 SvXMLShapeContext* XMLShapeImportHelper::CreateGroupChildContext(
250     SvXMLImport& rImport,
251     sal_Int32 nElement,
252     const uno::Reference< xml::sax::XFastAttributeList>& xAttrList,
253     uno::Reference< drawing::XShapes > const & rShapes,
254     bool bTemporaryShape)
255 {
256     SdXMLShapeContext *pContext = nullptr;
257     switch (nElement)
258     {
259         case XML_ELEMENT(DRAW, XML_G):
260             // draw:g inside group context (RECURSIVE)
261             pContext = new SdXMLGroupShapeContext( rImport, xAttrList, rShapes, bTemporaryShape);
262             break;
263         case XML_ELEMENT(DR3D, XML_SCENE):
264         {
265             // dr3d:3dscene inside group context
266             pContext = new SdXML3DSceneShapeContext( rImport, xAttrList, rShapes, bTemporaryShape);
267             break;
268         }
269         case XML_ELEMENT(DRAW, XML_RECT):
270         {
271             // draw:rect inside group context
272             pContext = new SdXMLRectShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
273             break;
274         }
275         case XML_ELEMENT(DRAW, XML_LINE):
276         {
277             // draw:line inside group context
278             pContext = new SdXMLLineShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
279             break;
280         }
281         case XML_ELEMENT(DRAW, XML_CIRCLE):
282         case XML_ELEMENT(DRAW, XML_ELLIPSE):
283         {
284             // draw:circle or draw:ellipse inside group context
285             pContext = new SdXMLEllipseShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
286             break;
287         }
288         case XML_ELEMENT(DRAW, XML_POLYGON):
289         case XML_ELEMENT(DRAW, XML_POLYLINE):
290         {
291             // draw:polygon or draw:polyline inside group context
292             pContext = new SdXMLPolygonShapeContext( rImport, xAttrList, rShapes,
293                            nElement == XML_ELEMENT(DRAW, XML_POLYGON), bTemporaryShape );
294             break;
295         }
296         case XML_ELEMENT(DRAW, XML_PATH):
297         {
298             // draw:path inside group context
299             pContext = new SdXMLPathShapeContext( rImport, xAttrList, rShapes, bTemporaryShape);
300             break;
301         }
302         case XML_ELEMENT(DRAW, XML_FRAME):
303         {
304             // text:text-box inside group context
305             pContext = new SdXMLFrameShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
306             break;
307         }
308         case XML_ELEMENT(DRAW, XML_CONTROL):
309         {
310             // draw:control inside group context
311             pContext = new SdXMLControlShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
312             break;
313         }
314         case XML_ELEMENT(DRAW, XML_CONNECTOR):
315         {
316             // draw:connector inside group context
317             pContext = new SdXMLConnectorShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
318             break;
319         }
320         case XML_ELEMENT(DRAW, XML_MEASURE):
321         {
322             // draw:measure inside group context
323             pContext = new SdXMLMeasureShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
324             break;
325         }
326         case XML_ELEMENT(DRAW, XML_PAGE_THUMBNAIL):
327         {
328             // draw:page inside group context
329             pContext = new SdXMLPageShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
330             break;
331         }
332         case XML_ELEMENT(DRAW, XML_CAPTION):
333         case XML_ELEMENT(OFFICE, XML_ANNOTATION):
334         {
335             // draw:caption inside group context
336             pContext = new SdXMLCaptionShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
337             break;
338         }
339         case XML_ELEMENT(CHART, XML_CHART):
340         {
341             // chart:chart inside group context
342             pContext = new SdXMLChartShapeContext( rImport, xAttrList, rShapes, bTemporaryShape );
343             break;
344         }
345         case XML_ELEMENT(DRAW, XML_CUSTOM_SHAPE):
346         {
347             // draw:customshape
348             pContext = new SdXMLCustomShapeContext( rImport, xAttrList, rShapes );
349             break;
350         }
351         case XML_ELEMENT(DRAW, XML_A):
352             return new SdXMLShapeLinkContext( rImport, xAttrList, rShapes );
353         // add other shapes here...
354         default:
355             XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
356             return new SvXMLShapeContext( rImport, bTemporaryShape );
357     }
358 
359     // now parse the attribute list and call the child context for each unknown attribute
360     for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
361     {
362         if (!pContext->processAttribute( aIter ))
363             XMLOFF_WARN_UNKNOWN("xmloff", aIter);
364     }
365     return pContext;
366 }
367 
368 // This method is called from SdXMLFrameShapeContext to create children of draw:frame
CreateFrameChildContext(SvXMLImport & rImport,sal_Int32 nElement,const uno::Reference<xml::sax::XFastAttributeList> & rAttrList,uno::Reference<drawing::XShapes> const & rShapes,const uno::Reference<xml::sax::XFastAttributeList> & rFrameAttrList)369 SvXMLShapeContext* XMLShapeImportHelper::CreateFrameChildContext(
370     SvXMLImport& rImport,
371     sal_Int32 nElement,
372     const uno::Reference< xml::sax::XFastAttributeList>& rAttrList,
373     uno::Reference< drawing::XShapes > const & rShapes,
374     const uno::Reference< xml::sax::XFastAttributeList>& rFrameAttrList)
375 {
376     SdXMLShapeContext *pContext = nullptr;
377 
378     rtl::Reference<sax_fastparser::FastAttributeList> xCombinedAttrList = new sax_fastparser::FastAttributeList(rAttrList);
379     if( rFrameAttrList.is() )
380         xCombinedAttrList->add(rFrameAttrList);
381 
382     switch(nElement)
383     {
384         case XML_ELEMENT(DRAW, XML_TEXT_BOX):
385         {
386             // text:text-box inside group context
387             pContext = new SdXMLTextBoxShapeContext( rImport, xCombinedAttrList, rShapes );
388             break;
389         }
390         case XML_ELEMENT(DRAW, XML_IMAGE):
391         {
392             // office:image inside group context
393             pContext = new SdXMLGraphicObjectShapeContext( rImport, xCombinedAttrList, rShapes );
394             break;
395         }
396         case XML_ELEMENT(DRAW, XML_OBJECT):
397         case XML_ELEMENT(DRAW, XML_OBJECT_OLE):
398         {
399             // draw:object or draw:object_ole
400             pContext = new SdXMLObjectShapeContext( rImport, xCombinedAttrList, rShapes );
401             break;
402         }
403         case XML_ELEMENT(TABLE, XML_TABLE):
404         {
405             // draw:object or draw:object_ole
406             if( rImport.IsTableShapeSupported() )
407                 pContext = new SdXMLTableShapeContext( rImport, xCombinedAttrList, rShapes );
408             break;
409 
410         }
411         case XML_ELEMENT(DRAW, XML_PLUGIN):
412         {
413             // draw:plugin
414             pContext = new SdXMLPluginShapeContext( rImport, xCombinedAttrList, rShapes );
415             break;
416         }
417         case XML_ELEMENT(DRAW, XML_FLOATING_FRAME):
418         {
419             // draw:floating-frame
420             pContext = new SdXMLFloatingFrameShapeContext( rImport, xCombinedAttrList, rShapes );
421             break;
422         }
423         case XML_ELEMENT(DRAW, XML_APPLET):
424         {
425             // draw:applet
426             pContext = new SdXMLAppletShapeContext( rImport, xCombinedAttrList, rShapes );
427             break;
428         }
429         // add other shapes here...
430         default:
431             XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
432             break;
433     }
434 
435     if( pContext )
436     {
437         // now parse the attribute list and call the child context for each unknown attribute
438         for(auto& aIter : *xCombinedAttrList)
439         {
440             if (!pContext->processAttribute( aIter ))
441                 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
442         }
443     }
444 
445     return pContext;
446 }
447 
CreateFrameChildContext(SvXMLImportContext * pThisContext,sal_Int32 nElement,const uno::Reference<xml::sax::XFastAttributeList> & xAttrList)448 css::uno::Reference< css::xml::sax::XFastContextHandler > XMLShapeImportHelper::CreateFrameChildContext(
449     SvXMLImportContext *pThisContext,
450     sal_Int32 nElement,
451     const uno::Reference< xml::sax::XFastAttributeList>& xAttrList )
452 {
453     css::uno::Reference< css::xml::sax::XFastContextHandler > xContext;
454     SdXMLFrameShapeContext *pFrameContext = dynamic_cast<SdXMLFrameShapeContext*>( pThisContext  );
455     if (pFrameContext)
456         xContext = pFrameContext->createFastChildContext( nElement, xAttrList );
457 
458     if (!xContext)
459         XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
460     return xContext;
461 }
462 
463 /** this function is called whenever the implementation classes like to add this new
464     shape to the given XShapes.
465 */
addShape(uno::Reference<drawing::XShape> & rShape,const uno::Reference<xml::sax::XFastAttributeList> &,uno::Reference<drawing::XShapes> & rShapes)466 void XMLShapeImportHelper::addShape( uno::Reference< drawing::XShape >& rShape,
467                                      const uno::Reference< xml::sax::XFastAttributeList >&,
468                                      uno::Reference< drawing::XShapes >& rShapes)
469 {
470     if( rShape.is() && rShapes.is() )
471     {
472         // add new shape to parent
473         rShapes->add( rShape );
474 
475         uno::Reference<beans::XPropertySet> xPropertySet(rShape, uno::UNO_QUERY);
476         if (xPropertySet.is())
477         {
478             xPropertySet->setPropertyValue("HandlePathObjScale", uno::makeAny(true));
479         }
480     }
481 }
482 
483 /** this function is called whenever the implementation classes have finished importing
484     a shape to the given XShapes. The shape is already inserted into its XShapes and
485     all properties and styles are set.
486 */
finishShape(css::uno::Reference<css::drawing::XShape> & rShape,const css::uno::Reference<css::xml::sax::XFastAttributeList> &,css::uno::Reference<css::drawing::XShapes> &)487 void XMLShapeImportHelper::finishShape(
488         css::uno::Reference< css::drawing::XShape >& rShape,
489         const css::uno::Reference< css::xml::sax::XFastAttributeList >&,
490         css::uno::Reference< css::drawing::XShapes >&)
491 {
492     /* Set property <PositionLayoutDir>
493        to <PositionInHoriL2R>, if it exists and the import states that
494        the shape positioning attributes are in horizontal left-to-right
495        layout. This is the case for the OpenOffice.org file format.
496        This setting is done for Writer documents, because the property
497        only exists at service css::text::Shape - the Writer
498        UNO service for shapes.
499        The value indicates that the positioning attributes are given
500        in horizontal left-to-right layout. The property is evaluated
501        during the first positioning of the shape in order to convert
502        the shape position given in the OpenOffice.org file format to
503        the one for the OASIS Open Office file format. (#i28749#, #i36248#)
504     */
505     uno::Reference< beans::XPropertySet > xPropSet(rShape, uno::UNO_QUERY);
506     if ( xPropSet.is() )
507     {
508         if ( mrImporter.IsShapePositionInHoriL2R() &&
509              xPropSet->getPropertySetInfo()->hasPropertyByName(
510                 "PositionLayoutDir") )
511         {
512             uno::Any aPosLayoutDir;
513             aPosLayoutDir <<= text::PositionLayoutDir::PositionInHoriL2R;
514             xPropSet->setPropertyValue( "PositionLayoutDir", aPosLayoutDir );
515         }
516     }
517 }
518 
519 namespace {
520 
521 // helper functions for z-order sorting
522 struct ZOrderHint
523 {
524     sal_Int32 nIs;
525     sal_Int32 nShould;
526     /// The hint is for this shape.
527     uno::Reference<drawing::XShape> xShape;
528 
operator <__anon61edc7f20311::ZOrderHint529     bool operator<(const ZOrderHint& rComp) const { return nShould < rComp.nShould; }
530 };
531 
532 // a) handle z-order of group contents after it has been imported
533 // b) apply group events over group contents after it has been imported
534 class ShapeGroupContext
535 {
536 public:
537     uno::Reference< drawing::XShapes > mxShapes;
538     std::vector<SdXMLEventContextData> maEventData;
539     vector<ZOrderHint>              maZOrderList;
540     vector<ZOrderHint>              maUnsortedList;
541 
542     sal_Int32                       mnCurrentZ;
543     std::shared_ptr<ShapeGroupContext> mpParentContext;
544 
545     ShapeGroupContext( uno::Reference< drawing::XShapes > const & rShapes, std::shared_ptr<ShapeGroupContext> pParentContext );
546 
547     void popGroupAndPostProcess();
548 private:
549     void moveShape( sal_Int32 nSourcePos, sal_Int32 nDestPos );
550 };
551 
552 }
553 
ShapeGroupContext(uno::Reference<drawing::XShapes> const & rShapes,std::shared_ptr<ShapeGroupContext> pParentContext)554 ShapeGroupContext::ShapeGroupContext( uno::Reference< drawing::XShapes > const & rShapes, std::shared_ptr<ShapeGroupContext> pParentContext )
555 :   mxShapes( rShapes ), mnCurrentZ( 0 ), mpParentContext( std::move(pParentContext) )
556 {
557 }
558 
moveShape(sal_Int32 nSourcePos,sal_Int32 nDestPos)559 void ShapeGroupContext::moveShape( sal_Int32 nSourcePos, sal_Int32 nDestPos )
560 {
561     uno::Any aAny( mxShapes->getByIndex( nSourcePos ) );
562     uno::Reference< beans::XPropertySet > xPropSet;
563     aAny >>= xPropSet;
564 
565     if( !(xPropSet.is() && xPropSet->getPropertySetInfo()->hasPropertyByName( "ZOrder" )) )
566         return;
567 
568     xPropSet->setPropertyValue( "ZOrder", uno::Any(nDestPos) );
569 
570     for( ZOrderHint& rHint : maZOrderList )
571     {
572         if( rHint.nIs < nSourcePos )
573         {
574             DBG_ASSERT(rHint.nIs >= nDestPos, "Shape sorting failed" );
575             rHint.nIs++;
576         }
577     }
578 
579     for( ZOrderHint& rHint : maUnsortedList )
580     {
581         if( rHint.nIs < nSourcePos )
582         {
583             SAL_WARN_IF( rHint.nIs < nDestPos, "xmloff", "shape sorting failed" );
584             rHint.nIs++;
585         }
586     }
587 }
588 
589 // sort shapes
popGroupAndPostProcess()590 void ShapeGroupContext::popGroupAndPostProcess()
591 {
592     if (!maEventData.empty())
593     {
594         // tdf#127791 wait until a group is popped to set its event data
595         for (auto& event : maEventData)
596             event.ApplyProperties();
597         maEventData.clear();
598     }
599 
600     // only do something if we have shapes to sort
601     if( maZOrderList.empty() )
602         return;
603 
604     // check if there are more shapes than inserted with ::shapeWithZIndexAdded()
605     // This can happen if there where already shapes on the page before import
606     // Since the writer may delete some of this shapes during import, we need
607     // to do this here and not in our c'tor anymore
608 
609     // check if we have more shapes than we know of
610     sal_Int32 nCount = mxShapes->getCount();
611 
612     nCount -= maZOrderList.size();
613     nCount -= maUnsortedList.size();
614 
615     if( nCount > 0 )
616     {
617         // first update offsets of added shapes
618         for (ZOrderHint& rHint : maZOrderList)
619             rHint.nIs += nCount;
620         for (ZOrderHint& rHint : maUnsortedList)
621             rHint.nIs += nCount;
622 
623         // second add the already existing shapes in the unsorted list
624         ZOrderHint aNewHint;
625         do
626         {
627             nCount--;
628 
629             aNewHint.nIs = nCount;
630             aNewHint.nShould = -1;
631 
632             maUnsortedList.insert(maUnsortedList.begin(), aNewHint);
633         }
634         while( nCount );
635     }
636 
637     bool bSorted = std::is_sorted(maZOrderList.begin(), maZOrderList.end(),
638                        [&](const ZOrderHint& rLeft, const ZOrderHint& rRight)
639                        { return rLeft.nShould < rRight.nShould; } );
640 
641     if (bSorted)
642         return; // nothin' to do
643 
644     // sort z-ordered shapes by nShould field
645     std::sort(maZOrderList.begin(), maZOrderList.end());
646 
647     uno::Reference<drawing::XShapes3> xShapes3(mxShapes, uno::UNO_QUERY);
648     if( xShapes3.is())
649     {
650         uno::Sequence<sal_Int32> aNewOrder(maZOrderList.size() + maUnsortedList.size());
651         sal_Int32 nIndex = 0;
652 
653         for (ZOrderHint& rHint : maZOrderList)
654         {
655             // fill in the gaps from unordered list
656             for (vector<ZOrderHint>::iterator aIt = maUnsortedList.begin(); aIt != maUnsortedList.end() && nIndex < rHint.nShould; )
657             {
658                 aNewOrder[nIndex++] = (*aIt).nIs;
659                 aIt = maUnsortedList.erase(aIt);
660             }
661 
662             aNewOrder[nIndex] = rHint.nIs;
663             nIndex++;
664         }
665 
666         try
667         {
668             xShapes3->sort(aNewOrder);
669             maZOrderList.clear();
670             return;
671         }
672         catch (const css::lang::IllegalArgumentException& /*e*/)
673         {}
674     }
675 
676     // this is the current index, all shapes before that
677     // index are finished
678     sal_Int32 nIndex = 0;
679     for (const ZOrderHint& rHint : maZOrderList)
680     {
681         for (vector<ZOrderHint>::iterator aIt = maUnsortedList.begin(); aIt != maUnsortedList.end() && nIndex < rHint.nShould; )
682         {
683             moveShape( (*aIt).nIs, nIndex++ );
684             aIt = maUnsortedList.erase(aIt);
685 
686         }
687 
688         if(rHint.nIs != nIndex )
689             moveShape( rHint.nIs, nIndex );
690 
691         nIndex++;
692     }
693     maZOrderList.clear();
694 }
695 
pushGroupForPostProcessing(uno::Reference<drawing::XShapes> & rShapes)696 void XMLShapeImportHelper::pushGroupForPostProcessing( uno::Reference< drawing::XShapes >& rShapes )
697 {
698     mpImpl->mpGroupContext = std::make_shared<ShapeGroupContext>( rShapes, mpImpl->mpGroupContext );
699 }
700 
addShapeEvents(SdXMLEventContextData & rData)701 void XMLShapeImportHelper::addShapeEvents(SdXMLEventContextData& rData)
702 {
703     if (mpImpl->mpGroupContext && mpImpl->mpGroupContext->mxShapes == rData.mxShape)
704     {
705         // tdf#127791 wait until a group is popped to set its event data so
706         // that the events are applied to all its children, which are not available
707         // at the start of the group tag
708         mpImpl->mpGroupContext->maEventData.push_back(rData);
709     }
710     else
711         rData.ApplyProperties();
712 }
713 
popGroupAndPostProcess()714 void XMLShapeImportHelper::popGroupAndPostProcess()
715 {
716     SAL_WARN_IF( !mpImpl->mpGroupContext, "xmloff", "No context to sort!" );
717     if( !mpImpl->mpGroupContext )
718         return;
719 
720     try
721     {
722         mpImpl->mpGroupContext->popGroupAndPostProcess();
723     }
724     catch( const uno::Exception& )
725     {
726         DBG_UNHANDLED_EXCEPTION("xmloff", "exception while sorting shapes, sorting failed");
727     }
728 
729     // put parent on top and drop current context, we are done
730     mpImpl->mpGroupContext = mpImpl->mpGroupContext->mpParentContext;
731 }
732 
shapeWithZIndexAdded(css::uno::Reference<css::drawing::XShape> const & xShape,sal_Int32 nZIndex)733 void XMLShapeImportHelper::shapeWithZIndexAdded( css::uno::Reference< css::drawing::XShape > const & xShape, sal_Int32 nZIndex )
734 {
735     if( !mpImpl->mpGroupContext)
736         return;
737 
738     ZOrderHint aNewHint;
739     aNewHint.nIs = mpImpl->mpGroupContext->mnCurrentZ++;
740     aNewHint.nShould = nZIndex;
741     aNewHint.xShape = xShape;
742 
743     if( nZIndex == -1 )
744     {
745         // don't care, so add to unsorted list
746         mpImpl->mpGroupContext->maUnsortedList.push_back(aNewHint);
747     }
748     else
749     {
750         // insert into sort list
751         mpImpl->mpGroupContext->maZOrderList.push_back(aNewHint);
752     }
753 }
754 
shapeRemoved(const uno::Reference<drawing::XShape> & xShape)755 void XMLShapeImportHelper::shapeRemoved(const uno::Reference<drawing::XShape>& xShape)
756 {
757     auto it = std::find_if(mpImpl->mpGroupContext->maZOrderList.begin(), mpImpl->mpGroupContext->maZOrderList.end(), [&xShape](const ZOrderHint& rHint)
758     {
759         return rHint.xShape == xShape;
760     });
761     if (it == mpImpl->mpGroupContext->maZOrderList.end())
762         // Part of the unsorted list, nothing to do.
763         return;
764 
765     sal_Int32 nZIndex = it->nIs;
766 
767     for (it = mpImpl->mpGroupContext->maZOrderList.begin(); it != mpImpl->mpGroupContext->maZOrderList.end();)
768     {
769         if (it->nIs == nZIndex)
770         {
771             // This is xShape: remove it and adjust the max of indexes
772             // accordingly.
773             it = mpImpl->mpGroupContext->maZOrderList.erase(it);
774             mpImpl->mpGroupContext->mnCurrentZ--;
775             continue;
776         }
777         else if (it->nIs > nZIndex)
778             // On top of xShape: adjust actual index to reflect removal.
779             it->nIs--;
780 
781         // On top of or below xShape.
782         ++it;
783     }
784 }
785 
addShapeConnection(css::uno::Reference<css::drawing::XShape> const & rConnectorShape,bool bStart,const OUString & rDestShapeId,sal_Int32 nDestGlueId)786 void XMLShapeImportHelper::addShapeConnection( css::uno::Reference< css::drawing::XShape > const & rConnectorShape,
787                          bool bStart,
788                          const OUString& rDestShapeId,
789                          sal_Int32 nDestGlueId )
790 {
791     ConnectionHint aHint;
792     aHint.mxConnector = rConnectorShape;
793     aHint.bStart = bStart;
794     aHint.aDestShapeId = rDestShapeId;
795     aHint.nDestGlueId = nDestGlueId;
796 
797     mpImpl->maConnections.push_back( aHint );
798 }
799 
restoreConnections()800 void XMLShapeImportHelper::restoreConnections()
801 {
802     const vector<ConnectionHint>::size_type nCount = mpImpl->maConnections.size();
803     for( vector<ConnectionHint>::size_type i = 0; i < nCount; i++ )
804     {
805         ConnectionHint& rHint = mpImpl->maConnections[i];
806         uno::Reference< beans::XPropertySet > xConnector( rHint.mxConnector, uno::UNO_QUERY );
807         if( xConnector.is() )
808         {
809             // #86637# remember line deltas
810             uno::Any aLine1Delta;
811             uno::Any aLine2Delta;
812             uno::Any aLine3Delta;
813             OUString aStr1("EdgeLine1Delta");
814             OUString aStr2("EdgeLine2Delta");
815             OUString aStr3("EdgeLine3Delta");
816             aLine1Delta = xConnector->getPropertyValue(aStr1);
817             aLine2Delta = xConnector->getPropertyValue(aStr2);
818             aLine3Delta = xConnector->getPropertyValue(aStr3);
819 
820             // #86637# simply setting these values WILL force the connector to do
821             // a new layout promptly. So the line delta values have to be rescued
822             // and restored around connector changes.
823             uno::Reference< drawing::XShape > xShape(
824                 mrImporter.getInterfaceToIdentifierMapper().getReference( rHint.aDestShapeId ), uno::UNO_QUERY );
825             if( xShape.is() )
826             {
827                 if (rHint.bStart)
828                     xConnector->setPropertyValue( gsStartShape, uno::Any(xShape) );
829                 else
830                     xConnector->setPropertyValue( gsEndShape, uno::Any(xShape) );
831 
832                 sal_Int32 nGlueId = rHint.nDestGlueId < 4 ? rHint.nDestGlueId : getGluePointId( xShape, rHint.nDestGlueId );
833                 if(rHint.bStart)
834                     xConnector->setPropertyValue( gsStartGluePointIndex, uno::Any(nGlueId) );
835                 else
836                     xConnector->setPropertyValue( gsEndGluePointIndex, uno::Any(nGlueId) );
837             }
838 
839             // #86637# restore line deltas
840             xConnector->setPropertyValue(aStr1, aLine1Delta );
841             xConnector->setPropertyValue(aStr2, aLine2Delta );
842             xConnector->setPropertyValue(aStr3, aLine3Delta );
843         }
844     }
845     mpImpl->maConnections.clear();
846 }
847 
CreateShapePropMapper(const uno::Reference<frame::XModel> & rModel,SvXMLImport & rImport)848 SvXMLImportPropertyMapper* XMLShapeImportHelper::CreateShapePropMapper( const uno::Reference< frame::XModel>& rModel, SvXMLImport& rImport )
849 {
850     rtl::Reference< XMLPropertyHandlerFactory > xFactory = new XMLSdPropHdlFactory( rModel, rImport );
851     rtl::Reference < XMLPropertySetMapper > xMapper = new XMLShapePropertySetMapper( xFactory, false );
852     SvXMLImportPropertyMapper* pResult = new SvXMLImportPropertyMapper( xMapper, rImport );
853 
854     // chain text attributes
855     pResult->ChainImportMapper( XMLTextImportHelper::CreateParaExtPropMapper( rImport ) );
856     return pResult;
857 }
858 
859 /** adds a mapping for a glue point identifier from an xml file to the identifier created after inserting
860     the new glue point into the core. The saved mappings can be retrieved by getGluePointId() */
addGluePointMapping(css::uno::Reference<css::drawing::XShape> const & xShape,sal_Int32 nSourceId,sal_Int32 nDestinnationId)861 void XMLShapeImportHelper::addGluePointMapping( css::uno::Reference< css::drawing::XShape > const & xShape,
862                           sal_Int32 nSourceId, sal_Int32 nDestinnationId )
863 {
864     if( mpPageContext )
865         mpPageContext->maShapeGluePointsMap[xShape][nSourceId] = nDestinnationId;
866 }
867 
868 /** moves all current DestinationId's by n */
moveGluePointMapping(const css::uno::Reference<css::drawing::XShape> & xShape,const sal_Int32 n)869 void XMLShapeImportHelper::moveGluePointMapping( const css::uno::Reference< css::drawing::XShape >& xShape, const sal_Int32 n )
870 {
871     if( mpPageContext )
872     {
873         ShapeGluePointsMap::iterator aShapeIter( mpPageContext->maShapeGluePointsMap.find( xShape ) );
874         if( aShapeIter != mpPageContext->maShapeGluePointsMap.end() )
875         {
876             for ( auto& rShapeId : (*aShapeIter).second )
877             {
878                 if ( rShapeId.second != -1 )
879                     rShapeId.second += n;
880             }
881         }
882     }
883 }
884 
885 /** retrieves a mapping for a glue point identifier from the current xml file to the identifier created after
886     inserting the new glue point into the core. The mapping must be initialized first with addGluePointMapping() */
getGluePointId(const css::uno::Reference<css::drawing::XShape> & xShape,sal_Int32 nSourceId)887 sal_Int32 XMLShapeImportHelper::getGluePointId( const css::uno::Reference< css::drawing::XShape >& xShape, sal_Int32 nSourceId )
888 {
889     if( mpPageContext )
890     {
891         ShapeGluePointsMap::iterator aShapeIter( mpPageContext->maShapeGluePointsMap.find( xShape ) );
892         if( aShapeIter != mpPageContext->maShapeGluePointsMap.end() )
893         {
894             GluePointIdMap::iterator aIdIter = (*aShapeIter).second.find(nSourceId);
895             if( aIdIter != (*aShapeIter).second.end() )
896                 return (*aIdIter).second;
897         }
898     }
899 
900     return -1;
901 }
902 
903 /** this method must be calling before the first shape is imported for the given page */
startPage(css::uno::Reference<css::drawing::XShapes> const & rShapes)904 void XMLShapeImportHelper::startPage( css::uno::Reference< css::drawing::XShapes > const & rShapes )
905 {
906     const std::shared_ptr<XMLShapeImportPageContextImpl> pOldContext = mpPageContext;
907     mpPageContext = std::make_shared<XMLShapeImportPageContextImpl>();
908     mpPageContext->mpNext = pOldContext;
909     mpPageContext->mxShapes = rShapes;
910 }
911 
912 /** this method must be calling after the last shape is imported for the given page */
endPage(css::uno::Reference<css::drawing::XShapes> const & rShapes)913 void XMLShapeImportHelper::endPage( css::uno::Reference< css::drawing::XShapes > const & rShapes )
914 {
915     SAL_WARN_IF( !mpPageContext || (mpPageContext->mxShapes != rShapes), "xmloff", "wrong call to endPage(), no startPage called or wrong page" );
916     if( nullptr == mpPageContext )
917         return;
918 
919     restoreConnections();
920 
921     mpPageContext = mpPageContext->mpNext;
922 }
923 
924 /** defines if the import should increment the progress bar or not */
enableHandleProgressBar()925 void XMLShapeImportHelper::enableHandleProgressBar()
926 {
927     mpImpl->mbHandleProgressBar = true;
928 }
929 
IsHandleProgressBarEnabled() const930 bool XMLShapeImportHelper::IsHandleProgressBarEnabled() const
931 {
932     return mpImpl->mbHandleProgressBar;
933 }
934 
935 /** queries the capability of the current model to create presentation shapes */
IsPresentationShapesSupported() const936 bool XMLShapeImportHelper::IsPresentationShapesSupported() const
937 {
938     return mpImpl->mbIsPresentationShapesSupported;
939 }
940 
GetShapeTableImport()941 const rtl::Reference< XMLTableImport >& XMLShapeImportHelper::GetShapeTableImport()
942 {
943     if( !mxShapeTableImport.is() )
944     {
945         rtl::Reference< XMLPropertyHandlerFactory > xFactory( new XMLSdPropHdlFactory( mrImporter.GetModel(), mrImporter ) );
946         rtl::Reference< XMLPropertySetMapper > xPropertySetMapper( new XMLShapePropertySetMapper( xFactory, false ) );
947         mxShapeTableImport = new XMLTableImport( mrImporter, xPropertySetMapper, xFactory );
948     }
949 
950     return mxShapeTableImport;
951 }
952 
setHyperlink(const OUString & rHyperlink)953 void SvXMLShapeContext::setHyperlink( const OUString& rHyperlink )
954 {
955     msHyperlink = rHyperlink;
956 }
957 
958 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
959