tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / oox / source / export / shapes.cxx
blob0bb8d1ebeecc944df1a6b4a9274073dc70468aac
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <config_wasm_strip.h>
22 #include <sal/config.h>
23 #include <sal/log.hxx>
25 #include <filter/msfilter/util.hxx>
26 #include <o3tl/string_view.hxx>
27 #include <o3tl/any.hxx>
28 #include <oox/core/xmlfilterbase.hxx>
29 #include <oox/export/shapes.hxx>
30 #include <oox/export/utils.hxx>
31 #include <oox/token/namespaces.hxx>
32 #include <oox/token/relationship.hxx>
33 #include <oox/token/tokens.hxx>
35 #include <initializer_list>
36 #include <string_view>
38 #include <com/sun/star/beans/PropertyValues.hpp>
39 #include <com/sun/star/beans/XPropertySet.hpp>
40 #include <com/sun/star/beans/XPropertySetInfo.hpp>
41 #include <com/sun/star/beans/XPropertyState.hpp>
42 #include <com/sun/star/container/XChild.hpp>
43 #include <com/sun/star/document/XExporter.hpp>
44 #include <com/sun/star/document/XStorageBasedDocument.hpp>
45 #include <com/sun/star/drawing/CircleKind.hpp>
46 #include <com/sun/star/drawing/FillStyle.hpp>
47 #include <com/sun/star/drawing/ConnectorType.hpp>
48 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
49 #include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
50 #include <com/sun/star/embed/Aspects.hpp>
51 #include <com/sun/star/embed/EmbedStates.hpp>
52 #include <com/sun/star/embed/XEmbeddedObject.hpp>
53 #include <com/sun/star/embed/XEmbedPersist.hpp>
54 #include <com/sun/star/frame/XStorable.hpp>
55 #include <com/sun/star/graphic/XGraphic.hpp>
56 #include <com/sun/star/i18n/ScriptType.hpp>
57 #include <com/sun/star/io/XOutputStream.hpp>
58 #include <com/sun/star/text/XSimpleText.hpp>
59 #include <com/sun/star/text/XText.hpp>
60 #include <com/sun/star/table/XTable.hpp>
61 #include <com/sun/star/table/XMergeableCell.hpp>
62 #include <com/sun/star/chart2/XChartDocument.hpp>
63 #include <com/sun/star/frame/XModel.hpp>
64 #include <com/sun/star/uno/XComponentContext.hpp>
65 #include <com/sun/star/drawing/XDrawPages.hpp>
66 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
67 #include <com/sun/star/presentation/ClickAction.hpp>
68 #include <com/sun/star/drawing/XGluePointsSupplier.hpp>
69 #include <com/sun/star/container/XIdentifierAccess.hpp>
70 #include <com/sun/star/table/BorderLineStyle.hpp>
71 #include <tools/globname.hxx>
72 #include <comphelper/classids.hxx>
73 #include <comphelper/propertysequence.hxx>
74 #include <comphelper/storagehelper.hxx>
75 #include <sot/exchange.hxx>
76 #include <utility>
77 #include <vcl/graph.hxx>
78 #include <vcl/outdev.hxx>
79 #include <filter/msfilter/escherex.hxx>
80 #include <svtools/embedhlp.hxx>
81 #include <svx/svdoashp.hxx>
82 #include <svx/svdoole2.hxx>
83 #include <comphelper/diagnose_ex.hxx>
84 #include <oox/export/chartexport.hxx>
85 #include <oox/mathml/imexport.hxx>
86 #include <basegfx/numeric/ftools.hxx>
87 #include <oox/export/DMLPresetShapeExport.hxx>
89 #include <frozen/bits/defines.h>
90 #include <frozen/bits/elsa_std.h>
91 #include <frozen/set.h>
92 #include <frozen/unordered_map.h>
94 #include <sax/fastattribs.hxx>
96 using namespace ::css;
97 using namespace ::css::beans;
98 using namespace ::css::uno;
99 using namespace ::css::drawing;
100 using namespace ::css::table;
101 using namespace ::css::container;
102 using namespace ::css::document;
103 using namespace ::css::text;
105 using ::css::io::XOutputStream;
106 using ::css::chart2::XChartDocument;
107 using ::css::frame::XModel;
109 using ::oox::core::XmlFilterBase;
110 using ::sax_fastparser::FSHelperPtr;
113 namespace oox {
115 static void lcl_ConvertProgID(std::u16string_view rProgID,
116 OUString & o_rMediaType, OUString & o_rRelationType, OUString & o_rFileExtension)
118 if (rProgID == u"Excel.Sheet.12")
120 o_rMediaType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
121 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
122 o_rFileExtension = "xlsx";
124 else if (o3tl::starts_with(rProgID, u"Excel.SheetBinaryMacroEnabled.12") )
126 o_rMediaType = "application/vnd.ms-excel.sheet.binary.macroEnabled.12";
127 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
128 o_rFileExtension = "xlsb";
130 else if (o3tl::starts_with(rProgID, u"Excel.SheetMacroEnabled.12"))
132 o_rMediaType = "application/vnd.ms-excel.sheet.macroEnabled.12";
133 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
134 o_rFileExtension = "xlsm";
136 else if (o3tl::starts_with(rProgID, u"Excel.Sheet"))
138 o_rMediaType = "application/vnd.ms-excel";
139 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
140 o_rFileExtension = "xls";
142 else if (rProgID == u"PowerPoint.Show.12")
144 o_rMediaType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
145 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
146 o_rFileExtension = "pptx";
148 else if (rProgID == u"PowerPoint.ShowMacroEnabled.12")
150 o_rMediaType = "application/vnd.ms-powerpoint.presentation.macroEnabled.12";
151 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
152 o_rFileExtension = "pptm";
154 else if (o3tl::starts_with(rProgID, u"PowerPoint.Show"))
156 o_rMediaType = "application/vnd.ms-powerpoint";
157 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
158 o_rFileExtension = "ppt";
160 else if (o3tl::starts_with(rProgID, u"PowerPoint.Slide.12"))
162 o_rMediaType = "application/vnd.openxmlformats-officedocument.presentationml.slide";
163 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
164 o_rFileExtension = "sldx";
166 else if (rProgID == u"PowerPoint.SlideMacroEnabled.12")
168 o_rMediaType = "application/vnd.ms-powerpoint.slide.macroEnabled.12";
169 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
170 o_rFileExtension = "sldm";
172 else if (rProgID == u"Word.DocumentMacroEnabled.12")
174 o_rMediaType = "application/vnd.ms-word.document.macroEnabled.12";
175 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
176 o_rFileExtension = "docm";
178 else if (rProgID == u"Word.Document.12")
180 o_rMediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
181 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
182 o_rFileExtension = "docx";
184 else if (rProgID == u"Word.Document.8")
186 o_rMediaType = "application/msword";
187 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
188 o_rFileExtension = "doc";
190 else if (rProgID == u"Excel.Chart.8")
192 o_rMediaType = "application/vnd.ms-excel";
193 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
194 o_rFileExtension = "xls";
196 else if (rProgID == u"AcroExch.Document.11")
198 o_rMediaType = "application/pdf";
199 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
200 o_rFileExtension = "pdf";
202 else
204 o_rMediaType = "application/vnd.openxmlformats-officedocument.oleObject";
205 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
206 o_rFileExtension = "bin";
210 static uno::Reference<io::XInputStream> lcl_StoreOwnAsOOXML(
211 uno::Reference<uno::XComponentContext> const& xContext,
212 uno::Reference<embed::XEmbeddedObject> const& xObj,
213 char const*& o_rpProgID,
214 OUString & o_rMediaType, OUString & o_rRelationType, OUString & o_rSuffix)
216 static struct {
217 struct {
218 sal_uInt32 n1;
219 sal_uInt16 n2, n3;
220 sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15;
221 } ClassId;
222 char const* pFilterName;
223 char const* pMediaType;
224 char const* pProgID;
225 char const* pSuffix;
226 } const s_Mapping[] = {
227 { {SO3_SW_CLASSID_60}, "MS Word 2007 XML", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "Word.Document.12", "docx" },
228 { {SO3_SC_CLASSID_60}, "Calc MS Excel 2007 XML", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Excel.Sheet.12", "xlsx" },
229 { {SO3_SIMPRESS_CLASSID_60}, "Impress MS PowerPoint 2007 XML", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "PowerPoint.Show.12", "pptx" },
230 // FIXME: Draw does not appear to have a MSO format export filter?
231 // { {SO3_SDRAW_CLASSID}, "", "", "", "" },
232 { {SO3_SCH_CLASSID_60}, "unused", "", "", "" },
233 { {SO3_SM_CLASSID_60}, "unused", "", "", "" },
236 const char * pFilterName(nullptr);
237 SvGlobalName const classId(xObj->getClassID());
238 for (auto & i : s_Mapping)
240 auto const& rId(i.ClassId);
241 SvGlobalName const temp(rId.n1, rId.n2, rId.n3, rId.b8, rId.b9, rId.b10, rId.b11, rId.b12, rId.b13, rId.b14, rId.b15);
242 if (temp == classId)
244 assert(SvGlobalName(SO3_SCH_CLASSID_60) != classId); // chart should be written elsewhere!
245 assert(SvGlobalName(SO3_SM_CLASSID_60) != classId); // formula should be written elsewhere!
246 pFilterName = i.pFilterName;
247 o_rMediaType = OUString::createFromAscii(i.pMediaType);
248 o_rpProgID = i.pProgID;
249 o_rSuffix = OUString::createFromAscii(i.pSuffix);
250 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
251 break;
255 if (!pFilterName)
257 SAL_WARN("oox.shape", "oox::GetOLEObjectStream: unknown ClassId " << classId.GetHexName());
258 return nullptr;
261 if (embed::EmbedStates::LOADED == xObj->getCurrentState())
263 xObj->changeState(embed::EmbedStates::RUNNING);
265 // use a temp stream - while it would work to store directly to a
266 // fragment stream, an error during export means we'd have to delete it
267 uno::Reference<io::XStream> const xTempStream(
268 xContext->getServiceManager()->createInstanceWithContext(
269 u"com.sun.star.comp.MemoryStream"_ustr, xContext),
270 uno::UNO_QUERY_THROW);
271 uno::Sequence<beans::PropertyValue> args( comphelper::InitPropertySequence({
272 { "OutputStream", Any(xTempStream->getOutputStream()) },
273 { "FilterName", Any(OUString::createFromAscii(pFilterName)) }
274 }));
275 uno::Reference<frame::XStorable> xStorable(xObj->getComponent(), uno::UNO_QUERY);
278 xStorable->storeToURL(u"private:stream"_ustr, args);
280 catch (uno::Exception const&)
282 TOOLS_WARN_EXCEPTION("oox.shape", "oox::GetOLEObjectStream");
283 return nullptr;
285 xTempStream->getOutputStream()->closeOutput();
286 return xTempStream->getInputStream();
289 uno::Reference<io::XInputStream> GetOLEObjectStream(
290 uno::Reference<uno::XComponentContext> const& xContext,
291 uno::Reference<embed::XEmbeddedObject> const& xObj,
292 std::u16string_view i_rProgID,
293 OUString & o_rMediaType,
294 OUString & o_rRelationType,
295 OUString & o_rSuffix,
296 const char *& o_rpProgID)
298 uno::Reference<io::XInputStream> xInStream;
301 uno::Reference<document::XStorageBasedDocument> const xParent(
302 uno::Reference<container::XChild>(xObj, uno::UNO_QUERY_THROW)->getParent(),
303 uno::UNO_QUERY_THROW);
304 uno::Reference<embed::XStorage> const xParentStorage(xParent->getDocumentStorage());
305 OUString const entryName(
306 uno::Reference<embed::XEmbedPersist>(xObj, uno::UNO_QUERY_THROW)->getEntryName());
308 if (xParentStorage->isStreamElement(entryName))
310 lcl_ConvertProgID(i_rProgID, o_rMediaType, o_rRelationType, o_rSuffix);
311 xInStream = xParentStorage->cloneStreamElement(entryName)->getInputStream();
312 // TODO: make it possible to take the sMediaType from the stream
314 else // the object is ODF - either the whole document is
315 { // ODF, or the OLE was edited so it was converted to ODF
316 xInStream = lcl_StoreOwnAsOOXML(xContext, xObj,
317 o_rpProgID, o_rMediaType, o_rRelationType, o_rSuffix);
320 catch (uno::Exception const&)
322 TOOLS_WARN_EXCEPTION("oox.shape", "oox::GetOLEObjectStream");
324 return xInStream;
327 } // namespace oox
329 namespace oox::drawingml {
331 ShapeExport::ShapeExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, ShapeHashMap* pShapeMap, XmlFilterBase* pFB, DocumentType eDocumentType, DMLTextExport* pTextExport, bool bUserShapes )
332 : DrawingML( std::move(pFS), pFB, eDocumentType, pTextExport )
333 , m_nEmbeddedObjects(0)
334 , mnShapeIdMax( 1 )
335 , mbUserShapes( bUserShapes )
336 , mnXmlNamespace( nXmlNamespace )
337 , maMapModeSrc( MapUnit::Map100thMM )
338 , maMapModeDest( MapUnit::MapInch, Point(), Fraction( 1, 576 ), Fraction( 1, 576 ) )
339 , mpShapeMap( pShapeMap ? pShapeMap : &maShapeMap )
341 mpURLTransformer = std::make_shared<URLTransformer>();
344 void ShapeExport::SetURLTranslator(const std::shared_ptr<URLTransformer>& pTransformer)
346 mpURLTransformer = pTransformer;
349 awt::Size ShapeExport::MapSize( const awt::Size& rSize ) const
351 Size aRetSize( OutputDevice::LogicToLogic( Size( rSize.Width, rSize.Height ), maMapModeSrc, maMapModeDest ) );
353 if ( !aRetSize.Width() )
354 aRetSize.AdjustWidth( 1 );
355 if ( !aRetSize.Height() )
356 aRetSize.AdjustHeight( 1 );
357 return awt::Size( aRetSize.Width(), aRetSize.Height() );
360 static bool IsNonEmptySimpleText(const Reference<XInterface>& xIface)
362 if (Reference<XSimpleText> xText{ xIface, UNO_QUERY })
363 return xText->getString().getLength();
365 return false;
368 bool ShapeExport::NonEmptyText( const Reference< XInterface >& xIface )
370 Reference< XPropertySet > xPropSet( xIface, UNO_QUERY );
372 if( xPropSet.is() )
374 Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
375 if ( xPropSetInfo.is() )
377 if ( xPropSetInfo->hasPropertyByName( u"IsEmptyPresentationObject"_ustr ) )
379 bool bIsEmptyPresObj = false;
380 if ( xPropSet->getPropertyValue( u"IsEmptyPresentationObject"_ustr ) >>= bIsEmptyPresObj )
382 SAL_INFO("oox.shape", "empty presentation object " << bIsEmptyPresObj << " , props:");
383 if( bIsEmptyPresObj )
384 return true;
388 if ( xPropSetInfo->hasPropertyByName( u"IsPresentationObject"_ustr ) )
390 bool bIsPresObj = false;
391 if ( xPropSet->getPropertyValue( u"IsPresentationObject"_ustr ) >>= bIsPresObj )
393 SAL_INFO("oox.shape", "presentation object " << bIsPresObj << ", props:");
394 if( bIsPresObj )
395 return true;
401 return IsNonEmptySimpleText(xIface);
404 static void AddExtLst(FSHelperPtr const& pFS, Reference<XPropertySet> const& xShape)
406 if (xShape->getPropertySetInfo()->hasPropertyByName(u"Decorative"_ustr)
407 && xShape->getPropertyValue(u"Decorative"_ustr).get<bool>())
409 pFS->startElementNS(XML_a, XML_extLst);
410 // FSNS(XML_xmlns, XML_a), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
411 pFS->startElementNS(XML_a, XML_ext,
412 // MSO uses this "URI" which is obviously not a URI
413 XML_uri, "{C183D7F6-B498-43B3-948B-1728B52AA6E4}");
414 pFS->singleElementNS(XML_adec, XML_decorative,
415 FSNS(XML_xmlns, XML_adec), "http://schemas.microsoft.com/office/drawing/2017/decorative",
416 XML_val, "1");
417 pFS->endElementNS(XML_a, XML_ext);
418 pFS->endElementNS(XML_a, XML_extLst);
422 ShapeExport& ShapeExport::WritePolyPolygonShape( const Reference< XShape >& xShape, const bool bClosed )
424 SAL_INFO("oox.shape", "write polypolygon shape");
426 FSHelperPtr pFS = GetFS();
427 pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp));
429 awt::Point aPos = xShape->getPosition();
430 // Position is relative to group for child elements in Word, but absolute in API.
431 if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes && m_xParent.is())
433 awt::Point aParentPos = m_xParent->getPosition();
434 aPos.X -= aParentPos.X;
435 aPos.Y -= aParentPos.Y;
437 awt::Size aSize = xShape->getSize();
438 tools::Rectangle aRect(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height));
440 #if OSL_DEBUG_LEVEL > 0
441 tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon(xShape);
442 awt::Size size = MapSize( awt::Size( aRect.GetWidth(), aRect.GetHeight() ) );
443 SAL_INFO("oox.shape", "poly count " << aPolyPolygon.Count());
444 SAL_INFO("oox.shape", "size: " << size.Width << " x " << size.Height);
445 #endif
447 Reference<XPropertySet> const xProps(xShape, UNO_QUERY);
448 // non visual shape properties
449 if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
451 pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
452 pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
453 XML_id, OString::number(GetNewShapeID(xShape)),
454 XML_name, GetShapeName(xShape));
455 AddExtLst(pFS, xProps);
456 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
458 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
459 if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
461 WriteNonVisualProperties( xShape );
462 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
465 // visual shape properties
466 pFS->startElementNS(mnXmlNamespace, XML_spPr);
467 WriteTransformation( xShape, aRect, XML_a );
468 WritePolyPolygon(xShape, bClosed);
469 if( xProps.is() ) {
470 if( bClosed )
471 WriteFill(xProps, aSize);
472 WriteOutline( xProps );
475 pFS->endElementNS( mnXmlNamespace, XML_spPr );
477 // write text
478 WriteTextBox( xShape, mnXmlNamespace );
480 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );
482 return *this;
485 ShapeExport& ShapeExport::WriteClosedPolyPolygonShape( const Reference< XShape >& xShape )
487 return WritePolyPolygonShape( xShape, true );
490 ShapeExport& ShapeExport::WriteOpenPolyPolygonShape( const Reference< XShape >& xShape )
492 return WritePolyPolygonShape( xShape, false );
495 ShapeExport& ShapeExport::WriteGroupShape(const uno::Reference<drawing::XShape>& xShape)
497 FSHelperPtr pFS = GetFS();
499 sal_Int32 nGroupShapeToken = XML_grpSp;
500 if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes)
502 if (!m_xParent.is())
503 nGroupShapeToken = XML_wgp; // toplevel
504 else
505 mnXmlNamespace = XML_wpg;
508 pFS->startElementNS(mnXmlNamespace, nGroupShapeToken);
510 // non visual properties
511 if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
513 pFS->startElementNS(mnXmlNamespace, XML_nvGrpSpPr);
514 pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
515 XML_id, OString::number(GetNewShapeID(xShape)),
516 XML_name, GetShapeName(xShape));
517 uno::Reference<beans::XPropertySet> const xShapeProps(xShape, uno::UNO_QUERY_THROW);
518 AddExtLst(pFS, xShapeProps);
519 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
520 pFS->singleElementNS(mnXmlNamespace, XML_cNvGrpSpPr);
521 WriteNonVisualProperties(xShape );
522 pFS->endElementNS(mnXmlNamespace, XML_nvGrpSpPr);
524 else
525 pFS->singleElementNS(mnXmlNamespace, XML_cNvGrpSpPr);
527 // visual properties
528 pFS->startElementNS(mnXmlNamespace, XML_grpSpPr);
529 WriteShapeTransformation(xShape, XML_a, false, false, true);
530 pFS->endElementNS(mnXmlNamespace, XML_grpSpPr);
532 uno::Reference<drawing::XShapes> xGroupShape(xShape, uno::UNO_QUERY_THROW);
533 uno::Reference<drawing::XShape> xParent = m_xParent;
534 m_xParent = xShape;
535 for (sal_Int32 i = 0; i < xGroupShape->getCount(); ++i)
537 uno::Reference<drawing::XShape> xChild(xGroupShape->getByIndex(i), uno::UNO_QUERY_THROW);
538 sal_Int32 nSavedNamespace = mnXmlNamespace;
540 uno::Reference<lang::XServiceInfo> xServiceInfo(xChild, uno::UNO_QUERY_THROW);
541 if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes)
543 // tdf#128820: WriteGraphicObjectShapePart calls WriteTextShape for non-empty simple
544 // text objects, which needs writing into wps::wsp element, so make sure to use wps
545 // namespace for those objects
546 if (xServiceInfo->supportsService(u"com.sun.star.drawing.GraphicObjectShape"_ustr)
547 && !IsNonEmptySimpleText(xChild))
548 mnXmlNamespace = XML_pic;
549 else
550 mnXmlNamespace = XML_wps;
552 WriteShape(xChild);
554 mnXmlNamespace = nSavedNamespace;
556 m_xParent = std::move(xParent);
558 pFS->endElementNS(mnXmlNamespace, nGroupShapeToken);
559 return *this;
561 namespace
564 constexpr frozen::set<std::u16string_view, 57> constDenySet(
566 u"block-arc",
567 u"rectangle",
568 u"ellipse",
569 u"ring",
570 u"can",
571 u"cube",
572 u"paper",
573 u"frame",
574 u"forbidden",
575 u"smiley",
576 u"sun",
577 u"flower",
578 u"bracket-pair",
579 u"brace-pair",
580 u"quad-bevel",
581 u"round-rectangular-callout",
582 u"rectangular-callout",
583 u"round-callout",
584 u"cloud-callout",
585 u"line-callout-1",
586 u"line-callout-2",
587 u"line-callout-3",
588 u"paper",
589 u"vertical-scroll",
590 u"horizontal-scroll",
591 u"mso-spt34",
592 u"mso-spt75",
593 u"mso-spt164",
594 u"mso-spt180",
595 u"flowchart-process",
596 u"flowchart-alternate-process",
597 u"flowchart-decision",
598 u"flowchart-data",
599 u"flowchart-predefined-process",
600 u"flowchart-internal-storage",
601 u"flowchart-document",
602 u"flowchart-multidocument",
603 u"flowchart-terminator",
604 u"flowchart-preparation",
605 u"flowchart-manual-input",
606 u"flowchart-manual-operation",
607 u"flowchart-connector",
608 u"flowchart-off-page-connector",
609 u"flowchart-card",
610 u"flowchart-punched-tape",
611 u"flowchart-summing-junction",
612 u"flowchart-or",
613 u"flowchart-collate",
614 u"flowchart-sort",
615 u"flowchart-extract",
616 u"flowchart-merge",
617 u"flowchart-stored-data",
618 u"flowchart-delay",
619 u"flowchart-sequential-access",
620 u"flowchart-magnetic-disk",
621 u"flowchart-direct-access-storage",
622 u"flowchart-display"
625 constexpr frozen::set<std::u16string_view, 5> constAllowSet(
627 u"heart",
628 u"puzzle",
629 u"col-60da8460",
630 u"col-502ad400",
631 u"sinusoid"
634 } // end anonymous namespace
636 static bool lcl_IsOnDenylist(OUString const & rShapeType)
638 return constDenySet.find(rShapeType) != constDenySet.end();
641 static bool lcl_IsOnAllowlist(OUString const & rShapeType)
643 return constAllowSet.find(rShapeType) != constAllowSet.end();
646 static bool lcl_GetHandlePosition( sal_Int32 &nValue, const EnhancedCustomShapeParameter &rParam, const Sequence< EnhancedCustomShapeAdjustmentValue > &rSeq)
648 bool bAdj = false;
649 if ( rParam.Value.getValueTypeClass() == TypeClass_DOUBLE )
651 double fValue(0.0);
652 if ( rParam.Value >>= fValue )
653 nValue = static_cast<sal_Int32>(fValue);
655 else
656 rParam.Value >>= nValue;
658 if ( rParam.Type == EnhancedCustomShapeParameterType::ADJUSTMENT)
660 bAdj = true;
661 sal_Int32 nIdx = nValue;
662 if ( nIdx < rSeq.getLength() )
664 if ( rSeq[ nIdx ] .Value.getValueTypeClass() == TypeClass_DOUBLE )
666 double fValue(0.0);
667 rSeq[ nIdx ].Value >>= fValue;
668 nValue = fValue;
671 else
673 rSeq[ nIdx ].Value >>= nValue;
677 return bAdj;
680 static void lcl_AnalyzeHandles( const uno::Sequence<beans::PropertyValues> & rHandles,
681 std::vector< std::pair< sal_Int32, sal_Int32> > &rHandlePositionList,
682 const Sequence< EnhancedCustomShapeAdjustmentValue > &rSeq)
684 for ( const Sequence< PropertyValue >& rPropSeq : rHandles )
686 static constexpr OUStringLiteral sPosition( u"Position" );
687 bool bPosition = false;
688 EnhancedCustomShapeParameterPair aPosition;
689 for ( const PropertyValue& rPropVal: rPropSeq )
691 if ( rPropVal.Name == sPosition )
693 if ( rPropVal.Value >>= aPosition )
694 bPosition = true;
697 if ( bPosition )
699 sal_Int32 nXPosition = 0;
700 sal_Int32 nYPosition = 0;
701 // For polar handles, nXPosition is radius and nYPosition is angle
702 lcl_GetHandlePosition( nXPosition, aPosition.First , rSeq );
703 lcl_GetHandlePosition( nYPosition, aPosition.Second, rSeq );
704 rHandlePositionList.emplace_back( nXPosition, nYPosition );
709 static void lcl_AppendAdjustmentValue( std::vector< std::pair< sal_Int32, sal_Int32> > &rAvList, sal_Int32 nAdjIdx, sal_Int32 nValue )
711 rAvList.emplace_back( nAdjIdx , nValue );
714 static sal_Int32 lcl_NormalizeAngle( sal_Int32 nAngle )
716 nAngle = nAngle % 360;
717 return nAngle < 0 ? ( nAngle + 360 ) : nAngle ;
720 static sal_Int32 lcl_CircleAngle2CustomShapeEllipseAngleOOX(const sal_Int32 nInternAngle, const sal_Int32 nWidth, const sal_Int32 nHeight)
722 if (nWidth != 0 || nHeight != 0)
724 double fAngle = basegfx::deg2rad<100>(nInternAngle); // intern 1/100 deg to rad
725 fAngle = atan2(nHeight * sin(fAngle), nWidth * cos(fAngle)); // circle to ellipse
726 fAngle = basegfx::rad2deg<60000>(fAngle); // rad to OOXML angle unit
727 sal_Int32 nAngle = basegfx::fround(fAngle); // normalize
728 nAngle = nAngle % 21600000;
729 return nAngle < 0 ? (nAngle + 21600000) : nAngle;
731 else // should be handled by caller, dummy value
732 return 0;
735 static OUString lcl_GetTarget(const css::uno::Reference<css::frame::XModel>& xModel,
736 std::u16string_view rURL)
738 Reference<drawing::XDrawPagesSupplier> xDPS(xModel, uno::UNO_QUERY_THROW);
739 Reference<drawing::XDrawPages> xDrawPages(xDPS->getDrawPages(), uno::UNO_SET_THROW);
740 sal_uInt32 nPageCount = xDrawPages->getCount();
741 OUString sTarget;
743 for (sal_uInt32 i = 0; i < nPageCount; ++i)
745 Reference<XDrawPage> xDrawPage;
746 xDrawPages->getByIndex(i) >>= xDrawPage;
747 Reference<container::XNamed> xNamed(xDrawPage, UNO_QUERY);
748 if (!xNamed)
749 continue;
750 OUString sSlideName = "#" + xNamed->getName();
751 if (rURL == sSlideName)
753 sTarget = "slide" + OUString::number(i + 1) + ".xml";
754 break;
758 return sTarget;
761 ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape )
763 SAL_INFO("oox.shape", "write custom shape");
764 Reference< XPropertySet > rXPropSet( xShape, UNO_QUERY );
765 // First check, if this is a Fontwork-shape. For DrawingML, such a shape is a
766 // TextBox shape with body property prstTxWarp.
767 if (IsFontworkShape(rXPropSet))
769 ShapeExport::WriteTextShape(xShape); // qualifier to prevent PowerPointShapeExport
770 return *this;
773 bool bHasGeometrySeq(false);
774 Sequence< PropertyValue > aGeometrySeq;
775 OUString sShapeType(u"non-primitive"_ustr); // default in ODF
776 if (GetProperty(rXPropSet, u"CustomShapeGeometry"_ustr))
778 SAL_INFO("oox.shape", "got custom shape geometry");
779 if (mAny >>= aGeometrySeq)
781 bHasGeometrySeq = true;
782 SAL_INFO("oox.shape", "got custom shape geometry sequence");
783 for (const PropertyValue& rProp : aGeometrySeq)
785 SAL_INFO("oox.shape", "geometry property: " << rProp.Name);
786 if (rProp.Name == "Type")
787 rProp.Value >>= sShapeType;
792 bool bPredefinedHandlesUsed = true;
793 bool bHasHandles = false;
795 ShapeFlag nMirrorFlags = ShapeFlag::NONE;
796 MSO_SPT eShapeType = EscherPropertyContainer::GetCustomShapeType( xShape, nMirrorFlags, sShapeType );
797 assert(dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(xShape)) && "Not a SdrObjCustomShape (!)");
798 SdrObjCustomShape& rSdrObjCustomShape(static_cast< SdrObjCustomShape& >(*SdrObject::getSdrObjectFromXShape(xShape)));
799 const bool bIsDefaultObject(
800 EscherPropertyContainer::IsDefaultObject(
801 rSdrObjCustomShape,
802 eShapeType));
803 OString sPresetShape = msfilter::util::GetOOXMLPresetGeometry(sShapeType);
804 SAL_INFO("oox.shape", "custom shape type: " << sShapeType << " ==> " << sPresetShape);
806 sal_Int32 nAdjustmentValuesIndex = -1;
807 awt::Rectangle aViewBox;
808 uno::Sequence<beans::PropertyValues> aHandles;
810 bool bFlipH = false;
811 bool bFlipV = false;
813 if (bHasGeometrySeq)
815 for (int i = 0; i < aGeometrySeq.getLength(); i++)
817 const PropertyValue& rProp = aGeometrySeq[ i ];
818 SAL_INFO("oox.shape", "geometry property: " << rProp.Name);
820 if ( rProp.Name == "MirroredX" )
821 rProp.Value >>= bFlipH;
823 if ( rProp.Name == "MirroredY" )
824 rProp.Value >>= bFlipV;
825 if ( rProp.Name == "AdjustmentValues" )
826 nAdjustmentValuesIndex = i;
827 else if ( rProp.Name == "Handles" )
829 rProp.Value >>= aHandles;
830 if ( aHandles.hasElements() )
831 bHasHandles = true;
832 if( !bIsDefaultObject )
833 bPredefinedHandlesUsed = false;
834 // TODO: update nAdjustmentsWhichNeedsToBeConverted here
836 else if ( rProp.Name == "ViewBox" )
837 rProp.Value >>= aViewBox;
841 FSHelperPtr pFS = GetFS();
842 // non visual shape properties
843 if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
845 // get InteropGrabBag to export attributes stored in the grabbag
846 uno::Sequence<beans::PropertyValue> aGrabBagProps;
847 rXPropSet->getPropertyValue(u"InteropGrabBag"_ustr) >>= aGrabBagProps;
849 bool bUseBackground = false;
850 if (GetProperty(rXPropSet, u"FillUseSlideBackground"_ustr))
851 mAny >>= bUseBackground;
852 if (bUseBackground)
853 mpFS->startElementNS(mnXmlNamespace, XML_sp, XML_useBgFill, "1");
854 else
856 rtl::Reference<sax_fastparser::FastAttributeList> pAttrListSp
857 = sax_fastparser::FastSerializerHelper::createAttrList();
859 for (auto const& it : aGrabBagProps)
861 // export macro attribute of <sp> element
862 if (it.Name == u"mso-sp-macro"_ustr)
864 OUString sMacro;
865 it.Value >>= sMacro;
867 if (!sMacro.isEmpty())
868 pAttrListSp->add(XML_macro, sMacro);
871 // export textlink attribute of <sp> element
872 if (it.Name == u"mso-sp-textlink"_ustr)
874 OUString sTextLink;
875 it.Value >>= sTextLink;
877 if (!sTextLink.isEmpty())
878 pAttrListSp->add(XML_textlink, sTextLink);
881 // export fLocksText attribute of <sp> element
882 if (it.Name == u"mso-sp-fLocksText"_ustr)
884 bool bFLocksText = true; // default="true"
885 it.Value >>= bFLocksText;
886 pAttrListSp->add(XML_fLocksText, ToPsz10(bFLocksText));
889 // export fPublished attribute of <sp> element
890 if (it.Name == u"mso-sp-fPublished"_ustr)
892 bool bFPublished = false;
893 it.Value >>= bFPublished;
894 pAttrListSp->add(XML_fPublished, ToPsz10(bFPublished));
898 // export <sp> element (with a namespace prefix)
899 mpFS->startElementNS(mnXmlNamespace, XML_sp, pAttrListSp);
902 bool isVisible = true ;
903 if( GetProperty(rXPropSet, u"Visible"_ustr))
905 mAny >>= isVisible;
907 pFS->startElementNS( mnXmlNamespace, XML_nvSpPr );
909 // export descr attribute of <cNvPr> element
910 OUString sDescr;
911 if (GetProperty(rXPropSet, u"Description"_ustr))
912 mAny >>= sDescr;
914 // export title attribute of <cNvPr> element
915 OUString sTitle;
916 if (GetProperty(rXPropSet, u"Title"_ustr))
917 mAny >>= sTitle;
919 // export <cNvPr> element
920 pFS->startElementNS(
921 mnXmlNamespace, XML_cNvPr, XML_id,
922 OString::number(GetShapeID(xShape) == -1 ? GetNewShapeID(xShape) : GetShapeID(xShape)),
923 XML_name, GetShapeName(xShape), XML_hidden, sax_fastparser::UseIf("1", !isVisible),
924 XML_descr, sax_fastparser::UseIf(sDescr, !sDescr.isEmpty()), XML_title,
925 sax_fastparser::UseIf(sTitle, !sTitle.isEmpty()));
927 rtl::Reference<sax_fastparser::FastAttributeList> pAttrListHlinkClick
928 = sax_fastparser::FastSerializerHelper::createAttrList();
930 for (auto const& it : aGrabBagProps)
932 // export tooltip attribute of <hlinkClick> element
933 if (it.Name == u"mso-hlinkClick-tooltip"_ustr)
935 OUString sTooltip;
936 it.Value >>= sTooltip;
938 if (!sTooltip.isEmpty())
939 pAttrListHlinkClick->add(XML_tooltip, sTooltip);
943 if( GetProperty(rXPropSet, u"URL"_ustr) )
945 OUString sURL;
946 mAny >>= sURL;
947 if( !sURL.isEmpty() )
949 OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
950 oox::getRelationship(Relationship::HYPERLINK),
951 mpURLTransformer->getTransformedString(sURL),
952 mpURLTransformer->isExternalURL(sURL));
954 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
955 // pAttrListHlinkClick->add(FSNS(XML_r, XML_id), sRelId);
959 // // export <hlinkClick> element
960 // mpFS->singleElementNS(XML_a, XML_hlinkClick, pAttrListHlinkClick);
962 OUString sBookmark;
963 if (GetProperty(rXPropSet, u"Bookmark"_ustr))
964 mAny >>= sBookmark;
966 if (GetProperty(rXPropSet, u"OnClick"_ustr))
968 OUString sPPAction;
969 presentation::ClickAction eClickAction = presentation::ClickAction_NONE;
970 mAny >>= eClickAction;
971 if (eClickAction != presentation::ClickAction_NONE)
973 switch (eClickAction)
975 case presentation::ClickAction_STOPPRESENTATION:
976 sPPAction = "ppaction://hlinkshowjump?jump=endshow";
977 break;
978 case presentation::ClickAction_NEXTPAGE:
979 sPPAction = "ppaction://hlinkshowjump?jump=nextslide";
980 break;
981 case presentation::ClickAction_LASTPAGE:
982 sPPAction = "ppaction://hlinkshowjump?jump=lastslide";
983 break;
984 case presentation::ClickAction_PREVPAGE:
985 sPPAction = "ppaction://hlinkshowjump?jump=previousslide";
986 break;
987 case presentation::ClickAction_FIRSTPAGE:
988 sPPAction = "ppaction://hlinkshowjump?jump=firstslide";
989 break;
990 case presentation::ClickAction_BOOKMARK:
991 sBookmark = "#" + sBookmark;
992 break;
993 default:
994 break;
997 if (!sPPAction.isEmpty())
998 pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), "", XML_action,
999 sPPAction);
1001 if (!sBookmark.isEmpty())
1003 bool bExtURL = URLTransformer().isExternalURL(sBookmark);
1004 sBookmark = bExtURL ? sBookmark : lcl_GetTarget(GetFB()->getModel(), sBookmark);
1006 OUString sRelId
1007 = mpFB->addRelation(mpFS->getOutputStream(),
1008 bExtURL ? oox::getRelationship(Relationship::HYPERLINK)
1009 : oox::getRelationship(Relationship::SLIDE),
1010 sBookmark, bExtURL);
1011 if (bExtURL)
1012 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
1013 else
1014 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
1015 XML_action, "ppaction://hlinksldjump");
1017 AddExtLst(pFS, rXPropSet);
1018 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1019 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
1020 WriteNonVisualProperties( xShape );
1021 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
1023 else
1025 pFS->startElementNS(mnXmlNamespace, XML_wsp);
1026 if (m_xParent.is())
1028 pFS->startElementNS(mnXmlNamespace, XML_cNvPr, XML_id,
1029 OString::number(GetShapeID(xShape) == -1 ? GetNewShapeID(xShape)
1030 : GetShapeID(xShape)),
1031 XML_name, GetShapeName(xShape));
1033 if (GetProperty(rXPropSet, u"Hyperlink"_ustr))
1035 OUString sURL;
1036 mAny >>= sURL;
1037 if (!sURL.isEmpty())
1039 OUString sRelId = mpFB->addRelation(
1040 mpFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
1041 mpURLTransformer->getTransformedString(sURL),
1042 mpURLTransformer->isExternalURL(sURL));
1044 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
1047 AddExtLst(pFS, rXPropSet);
1048 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1050 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
1053 // visual shape properties
1054 pFS->startElementNS(mnXmlNamespace, XML_spPr);
1056 // we export non-primitive shapes to custom geometry
1057 // we also export non-ooxml shapes which have handles/equations to custom geometry, because
1058 // we cannot convert ODF equations to DrawingML equations. TODO: see what binary DOC export filter does.
1059 // but our WritePolyPolygon()/WriteCustomGeometry() functions are incomplete, therefore we use a denylist
1060 // we use a allowlist for shapes where mapping to MSO preset shape is not optimal
1061 bool bCustGeom = true;
1062 bool bOnDenylist = false;
1063 if( sShapeType == "ooxml-non-primitive" )
1064 bCustGeom = true;
1065 else if( sShapeType.startsWith("ooxml") )
1066 bCustGeom = false;
1067 else if( lcl_IsOnAllowlist(sShapeType) )
1068 bCustGeom = true;
1069 else if( lcl_IsOnDenylist(sShapeType) )
1071 bCustGeom = false;
1072 bOnDenylist = true;
1075 bool bPresetWriteSuccessful = false;
1076 // Let the custom shapes what has name and preset information in OOXML, to be written
1077 // as preset ones with parameters. Try that with this converter class.
1078 if (!sShapeType.startsWith("ooxml") && sShapeType != "non-primitive" && !mbUserShapes
1079 && xShape->getShapeType() == "com.sun.star.drawing.CustomShape"
1080 && !lcl_IsOnAllowlist(sShapeType))
1082 DMLPresetShapeExporter aCustomShapeConverter(this, xShape);
1083 bPresetWriteSuccessful = aCustomShapeConverter.WriteShape();
1085 // If preset writing has problems try to write the shape as it done before
1086 if (bPresetWriteSuccessful)
1087 ;// Already written do nothing.
1088 else if (bCustGeom)
1090 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
1091 bool bSuccess = WriteCustomGeometry(xShape, rSdrObjCustomShape);
1092 // In case of Writer, the parent element is <wps:spPr>, and there the <a:custGeom> element
1093 // is not optional.
1094 if (!bSuccess && GetDocumentType() == DOCUMENT_DOCX)
1096 WriteEmptyCustomGeometry();
1099 else if (bOnDenylist && bHasHandles && nAdjustmentValuesIndex !=-1 && !sShapeType.startsWith("mso-spt"))
1101 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
1102 Sequence< EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
1103 std::vector< std::pair< sal_Int32, sal_Int32> > aHandlePositionList;
1104 std::vector< std::pair< sal_Int32, sal_Int32> > aAvList;
1105 aGeometrySeq[ nAdjustmentValuesIndex ].Value >>= aAdjustmentSeq ;
1107 lcl_AnalyzeHandles( aHandles, aHandlePositionList, aAdjustmentSeq );
1109 sal_Int32 nXPosition = 0;
1110 sal_Int32 nYPosition = 0;
1111 if ( !aHandlePositionList.empty() )
1113 nXPosition = aHandlePositionList[0].first ;
1114 nYPosition = aHandlePositionList[0].second ;
1116 switch( eShapeType )
1118 case mso_sptBorderCallout1:
1120 sal_Int32 adj3 = double(nYPosition)/aViewBox.Height *100000;
1121 sal_Int32 adj4 = double(nXPosition)/aViewBox.Width *100000;
1122 lcl_AppendAdjustmentValue( aAvList, 1, 18750 );
1123 lcl_AppendAdjustmentValue( aAvList, 2, -8333 );
1124 lcl_AppendAdjustmentValue( aAvList, 3, adj3 );
1125 lcl_AppendAdjustmentValue( aAvList, 4, adj4 );
1126 break;
1128 case mso_sptBorderCallout2:
1130 sal_Int32 adj5 = double(nYPosition)/aViewBox.Height *100000;
1131 sal_Int32 adj6 = double(nXPosition)/aViewBox.Width *100000;
1132 sal_Int32 adj3 = 18750;
1133 sal_Int32 adj4 = -16667;
1134 lcl_AppendAdjustmentValue( aAvList, 1, 18750 );
1135 lcl_AppendAdjustmentValue( aAvList, 2, -8333 );
1136 if ( aHandlePositionList.size() > 1 )
1138 nXPosition = aHandlePositionList[1].first ;
1139 nYPosition = aHandlePositionList[1].second ;
1140 adj3 = double(nYPosition)/aViewBox.Height *100000;
1141 adj4 = double(nXPosition)/aViewBox.Width *100000;
1143 lcl_AppendAdjustmentValue( aAvList, 3, adj3 );
1144 lcl_AppendAdjustmentValue( aAvList, 4, adj4 );
1145 lcl_AppendAdjustmentValue( aAvList, 5, adj5 );
1146 lcl_AppendAdjustmentValue( aAvList, 6, adj6 );
1147 break;
1149 case mso_sptWedgeRectCallout:
1150 case mso_sptWedgeRRectCallout:
1151 case mso_sptWedgeEllipseCallout:
1152 case mso_sptCloudCallout:
1154 sal_Int32 adj1 = (double(nXPosition)/aViewBox.Width -0.5) *100000;
1155 sal_Int32 adj2 = (double(nYPosition)/aViewBox.Height -0.5) *100000;
1156 lcl_AppendAdjustmentValue( aAvList, 1, adj1 );
1157 lcl_AppendAdjustmentValue( aAvList, 2, adj2 );
1158 if ( eShapeType == mso_sptWedgeRRectCallout)
1160 lcl_AppendAdjustmentValue( aAvList, 3, 16667);
1163 break;
1165 case mso_sptFoldedCorner:
1167 sal_Int32 adj = double( aViewBox.Width - nXPosition) / std::min( aViewBox.Width,aViewBox.Height ) * 100000;
1168 lcl_AppendAdjustmentValue( aAvList, 0, adj );
1169 break;
1171 case mso_sptDonut:
1172 case mso_sptSun:
1173 case mso_sptMoon:
1174 case mso_sptNoSmoking:
1175 case mso_sptHorizontalScroll:
1176 case mso_sptBevel:
1177 case mso_sptBracketPair:
1179 sal_Int32 adj = double( nXPosition )/aViewBox.Width*100000 ;
1180 lcl_AppendAdjustmentValue( aAvList, 0, adj );
1181 break;
1183 case mso_sptCan:
1184 case mso_sptCube:
1185 case mso_sptBracePair:
1186 case mso_sptVerticalScroll:
1188 sal_Int32 adj = double( nYPosition )/aViewBox.Height *100000 ;
1189 lcl_AppendAdjustmentValue( aAvList, 0, adj );
1190 break;
1192 case mso_sptSmileyFace:
1194 sal_Int32 adj = double( nYPosition )/aViewBox.Height *100000 - 76458.0;
1195 lcl_AppendAdjustmentValue( aAvList, 0, adj );
1196 break;
1198 case mso_sptBlockArc:
1200 sal_Int32 nRadius = 50000 * ( 1 - double(nXPosition) / 10800);
1201 sal_Int32 nAngleStart = lcl_NormalizeAngle( nYPosition );
1202 sal_Int32 nAngleEnd = lcl_NormalizeAngle( 180 - nAngleStart );
1203 lcl_AppendAdjustmentValue( aAvList, 1, 21600000 / 360 * nAngleStart );
1204 lcl_AppendAdjustmentValue( aAvList, 2, 21600000 / 360 * nAngleEnd );
1205 lcl_AppendAdjustmentValue( aAvList, 3, nRadius );
1206 break;
1208 // case mso_sptNil:
1209 // case mso_sptBentConnector3:
1210 // case mso_sptBorderCallout3:
1211 default:
1213 if ( sPresetShape == "frame" )
1215 sal_Int32 adj1 = double( nYPosition )/aViewBox.Height *100000 ;
1216 lcl_AppendAdjustmentValue( aAvList, 1, adj1 );
1218 break;
1221 WritePresetShape( sPresetShape , aAvList );
1223 else // preset geometry
1225 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
1226 if( nAdjustmentValuesIndex != -1 )
1228 WritePresetShape( sPresetShape, eShapeType, bPredefinedHandlesUsed,
1229 aGeometrySeq[ nAdjustmentValuesIndex ] );
1231 else
1232 WritePresetShape( sPresetShape );
1234 if( rXPropSet.is() )
1236 WriteFill(rXPropSet, xShape->getSize());
1237 WriteOutline( rXPropSet );
1238 WriteShapeEffects( rXPropSet );
1240 bool bHas3DEffectinShape = false;
1241 uno::Sequence<beans::PropertyValue> grabBag;
1242 rXPropSet->getPropertyValue(u"InteropGrabBag"_ustr) >>= grabBag;
1244 for (auto const& it : grabBag)
1245 if (it.Name == "3DEffectProperties")
1246 bHas3DEffectinShape = true;
1248 if( bHas3DEffectinShape)
1249 Write3DEffects( rXPropSet, /*bIsText=*/false );
1252 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1254 pFS->startElementNS(mnXmlNamespace, XML_style);
1255 WriteShapeStyle( rXPropSet );
1256 pFS->endElementNS( mnXmlNamespace, XML_style );
1258 // write text
1259 WriteTextBox( xShape, mnXmlNamespace );
1261 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );
1263 return *this;
1266 ShapeExport& ShapeExport::WriteEllipseShape( const Reference< XShape >& xShape )
1268 SAL_INFO("oox.shape", "write ellipse shape");
1270 FSHelperPtr pFS = GetFS();
1272 pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp));
1274 // TODO: connector ?
1276 Reference<XPropertySet> const xProps(xShape, UNO_QUERY);
1277 // non visual shape properties
1278 if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
1280 pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
1281 pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
1282 XML_id, OString::number(GetNewShapeID(xShape)),
1283 XML_name, GetShapeName(xShape));
1284 AddExtLst(pFS, xProps);
1285 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1286 pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr );
1287 WriteNonVisualProperties( xShape );
1288 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
1290 else
1291 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
1293 CircleKind eCircleKind(CircleKind_FULL);
1294 if (xProps.is())
1295 xProps->getPropertyValue(u"CircleKind"_ustr ) >>= eCircleKind;
1297 // visual shape properties
1298 pFS->startElementNS( mnXmlNamespace, XML_spPr );
1299 WriteShapeTransformation( xShape, XML_a );
1301 if (CircleKind_FULL == eCircleKind)
1302 WritePresetShape("ellipse"_ostr);
1303 else
1305 sal_Int32 nStartAngleIntern(9000);
1306 sal_Int32 nEndAngleIntern(0);
1307 if (xProps.is())
1309 xProps->getPropertyValue(u"CircleStartAngle"_ustr ) >>= nStartAngleIntern;
1310 xProps->getPropertyValue(u"CircleEndAngle"_ustr) >>= nEndAngleIntern;
1312 std::vector< std::pair<sal_Int32,sal_Int32>> aAvList;
1313 awt::Size aSize = xShape->getSize();
1314 if (aSize.Width != 0 || aSize.Height != 0)
1316 // Our arc has 90° up, OOXML has 90° down, so mirror it.
1317 // API angles are 1/100 degree.
1318 sal_Int32 nStartAngleOOXML(lcl_CircleAngle2CustomShapeEllipseAngleOOX(36000 - nEndAngleIntern, aSize.Width, aSize.Height));
1319 sal_Int32 nEndAngleOOXML(lcl_CircleAngle2CustomShapeEllipseAngleOOX(36000 - nStartAngleIntern, aSize.Width, aSize.Height));
1320 lcl_AppendAdjustmentValue( aAvList, 1, nStartAngleOOXML);
1321 lcl_AppendAdjustmentValue( aAvList, 2, nEndAngleOOXML);
1323 switch (eCircleKind)
1325 case CircleKind_ARC :
1326 WritePresetShape("arc"_ostr, aAvList);
1327 break;
1328 case CircleKind_SECTION :
1329 WritePresetShape("pie"_ostr, aAvList);
1330 break;
1331 case CircleKind_CUT :
1332 WritePresetShape("chord"_ostr, aAvList);
1333 break;
1334 default :
1335 WritePresetShape("ellipse"_ostr);
1338 if( xProps.is() )
1340 if (CircleKind_ARC == eCircleKind)
1342 // An arc in ODF is never filled, even if a fill style other than
1343 // "none" is set. OOXML arc can be filled, so set fill explicit to
1344 // NONE, otherwise some hidden or inherited filling is shown.
1345 FillStyle eFillStyle(FillStyle_NONE);
1346 uno::Any aNewValue;
1347 aNewValue <<= eFillStyle;
1348 xProps->setPropertyValue(u"FillStyle"_ustr, aNewValue);
1350 WriteFill( xProps );
1351 WriteOutline( xProps );
1353 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1355 // write text
1356 WriteTextBox( xShape, mnXmlNamespace );
1358 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );
1360 return *this;
1363 ShapeExport& ShapeExport::WriteGraphicObjectShape( const Reference< XShape >& xShape )
1365 WriteGraphicObjectShapePart( xShape );
1367 return *this;
1370 void ShapeExport::WriteGraphicObjectShapePart( const Reference< XShape >& xShape, const Graphic* pGraphic )
1372 SAL_INFO("oox.shape", "write graphic object shape");
1374 if (IsNonEmptySimpleText(xShape))
1376 SAL_INFO("oox.shape", "graphicObject: wrote only text");
1378 WriteTextShape(xShape);
1380 return;
1383 SAL_INFO("oox.shape", "graphicObject without text");
1385 uno::Reference<graphic::XGraphic> xGraphic;
1386 OUString sMediaURL;
1388 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1390 if (pGraphic)
1392 xGraphic.set(pGraphic->GetXGraphic());
1394 else if (xShapeProps.is() && xShapeProps->getPropertySetInfo()->hasPropertyByName(u"Graphic"_ustr))
1396 xShapeProps->getPropertyValue(u"Graphic"_ustr) >>= xGraphic;
1399 // tdf#155903 Only for PPTX, Microsoft does not support this feature in Word and Excel.
1400 bool bHasMediaURL = GetDocumentType() == DOCUMENT_PPTX && xShapeProps.is()
1401 && xShapeProps->getPropertySetInfo()->hasPropertyByName(u"MediaURL"_ustr)
1402 && (xShapeProps->getPropertyValue(u"MediaURL"_ustr) >>= sMediaURL);
1404 if (!xGraphic.is() && !bHasMediaURL)
1406 SAL_INFO("oox.shape", "no graphic or media URL found");
1407 return;
1410 FSHelperPtr pFS = GetFS();
1411 XmlFilterBase* pFB = GetFB();
1413 if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
1414 pFS->startElementNS(mnXmlNamespace, XML_pic);
1415 else
1416 pFS->startElementNS(mnXmlNamespace, XML_pic,
1417 FSNS(XML_xmlns, XML_pic), pFB->getNamespaceURL(OOX_NS(dmlPicture)));
1419 pFS->startElementNS(mnXmlNamespace, XML_nvPicPr);
1421 presentation::ClickAction eClickAction = presentation::ClickAction_NONE;
1422 OUString sDescr, sURL, sBookmark, sPPAction;
1423 bool bHaveDesc;
1425 if ( ( bHaveDesc = GetProperty( xShapeProps, u"Description"_ustr ) ) )
1426 mAny >>= sDescr;
1427 if ( GetProperty( xShapeProps, u"URL"_ustr ) )
1428 mAny >>= sURL;
1429 if (GetProperty(xShapeProps, u"Bookmark"_ustr))
1430 mAny >>= sBookmark;
1431 if (GetProperty(xShapeProps, u"OnClick"_ustr))
1432 mAny >>= eClickAction;
1434 pFS->startElementNS( mnXmlNamespace, XML_cNvPr,
1435 XML_id, OString::number(GetNewShapeID(xShape)),
1436 XML_name, GetShapeName(xShape),
1437 XML_descr, sax_fastparser::UseIf(sDescr, bHaveDesc));
1439 if (eClickAction != presentation::ClickAction_NONE)
1441 switch (eClickAction)
1443 case presentation::ClickAction_STOPPRESENTATION:
1444 sPPAction = "ppaction://hlinkshowjump?jump=endshow";
1445 break;
1446 case presentation::ClickAction_NEXTPAGE:
1447 sPPAction = "ppaction://hlinkshowjump?jump=nextslide";
1448 break;
1449 case presentation::ClickAction_LASTPAGE:
1450 sPPAction = "ppaction://hlinkshowjump?jump=lastslide";
1451 break;
1452 case presentation::ClickAction_PREVPAGE:
1453 sPPAction = "ppaction://hlinkshowjump?jump=previousslide";
1454 break;
1455 case presentation::ClickAction_FIRSTPAGE:
1456 sPPAction = "ppaction://hlinkshowjump?jump=firstslide";
1457 break;
1458 case presentation::ClickAction_BOOKMARK:
1459 sBookmark = "#" + sBookmark;
1460 break;
1461 default:
1462 break;
1466 // OOXTODO: //cNvPr children: XML_extLst, XML_hlinkHover
1467 if (bHasMediaURL || !sPPAction.isEmpty())
1468 pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), "", XML_action,
1469 bHasMediaURL ? u"ppaction://media"_ustr : sPPAction);
1470 if( !sURL.isEmpty() )
1472 OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
1473 oox::getRelationship(Relationship::HYPERLINK),
1474 mpURLTransformer->getTransformedString(sURL),
1475 mpURLTransformer->isExternalURL(sURL));
1477 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
1480 if (!sBookmark.isEmpty())
1482 bool bExtURL = URLTransformer().isExternalURL(sBookmark);
1483 sBookmark = bExtURL ? sBookmark : lcl_GetTarget(GetFB()->getModel(), sBookmark);
1485 OUString sRelId = mpFB->addRelation(mpFS->getOutputStream(),
1486 bExtURL ? oox::getRelationship(Relationship::HYPERLINK)
1487 : oox::getRelationship(Relationship::SLIDE),
1488 sBookmark, bExtURL);
1490 if (bExtURL)
1491 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
1492 else
1493 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId, XML_action,
1494 "ppaction://hlinksldjump");
1496 AddExtLst(pFS, xShapeProps);
1497 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1499 pFS->singleElementNS(mnXmlNamespace, XML_cNvPicPr
1500 // OOXTODO: XML_preferRelativeSize
1502 if (bHasMediaURL)
1503 WriteMediaNonVisualProperties(xShape);
1504 else
1505 WriteNonVisualProperties(xShape);
1507 pFS->endElementNS( mnXmlNamespace, XML_nvPicPr );
1509 pFS->startElementNS(mnXmlNamespace, XML_blipFill);
1511 if (xGraphic.is())
1513 WriteXGraphicBlip(xShapeProps, xGraphic, mbUserShapes);
1515 else if (bHasMediaURL)
1517 Reference<graphic::XGraphic> xFallbackGraphic;
1518 if (xShapeProps->getPropertySetInfo()->hasPropertyByName(u"FallbackGraphic"_ustr))
1519 xShapeProps->getPropertyValue(u"FallbackGraphic"_ustr) >>= xFallbackGraphic;
1521 WriteXGraphicBlip(xShapeProps, xFallbackGraphic, mbUserShapes);
1524 if (xGraphic.is())
1526 WriteSrcRectXGraphic(xShapeProps, xGraphic);
1529 // now we stretch always when we get pGraphic (when changing that
1530 // behavior, test n#780830 for regression, where the OLE sheet might get tiled
1531 bool bStretch = false;
1532 if( !pGraphic && GetProperty( xShapeProps, u"FillBitmapStretch"_ustr ) )
1533 mAny >>= bStretch;
1535 if ( pGraphic || bStretch )
1536 pFS->singleElementNS(XML_a, XML_stretch);
1538 if (bHasMediaURL)
1540 // Graphic of media shapes is always stretched.
1541 pFS->startElementNS(XML_a, XML_stretch);
1542 pFS->singleElementNS(XML_a, XML_fillRect);
1543 pFS->endElementNS(XML_a, XML_stretch);
1546 pFS->endElementNS( mnXmlNamespace, XML_blipFill );
1548 // visual shape properties
1549 pFS->startElementNS(mnXmlNamespace, XML_spPr);
1550 bool bFlipH = false;
1551 if( xShapeProps->getPropertySetInfo()->hasPropertyByName(u"IsMirrored"_ustr) )
1553 xShapeProps->getPropertyValue(u"IsMirrored"_ustr) >>= bFlipH;
1555 WriteShapeTransformation( xShape, XML_a, bFlipH, false, false, false, true );
1556 WritePresetShape( "rect"_ostr );
1557 WriteFill(xShapeProps);
1558 // graphic object can come with the frame (bnc#654525)
1559 WriteOutline( xShapeProps );
1561 WriteShapeEffects( xShapeProps );
1562 Write3DEffects( xShapeProps, /*bIsText=*/false );
1564 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1566 pFS->endElementNS( mnXmlNamespace, XML_pic );
1569 static void lcl_Rotate(sal_Int32 nAngle, Point center, awt::Point& pt)
1571 sal_Int16 nCos, nSin;
1572 switch (nAngle)
1574 case 90:
1575 nCos = 0;
1576 nSin = 1;
1577 break;
1578 case 180:
1579 nCos = -1;
1580 nSin = 0;
1581 break;
1582 case 270:
1583 nCos = 0;
1584 nSin = -1;
1585 break;
1586 default:
1587 return;
1589 sal_Int32 x = pt.X - center.X();
1590 sal_Int32 y = pt.Y - center.Y();
1591 pt.X = center.X() + x * nCos - y * nSin;
1592 pt.Y = center.Y() + y * nCos + x * nSin;
1595 static void lcl_FlipHFlipV(const tools::Polygon& rPoly, sal_Int32 nAngle, bool& rFlipH, bool& rFlipV)
1597 Point aStart = rPoly[0];
1598 Point aEnd = rPoly[rPoly.GetSize() - 1];
1600 if (aStart.X() > aEnd.X() && aStart.Y() > aEnd.Y())
1602 if (nAngle)
1604 if (nAngle == 90)
1605 rFlipH = true;
1606 if (nAngle == 270)
1607 rFlipV = true;
1609 else // 0°
1611 rFlipH = true;
1612 rFlipV = true;
1616 if (aStart.X() < aEnd.X() && aStart.Y() < aEnd.Y())
1618 if (nAngle)
1620 if (nAngle != 270)
1622 rFlipH = true;
1623 rFlipV = true;
1625 else
1626 rFlipH = true;
1630 if (aStart.Y() < aEnd.Y() && aStart.X() > aEnd.X())
1632 if (nAngle)
1634 if (nAngle == 180)
1635 rFlipV = true;
1636 if (nAngle == 270)
1638 rFlipV = true;
1639 rFlipH = true;
1642 else // 0°
1644 rFlipH = true;
1648 if (aStart.Y() > aEnd.Y() && aStart.X() < aEnd.X())
1650 if (nAngle)
1652 if (nAngle == 90)
1654 rFlipH = true;
1655 rFlipV = true;
1657 if (nAngle == 180)
1658 rFlipH = true;
1660 else // 0°
1661 rFlipV = true;
1665 static sal_Int32 lcl_GetAngle(const tools::Polygon& rPoly)
1667 sal_Int32 nAngle;
1668 Point aStartPoint = rPoly[0];
1669 Point aEndPoint = rPoly[rPoly.GetSize() - 1];
1670 if (aStartPoint.X() == rPoly[1].X())
1672 if ((aStartPoint.X() < aEndPoint.X() && aStartPoint.Y() > aEndPoint.Y())
1673 || (aStartPoint.X() > aEndPoint.X() && aStartPoint.Y() < aEndPoint.Y()))
1675 nAngle = 90;
1677 else
1678 nAngle = 270;
1680 else
1682 if (aStartPoint.X() > rPoly[1].X())
1683 nAngle = 180;
1684 else
1685 nAngle = 0;
1688 return nAngle;
1691 // Adjust value decide the position, where the connector should turn.
1692 static void lcl_GetConnectorAdjustValue(const Reference<XShape>& xShape, const tools::Polygon& rPoly,
1693 ConnectorType eConnectorType,
1694 std::vector<std::pair<sal_Int32, sal_Int32>>& rAvList)
1696 Reference<XPropertySet> xShapeProps(xShape, UNO_QUERY);
1697 bool bIsOOXMLCurve(false);
1698 xShapeProps->getPropertyValue(u"EdgeOOXMLCurve"_ustr) >>= bIsOOXMLCurve;
1699 sal_Int32 nAdjCount = 0;
1700 if (eConnectorType == ConnectorType_CURVE)
1702 if (bIsOOXMLCurve)
1704 nAdjCount = (rPoly.GetSize() - 4) / 3;
1706 else if (rPoly.GetSize() == 4)
1708 if ((rPoly[0].X() == rPoly[1].X() && rPoly[2].X() == rPoly[3].X())
1709 || (rPoly[0].Y() == rPoly[1].Y() && rPoly[2].Y() == rPoly[3].Y()))
1711 nAdjCount = 1; // curvedConnector3, control vectors parallel
1713 else
1714 nAdjCount = 0; // curvedConnector2, control vectors orthogonal
1716 else if (rPoly.GetSize() > 4)
1718 if ((rPoly[2].X() == rPoly[3].X() && rPoly[3].X() == rPoly[4].X())
1719 || (rPoly[2].Y() == rPoly[3].Y() && rPoly[3].Y() == rPoly[4].Y()))
1721 nAdjCount = 3; // curvedConnector5
1723 else
1724 nAdjCount = 2; // curvedConnector4
1727 else
1729 switch (rPoly.GetSize())
1731 case 3:
1732 nAdjCount = 0; // bentConnector2
1733 break;
1734 case 4:
1735 nAdjCount = 1; // bentConnector3
1736 break;
1737 case 5:
1738 nAdjCount = 2; // bentConnector4
1739 break;
1740 case 6:
1741 nAdjCount = 3; // bentConnector5
1742 break;
1746 if (nAdjCount)
1748 sal_Int32 nAdjustValue;
1749 Point aStart = rPoly[0];
1750 Point aEnd = rPoly[rPoly.GetSize() - 1];
1752 for (sal_Int32 i = 1; i <= nAdjCount; ++i)
1754 Point aPt = rPoly[i];
1756 if (aEnd.Y() == aStart.Y())
1757 aEnd.setY(aStart.Y() + 1);
1758 if (aEnd.X() == aStart.X())
1759 aEnd.setX(aStart.X() + 1);
1761 bool bVertical = rPoly[1].X() - aStart.X() != 0 ? true : false;
1762 // vertical and horizon alternate
1763 if (i % 2 == 1)
1764 bVertical = !bVertical;
1766 if (eConnectorType == ConnectorType_CURVE)
1768 if (bIsOOXMLCurve)
1770 aPt = rPoly[3 * i];
1772 else
1774 awt::Size aSize = xShape->getSize();
1775 awt::Point aShapePosition = xShape->getPosition();
1776 tools::Rectangle aBoundRect = rPoly.GetBoundRect();
1778 if (bVertical)
1780 if ((aBoundRect.GetSize().Height() - aSize.Height) == 1)
1781 aPt.setY(rPoly[i + 1].Y());
1782 else if (aStart.Y() > aPt.Y())
1783 aPt.setY(aShapePosition.Y);
1784 else
1785 aPt.setY(aShapePosition.Y + aSize.Height);
1787 else
1789 if ((aBoundRect.GetSize().Width() - aSize.Width) == 1)
1790 aPt.setX(rPoly[i + 1].X());
1791 else if (aStart.X() > aPt.X())
1792 aPt.setX(aShapePosition.X);
1793 else
1794 aPt.setX(aShapePosition.X + aSize.Width);
1799 if (bVertical)
1800 nAdjustValue = ((aPt.Y() - aStart.Y()) * 100000) / (aEnd.Y() - aStart.Y());
1801 else
1802 nAdjustValue = ((aPt.X() - aStart.X()) * 100000) / (aEnd.X() - aStart.X());
1804 rAvList.emplace_back(i, nAdjustValue);
1809 static sal_Int32 lcl_GetGluePointId(const Reference<XShape>& xShape, sal_Int32 nGluePointId)
1811 if (nGluePointId > 3)
1812 return nGluePointId - 4;
1813 else
1815 bool bFlipH = false;
1816 bool bFlipV = false;
1817 Reference<XPropertySet> xShapeProps(xShape, UNO_QUERY);
1818 if (xShapeProps.is() && xShapeProps->getPropertySetInfo()
1819 && xShapeProps->getPropertySetInfo()->hasPropertyByName(u"CustomShapeGeometry"_ustr))
1821 Sequence<PropertyValue> aGeometrySeq;
1822 xShapeProps->getPropertyValue(u"CustomShapeGeometry"_ustr) >>= aGeometrySeq;
1823 for (int i = 0; i < aGeometrySeq.getLength(); i++)
1825 const PropertyValue& rProp = aGeometrySeq[i];
1826 if (rProp.Name == "MirroredX")
1827 rProp.Value >>= bFlipH;
1829 if (rProp.Name == "MirroredY")
1830 rProp.Value >>= bFlipV;
1834 if ((!bFlipH && !bFlipV) || (bFlipH && bFlipV))
1836 // change id of the bounding box (1 <-> 3)
1837 if (nGluePointId == 1)
1838 nGluePointId = 3; // Right
1839 else if (nGluePointId == 3)
1840 nGluePointId = 1; // Left
1844 return nGluePointId;
1847 ShapeExport& ShapeExport::WriteConnectorShape( const Reference< XShape >& xShape )
1849 bool bFlipH = false;
1850 bool bFlipV = false;
1851 sal_Int32 nAngle = 0;
1852 sal_Int32 nStartGlueId = 0;
1853 sal_Int32 nEndGlueId = 0;
1855 SAL_INFO("oox.shape", "write connector shape");
1857 FSHelperPtr pFS = GetFS();
1859 OUString sGeometry;
1860 std::vector<std::pair<sal_Int32, sal_Int32>> aAdjustValueList;
1861 Reference< XPropertySet > rXPropSet( xShape, UNO_QUERY );
1862 Reference< XPropertyState > rXPropState( xShape, UNO_QUERY );
1863 awt::Point aStartPoint, aEndPoint;
1864 Reference< XShape > rXShapeA;
1865 Reference< XShape > rXShapeB;
1866 PropertyState eState;
1867 ConnectorType eConnectorType = ConnectorType_STANDARD;
1868 if (GetProperty(rXPropSet, u"EdgeKind"_ustr))
1869 mAny >>= eConnectorType;
1871 switch( eConnectorType ) {
1872 case ConnectorType_CURVE:
1873 sGeometry = "curvedConnector";
1874 break;
1875 case ConnectorType_LINES:
1876 case ConnectorType_STANDARD:
1877 sGeometry = "bentConnector";
1878 break;
1879 default:
1880 case ConnectorType_LINE:
1881 sGeometry = "straightConnector1";
1882 break;
1885 if (GetPropertyAndState( rXPropSet, rXPropState, u"EdgeStartPoint"_ustr, eState ) && eState == beans::PropertyState_DIRECT_VALUE )
1887 mAny >>= aStartPoint;
1888 if (GetPropertyAndState( rXPropSet, rXPropState, u"EdgeEndPoint"_ustr, eState ) && eState == beans::PropertyState_DIRECT_VALUE )
1889 mAny >>= aEndPoint;
1891 if (GetProperty(rXPropSet, u"EdgeStartConnection"_ustr))
1892 mAny >>= rXShapeA;
1893 if (GetProperty(rXPropSet, u"EdgeEndConnection"_ustr))
1894 mAny >>= rXShapeB;
1896 if (GetProperty(rXPropSet, u"StartGluePointIndex"_ustr))
1897 mAny >>= nStartGlueId;
1898 if (nStartGlueId != -1)
1899 nStartGlueId = lcl_GetGluePointId(rXShapeA, nStartGlueId);
1901 if (GetProperty(rXPropSet, u"EndGluePointIndex"_ustr))
1902 mAny >>= nEndGlueId;
1903 if (nEndGlueId != -1)
1904 nEndGlueId = lcl_GetGluePointId(rXShapeB, nEndGlueId);
1906 // Position is relative to group in Word, but relative to anchor of group in API.
1907 if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes && m_xParent.is())
1909 awt::Point aParentPos = m_xParent->getPosition();
1910 aStartPoint.X -= aParentPos.X;
1911 aStartPoint.Y -= aParentPos.Y;
1912 aEndPoint.X -= aParentPos.X;
1913 aEndPoint.Y -= aParentPos.Y;
1915 EscherConnectorListEntry aConnectorEntry( xShape, aStartPoint, rXShapeA, aEndPoint, rXShapeB );
1917 if (eConnectorType != ConnectorType_LINE)
1919 tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon(xShape);
1920 if (aPolyPolygon.Count() > 0)
1922 const tools::Polygon& aPoly = aPolyPolygon.GetObject(0);
1923 lcl_GetConnectorAdjustValue(xShape, aPoly, eConnectorType, aAdjustValueList);
1924 nAngle = lcl_GetAngle(aPoly);
1925 lcl_FlipHFlipV(aPoly, nAngle, bFlipH, bFlipV);
1926 if (nAngle)
1928 Point center((aEndPoint.X + aStartPoint.X) / 2, (aEndPoint.Y + aStartPoint.Y) / 2);
1929 lcl_Rotate(nAngle, center, aStartPoint);
1930 lcl_Rotate(nAngle, center, aEndPoint);
1931 nAngle *= 60000;
1933 sGeometry = sGeometry + OUString::number(aAdjustValueList.size() + 2);
1937 tools::Rectangle aRect( Point( aStartPoint.X, aStartPoint.Y ), Point( aEndPoint.X, aEndPoint.Y ) );
1938 if( aRect.getOpenWidth() < 0 ) {
1939 aRect.SetLeft(aEndPoint.X);
1940 aRect.setWidth( aStartPoint.X - aEndPoint.X );
1941 if (eConnectorType == ConnectorType_LINE)
1942 bFlipH = true;
1945 if( aRect.getOpenHeight() < 0 ) {
1946 aRect.SetTop(aEndPoint.Y);
1947 aRect.setHeight( aStartPoint.Y - aEndPoint.Y );
1948 if (eConnectorType == ConnectorType_LINE)
1949 bFlipV = true;
1952 // tdf#99810 connector shape (cxnSp) is not valid with namespace 'wps'
1953 const auto nShapeNode = (mnXmlNamespace == XML_wps ? XML_wsp : XML_cxnSp);
1954 pFS->startElementNS(mnXmlNamespace, nShapeNode);
1956 if (mnXmlNamespace == XML_wps)
1958 // non visual connector shape drawing properties
1959 pFS->singleElementNS(mnXmlNamespace, XML_cNvCnPr);
1961 else
1963 // non visual shape properties
1964 pFS->startElementNS(mnXmlNamespace, XML_nvCxnSpPr);
1965 pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
1966 XML_id, OString::number(GetNewShapeID(xShape)),
1967 XML_name, GetShapeName(xShape));
1968 AddExtLst(pFS, rXPropSet);
1969 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1970 // non visual connector shape drawing properties
1971 pFS->startElementNS(mnXmlNamespace, XML_cNvCxnSpPr);
1973 if (GetShapeID(rXShapeA) == -1)
1974 GetNewShapeID(rXShapeA);
1975 if (GetShapeID(rXShapeB) == -1)
1976 GetNewShapeID(rXShapeB);
1977 WriteConnectorConnections(nStartGlueId, nEndGlueId, GetShapeID(rXShapeA), GetShapeID(rXShapeB));
1978 pFS->endElementNS(mnXmlNamespace, XML_cNvCxnSpPr);
1979 if (GetDocumentType() == DOCUMENT_PPTX)
1980 pFS->singleElementNS(mnXmlNamespace, XML_nvPr);
1981 pFS->endElementNS(mnXmlNamespace, XML_nvCxnSpPr);
1984 // visual shape properties
1985 pFS->startElementNS(mnXmlNamespace, XML_spPr);
1986 WriteTransformation( xShape, aRect, XML_a, bFlipH, bFlipV, nAngle );
1987 // TODO: write adjustments (ppt export doesn't work well there either)
1988 WritePresetShape( sGeometry.toUtf8(), aAdjustValueList);
1989 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1990 if( xShapeProps.is() )
1991 WriteOutline( xShapeProps );
1992 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1994 // connector shape (cxnSp) cannot contain text (txBody) (according to schema)
1995 if( nShapeNode != XML_cxnSp )
1997 // write text
1998 WriteTextBox( xShape, mnXmlNamespace );
2001 pFS->endElementNS(mnXmlNamespace, nShapeNode);
2003 return *this;
2006 ShapeExport& ShapeExport::WriteLineShape( const Reference< XShape >& xShape )
2008 bool bFlipH = false;
2009 bool bFlipV = false;
2011 SAL_INFO("oox.shape", "write line shape");
2013 FSHelperPtr pFS = GetFS();
2015 pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp));
2017 tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon( xShape );
2018 if( aPolyPolygon.Count() == 1 && aPolyPolygon[ 0 ].GetSize() == 2)
2020 const tools::Polygon& rPoly = aPolyPolygon[ 0 ];
2022 bFlipH = ( rPoly[ 0 ].X() > rPoly[ 1 ].X() );
2023 bFlipV = ( rPoly[ 0 ].Y() > rPoly[ 1 ].Y() );
2026 Reference<XPropertySet> const xShapeProps(xShape, UNO_QUERY);
2027 // non visual shape properties
2028 if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
2030 pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
2031 pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
2032 XML_id, OString::number(GetNewShapeID(xShape)),
2033 XML_name, GetShapeName(xShape));
2034 AddExtLst(pFS, xShapeProps);
2035 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2037 pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr );
2038 if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
2040 WriteNonVisualProperties( xShape );
2041 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
2044 // visual shape properties
2045 pFS->startElementNS(mnXmlNamespace, XML_spPr);
2046 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV, true);
2047 WritePresetShape( "line"_ostr );
2048 if( xShapeProps.is() )
2049 WriteOutline( xShapeProps );
2050 pFS->endElementNS( mnXmlNamespace, XML_spPr );
2052 //write style
2053 pFS->startElementNS(mnXmlNamespace, XML_style);
2054 WriteShapeStyle( xShapeProps );
2055 pFS->endElementNS( mnXmlNamespace, XML_style );
2057 // write text
2058 WriteTextBox( xShape, mnXmlNamespace );
2060 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );
2062 return *this;
2065 ShapeExport& ShapeExport::WriteNonVisualDrawingProperties( const Reference< XShape >& xShape, const char* pName )
2067 FSHelperPtr pFS = GetFS();
2069 Reference<XPropertySet> const xShapeProps(xShape, UNO_QUERY);
2070 pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
2071 XML_id, OString::number(GetNewShapeID(xShape)),
2072 XML_name, pName );
2073 AddExtLst(pFS, xShapeProps);
2074 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2076 return *this;
2079 ShapeExport& ShapeExport::WriteNonVisualProperties( const Reference< XShape >& )
2081 // Override to generate //nvPr elements.
2082 return *this;
2085 ShapeExport& ShapeExport::WriteRectangleShape( const Reference< XShape >& xShape )
2087 SAL_INFO("oox.shape", "write rectangle shape");
2089 FSHelperPtr pFS = GetFS();
2091 pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp));
2093 sal_Int32 nRadius = 0;
2095 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
2096 if( xShapeProps.is() )
2098 xShapeProps->getPropertyValue( u"CornerRadius"_ustr ) >>= nRadius;
2101 if( nRadius )
2103 nRadius = MapSize( awt::Size( nRadius, 0 ) ).Width;
2105 //TODO: use nRadius value more precisely than just deciding whether to use
2106 // "rect" or "roundRect" preset shape below
2108 // non visual shape properties
2109 if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes)
2110 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
2111 pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
2112 pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
2113 XML_id, OString::number(GetNewShapeID(xShape)),
2114 XML_name, GetShapeName(xShape));
2115 AddExtLst(pFS, xShapeProps);
2116 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2117 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
2118 WriteNonVisualProperties( xShape );
2119 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
2121 // visual shape properties
2122 pFS->startElementNS(mnXmlNamespace, XML_spPr);
2123 WriteShapeTransformation( xShape, XML_a );
2124 WritePresetShape( nRadius == 0 ? "rect" : "roundRect" );
2125 Reference< XPropertySet > xProps( xShape, UNO_QUERY );
2126 if( xProps.is() )
2128 WriteFill( xProps );
2129 WriteOutline( xProps );
2131 pFS->endElementNS( mnXmlNamespace, XML_spPr );
2133 // write text
2134 WriteTextBox( xShape, mnXmlNamespace );
2136 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );
2138 return *this;
2142 typedef ShapeExport& (ShapeExport::*ShapeConverter)( const Reference< XShape >& );
2143 typedef std::unordered_map< const char*, ShapeConverter, rtl::CStringHash, rtl::CStringEqual> NameToConvertMapType;
2145 namespace
2148 constexpr auto constMap = frozen::make_unordered_map<std::u16string_view, ShapeConverter>(
2150 { u"com.sun.star.drawing.CaptionShape", &ShapeExport::WriteTextShape },
2151 { u"com.sun.star.drawing.ClosedBezierShape", &ShapeExport::WriteClosedPolyPolygonShape },
2152 { u"com.sun.star.drawing.ConnectorShape", &ShapeExport::WriteConnectorShape },
2153 { u"com.sun.star.drawing.CustomShape", &ShapeExport::WriteCustomShape },
2154 { u"com.sun.star.drawing.EllipseShape", &ShapeExport::WriteEllipseShape },
2155 { u"com.sun.star.drawing.GraphicObjectShape", &ShapeExport::WriteGraphicObjectShape },
2156 { u"com.sun.star.drawing.LineShape", &ShapeExport::WriteLineShape },
2157 { u"com.sun.star.drawing.MediaShape", &ShapeExport::WriteGraphicObjectShape },
2158 { u"com.sun.star.drawing.OpenBezierShape", &ShapeExport::WriteOpenPolyPolygonShape },
2159 { u"com.sun.star.drawing.PolyPolygonShape", &ShapeExport::WriteClosedPolyPolygonShape },
2160 { u"com.sun.star.drawing.PolyLineShape", &ShapeExport::WriteOpenPolyPolygonShape },
2161 { u"com.sun.star.drawing.RectangleShape", &ShapeExport::WriteRectangleShape },
2162 { u"com.sun.star.drawing.OLE2Shape", &ShapeExport::WriteOLE2Shape },
2163 { u"com.sun.star.drawing.TableShape", &ShapeExport::WriteTableShape },
2164 { u"com.sun.star.drawing.TextShape", &ShapeExport::WriteTextShape },
2165 { u"com.sun.star.drawing.GroupShape", &ShapeExport::WriteGroupShape },
2166 { u"com.sun.star.presentation.GraphicObjectShape", &ShapeExport::WriteGraphicObjectShape },
2167 { u"com.sun.star.presentation.MediaShape", &ShapeExport::WriteGraphicObjectShape },
2168 { u"com.sun.star.presentation.ChartShape", &ShapeExport::WriteOLE2Shape },
2169 { u"com.sun.star.presentation.OLE2Shape", &ShapeExport::WriteOLE2Shape },
2170 { u"com.sun.star.presentation.TableShape", &ShapeExport::WriteTableShape },
2171 { u"com.sun.star.presentation.TextShape", &ShapeExport::WriteTextShape },
2172 { u"com.sun.star.presentation.DateTimeShape", &ShapeExport::WriteTextShape },
2173 { u"com.sun.star.presentation.FooterShape", &ShapeExport::WriteTextShape },
2174 { u"com.sun.star.presentation.HeaderShape", &ShapeExport::WriteTextShape },
2175 { u"com.sun.star.presentation.NotesShape", &ShapeExport::WriteTextShape },
2176 { u"com.sun.star.presentation.OutlinerShape", &ShapeExport::WriteTextShape },
2177 { u"com.sun.star.presentation.SlideNumberShape", &ShapeExport::WriteTextShape },
2178 { u"com.sun.star.presentation.TitleTextShape", &ShapeExport::WriteTextShape },
2181 } // end anonymous namespace
2183 ShapeExport& ShapeExport::WriteShape( const Reference< XShape >& xShape )
2185 if (!xShape)
2186 throw lang::IllegalArgumentException();
2188 OUString sShapeType = xShape->getShapeType();
2189 SAL_INFO("oox.shape", "write shape: " << sShapeType);
2190 auto aConverterIterator = constMap.find(sShapeType);
2191 if (aConverterIterator == constMap.end())
2193 SAL_INFO("oox.shape", "unknown shape");
2194 return WriteUnknownShape( xShape );
2197 if (GetDocumentType() == DOCUMENT_PPTX)
2199 Reference< XPropertySet > xShapeProperties(xShape, UNO_QUERY);
2200 if (xShapeProperties && xShapeProperties->getPropertySetInfo()
2201 && xShapeProperties->getPropertySetInfo()->hasPropertyByName(u"IsPresentationObject"_ustr)
2202 && xShapeProperties->getPropertyValue(u"IsPresentationObject"_ustr).hasValue())
2203 mbPlaceholder = xShapeProperties->getPropertyValue(u"IsPresentationObject"_ustr).get<bool>();
2206 (this->*(aConverterIterator->second))(xShape);
2208 return *this;
2211 static bool lcl_isTextBox(const Reference<XInterface>& xIface)
2213 uno::Reference<beans::XPropertySet> xPropertySet(xIface, uno::UNO_QUERY);
2214 if (!xPropertySet.is())
2215 return false;
2216 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
2217 if (!xPropertySetInfo->hasPropertyByName(u"TextBox"_ustr))
2218 return false;
2219 css::uno::Any aTextBox(xPropertySet->getPropertyValue(u"TextBox"_ustr));
2220 if (!aTextBox.hasValue())
2221 return false;
2222 return aTextBox.get<bool>();
2225 ShapeExport& ShapeExport::WriteTextBox( const Reference< XInterface >& xIface, sal_Int32 nXmlNamespace, bool bWritePropertiesAsLstStyles )
2227 // In case this shape has an associated textbox, then export that, and we're done.
2228 if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes && GetTextExport())
2230 if (lcl_isTextBox(xIface))
2232 GetTextExport()->WriteTextBox(uno::Reference<drawing::XShape>(xIface, uno::UNO_QUERY_THROW));
2233 WriteText( xIface, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace );
2234 return *this;
2238 Reference< XText > xXText( xIface, UNO_QUERY );
2239 if( (NonEmptyText( xIface ) || GetDocumentType() == DOCUMENT_PPTX)
2240 && xXText.is() )
2242 FSHelperPtr pFS = GetFS();
2244 pFS->startElementNS(nXmlNamespace,
2245 (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_txBody : XML_txbx));
2246 WriteText(xIface, /*bBodyPr=*/(GetDocumentType() != DOCUMENT_DOCX || mbUserShapes), /*bText=*/true,
2247 /*nXmlNamespace=*/0, /*bWritePropertiesAsLstStyles=*/bWritePropertiesAsLstStyles);
2248 pFS->endElementNS( nXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_txBody : XML_txbx) );
2249 if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes)
2250 WriteText( xIface, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace );
2252 else if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes)
2253 mpFS->singleElementNS(nXmlNamespace, XML_bodyPr);
2255 return *this;
2258 void ShapeExport::WriteTable( const Reference< XShape >& rXShape )
2260 Reference< XTable > xTable;
2261 Reference< XPropertySet > xPropSet( rXShape, UNO_QUERY );
2263 mpFS->startElementNS(XML_a, XML_graphic);
2264 mpFS->startElementNS(XML_a, XML_graphicData,
2265 XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/table");
2267 if ( xPropSet.is() && ( xPropSet->getPropertyValue( u"Model"_ustr ) >>= xTable ) )
2269 mpFS->startElementNS(XML_a, XML_tbl);
2270 mpFS->startElementNS(XML_a, XML_tblPr);
2271 WriteShapeEffects(xPropSet);
2272 mpFS->endElementNS(XML_a, XML_tblPr);
2274 Reference< container::XIndexAccess > xColumns( xTable->getColumns(), UNO_QUERY_THROW );
2275 Reference< container::XIndexAccess > xRows( xTable->getRows(), UNO_QUERY_THROW );
2276 sal_uInt16 nRowCount = static_cast< sal_uInt16 >( xRows->getCount() );
2277 sal_uInt16 nColumnCount = static_cast< sal_uInt16 >( xColumns->getCount() );
2279 mpFS->startElementNS(XML_a, XML_tblGrid);
2281 for ( sal_Int32 x = 0; x < nColumnCount; x++ )
2283 Reference< XPropertySet > xColPropSet( xColumns->getByIndex( x ), UNO_QUERY_THROW );
2284 sal_Int32 nWidth(0);
2285 xColPropSet->getPropertyValue( u"Width"_ustr ) >>= nWidth;
2287 mpFS->singleElementNS(XML_a, XML_gridCol,
2288 XML_w, OString::number(oox::drawingml::convertHmmToEmu(nWidth)));
2291 mpFS->endElementNS( XML_a, XML_tblGrid );
2293 // map for holding the transpose index of the merged cells and pair<parentTransposeIndex, parentCell>
2294 typedef std::unordered_map<sal_Int32, std::pair<sal_Int32, Reference< XMergeableCell> > > transposeTableMap;
2295 transposeTableMap mergedCellMap;
2297 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
2299 Reference< XPropertySet > xRowPropSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
2300 sal_Int32 nRowHeight(0);
2302 xRowPropSet->getPropertyValue( u"Height"_ustr ) >>= nRowHeight;
2304 mpFS->startElementNS(XML_a, XML_tr,
2305 XML_h, OString::number(oox::drawingml::convertHmmToEmu(nRowHeight)));
2306 for( sal_Int32 nColumn = 0; nColumn < nColumnCount; nColumn++ )
2308 Reference< XMergeableCell > xCell( xTable->getCellByPosition( nColumn, nRow ),
2309 UNO_QUERY_THROW );
2310 sal_Int32 transposedIndexofCell = (nRow * nColumnCount) + nColumn;
2312 //assume we will open a cell, set to false below if we won't
2313 bool bCellOpened = true;
2315 if(xCell->getColumnSpan() > 1 && xCell->getRowSpan() > 1)
2317 // having both : horizontal and vertical merge
2318 mpFS->startElementNS(XML_a, XML_tc,
2319 XML_gridSpan, OString::number(xCell->getColumnSpan()),
2320 XML_rowSpan, OString::number(xCell->getRowSpan()));
2321 // since, XMergeableCell doesn't have the information about
2322 // cell having hMerge or vMerge.
2323 // So, Populating the merged cell map in-order to use it to
2324 // decide the attribute for the individual cell.
2325 for(sal_Int32 columnIndex = nColumn; columnIndex < nColumn+xCell->getColumnSpan(); ++columnIndex)
2327 for(sal_Int32 rowIndex = nRow; rowIndex < nRow+xCell->getRowSpan(); ++rowIndex)
2329 sal_Int32 transposeIndexForMergeCell =
2330 (rowIndex * nColumnCount) + columnIndex;
2331 mergedCellMap[transposeIndexForMergeCell] =
2332 std::make_pair(transposedIndexofCell, xCell);
2337 else if(xCell->getColumnSpan() > 1)
2339 // having : horizontal merge
2340 mpFS->startElementNS(XML_a, XML_tc,
2341 XML_gridSpan, OString::number(xCell->getColumnSpan()));
2342 for(sal_Int32 columnIndex = nColumn; columnIndex < nColumn + xCell->getColumnSpan(); ++columnIndex) {
2343 sal_Int32 transposeIndexForMergeCell = (nRow*nColumnCount) + columnIndex;
2344 mergedCellMap[transposeIndexForMergeCell] =
2345 std::make_pair(transposedIndexofCell, xCell);
2348 else if(xCell->getRowSpan() > 1)
2350 // having : vertical merge
2351 mpFS->startElementNS(XML_a, XML_tc,
2352 XML_rowSpan, OString::number(xCell->getRowSpan()));
2354 for(sal_Int32 rowIndex = nRow; rowIndex < nRow + xCell->getRowSpan(); ++rowIndex) {
2355 sal_Int32 transposeIndexForMergeCell = (rowIndex*nColumnCount) + nColumn;
2356 mergedCellMap[transposeIndexForMergeCell] =
2357 std::make_pair(transposedIndexofCell, xCell);
2360 else
2362 // now, the cell can be an independent cell or
2363 // it can be a cell which is been merged to some parent cell
2364 if(!xCell->isMerged())
2366 // independent cell
2367 mpFS->startElementNS(XML_a, XML_tc);
2369 else
2371 // it a merged cell to some parent cell
2372 // find the parent cell for the current cell at hand
2373 transposeTableMap::iterator it = mergedCellMap.find(transposedIndexofCell);
2374 if(it != mergedCellMap.end())
2376 sal_Int32 transposeIndexOfParent = it->second.first;
2377 Reference< XMergeableCell > parentCell = it->second.second;
2378 // finding the row and column index for the parent cell from transposed index
2379 sal_Int32 parentColumnIndex = transposeIndexOfParent % nColumnCount;
2380 sal_Int32 parentRowIndex = transposeIndexOfParent / nColumnCount;
2381 if(nColumn == parentColumnIndex)
2383 // the cell is vertical merge and it might have gridspan
2384 if(parentCell->getColumnSpan() > 1)
2386 // vMerge and has gridSpan
2387 mpFS->startElementNS(XML_a, XML_tc,
2388 XML_vMerge, OString::number(1),
2389 XML_gridSpan, OString::number(xCell->getColumnSpan()));
2391 else
2393 // only vMerge
2394 mpFS->startElementNS(XML_a, XML_tc,
2395 XML_vMerge, OString::number(1));
2398 else if(nRow == parentRowIndex)
2400 // the cell is horizontal merge and it might have rowspan
2401 if(parentCell->getRowSpan() > 1)
2403 // hMerge and has rowspan
2404 mpFS->startElementNS(XML_a, XML_tc,
2405 XML_hMerge, OString::number(1),
2406 XML_rowSpan, OString::number(xCell->getRowSpan()));
2408 else
2410 // only hMerge
2411 mpFS->startElementNS(XML_a, XML_tc,
2412 XML_hMerge, OString::number(1));
2415 else
2417 // has hMerge and vMerge
2418 mpFS->startElementNS(XML_a, XML_tc,
2419 XML_vMerge, OString::number(1),
2420 XML_hMerge, OString::number(1));
2423 else
2424 bCellOpened = false;
2428 if (bCellOpened)
2430 WriteTextBox( xCell, XML_a );
2432 Reference< XPropertySet > xCellPropSet(xCell, UNO_QUERY_THROW);
2433 WriteTableCellProperties(xCellPropSet);
2435 mpFS->endElementNS( XML_a, XML_tc );
2439 mpFS->endElementNS( XML_a, XML_tr );
2442 mpFS->endElementNS( XML_a, XML_tbl );
2445 mpFS->endElementNS( XML_a, XML_graphicData );
2446 mpFS->endElementNS( XML_a, XML_graphic );
2449 void ShapeExport::WriteTableCellProperties(const Reference< XPropertySet>& xCellPropSet)
2451 sal_Int32 nLeftMargin(0), nRightMargin(0);
2452 TextVerticalAdjust eVerticalAlignment;
2453 const char* sVerticalAlignment;
2455 Any aLeftMargin = xCellPropSet->getPropertyValue(u"TextLeftDistance"_ustr);
2456 aLeftMargin >>= nLeftMargin;
2458 Any aRightMargin = xCellPropSet->getPropertyValue(u"TextRightDistance"_ustr);
2459 aRightMargin >>= nRightMargin;
2461 Any aVerticalAlignment = xCellPropSet->getPropertyValue(u"TextVerticalAdjust"_ustr);
2462 aVerticalAlignment >>= eVerticalAlignment;
2463 sVerticalAlignment = GetTextVerticalAdjust(eVerticalAlignment);
2465 sal_Int32 nRotateAngle = 0;
2466 Any aRotateAngle = xCellPropSet->getPropertyValue(u"RotateAngle"_ustr);
2467 aRotateAngle >>= nRotateAngle;
2468 std::optional<OString> aTextVerticalValue = GetTextVerticalType(nRotateAngle);
2470 Sequence<PropertyValue> aGrabBag;
2471 if( !aTextVerticalValue &&
2472 (xCellPropSet->getPropertyValue(u"CellInteropGrabBag"_ustr) >>= aGrabBag) )
2474 for (auto const& rIt : aGrabBag)
2476 if (rIt.Name == "mso-tcPr-vert-value")
2478 aTextVerticalValue = rIt.Value.get<OUString>().toUtf8();
2479 break;
2484 mpFS->startElementNS(XML_a, XML_tcPr, XML_anchor, sVerticalAlignment,
2485 XML_vert, aTextVerticalValue,
2486 XML_marL, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeftMargin)), nLeftMargin > 0),
2487 XML_marR, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRightMargin)), nRightMargin > 0));
2489 // Write background fill for table cell.
2490 // TODO
2491 // tcW : Table cell width
2492 WriteTableCellBorders(xCellPropSet);
2493 DrawingML::WriteFill(xCellPropSet);
2494 mpFS->endElementNS( XML_a, XML_tcPr );
2497 void ShapeExport::WriteBorderLine(const sal_Int32 xml_line_element, const BorderLine2& rBorderLine)
2499 // While importing the table cell border line width, it converts EMU->Hmm then divided result by 2.
2500 // To get original value of LineWidth need to multiple by 2.
2501 sal_Int32 nBorderWidth = rBorderLine.LineWidth;
2502 nBorderWidth *= 2;
2503 nBorderWidth = oox::drawingml::convertHmmToEmu( nBorderWidth );
2505 if ( nBorderWidth > 0 )
2507 mpFS->startElementNS(XML_a, xml_line_element, XML_w, OString::number(nBorderWidth));
2508 if ( rBorderLine.Color == sal_Int32( COL_AUTO ) )
2509 mpFS->singleElementNS(XML_a, XML_noFill);
2510 else
2511 DrawingML::WriteSolidFill( ::Color(ColorTransparency, rBorderLine.Color) );
2513 OUString sBorderStyle;
2514 sal_Int16 nStyle = rBorderLine.LineStyle;
2515 mAny.setValue(&nStyle, cppu::UnoType<sal_Int16>::get());
2516 switch (*o3tl::doAccess<sal_Int16>(mAny))
2518 case ::table::BorderLineStyle::SOLID:
2519 sBorderStyle = "solid";
2520 break;
2521 case ::table::BorderLineStyle::DOTTED:
2522 sBorderStyle = "dot";
2523 break;
2524 case ::table::BorderLineStyle::DASHED:
2525 sBorderStyle = "dash";
2526 break;
2527 case ::table::BorderLineStyle::DASH_DOT:
2528 sBorderStyle = "dashDot";
2529 break;
2530 case ::table::BorderLineStyle::DASH_DOT_DOT:
2531 sBorderStyle = "sysDashDotDot";
2532 break;
2534 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, sBorderStyle);
2535 mpFS->endElementNS(XML_a, xml_line_element);
2537 else if( nBorderWidth == 0)
2539 mpFS->startElementNS(XML_a, xml_line_element);
2540 mpFS->singleElementNS(XML_a, XML_noFill);
2541 mpFS->endElementNS(XML_a, xml_line_element);
2545 void ShapeExport::WriteTableCellBorders(const Reference< XPropertySet>& xCellPropSet)
2547 BorderLine2 aBorderLine;
2549 // lnL - Left Border Line Properties of table cell
2550 xCellPropSet->getPropertyValue(u"LeftBorder"_ustr) >>= aBorderLine;
2551 WriteBorderLine( XML_lnL, aBorderLine );
2553 // lnR - Right Border Line Properties of table cell
2554 xCellPropSet->getPropertyValue(u"RightBorder"_ustr) >>= aBorderLine;
2555 WriteBorderLine( XML_lnR, aBorderLine );
2557 // lnT - Top Border Line Properties of table cell
2558 xCellPropSet->getPropertyValue(u"TopBorder"_ustr) >>= aBorderLine;
2559 WriteBorderLine( XML_lnT, aBorderLine );
2561 // lnB - Bottom Border Line Properties of table cell
2562 xCellPropSet->getPropertyValue(u"BottomBorder"_ustr) >>= aBorderLine;
2563 WriteBorderLine( XML_lnB, aBorderLine );
2566 ShapeExport& ShapeExport::WriteTableShape( const Reference< XShape >& xShape )
2568 FSHelperPtr pFS = GetFS();
2570 pFS->startElementNS(mnXmlNamespace, XML_graphicFrame);
2572 pFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr);
2574 Reference<XPropertySet> const xShapeProps(xShape, UNO_QUERY);
2575 pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
2576 XML_id, OString::number(GetNewShapeID(xShape)),
2577 XML_name, GetShapeName(xShape));
2578 AddExtLst(pFS, xShapeProps);
2579 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2581 pFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr);
2583 if( GetDocumentType() == DOCUMENT_PPTX )
2584 pFS->singleElementNS(mnXmlNamespace, XML_nvPr);
2585 pFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
2587 WriteShapeTransformation( xShape, mnXmlNamespace );
2588 WriteTable( xShape );
2590 pFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
2592 return *this;
2595 ShapeExport& ShapeExport::WriteTextShape( const Reference< XShape >& xShape )
2597 FSHelperPtr pFS = GetFS();
2598 Reference<XPropertySet> xShapeProps(xShape, UNO_QUERY);
2600 pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp));
2602 // non visual shape properties
2603 if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
2605 pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
2606 pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
2607 XML_id, OString::number(GetNewShapeID(xShape)),
2608 XML_name, GetShapeName(xShape));
2609 OUString sURL;
2610 if (GetProperty(xShapeProps, u"URL"_ustr))
2611 mAny >>= sURL;
2613 if (!sURL.isEmpty())
2615 OUString sRelId = mpFB->addRelation(mpFS->getOutputStream(),
2616 oox::getRelationship(Relationship::HYPERLINK),
2617 mpURLTransformer->getTransformedString(sURL),
2618 mpURLTransformer->isExternalURL(sURL));
2620 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
2622 AddExtLst(pFS, xShapeProps);
2623 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2625 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr, XML_txBox, "1");
2626 if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
2628 WriteNonVisualProperties( xShape );
2629 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
2632 // visual shape properties
2633 pFS->startElementNS(mnXmlNamespace, XML_spPr);
2634 WriteShapeTransformation( xShape, XML_a );
2635 WritePresetShape( "rect"_ostr );
2636 uno::Reference<beans::XPropertySet> xPropertySet(xShape, UNO_QUERY);
2637 if (!IsFontworkShape(xShapeProps)) // Fontwork needs fill and outline in run properties instead.
2639 WriteBlipOrNormalFill(xPropertySet, u"Graphic"_ustr, xShape->getSize());
2640 WriteOutline(xPropertySet);
2641 WriteShapeEffects(xPropertySet);
2643 pFS->endElementNS( mnXmlNamespace, XML_spPr );
2645 WriteTextBox( xShape, mnXmlNamespace );
2647 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );
2649 return *this;
2652 void ShapeExport::WriteMathShape(Reference<XShape> const& xShape)
2654 Reference<XPropertySet> const xPropSet(xShape, UNO_QUERY);
2655 assert(xPropSet.is());
2656 Reference<XModel> xMathModel;
2657 xPropSet->getPropertyValue(u"Model"_ustr) >>= xMathModel;
2658 assert(xMathModel.is());
2659 assert(GetDocumentType() != DOCUMENT_DOCX); // should be written in DocxAttributeOutput
2660 SAL_WARN_IF(GetDocumentType() == DOCUMENT_XLSX, "oox.shape", "Math export to XLSX isn't tested, should it happen here?");
2661 const OString cNvPr_id = OString::number(GetNewShapeID(xShape));
2662 const OUString shapeName = GetShapeName(xShape);
2664 // ECMA standard does not actually allow oMath outside of
2665 // WordProcessingML so write a MCE like PPT 2010 does
2666 mpFS->startElementNS(XML_mc, XML_AlternateContent);
2667 mpFS->startElementNS(XML_mc, XML_Choice,
2668 FSNS(XML_xmlns, XML_a14), mpFB->getNamespaceURL(OOX_NS(a14)),
2669 XML_Requires, "a14");
2670 mpFS->startElementNS(mnXmlNamespace, XML_sp);
2671 mpFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
2672 mpFS->startElementNS(mnXmlNamespace, XML_cNvPr, XML_id, cNvPr_id, XML_name, shapeName);
2673 AddExtLst(mpFS, xPropSet);
2674 mpFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2675 mpFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr, XML_txBox, "1");
2676 mpFS->singleElementNS(mnXmlNamespace, XML_nvPr);
2677 mpFS->endElementNS(mnXmlNamespace, XML_nvSpPr);
2678 mpFS->startElementNS(mnXmlNamespace, XML_spPr);
2679 WriteShapeTransformation(xShape, XML_a);
2680 WritePresetShape("rect"_ostr);
2681 mpFS->endElementNS(mnXmlNamespace, XML_spPr);
2682 mpFS->startElementNS(mnXmlNamespace, XML_txBody);
2683 mpFS->startElementNS(XML_a, XML_bodyPr);
2684 mpFS->endElementNS(XML_a, XML_bodyPr);
2685 mpFS->startElementNS(XML_a, XML_p);
2686 mpFS->startElementNS(XML_a14, XML_m);
2688 oox::FormulaImExportBase *const pMagic(
2689 dynamic_cast<oox::FormulaImExportBase*>(xMathModel.get()));
2690 assert(pMagic);
2691 pMagic->writeFormulaOoxml(GetFS(), GetFB()->getVersion(), GetDocumentType(),
2692 FormulaImExportBase::eFormulaAlign::INLINE);
2694 mpFS->endElementNS(XML_a14, XML_m);
2695 mpFS->endElementNS(XML_a, XML_p);
2696 mpFS->endElementNS(mnXmlNamespace, XML_txBody);
2697 mpFS->endElementNS(mnXmlNamespace, XML_sp);
2698 mpFS->endElementNS(XML_mc, XML_Choice);
2699 mpFS->startElementNS(XML_mc, XML_Fallback);
2701 svt::EmbeddedObjectRef ref(
2702 xPropSet->getPropertyValue(u"EmbeddedObject"_ustr).query<css::embed::XEmbeddedObject>(),
2703 embed::Aspects::MSOLE_CONTENT);
2704 if (auto* graphic = ref.GetGraphic(); graphic && graphic->GetType() != GraphicType::NONE)
2706 if (OUString r_id = writeGraphicToStorage(*graphic); !r_id.isEmpty())
2708 mpFS->startElementNS(mnXmlNamespace, XML_sp);
2709 mpFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
2710 mpFS->startElementNS(mnXmlNamespace, XML_cNvPr, XML_id, cNvPr_id, XML_name, shapeName);
2711 AddExtLst(mpFS, xPropSet);
2712 mpFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2713 mpFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr, XML_txBox, "1");
2714 mpFS->singleElementNS(mnXmlNamespace, XML_nvPr);
2715 mpFS->endElementNS(mnXmlNamespace, XML_nvSpPr);
2716 mpFS->startElementNS(mnXmlNamespace, XML_spPr);
2717 WriteShapeTransformation(xShape, XML_a);
2718 WritePresetShape("rect"_ostr);
2719 mpFS->startElementNS(XML_a, XML_blipFill);
2720 mpFS->singleElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), r_id);
2721 mpFS->startElementNS(XML_a, XML_stretch);
2722 mpFS->singleElementNS(XML_a, XML_fillRect);
2723 mpFS->endElementNS(XML_a, XML_stretch);
2724 mpFS->endElementNS(XML_a, XML_blipFill);
2725 mpFS->endElementNS(mnXmlNamespace, XML_spPr);
2726 mpFS->endElementNS(mnXmlNamespace, XML_sp);
2730 mpFS->endElementNS(XML_mc, XML_Fallback);
2731 mpFS->endElementNS(XML_mc, XML_AlternateContent);
2734 ShapeExport& ShapeExport::WriteOLE2Shape( const Reference< XShape >& xShape )
2736 Reference< XPropertySet > xPropSet( xShape, UNO_QUERY );
2737 if (!xPropSet.is())
2738 return *this;
2740 enum { CHART, MATH, OTHER } eType(OTHER);
2741 OUString clsid;
2742 xPropSet->getPropertyValue(u"CLSID"_ustr) >>= clsid;
2743 if (!clsid.isEmpty())
2745 SvGlobalName aClassID;
2746 bool const isValid = aClassID.MakeId(clsid);
2747 assert(isValid); (void)isValid;
2748 if (SotExchange::IsChart(aClassID))
2749 eType = CHART;
2750 else if (SotExchange::IsMath(aClassID))
2751 eType = MATH;
2754 if (CHART == eType)
2756 Reference< XChartDocument > xChartDoc;
2757 xPropSet->getPropertyValue(u"Model"_ustr) >>= xChartDoc;
2758 assert(xChartDoc.is());
2759 //export the chart
2760 #if !ENABLE_WASM_STRIP_CHART
2761 // WASM_CHART change
2762 // TODO: With Chart extracted this cannot really happen since
2763 // no Chart could've been added at all
2764 ChartExport aChartExport( mnXmlNamespace, GetFS(), xChartDoc, GetFB(), GetDocumentType() );
2765 aChartExport.WriteChartObj( xShape, GetNewShapeID( xShape ), ++mnChartCount );
2766 #endif
2767 return *this;
2770 if (MATH == eType)
2772 WriteMathShape(xShape);
2773 return *this;
2776 uno::Reference<embed::XEmbeddedObject> const xObj(
2777 xPropSet->getPropertyValue(u"EmbeddedObject"_ustr), uno::UNO_QUERY);
2779 if (!xObj.is())
2781 SAL_WARN("oox.shape", "ShapeExport::WriteOLE2Shape: no object");
2783 // tdf#152436 Export the preview graphic of the object if the object is missing.
2784 SdrObject* pSdrOLE2(SdrObject::getSdrObjectFromXShape(xShape));
2785 if (auto pOle2Obj = dynamic_cast<SdrOle2Obj*>(pSdrOLE2))
2787 const Graphic* pGraphic = pOle2Obj->GetGraphic();
2788 if (pGraphic)
2789 WriteGraphicObjectShapePart(xShape, pGraphic);
2792 return *this;
2795 uno::Sequence<beans::PropertyValue> grabBag;
2796 OUString entryName;
2799 uno::Reference<beans::XPropertySet> const xParent(
2800 uno::Reference<container::XChild>(xObj, uno::UNO_QUERY_THROW)->getParent(),
2801 uno::UNO_QUERY_THROW);
2803 xParent->getPropertyValue(u"InteropGrabBag"_ustr) >>= grabBag;
2805 entryName = uno::Reference<embed::XEmbedPersist>(xObj, uno::UNO_QUERY_THROW)->getEntryName();
2807 catch (uno::Exception const&)
2809 TOOLS_WARN_EXCEPTION("oox.shape", "ShapeExport::WriteOLE2Shape");
2810 return *this;
2813 OUString progID;
2815 for (auto const& it : grabBag)
2817 if (it.Name == "EmbeddedObjects")
2819 uno::Sequence<beans::PropertyValue> objects;
2820 it.Value >>= objects;
2821 for (auto const& object : objects)
2823 if (object.Name == entryName)
2825 uno::Sequence<beans::PropertyValue> props;
2826 object.Value >>= props;
2827 for (auto const& prop : props)
2829 if (prop.Name == "ProgID")
2831 prop.Value >>= progID;
2832 break;
2835 break;
2838 break;
2842 OUString sMediaType;
2843 OUString sRelationType;
2844 OUString sSuffix;
2845 const char * pProgID(nullptr);
2846 OString anotherProgID;
2848 uno::Reference<io::XInputStream> const xInStream =
2849 oox::GetOLEObjectStream(
2850 mpFB->getComponentContext(), xObj, progID,
2851 sMediaType, sRelationType, sSuffix, pProgID);
2853 OUString sURL;
2854 OUString sRelId;
2855 if (!xInStream.is())
2857 xPropSet->getPropertyValue(u"LinkURL"_ustr) >>= sURL;
2858 if (sURL.isEmpty())
2859 return *this;
2861 sRelId = mpFB->addRelation(mpFS->getOutputStream(),
2862 oox::getRelationship(Relationship::OLEOBJECT), sURL, true);
2864 else
2866 if (!pProgID && !progID.isEmpty())
2868 anotherProgID = OUStringToOString(progID, RTL_TEXTENCODING_UTF8);
2869 pProgID = anotherProgID.getStr();
2872 assert(!sMediaType.isEmpty());
2873 assert(!sRelationType.isEmpty());
2874 assert(!sSuffix.isEmpty());
2876 OUString sNumber = OUString::number(++m_nEmbeddedObjects);
2877 OUString sFileName = u"embeddings/oleObject"_ustr + sNumber + u"."_ustr + sSuffix;
2878 OUString sFilePath = GetComponentDir() + u"/"_ustr + sFileName;
2879 uno::Reference<io::XOutputStream> const xOutStream(mpFB->openFragmentStream(sFilePath, sMediaType));
2880 assert(xOutStream.is()); // no reason why that could fail
2884 ::comphelper::OStorageHelper::CopyInputToOutput(xInStream, xOutStream);
2886 catch (uno::Exception const&)
2888 TOOLS_WARN_EXCEPTION("oox.shape", "ShapeExport::WriteOLEObject");
2891 sRelId = mpFB->addRelation(
2892 mpFS->getOutputStream(), sRelationType,
2893 Concat2View(GetRelationCompPrefix() + sFileName));
2896 sal_Int64 nAspect;
2897 bool bShowAsIcon = (xPropSet->getPropertyValue(u"Aspect"_ustr) >>= nAspect)
2898 && nAspect == embed::Aspects::MSOLE_ICON;
2900 mpFS->startElementNS(mnXmlNamespace, XML_graphicFrame);
2902 mpFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr);
2904 mpFS->startElementNS(mnXmlNamespace, XML_cNvPr,
2905 XML_id, OString::number(GetNewShapeID(xShape)),
2906 XML_name, GetShapeName(xShape));
2907 AddExtLst(mpFS, xPropSet);
2908 mpFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2910 mpFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr);
2912 if (GetDocumentType() == DOCUMENT_PPTX)
2913 mpFS->singleElementNS(mnXmlNamespace, XML_nvPr);
2914 mpFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
2916 WriteShapeTransformation( xShape, mnXmlNamespace );
2918 mpFS->startElementNS(XML_a, XML_graphic);
2919 mpFS->startElementNS(XML_a, XML_graphicData,
2920 XML_uri, "http://schemas.openxmlformats.org/presentationml/2006/ole");
2921 if (pProgID)
2923 mpFS->startElementNS( mnXmlNamespace, XML_oleObj,
2924 XML_showAsIcon, sax_fastparser::UseIf("1", bShowAsIcon),
2925 XML_progId, pProgID,
2926 FSNS(XML_r, XML_id), sRelId,
2927 XML_spid, "" );
2929 else
2931 mpFS->startElementNS( mnXmlNamespace, XML_oleObj,
2932 //? XML_name, "Document",
2933 XML_showAsIcon, sax_fastparser::UseIf("1", bShowAsIcon),
2934 FSNS(XML_r, XML_id), sRelId,
2935 // The spec says that this is a required attribute, but PowerPoint can only handle an empty value.
2936 XML_spid, "" );
2939 if (sURL.isEmpty())
2940 mpFS->singleElementNS(mnXmlNamespace, XML_embed);
2941 else
2942 mpFS->singleElementNS(mnXmlNamespace, XML_link, XML_updateAutomatic, "1");
2944 // pic element
2945 SdrObject* pSdrOLE2(SdrObject::getSdrObjectFromXShape(xShape));
2946 if (auto pOle2Obj = dynamic_cast<SdrOle2Obj*>(pSdrOLE2))
2948 const Graphic* pGraphic = pOle2Obj->GetGraphic();
2949 if (pGraphic)
2950 WriteGraphicObjectShapePart(xShape, pGraphic);
2953 mpFS->endElementNS( mnXmlNamespace, XML_oleObj );
2955 mpFS->endElementNS( XML_a, XML_graphicData );
2956 mpFS->endElementNS( XML_a, XML_graphic );
2958 mpFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
2960 return *this;
2963 ShapeExport& ShapeExport::WriteUnknownShape( const Reference< XShape >& )
2965 // Override this method to do something useful.
2966 return *this;
2969 sal_Int32 ShapeExport::GetNewShapeID( const Reference< XShape >& rXShape )
2971 return GetNewShapeID( rXShape, GetFB() );
2974 sal_Int32 ShapeExport::GetNewShapeID( const Reference< XShape >& rXShape, XmlFilterBase* pFB )
2976 if( !rXShape.is() )
2977 return -1;
2979 sal_Int32 nID = pFB->GetUniqueId();
2981 (*mpShapeMap)[ rXShape ] = nID;
2983 return nID;
2986 sal_Int32 ShapeExport::GetShapeID( const Reference< XShape >& rXShape )
2988 return GetShapeID( rXShape, mpShapeMap );
2991 sal_Int32 ShapeExport::GetShapeID( const Reference< XShape >& rXShape, ShapeHashMap* pShapeMap )
2993 if( !rXShape.is() )
2994 return -1;
2996 ShapeHashMap::const_iterator aIter = pShapeMap->find( rXShape );
2998 if( aIter == pShapeMap->end() )
2999 return -1;
3001 return aIter->second;
3004 OUString ShapeExport::GetShapeName(const Reference<XShape>& xShape)
3006 Reference<XPropertySet> rXPropSet(xShape, UNO_QUERY);
3008 // Empty name keeps the object unnamed.
3009 OUString sName;
3011 if (GetProperty(rXPropSet, u"Name"_ustr))
3012 mAny >>= sName;
3013 return sName;
3018 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */