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