1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
77 #include <vcl/graph.hxx>
78 #include <vcl/outdev.hxx>
79 #include <filter/msfilter/escherex.hxx>
80 #include <svx/svdoashp.hxx>
81 #include <svx/svdoole2.hxx>
82 #include <comphelper/diagnose_ex.hxx>
83 #include <oox/export/chartexport.hxx>
84 #include <oox/mathml/imexport.hxx>
85 #include <basegfx/numeric/ftools.hxx>
86 #include <oox/export/DMLPresetShapeExport.hxx>
88 using namespace ::css
;
89 using namespace ::css::beans
;
90 using namespace ::css::uno
;
91 using namespace ::css::drawing
;
92 using namespace ::css::i18n
;
93 using namespace ::css::table
;
94 using namespace ::css::container
;
95 using namespace ::css::document
;
96 using namespace ::css::text
;
98 using ::css::io::XOutputStream
;
99 using ::css::chart2::XChartDocument
;
100 using ::css::frame::XModel
;
102 using ::oox::core::XmlFilterBase
;
103 using ::sax_fastparser::FSHelperPtr
;
108 static void lcl_ConvertProgID(std::u16string_view rProgID
,
109 OUString
& o_rMediaType
, OUString
& o_rRelationType
, OUString
& o_rFileExtension
)
111 if (rProgID
== u
"Excel.Sheet.12")
113 o_rMediaType
= "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
114 o_rRelationType
= oox::getRelationship(Relationship::PACKAGE
);
115 o_rFileExtension
= "xlsx";
117 else if (o3tl::starts_with(rProgID
, u
"Excel.SheetBinaryMacroEnabled.12") )
119 o_rMediaType
= "application/vnd.ms-excel.sheet.binary.macroEnabled.12";
120 o_rRelationType
= oox::getRelationship(Relationship::PACKAGE
);
121 o_rFileExtension
= "xlsb";
123 else if (o3tl::starts_with(rProgID
, u
"Excel.SheetMacroEnabled.12"))
125 o_rMediaType
= "application/vnd.ms-excel.sheet.macroEnabled.12";
126 o_rRelationType
= oox::getRelationship(Relationship::PACKAGE
);
127 o_rFileExtension
= "xlsm";
129 else if (o3tl::starts_with(rProgID
, u
"Excel.Sheet"))
131 o_rMediaType
= "application/vnd.ms-excel";
132 o_rRelationType
= oox::getRelationship(Relationship::OLEOBJECT
);
133 o_rFileExtension
= "xls";
135 else if (rProgID
== u
"PowerPoint.Show.12")
137 o_rMediaType
= "application/vnd.openxmlformats-officedocument.presentationml.presentation";
138 o_rRelationType
= oox::getRelationship(Relationship::PACKAGE
);
139 o_rFileExtension
= "pptx";
141 else if (rProgID
== u
"PowerPoint.ShowMacroEnabled.12")
143 o_rMediaType
= "application/vnd.ms-powerpoint.presentation.macroEnabled.12";
144 o_rRelationType
= oox::getRelationship(Relationship::PACKAGE
);
145 o_rFileExtension
= "pptm";
147 else if (o3tl::starts_with(rProgID
, u
"PowerPoint.Show"))
149 o_rMediaType
= "application/vnd.ms-powerpoint";
150 o_rRelationType
= oox::getRelationship(Relationship::OLEOBJECT
);
151 o_rFileExtension
= "ppt";
153 else if (o3tl::starts_with(rProgID
, u
"PowerPoint.Slide.12"))
155 o_rMediaType
= "application/vnd.openxmlformats-officedocument.presentationml.slide";
156 o_rRelationType
= oox::getRelationship(Relationship::PACKAGE
);
157 o_rFileExtension
= "sldx";
159 else if (rProgID
== u
"PowerPoint.SlideMacroEnabled.12")
161 o_rMediaType
= "application/vnd.ms-powerpoint.slide.macroEnabled.12";
162 o_rRelationType
= oox::getRelationship(Relationship::PACKAGE
);
163 o_rFileExtension
= "sldm";
165 else if (rProgID
== u
"Word.DocumentMacroEnabled.12")
167 o_rMediaType
= "application/vnd.ms-word.document.macroEnabled.12";
168 o_rRelationType
= oox::getRelationship(Relationship::PACKAGE
);
169 o_rFileExtension
= "docm";
171 else if (rProgID
== u
"Word.Document.12")
173 o_rMediaType
= "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
174 o_rRelationType
= oox::getRelationship(Relationship::PACKAGE
);
175 o_rFileExtension
= "docx";
177 else if (rProgID
== u
"Word.Document.8")
179 o_rMediaType
= "application/msword";
180 o_rRelationType
= oox::getRelationship(Relationship::OLEOBJECT
);
181 o_rFileExtension
= "doc";
183 else if (rProgID
== u
"Excel.Chart.8")
185 o_rMediaType
= "application/vnd.ms-excel";
186 o_rRelationType
= oox::getRelationship(Relationship::OLEOBJECT
);
187 o_rFileExtension
= "xls";
189 else if (rProgID
== u
"AcroExch.Document.11")
191 o_rMediaType
= "application/pdf";
192 o_rRelationType
= oox::getRelationship(Relationship::OLEOBJECT
);
193 o_rFileExtension
= "pdf";
197 o_rMediaType
= "application/vnd.openxmlformats-officedocument.oleObject";
198 o_rRelationType
= oox::getRelationship(Relationship::OLEOBJECT
);
199 o_rFileExtension
= "bin";
203 static uno::Reference
<io::XInputStream
> lcl_StoreOwnAsOOXML(
204 uno::Reference
<uno::XComponentContext
> const& xContext
,
205 uno::Reference
<embed::XEmbeddedObject
> const& xObj
,
206 char const*& o_rpProgID
,
207 OUString
& o_rMediaType
, OUString
& o_rRelationType
, OUString
& o_rSuffix
)
213 sal_uInt8 b8
, b9
, b10
, b11
, b12
, b13
, b14
, b15
;
215 char const* pFilterName
;
216 char const* pMediaType
;
219 } const s_Mapping
[] = {
220 { {SO3_SW_CLASSID_60
}, "MS Word 2007 XML", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "Word.Document.12", "docx" },
221 { {SO3_SC_CLASSID_60
}, "Calc MS Excel 2007 XML", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Excel.Sheet.12", "xlsx" },
222 { {SO3_SIMPRESS_CLASSID_60
}, "Impress MS PowerPoint 2007 XML", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "PowerPoint.Show.12", "pptx" },
223 // FIXME: Draw does not appear to have a MSO format export filter?
224 // { {SO3_SDRAW_CLASSID}, "", "", "", "" },
225 { {SO3_SCH_CLASSID_60
}, "unused", "", "", "" },
226 { {SO3_SM_CLASSID_60
}, "unused", "", "", "" },
229 const char * pFilterName(nullptr);
230 SvGlobalName
const classId(xObj
->getClassID());
231 for (auto & i
: s_Mapping
)
233 auto const& rId(i
.ClassId
);
234 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
);
237 assert(SvGlobalName(SO3_SCH_CLASSID_60
) != classId
); // chart should be written elsewhere!
238 assert(SvGlobalName(SO3_SM_CLASSID_60
) != classId
); // formula should be written elsewhere!
239 pFilterName
= i
.pFilterName
;
240 o_rMediaType
= OUString::createFromAscii(i
.pMediaType
);
241 o_rpProgID
= i
.pProgID
;
242 o_rSuffix
= OUString::createFromAscii(i
.pSuffix
);
243 o_rRelationType
= oox::getRelationship(Relationship::PACKAGE
);
250 SAL_WARN("oox.shape", "oox::GetOLEObjectStream: unknown ClassId " << classId
.GetHexName());
254 if (embed::EmbedStates::LOADED
== xObj
->getCurrentState())
256 xObj
->changeState(embed::EmbedStates::RUNNING
);
258 // use a temp stream - while it would work to store directly to a
259 // fragment stream, an error during export means we'd have to delete it
260 uno::Reference
<io::XStream
> const xTempStream(
261 xContext
->getServiceManager()->createInstanceWithContext(
262 "com.sun.star.comp.MemoryStream", xContext
),
263 uno::UNO_QUERY_THROW
);
264 uno::Sequence
<beans::PropertyValue
> args( comphelper::InitPropertySequence({
265 { "OutputStream", Any(xTempStream
->getOutputStream()) },
266 { "FilterName", Any(OUString::createFromAscii(pFilterName
)) }
268 uno::Reference
<frame::XStorable
> xStorable(xObj
->getComponent(), uno::UNO_QUERY
);
271 xStorable
->storeToURL("private:stream", args
);
273 catch (uno::Exception
const&)
275 TOOLS_WARN_EXCEPTION("oox.shape", "oox::GetOLEObjectStream");
278 xTempStream
->getOutputStream()->closeOutput();
279 return xTempStream
->getInputStream();
282 uno::Reference
<io::XInputStream
> GetOLEObjectStream(
283 uno::Reference
<uno::XComponentContext
> const& xContext
,
284 uno::Reference
<embed::XEmbeddedObject
> const& xObj
,
285 std::u16string_view i_rProgID
,
286 OUString
& o_rMediaType
,
287 OUString
& o_rRelationType
,
288 OUString
& o_rSuffix
,
289 const char *& o_rpProgID
)
291 uno::Reference
<io::XInputStream
> xInStream
;
294 uno::Reference
<document::XStorageBasedDocument
> const xParent(
295 uno::Reference
<container::XChild
>(xObj
, uno::UNO_QUERY_THROW
)->getParent(),
296 uno::UNO_QUERY_THROW
);
297 uno::Reference
<embed::XStorage
> const xParentStorage(xParent
->getDocumentStorage());
298 OUString
const entryName(
299 uno::Reference
<embed::XEmbedPersist
>(xObj
, uno::UNO_QUERY_THROW
)->getEntryName());
301 if (xParentStorage
->isStreamElement(entryName
))
303 lcl_ConvertProgID(i_rProgID
, o_rMediaType
, o_rRelationType
, o_rSuffix
);
304 xInStream
= xParentStorage
->cloneStreamElement(entryName
)->getInputStream();
305 // TODO: make it possible to take the sMediaType from the stream
307 else // the object is ODF - either the whole document is
308 { // ODF, or the OLE was edited so it was converted to ODF
309 xInStream
= lcl_StoreOwnAsOOXML(xContext
, xObj
,
310 o_rpProgID
, o_rMediaType
, o_rRelationType
, o_rSuffix
);
313 catch (uno::Exception
const&)
315 TOOLS_WARN_EXCEPTION("oox.shape", "oox::GetOLEObjectStream");
322 namespace oox::drawingml
{
324 ShapeExport::ShapeExport( sal_Int32 nXmlNamespace
, FSHelperPtr pFS
, ShapeHashMap
* pShapeMap
, XmlFilterBase
* pFB
, DocumentType eDocumentType
, DMLTextExport
* pTextExport
, bool bUserShapes
)
325 : DrawingML( std::move(pFS
), pFB
, eDocumentType
, pTextExport
)
326 , m_nEmbeddedObjects(0)
328 , mbUserShapes( bUserShapes
)
329 , mnXmlNamespace( nXmlNamespace
)
330 , maMapModeSrc( MapUnit::Map100thMM
)
331 , maMapModeDest( MapUnit::MapInch
, Point(), Fraction( 1, 576 ), Fraction( 1, 576 ) )
332 , mpShapeMap( pShapeMap
? pShapeMap
: &maShapeMap
)
334 mpURLTransformer
= std::make_shared
<URLTransformer
>();
337 void ShapeExport::SetURLTranslator(const std::shared_ptr
<URLTransformer
>& pTransformer
)
339 mpURLTransformer
= pTransformer
;
342 awt::Size
ShapeExport::MapSize( const awt::Size
& rSize
) const
344 Size
aRetSize( OutputDevice::LogicToLogic( Size( rSize
.Width
, rSize
.Height
), maMapModeSrc
, maMapModeDest
) );
346 if ( !aRetSize
.Width() )
347 aRetSize
.AdjustWidth( 1 );
348 if ( !aRetSize
.Height() )
349 aRetSize
.AdjustHeight( 1 );
350 return awt::Size( aRetSize
.Width(), aRetSize
.Height() );
353 static bool IsNonEmptySimpleText(const Reference
<XInterface
>& xIface
)
355 if (Reference
<XSimpleText
> xText
{ xIface
, UNO_QUERY
})
356 return xText
->getString().getLength();
361 bool ShapeExport::NonEmptyText( const Reference
< XInterface
>& xIface
)
363 Reference
< XPropertySet
> xPropSet( xIface
, UNO_QUERY
);
367 Reference
< XPropertySetInfo
> xPropSetInfo
= xPropSet
->getPropertySetInfo();
368 if ( xPropSetInfo
.is() )
370 if ( xPropSetInfo
->hasPropertyByName( "IsEmptyPresentationObject" ) )
372 bool bIsEmptyPresObj
= false;
373 if ( xPropSet
->getPropertyValue( "IsEmptyPresentationObject" ) >>= bIsEmptyPresObj
)
375 SAL_INFO("oox.shape", "empty presentation object " << bIsEmptyPresObj
<< " , props:");
376 if( bIsEmptyPresObj
)
381 if ( xPropSetInfo
->hasPropertyByName( "IsPresentationObject" ) )
383 bool bIsPresObj
= false;
384 if ( xPropSet
->getPropertyValue( "IsPresentationObject" ) >>= bIsPresObj
)
386 SAL_INFO("oox.shape", "presentation object " << bIsPresObj
<< ", props:");
394 return IsNonEmptySimpleText(xIface
);
397 static void AddExtLst(FSHelperPtr
const& pFS
, Reference
<XPropertySet
> const& xShape
)
399 if (xShape
->getPropertySetInfo()->hasPropertyByName("Decorative")
400 && xShape
->getPropertyValue("Decorative").get
<bool>())
402 pFS
->startElementNS(XML_a
, XML_extLst
);
403 // FSNS(XML_xmlns, XML_a), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
404 pFS
->startElementNS(XML_a
, XML_ext
,
405 // MSO uses this "URI" which is obviously not a URI
406 XML_uri
, "{C183D7F6-B498-43B3-948B-1728B52AA6E4}");
407 pFS
->singleElementNS(XML_adec
, XML_decorative
,
408 FSNS(XML_xmlns
, XML_adec
), "http://schemas.microsoft.com/office/drawing/2017/decorative",
410 pFS
->endElementNS(XML_a
, XML_ext
);
411 pFS
->endElementNS(XML_a
, XML_extLst
);
415 ShapeExport
& ShapeExport::WritePolyPolygonShape( const Reference
< XShape
>& xShape
, const bool bClosed
)
417 SAL_INFO("oox.shape", "write polypolygon shape");
419 FSHelperPtr pFS
= GetFS();
420 pFS
->startElementNS(mnXmlNamespace
, (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_sp
: XML_wsp
));
422 awt::Point aPos
= xShape
->getPosition();
423 // Position is relative to group for child elements in Word, but absolute in API.
424 if (GetDocumentType() == DOCUMENT_DOCX
&& !mbUserShapes
&& m_xParent
.is())
426 awt::Point aParentPos
= m_xParent
->getPosition();
427 aPos
.X
-= aParentPos
.X
;
428 aPos
.Y
-= aParentPos
.Y
;
430 awt::Size aSize
= xShape
->getSize();
431 tools::Rectangle
aRect(Point(aPos
.X
, aPos
.Y
), Size(aSize
.Width
, aSize
.Height
));
433 #if OSL_DEBUG_LEVEL > 0
434 tools::PolyPolygon aPolyPolygon
= EscherPropertyContainer::GetPolyPolygon(xShape
);
435 awt::Size size
= MapSize( awt::Size( aRect
.GetWidth(), aRect
.GetHeight() ) );
436 SAL_INFO("oox.shape", "poly count " << aPolyPolygon
.Count());
437 SAL_INFO("oox.shape", "size: " << size
.Width
<< " x " << size
.Height
);
440 Reference
<XPropertySet
> const xProps(xShape
, UNO_QUERY
);
441 // non visual shape properties
442 if (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
)
444 pFS
->startElementNS(mnXmlNamespace
, XML_nvSpPr
);
445 pFS
->startElementNS(mnXmlNamespace
, XML_cNvPr
,
446 XML_id
, OString::number(GetNewShapeID(xShape
)),
447 XML_name
, GetShapeName(xShape
));
448 AddExtLst(pFS
, xProps
);
449 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
451 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvSpPr
);
452 if (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
)
454 WriteNonVisualProperties( xShape
);
455 pFS
->endElementNS( mnXmlNamespace
, XML_nvSpPr
);
458 // visual shape properties
459 pFS
->startElementNS(mnXmlNamespace
, XML_spPr
);
460 WriteTransformation( xShape
, aRect
, XML_a
);
461 WritePolyPolygon(xShape
, bClosed
);
464 WriteFill(xProps
, aSize
);
465 WriteOutline( xProps
);
468 pFS
->endElementNS( mnXmlNamespace
, XML_spPr
);
471 WriteTextBox( xShape
, mnXmlNamespace
);
473 pFS
->endElementNS( mnXmlNamespace
, (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_sp
: XML_wsp
) );
478 ShapeExport
& ShapeExport::WriteClosedPolyPolygonShape( const Reference
< XShape
>& xShape
)
480 return WritePolyPolygonShape( xShape
, true );
483 ShapeExport
& ShapeExport::WriteOpenPolyPolygonShape( const Reference
< XShape
>& xShape
)
485 return WritePolyPolygonShape( xShape
, false );
488 ShapeExport
& ShapeExport::WriteGroupShape(const uno::Reference
<drawing::XShape
>& xShape
)
490 FSHelperPtr pFS
= GetFS();
492 sal_Int32 nGroupShapeToken
= XML_grpSp
;
493 if (GetDocumentType() == DOCUMENT_DOCX
&& !mbUserShapes
)
496 nGroupShapeToken
= XML_wgp
; // toplevel
498 mnXmlNamespace
= XML_wpg
;
501 pFS
->startElementNS(mnXmlNamespace
, nGroupShapeToken
);
503 // non visual properties
504 if (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
)
506 pFS
->startElementNS(mnXmlNamespace
, XML_nvGrpSpPr
);
507 pFS
->startElementNS(mnXmlNamespace
, XML_cNvPr
,
508 XML_id
, OString::number(GetNewShapeID(xShape
)),
509 XML_name
, GetShapeName(xShape
));
510 uno::Reference
<beans::XPropertySet
> const xShapeProps(xShape
, uno::UNO_QUERY_THROW
);
511 AddExtLst(pFS
, xShapeProps
);
512 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
513 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvGrpSpPr
);
514 WriteNonVisualProperties(xShape
);
515 pFS
->endElementNS(mnXmlNamespace
, XML_nvGrpSpPr
);
518 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvGrpSpPr
);
521 pFS
->startElementNS(mnXmlNamespace
, XML_grpSpPr
);
522 WriteShapeTransformation(xShape
, XML_a
, false, false, true);
523 pFS
->endElementNS(mnXmlNamespace
, XML_grpSpPr
);
525 uno::Reference
<drawing::XShapes
> xGroupShape(xShape
, uno::UNO_QUERY_THROW
);
526 uno::Reference
<drawing::XShape
> xParent
= m_xParent
;
528 for (sal_Int32 i
= 0; i
< xGroupShape
->getCount(); ++i
)
530 uno::Reference
<drawing::XShape
> xChild(xGroupShape
->getByIndex(i
), uno::UNO_QUERY_THROW
);
531 sal_Int32 nSavedNamespace
= mnXmlNamespace
;
533 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xChild
, uno::UNO_QUERY_THROW
);
534 if (GetDocumentType() == DOCUMENT_DOCX
&& !mbUserShapes
)
536 // tdf#128820: WriteGraphicObjectShapePart calls WriteTextShape for non-empty simple
537 // text objects, which needs writing into wps::wsp element, so make sure to use wps
538 // namespace for those objects
539 if (xServiceInfo
->supportsService("com.sun.star.drawing.GraphicObjectShape")
540 && !IsNonEmptySimpleText(xChild
))
541 mnXmlNamespace
= XML_pic
;
543 mnXmlNamespace
= XML_wps
;
547 mnXmlNamespace
= nSavedNamespace
;
551 pFS
->endElementNS(mnXmlNamespace
, nGroupShapeToken
);
555 static bool lcl_IsOnDenylist(OUString
const & rShapeType
)
557 static const std::initializer_list
<std::u16string_view
> vDenylist
= {
573 u
"round-rectangular-callout",
574 u
"rectangular-callout",
582 u
"horizontal-scroll",
587 u
"flowchart-process",
588 u
"flowchart-alternate-process",
589 u
"flowchart-decision",
591 u
"flowchart-predefined-process",
592 u
"flowchart-internal-storage",
593 u
"flowchart-document",
594 u
"flowchart-multidocument",
595 u
"flowchart-terminator",
596 u
"flowchart-preparation",
597 u
"flowchart-manual-input",
598 u
"flowchart-manual-operation",
599 u
"flowchart-connector",
600 u
"flowchart-off-page-connector",
602 u
"flowchart-punched-tape",
603 u
"flowchart-summing-junction",
605 u
"flowchart-collate",
607 u
"flowchart-extract",
609 u
"flowchart-stored-data",
611 u
"flowchart-sequential-access",
612 u
"flowchart-magnetic-disk",
613 u
"flowchart-direct-access-storage",
617 return std::find(vDenylist
.begin(), vDenylist
.end(), rShapeType
) != vDenylist
.end();
620 static bool lcl_IsOnAllowlist(OUString
const & rShapeType
)
622 static const std::initializer_list
<std::u16string_view
> vAllowlist
= {
629 return std::find(vAllowlist
.begin(), vAllowlist
.end(), rShapeType
) != vAllowlist
.end();
632 static bool lcl_GetHandlePosition( sal_Int32
&nValue
, const EnhancedCustomShapeParameter
&rParam
, const Sequence
< EnhancedCustomShapeAdjustmentValue
> &rSeq
)
635 if ( rParam
.Value
.getValueTypeClass() == TypeClass_DOUBLE
)
638 if ( rParam
.Value
>>= fValue
)
639 nValue
= static_cast<sal_Int32
>(fValue
);
642 rParam
.Value
>>= nValue
;
644 if ( rParam
.Type
== EnhancedCustomShapeParameterType::ADJUSTMENT
)
647 sal_Int32 nIdx
= nValue
;
648 if ( nIdx
< rSeq
.getLength() )
650 if ( rSeq
[ nIdx
] .Value
.getValueTypeClass() == TypeClass_DOUBLE
)
653 rSeq
[ nIdx
].Value
>>= fValue
;
659 rSeq
[ nIdx
].Value
>>= nValue
;
666 static void lcl_AnalyzeHandles( const uno::Sequence
<beans::PropertyValues
> & rHandles
,
667 std::vector
< std::pair
< sal_Int32
, sal_Int32
> > &rHandlePositionList
,
668 const Sequence
< EnhancedCustomShapeAdjustmentValue
> &rSeq
)
670 for ( const Sequence
< PropertyValue
>& rPropSeq
: rHandles
)
672 static const OUStringLiteral
sPosition( u
"Position" );
673 bool bPosition
= false;
674 EnhancedCustomShapeParameterPair aPosition
;
675 for ( const PropertyValue
& rPropVal
: rPropSeq
)
677 if ( rPropVal
.Name
== sPosition
)
679 if ( rPropVal
.Value
>>= aPosition
)
685 sal_Int32 nXPosition
= 0;
686 sal_Int32 nYPosition
= 0;
687 // For polar handles, nXPosition is radius and nYPosition is angle
688 lcl_GetHandlePosition( nXPosition
, aPosition
.First
, rSeq
);
689 lcl_GetHandlePosition( nYPosition
, aPosition
.Second
, rSeq
);
690 rHandlePositionList
.emplace_back( nXPosition
, nYPosition
);
695 static void lcl_AppendAdjustmentValue( std::vector
< std::pair
< sal_Int32
, sal_Int32
> > &rAvList
, sal_Int32 nAdjIdx
, sal_Int32 nValue
)
697 rAvList
.emplace_back( nAdjIdx
, nValue
);
700 static sal_Int32
lcl_NormalizeAngle( sal_Int32 nAngle
)
702 nAngle
= nAngle
% 360;
703 return nAngle
< 0 ? ( nAngle
+ 360 ) : nAngle
;
706 static sal_Int32
lcl_CircleAngle2CustomShapeEllipseAngleOOX(const sal_Int32 nInternAngle
, const sal_Int32 nWidth
, const sal_Int32 nHeight
)
708 if (nWidth
!= 0 || nHeight
!= 0)
710 double fAngle
= basegfx::deg2rad
<100>(nInternAngle
); // intern 1/100 deg to rad
711 fAngle
= atan2(nHeight
* sin(fAngle
), nWidth
* cos(fAngle
)); // circle to ellipse
712 fAngle
= basegfx::rad2deg
<60000>(fAngle
); // rad to OOXML angle unit
713 sal_Int32 nAngle
= basegfx::fround(fAngle
); // normalize
714 nAngle
= nAngle
% 21600000;
715 return nAngle
< 0 ? (nAngle
+ 21600000) : nAngle
;
717 else // should be handled by caller, dummy value
721 static OUString
lcl_GetTarget(const css::uno::Reference
<css::frame::XModel
>& xModel
,
722 std::u16string_view rURL
)
724 Reference
<drawing::XDrawPagesSupplier
> xDPS(xModel
, uno::UNO_QUERY_THROW
);
725 Reference
<drawing::XDrawPages
> xDrawPages(xDPS
->getDrawPages(), uno::UNO_SET_THROW
);
726 sal_uInt32 nPageCount
= xDrawPages
->getCount();
729 for (sal_uInt32 i
= 0; i
< nPageCount
; ++i
)
731 Reference
<XDrawPage
> xDrawPage
;
732 xDrawPages
->getByIndex(i
) >>= xDrawPage
;
733 Reference
<container::XNamed
> xNamed(xDrawPage
, UNO_QUERY
);
736 OUString sSlideName
= "#" + xNamed
->getName();
737 if (rURL
== sSlideName
)
739 sTarget
= "slide" + OUString::number(i
+ 1) + ".xml";
747 ShapeExport
& ShapeExport::WriteCustomShape( const Reference
< XShape
>& xShape
)
749 SAL_INFO("oox.shape", "write custom shape");
750 Reference
< XPropertySet
> rXPropSet( xShape
, UNO_QUERY
);
751 // First check, if this is a Fontwork-shape. For DrawingML, such a shape is a
752 // TextBox shape with body property prstTxWarp.
753 if (IsFontworkShape(rXPropSet
))
755 ShapeExport::WriteTextShape(xShape
); // qualifier to prevent PowerPointShapeExport
759 bool bHasGeometrySeq(false);
760 Sequence
< PropertyValue
> aGeometrySeq
;
761 OUString
sShapeType("non-primitive"); // default in ODF
762 if (GetProperty(rXPropSet
, "CustomShapeGeometry"))
764 SAL_INFO("oox.shape", "got custom shape geometry");
765 if (mAny
>>= aGeometrySeq
)
767 bHasGeometrySeq
= true;
768 SAL_INFO("oox.shape", "got custom shape geometry sequence");
769 for (const PropertyValue
& rProp
: std::as_const(aGeometrySeq
))
771 SAL_INFO("oox.shape", "geometry property: " << rProp
.Name
);
772 if (rProp
.Name
== "Type")
773 rProp
.Value
>>= sShapeType
;
778 bool bPredefinedHandlesUsed
= true;
779 bool bHasHandles
= false;
781 ShapeFlag nMirrorFlags
= ShapeFlag::NONE
;
782 MSO_SPT eShapeType
= EscherPropertyContainer::GetCustomShapeType( xShape
, nMirrorFlags
, sShapeType
);
783 assert(dynamic_cast< SdrObjCustomShape
* >(SdrObject::getSdrObjectFromXShape(xShape
)) && "Not a SdrObjCustomShape (!)");
784 SdrObjCustomShape
& rSdrObjCustomShape(static_cast< SdrObjCustomShape
& >(*SdrObject::getSdrObjectFromXShape(xShape
)));
785 const bool bIsDefaultObject(
786 EscherPropertyContainer::IsDefaultObject(
789 OString sPresetShape
= msfilter::util::GetOOXMLPresetGeometry(sShapeType
);
790 SAL_INFO("oox.shape", "custom shape type: " << sShapeType
<< " ==> " << sPresetShape
);
792 sal_Int32 nAdjustmentValuesIndex
= -1;
793 awt::Rectangle aViewBox
;
794 uno::Sequence
<beans::PropertyValues
> aHandles
;
801 for (int i
= 0; i
< aGeometrySeq
.getLength(); i
++)
803 const PropertyValue
& rProp
= aGeometrySeq
[ i
];
804 SAL_INFO("oox.shape", "geometry property: " << rProp
.Name
);
806 if ( rProp
.Name
== "MirroredX" )
807 rProp
.Value
>>= bFlipH
;
809 if ( rProp
.Name
== "MirroredY" )
810 rProp
.Value
>>= bFlipV
;
811 if ( rProp
.Name
== "AdjustmentValues" )
812 nAdjustmentValuesIndex
= i
;
813 else if ( rProp
.Name
== "Handles" )
815 rProp
.Value
>>= aHandles
;
816 if ( aHandles
.hasElements() )
818 if( !bIsDefaultObject
)
819 bPredefinedHandlesUsed
= false;
820 // TODO: update nAdjustmentsWhichNeedsToBeConverted here
822 else if ( rProp
.Name
== "ViewBox" )
823 rProp
.Value
>>= aViewBox
;
827 FSHelperPtr pFS
= GetFS();
828 // non visual shape properties
829 if (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
)
831 bool bUseBackground
= false;
832 if (GetProperty(rXPropSet
, "FillUseSlideBackground"))
833 mAny
>>= bUseBackground
;
835 mpFS
->startElementNS(mnXmlNamespace
, XML_sp
, XML_useBgFill
, "1");
837 mpFS
->startElementNS(mnXmlNamespace
, XML_sp
);
839 bool isVisible
= true ;
840 if( GetProperty(rXPropSet
, "Visible"))
844 pFS
->startElementNS( mnXmlNamespace
, XML_nvSpPr
);
846 mnXmlNamespace
, XML_cNvPr
, XML_id
,
847 OString::number(GetShapeID(xShape
) == -1 ? GetNewShapeID(xShape
) : GetShapeID(xShape
)),
848 XML_name
, GetShapeName(xShape
), XML_hidden
, sax_fastparser::UseIf("1", !isVisible
));
850 if( GetProperty(rXPropSet
, "URL") )
854 if( !sURL
.isEmpty() )
856 OUString sRelId
= mpFB
->addRelation( mpFS
->getOutputStream(),
857 oox::getRelationship(Relationship::HYPERLINK
),
858 mpURLTransformer
->getTransformedString(sURL
),
859 mpURLTransformer
->isExternalURL(sURL
));
861 mpFS
->singleElementNS(XML_a
, XML_hlinkClick
, FSNS(XML_r
, XML_id
), sRelId
);
866 if (GetProperty(rXPropSet
, "Bookmark"))
869 if (GetProperty(rXPropSet
, "OnClick"))
872 presentation::ClickAction eClickAction
= presentation::ClickAction_NONE
;
873 mAny
>>= eClickAction
;
874 if (eClickAction
!= presentation::ClickAction_NONE
)
876 switch (eClickAction
)
878 case presentation::ClickAction_STOPPRESENTATION
:
879 sPPAction
= "ppaction://hlinkshowjump?jump=endshow";
881 case presentation::ClickAction_NEXTPAGE
:
882 sPPAction
= "ppaction://hlinkshowjump?jump=nextslide";
884 case presentation::ClickAction_LASTPAGE
:
885 sPPAction
= "ppaction://hlinkshowjump?jump=lastslide";
887 case presentation::ClickAction_PREVPAGE
:
888 sPPAction
= "ppaction://hlinkshowjump?jump=previousslide";
890 case presentation::ClickAction_FIRSTPAGE
:
891 sPPAction
= "ppaction://hlinkshowjump?jump=firstslide";
893 case presentation::ClickAction_BOOKMARK
:
894 sBookmark
= "#" + sBookmark
;
900 if (!sPPAction
.isEmpty())
901 pFS
->singleElementNS(XML_a
, XML_hlinkClick
, FSNS(XML_r
, XML_id
), "", XML_action
,
904 if (!sBookmark
.isEmpty())
906 bool bExtURL
= URLTransformer().isExternalURL(sBookmark
);
907 sBookmark
= bExtURL
? sBookmark
: lcl_GetTarget(GetFB()->getModel(), sBookmark
);
910 = mpFB
->addRelation(mpFS
->getOutputStream(),
911 bExtURL
? oox::getRelationship(Relationship::HYPERLINK
)
912 : oox::getRelationship(Relationship::SLIDE
),
915 mpFS
->singleElementNS(XML_a
, XML_hlinkClick
, FSNS(XML_r
, XML_id
), sRelId
);
917 mpFS
->singleElementNS(XML_a
, XML_hlinkClick
, FSNS(XML_r
, XML_id
), sRelId
,
918 XML_action
, "ppaction://hlinksldjump");
920 AddExtLst(pFS
, rXPropSet
);
921 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
922 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvSpPr
);
923 WriteNonVisualProperties( xShape
);
924 pFS
->endElementNS( mnXmlNamespace
, XML_nvSpPr
);
928 pFS
->startElementNS(mnXmlNamespace
, XML_wsp
);
931 pFS
->startElementNS(mnXmlNamespace
, XML_cNvPr
, XML_id
,
932 OString::number(GetShapeID(xShape
) == -1 ? GetNewShapeID(xShape
)
933 : GetShapeID(xShape
)),
934 XML_name
, GetShapeName(xShape
));
936 if (GetProperty(rXPropSet
, "Hyperlink"))
942 OUString sRelId
= mpFB
->addRelation(
943 mpFS
->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK
),
944 mpURLTransformer
->getTransformedString(sURL
),
945 mpURLTransformer
->isExternalURL(sURL
));
947 mpFS
->singleElementNS(XML_a
, XML_hlinkClick
, FSNS(XML_r
, XML_id
), sRelId
);
950 AddExtLst(pFS
, rXPropSet
);
951 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
953 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvSpPr
);
956 // visual shape properties
957 pFS
->startElementNS(mnXmlNamespace
, XML_spPr
);
959 // we export non-primitive shapes to custom geometry
960 // we also export non-ooxml shapes which have handles/equations to custom geometry, because
961 // we cannot convert ODF equations to DrawingML equations. TODO: see what binary DOC export filter does.
962 // but our WritePolyPolygon()/WriteCustomGeometry() functions are incomplete, therefore we use a denylist
963 // we use a allowlist for shapes where mapping to MSO preset shape is not optimal
964 bool bCustGeom
= true;
965 bool bOnDenylist
= false;
966 if( sShapeType
== "ooxml-non-primitive" )
968 else if( sShapeType
.startsWith("ooxml") )
970 else if( lcl_IsOnAllowlist(sShapeType
) )
972 else if( lcl_IsOnDenylist(sShapeType
) )
978 bool bPresetWriteSuccessful
= false;
979 // Let the custom shapes what has name and preset information in OOXML, to be written
980 // as preset ones with parameters. Try that with this converter class.
981 if (!sShapeType
.startsWith("ooxml") && sShapeType
!= "non-primitive" && !mbUserShapes
982 && xShape
->getShapeType() == "com.sun.star.drawing.CustomShape"
983 && !lcl_IsOnAllowlist(sShapeType
))
985 DMLPresetShapeExporter
aCustomShapeConverter(this, xShape
);
986 bPresetWriteSuccessful
= aCustomShapeConverter
.WriteShape();
988 // If preset writing has problems try to write the shape as it done before
989 if (bPresetWriteSuccessful
)
990 ;// Already written do nothing.
993 WriteShapeTransformation( xShape
, XML_a
, bFlipH
, bFlipV
);
994 bool bSuccess
= WriteCustomGeometry(xShape
, rSdrObjCustomShape
);
995 // In case of Writer, the parent element is <wps:spPr>, and there the <a:custGeom> element
997 if (!bSuccess
&& GetDocumentType() == DOCUMENT_DOCX
)
999 WriteEmptyCustomGeometry();
1002 else if (bOnDenylist
&& bHasHandles
&& nAdjustmentValuesIndex
!=-1 && !sShapeType
.startsWith("mso-spt"))
1004 WriteShapeTransformation( xShape
, XML_a
, bFlipH
, bFlipV
);
1005 Sequence
< EnhancedCustomShapeAdjustmentValue
> aAdjustmentSeq
;
1006 std::vector
< std::pair
< sal_Int32
, sal_Int32
> > aHandlePositionList
;
1007 std::vector
< std::pair
< sal_Int32
, sal_Int32
> > aAvList
;
1008 aGeometrySeq
[ nAdjustmentValuesIndex
].Value
>>= aAdjustmentSeq
;
1010 lcl_AnalyzeHandles( aHandles
, aHandlePositionList
, aAdjustmentSeq
);
1012 sal_Int32 nXPosition
= 0;
1013 sal_Int32 nYPosition
= 0;
1014 if ( !aHandlePositionList
.empty() )
1016 nXPosition
= aHandlePositionList
[0].first
;
1017 nYPosition
= aHandlePositionList
[0].second
;
1019 switch( eShapeType
)
1021 case mso_sptBorderCallout1
:
1023 sal_Int32 adj3
= double(nYPosition
)/aViewBox
.Height
*100000;
1024 sal_Int32 adj4
= double(nXPosition
)/aViewBox
.Width
*100000;
1025 lcl_AppendAdjustmentValue( aAvList
, 1, 18750 );
1026 lcl_AppendAdjustmentValue( aAvList
, 2, -8333 );
1027 lcl_AppendAdjustmentValue( aAvList
, 3, adj3
);
1028 lcl_AppendAdjustmentValue( aAvList
, 4, adj4
);
1031 case mso_sptBorderCallout2
:
1033 sal_Int32 adj5
= double(nYPosition
)/aViewBox
.Height
*100000;
1034 sal_Int32 adj6
= double(nXPosition
)/aViewBox
.Width
*100000;
1035 sal_Int32 adj3
= 18750;
1036 sal_Int32 adj4
= -16667;
1037 lcl_AppendAdjustmentValue( aAvList
, 1, 18750 );
1038 lcl_AppendAdjustmentValue( aAvList
, 2, -8333 );
1039 if ( aHandlePositionList
.size() > 1 )
1041 nXPosition
= aHandlePositionList
[1].first
;
1042 nYPosition
= aHandlePositionList
[1].second
;
1043 adj3
= double(nYPosition
)/aViewBox
.Height
*100000;
1044 adj4
= double(nXPosition
)/aViewBox
.Width
*100000;
1046 lcl_AppendAdjustmentValue( aAvList
, 3, adj3
);
1047 lcl_AppendAdjustmentValue( aAvList
, 4, adj4
);
1048 lcl_AppendAdjustmentValue( aAvList
, 5, adj5
);
1049 lcl_AppendAdjustmentValue( aAvList
, 6, adj6
);
1052 case mso_sptWedgeRectCallout
:
1053 case mso_sptWedgeRRectCallout
:
1054 case mso_sptWedgeEllipseCallout
:
1055 case mso_sptCloudCallout
:
1057 sal_Int32 adj1
= (double(nXPosition
)/aViewBox
.Width
-0.5) *100000;
1058 sal_Int32 adj2
= (double(nYPosition
)/aViewBox
.Height
-0.5) *100000;
1059 lcl_AppendAdjustmentValue( aAvList
, 1, adj1
);
1060 lcl_AppendAdjustmentValue( aAvList
, 2, adj2
);
1061 if ( eShapeType
== mso_sptWedgeRRectCallout
)
1063 lcl_AppendAdjustmentValue( aAvList
, 3, 16667);
1068 case mso_sptFoldedCorner
:
1070 sal_Int32 adj
= double( aViewBox
.Width
- nXPosition
) / std::min( aViewBox
.Width
,aViewBox
.Height
) * 100000;
1071 lcl_AppendAdjustmentValue( aAvList
, 0, adj
);
1077 case mso_sptNoSmoking
:
1078 case mso_sptHorizontalScroll
:
1080 case mso_sptBracketPair
:
1082 sal_Int32 adj
= double( nXPosition
)/aViewBox
.Width
*100000 ;
1083 lcl_AppendAdjustmentValue( aAvList
, 0, adj
);
1088 case mso_sptBracePair
:
1089 case mso_sptVerticalScroll
:
1091 sal_Int32 adj
= double( nYPosition
)/aViewBox
.Height
*100000 ;
1092 lcl_AppendAdjustmentValue( aAvList
, 0, adj
);
1095 case mso_sptSmileyFace
:
1097 sal_Int32 adj
= double( nYPosition
)/aViewBox
.Height
*100000 - 76458.0;
1098 lcl_AppendAdjustmentValue( aAvList
, 0, adj
);
1101 case mso_sptBlockArc
:
1103 sal_Int32 nRadius
= 50000 * ( 1 - double(nXPosition
) / 10800);
1104 sal_Int32 nAngleStart
= lcl_NormalizeAngle( nYPosition
);
1105 sal_Int32 nAngleEnd
= lcl_NormalizeAngle( 180 - nAngleStart
);
1106 lcl_AppendAdjustmentValue( aAvList
, 1, 21600000 / 360 * nAngleStart
);
1107 lcl_AppendAdjustmentValue( aAvList
, 2, 21600000 / 360 * nAngleEnd
);
1108 lcl_AppendAdjustmentValue( aAvList
, 3, nRadius
);
1112 // case mso_sptBentConnector3:
1113 // case mso_sptBorderCallout3:
1116 if ( sPresetShape
== "frame" )
1118 sal_Int32 adj1
= double( nYPosition
)/aViewBox
.Height
*100000 ;
1119 lcl_AppendAdjustmentValue( aAvList
, 1, adj1
);
1124 WritePresetShape( sPresetShape
, aAvList
);
1126 else // preset geometry
1128 WriteShapeTransformation( xShape
, XML_a
, bFlipH
, bFlipV
);
1129 if( nAdjustmentValuesIndex
!= -1 )
1131 WritePresetShape( sPresetShape
, eShapeType
, bPredefinedHandlesUsed
,
1132 aGeometrySeq
[ nAdjustmentValuesIndex
] );
1135 WritePresetShape( sPresetShape
);
1137 if( rXPropSet
.is() )
1139 WriteFill(rXPropSet
, xShape
->getSize());
1140 WriteOutline( rXPropSet
);
1141 WriteShapeEffects( rXPropSet
);
1143 bool bHas3DEffectinShape
= false;
1144 uno::Sequence
<beans::PropertyValue
> grabBag
;
1145 rXPropSet
->getPropertyValue("InteropGrabBag") >>= grabBag
;
1147 for (auto const& it
: std::as_const(grabBag
))
1148 if (it
.Name
== "3DEffectProperties")
1149 bHas3DEffectinShape
= true;
1151 if( bHas3DEffectinShape
)
1152 Write3DEffects( rXPropSet
, /*bIsText=*/false );
1155 pFS
->endElementNS( mnXmlNamespace
, XML_spPr
);
1157 pFS
->startElementNS(mnXmlNamespace
, XML_style
);
1158 WriteShapeStyle( rXPropSet
);
1159 pFS
->endElementNS( mnXmlNamespace
, XML_style
);
1162 WriteTextBox( xShape
, mnXmlNamespace
);
1164 pFS
->endElementNS( mnXmlNamespace
, (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_sp
: XML_wsp
) );
1169 ShapeExport
& ShapeExport::WriteEllipseShape( const Reference
< XShape
>& xShape
)
1171 SAL_INFO("oox.shape", "write ellipse shape");
1173 FSHelperPtr pFS
= GetFS();
1175 pFS
->startElementNS(mnXmlNamespace
, (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_sp
: XML_wsp
));
1177 // TODO: connector ?
1179 Reference
<XPropertySet
> const xProps(xShape
, UNO_QUERY
);
1180 // non visual shape properties
1181 if (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
)
1183 pFS
->startElementNS(mnXmlNamespace
, XML_nvSpPr
);
1184 pFS
->startElementNS(mnXmlNamespace
, XML_cNvPr
,
1185 XML_id
, OString::number(GetNewShapeID(xShape
)),
1186 XML_name
, GetShapeName(xShape
));
1187 AddExtLst(pFS
, xProps
);
1188 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
1189 pFS
->singleElementNS( mnXmlNamespace
, XML_cNvSpPr
);
1190 WriteNonVisualProperties( xShape
);
1191 pFS
->endElementNS( mnXmlNamespace
, XML_nvSpPr
);
1194 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvSpPr
);
1196 CircleKind
eCircleKind(CircleKind_FULL
);
1198 xProps
->getPropertyValue("CircleKind" ) >>= eCircleKind
;
1200 // visual shape properties
1201 pFS
->startElementNS( mnXmlNamespace
, XML_spPr
);
1202 WriteShapeTransformation( xShape
, XML_a
);
1204 if (CircleKind_FULL
== eCircleKind
)
1205 WritePresetShape("ellipse");
1208 sal_Int32
nStartAngleIntern(9000);
1209 sal_Int32
nEndAngleIntern(0);
1212 xProps
->getPropertyValue("CircleStartAngle" ) >>= nStartAngleIntern
;
1213 xProps
->getPropertyValue("CircleEndAngle") >>= nEndAngleIntern
;
1215 std::vector
< std::pair
<sal_Int32
,sal_Int32
>> aAvList
;
1216 awt::Size aSize
= xShape
->getSize();
1217 if (aSize
.Width
!= 0 || aSize
.Height
!= 0)
1219 // Our arc has 90° up, OOXML has 90° down, so mirror it.
1220 // API angles are 1/100 degree.
1221 sal_Int32
nStartAngleOOXML(lcl_CircleAngle2CustomShapeEllipseAngleOOX(36000 - nEndAngleIntern
, aSize
.Width
, aSize
.Height
));
1222 sal_Int32
nEndAngleOOXML(lcl_CircleAngle2CustomShapeEllipseAngleOOX(36000 - nStartAngleIntern
, aSize
.Width
, aSize
.Height
));
1223 lcl_AppendAdjustmentValue( aAvList
, 1, nStartAngleOOXML
);
1224 lcl_AppendAdjustmentValue( aAvList
, 2, nEndAngleOOXML
);
1226 switch (eCircleKind
)
1228 case CircleKind_ARC
:
1229 WritePresetShape("arc", aAvList
);
1231 case CircleKind_SECTION
:
1232 WritePresetShape("pie", aAvList
);
1234 case CircleKind_CUT
:
1235 WritePresetShape("chord", aAvList
);
1238 WritePresetShape("ellipse");
1243 if (CircleKind_ARC
== eCircleKind
)
1245 // An arc in ODF is never filled, even if a fill style other than
1246 // "none" is set. OOXML arc can be filled, so set fill explicit to
1247 // NONE, otherwise some hidden or inherited filling is shown.
1248 FillStyle
eFillStyle(FillStyle_NONE
);
1250 aNewValue
<<= eFillStyle
;
1251 xProps
->setPropertyValue("FillStyle", aNewValue
);
1253 WriteFill( xProps
);
1254 WriteOutline( xProps
);
1256 pFS
->endElementNS( mnXmlNamespace
, XML_spPr
);
1259 WriteTextBox( xShape
, mnXmlNamespace
);
1261 pFS
->endElementNS( mnXmlNamespace
, (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_sp
: XML_wsp
) );
1266 ShapeExport
& ShapeExport::WriteGraphicObjectShape( const Reference
< XShape
>& xShape
)
1268 WriteGraphicObjectShapePart( xShape
);
1273 void ShapeExport::WriteGraphicObjectShapePart( const Reference
< XShape
>& xShape
, const Graphic
* pGraphic
)
1275 SAL_INFO("oox.shape", "write graphic object shape");
1277 if (IsNonEmptySimpleText(xShape
))
1279 SAL_INFO("oox.shape", "graphicObject: wrote only text");
1281 WriteTextShape(xShape
);
1286 SAL_INFO("oox.shape", "graphicObject without text");
1288 uno::Reference
<graphic::XGraphic
> xGraphic
;
1291 Reference
< XPropertySet
> xShapeProps( xShape
, UNO_QUERY
);
1295 xGraphic
.set(pGraphic
->GetXGraphic());
1297 else if (xShapeProps
.is() && xShapeProps
->getPropertySetInfo()->hasPropertyByName("Graphic"))
1299 xShapeProps
->getPropertyValue("Graphic") >>= xGraphic
;
1302 // tdf#155903 Only for PPTX, Microsoft does not support this feature in Word and Excel.
1303 bool bHasMediaURL
= GetDocumentType() == DOCUMENT_PPTX
&& xShapeProps
.is()
1304 && xShapeProps
->getPropertySetInfo()->hasPropertyByName("MediaURL")
1305 && (xShapeProps
->getPropertyValue("MediaURL") >>= sMediaURL
);
1307 if (!xGraphic
.is() && !bHasMediaURL
)
1309 SAL_INFO("oox.shape", "no graphic or media URL found");
1313 FSHelperPtr pFS
= GetFS();
1314 XmlFilterBase
* pFB
= GetFB();
1316 if (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
)
1317 pFS
->startElementNS(mnXmlNamespace
, XML_pic
);
1319 pFS
->startElementNS(mnXmlNamespace
, XML_pic
,
1320 FSNS(XML_xmlns
, XML_pic
), pFB
->getNamespaceURL(OOX_NS(dmlPicture
)));
1322 pFS
->startElementNS(mnXmlNamespace
, XML_nvPicPr
);
1324 presentation::ClickAction eClickAction
= presentation::ClickAction_NONE
;
1325 OUString sDescr
, sURL
, sBookmark
, sPPAction
;
1328 if ( ( bHaveDesc
= GetProperty( xShapeProps
, "Description" ) ) )
1330 if ( GetProperty( xShapeProps
, "URL" ) )
1332 if (GetProperty(xShapeProps
, "Bookmark"))
1334 if (GetProperty(xShapeProps
, "OnClick"))
1335 mAny
>>= eClickAction
;
1337 pFS
->startElementNS( mnXmlNamespace
, XML_cNvPr
,
1338 XML_id
, OString::number(GetNewShapeID(xShape
)),
1339 XML_name
, GetShapeName(xShape
),
1340 XML_descr
, sax_fastparser::UseIf(sDescr
, bHaveDesc
));
1342 if (eClickAction
!= presentation::ClickAction_NONE
)
1344 switch (eClickAction
)
1346 case presentation::ClickAction_STOPPRESENTATION
:
1347 sPPAction
= "ppaction://hlinkshowjump?jump=endshow";
1349 case presentation::ClickAction_NEXTPAGE
:
1350 sPPAction
= "ppaction://hlinkshowjump?jump=nextslide";
1352 case presentation::ClickAction_LASTPAGE
:
1353 sPPAction
= "ppaction://hlinkshowjump?jump=lastslide";
1355 case presentation::ClickAction_PREVPAGE
:
1356 sPPAction
= "ppaction://hlinkshowjump?jump=previousslide";
1358 case presentation::ClickAction_FIRSTPAGE
:
1359 sPPAction
= "ppaction://hlinkshowjump?jump=firstslide";
1361 case presentation::ClickAction_BOOKMARK
:
1362 sBookmark
= "#" + sBookmark
;
1369 // OOXTODO: //cNvPr children: XML_extLst, XML_hlinkHover
1370 if (bHasMediaURL
|| !sPPAction
.isEmpty())
1371 pFS
->singleElementNS(XML_a
, XML_hlinkClick
, FSNS(XML_r
, XML_id
), "", XML_action
,
1372 bHasMediaURL
? "ppaction://media" : sPPAction
);
1373 if( !sURL
.isEmpty() )
1375 OUString sRelId
= mpFB
->addRelation( mpFS
->getOutputStream(),
1376 oox::getRelationship(Relationship::HYPERLINK
),
1377 mpURLTransformer
->getTransformedString(sURL
),
1378 mpURLTransformer
->isExternalURL(sURL
));
1380 mpFS
->singleElementNS(XML_a
, XML_hlinkClick
, FSNS(XML_r
, XML_id
), sRelId
);
1383 if (!sBookmark
.isEmpty())
1385 bool bExtURL
= URLTransformer().isExternalURL(sBookmark
);
1386 sBookmark
= bExtURL
? sBookmark
: lcl_GetTarget(GetFB()->getModel(), sBookmark
);
1388 OUString sRelId
= mpFB
->addRelation(mpFS
->getOutputStream(),
1389 bExtURL
? oox::getRelationship(Relationship::HYPERLINK
)
1390 : oox::getRelationship(Relationship::SLIDE
),
1391 sBookmark
, bExtURL
);
1394 mpFS
->singleElementNS(XML_a
, XML_hlinkClick
, FSNS(XML_r
, XML_id
), sRelId
);
1396 mpFS
->singleElementNS(XML_a
, XML_hlinkClick
, FSNS(XML_r
, XML_id
), sRelId
, XML_action
,
1397 "ppaction://hlinksldjump");
1399 AddExtLst(pFS
, xShapeProps
);
1400 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
1402 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvPicPr
1403 // OOXTODO: XML_preferRelativeSize
1406 WriteMediaNonVisualProperties(xShape
);
1408 WriteNonVisualProperties(xShape
);
1410 pFS
->endElementNS( mnXmlNamespace
, XML_nvPicPr
);
1412 pFS
->startElementNS(mnXmlNamespace
, XML_blipFill
);
1416 WriteXGraphicBlip(xShapeProps
, xGraphic
, mbUserShapes
);
1418 else if (bHasMediaURL
)
1420 Reference
<graphic::XGraphic
> xFallbackGraphic
;
1421 if (xShapeProps
->getPropertySetInfo()->hasPropertyByName("FallbackGraphic"))
1422 xShapeProps
->getPropertyValue("FallbackGraphic") >>= xFallbackGraphic
;
1424 WriteXGraphicBlip(xShapeProps
, xFallbackGraphic
, mbUserShapes
);
1429 WriteSrcRectXGraphic(xShapeProps
, xGraphic
);
1432 // now we stretch always when we get pGraphic (when changing that
1433 // behavior, test n#780830 for regression, where the OLE sheet might get tiled
1434 bool bStretch
= false;
1435 if( !pGraphic
&& GetProperty( xShapeProps
, "FillBitmapStretch" ) )
1438 if ( pGraphic
|| bStretch
)
1439 pFS
->singleElementNS(XML_a
, XML_stretch
);
1443 // Graphic of media shapes is always stretched.
1444 pFS
->startElementNS(XML_a
, XML_stretch
);
1445 pFS
->singleElementNS(XML_a
, XML_fillRect
);
1446 pFS
->endElementNS(XML_a
, XML_stretch
);
1449 pFS
->endElementNS( mnXmlNamespace
, XML_blipFill
);
1451 // visual shape properties
1452 pFS
->startElementNS(mnXmlNamespace
, XML_spPr
);
1453 bool bFlipH
= false;
1454 if( xShapeProps
->getPropertySetInfo()->hasPropertyByName("IsMirrored") )
1456 xShapeProps
->getPropertyValue("IsMirrored") >>= bFlipH
;
1458 WriteShapeTransformation( xShape
, XML_a
, bFlipH
, false, false, false, true );
1459 WritePresetShape( "rect" );
1460 // graphic object can come with the frame (bnc#654525)
1461 WriteOutline( xShapeProps
);
1463 WriteShapeEffects( xShapeProps
);
1464 Write3DEffects( xShapeProps
, /*bIsText=*/false );
1466 pFS
->endElementNS( mnXmlNamespace
, XML_spPr
);
1468 pFS
->endElementNS( mnXmlNamespace
, XML_pic
);
1471 static void lcl_Rotate(sal_Int32 nAngle
, Point center
, awt::Point
& pt
)
1473 sal_Int16 nCos
, nSin
;
1491 sal_Int32 x
= pt
.X
- center
.X();
1492 sal_Int32 y
= pt
.Y
- center
.Y();
1493 pt
.X
= center
.X() + x
* nCos
- y
* nSin
;
1494 pt
.Y
= center
.Y() + y
* nCos
+ x
* nSin
;
1497 static void lcl_FlipHFlipV(tools::Polygon aPoly
, sal_Int32 nAngle
, bool& rFlipH
, bool& rFlipV
)
1499 Point aStart
= aPoly
[0];
1500 Point aEnd
= aPoly
[aPoly
.GetSize() - 1];
1502 if (aStart
.X() > aEnd
.X() && aStart
.Y() > aEnd
.Y())
1518 if (aStart
.X() < aEnd
.X() && aStart
.Y() < aEnd
.Y())
1532 if (aStart
.Y() < aEnd
.Y() && aStart
.X() > aEnd
.X())
1550 if (aStart
.Y() > aEnd
.Y() && aStart
.X() < aEnd
.X())
1567 static sal_Int32
lcl_GetAngle(tools::Polygon aPoly
)
1570 Point aStartPoint
= aPoly
[0];
1571 Point aEndPoint
= aPoly
[aPoly
.GetSize() - 1];
1572 if (aStartPoint
.X() == aPoly
[1].X())
1574 if ((aStartPoint
.X() < aEndPoint
.X() && aStartPoint
.Y() > aEndPoint
.Y())
1575 || (aStartPoint
.X() > aEndPoint
.X() && aStartPoint
.Y() < aEndPoint
.Y()))
1584 if (aStartPoint
.X() > aPoly
[1].X())
1593 // Adjust value decide the position, where the connector should turn.
1594 static void lcl_GetConnectorAdjustValue(const Reference
<XShape
>& xShape
, tools::Polygon aPoly
,
1595 ConnectorType eConnectorType
,
1596 std::vector
<std::pair
<sal_Int32
, sal_Int32
>>& rAvList
)
1598 sal_Int32 nAdjCount
= 0;
1599 if (eConnectorType
== ConnectorType_CURVE
)
1601 if (aPoly
.GetSize() == 4)
1603 if ((aPoly
[0].X() == aPoly
[1].X() && aPoly
[2].X() == aPoly
[3].X())
1604 || (aPoly
[0].Y() == aPoly
[1].Y() && aPoly
[2].Y() == aPoly
[3].Y()))
1606 nAdjCount
= 1; // curvedConnector3
1609 nAdjCount
= 0; // curvedConnector2
1611 else if (aPoly
.GetSize() > 4)
1613 if ((aPoly
[2].X() == aPoly
[3].X() && aPoly
[3].X() == aPoly
[4].X())
1614 || (aPoly
[2].Y() == aPoly
[3].Y() && aPoly
[3].Y() == aPoly
[4].Y()))
1616 nAdjCount
= 3; // curvedConnector5
1619 nAdjCount
= 2; // curvedConnector4
1624 switch (aPoly
.GetSize())
1627 nAdjCount
= 0; // bentConnector2
1630 nAdjCount
= 1; // bentConnector3
1633 nAdjCount
= 2; // bentConnector4
1636 nAdjCount
= 3; // bentConnector5
1643 sal_Int32 nAdjustValue
;
1644 Point aStart
= aPoly
[0];
1645 Point aEnd
= aPoly
[aPoly
.GetSize() - 1];
1647 for (sal_Int32 i
= 1; i
<= nAdjCount
; ++i
)
1649 Point aPt
= aPoly
[i
];
1651 if (aEnd
.Y() == aStart
.Y())
1652 aEnd
.setY(aStart
.Y() + 1);
1653 if (aEnd
.X() == aStart
.X())
1654 aEnd
.setX(aStart
.X() + 1);
1656 bool bVertical
= aPoly
[1].X() - aStart
.X() != 0 ? true : false;
1657 // vertical and horizon alternate
1659 bVertical
= !bVertical
;
1661 if (eConnectorType
== ConnectorType_CURVE
)
1663 awt::Size aSize
= xShape
->getSize();
1664 awt::Point aShapePosition
= xShape
->getPosition();
1665 tools::Rectangle aBoundRect
= aPoly
.GetBoundRect();
1669 if ((aBoundRect
.GetSize().Height() - aSize
.Height
) == 1)
1670 aPt
.setY(aPoly
[i
+ 1].Y());
1671 else if (aStart
.Y() > aPt
.Y())
1672 aPt
.setY(aShapePosition
.Y
);
1674 aPt
.setY(aShapePosition
.Y
+ aSize
.Height
);
1678 if ((aBoundRect
.GetSize().Width() - aSize
.Width
) == 1)
1679 aPt
.setX(aPoly
[i
+ 1].X());
1680 else if (aStart
.X() > aPt
.X())
1681 aPt
.setX(aShapePosition
.X
);
1683 aPt
.setX(aShapePosition
.X
+ aSize
.Width
);
1688 nAdjustValue
= ((aPt
.Y() - aStart
.Y()) * 100000) / (aEnd
.Y() - aStart
.Y());
1690 nAdjustValue
= ((aPt
.X() - aStart
.X()) * 100000) / (aEnd
.X() - aStart
.X());
1692 rAvList
.emplace_back(i
, nAdjustValue
);
1697 static sal_Int32
lcl_GetGluePointId(const Reference
<XShape
>& xShape
, sal_Int32 nGluePointId
)
1699 if (nGluePointId
> 3)
1700 return nGluePointId
- 4;
1703 bool bFlipH
= false;
1704 bool bFlipV
= false;
1705 Reference
<XPropertySet
> xShapeProps(xShape
, UNO_QUERY
);
1706 if (xShapeProps
.is() && xShapeProps
->getPropertySetInfo()
1707 && xShapeProps
->getPropertySetInfo()->hasPropertyByName("CustomShapeGeometry"))
1709 Sequence
<PropertyValue
> aGeometrySeq
;
1710 xShapeProps
->getPropertyValue("CustomShapeGeometry") >>= aGeometrySeq
;
1711 for (int i
= 0; i
< aGeometrySeq
.getLength(); i
++)
1713 const PropertyValue
& rProp
= aGeometrySeq
[i
];
1714 if (rProp
.Name
== "MirroredX")
1715 rProp
.Value
>>= bFlipH
;
1717 if (rProp
.Name
== "MirroredY")
1718 rProp
.Value
>>= bFlipV
;
1722 if ((!bFlipH
&& !bFlipV
) || (bFlipH
&& bFlipV
))
1724 // change id of the bounding box (1 <-> 3)
1725 if (nGluePointId
== 1)
1726 nGluePointId
= 3; // Right
1727 else if (nGluePointId
== 3)
1728 nGluePointId
= 1; // Left
1732 return nGluePointId
;
1735 ShapeExport
& ShapeExport::WriteConnectorShape( const Reference
< XShape
>& xShape
)
1737 bool bFlipH
= false;
1738 bool bFlipV
= false;
1739 sal_Int32 nAngle
= 0;
1740 sal_Int32 nStartGlueId
= 0;
1741 sal_Int32 nEndGlueId
= 0;
1743 SAL_INFO("oox.shape", "write connector shape");
1745 FSHelperPtr pFS
= GetFS();
1748 std::vector
<std::pair
<sal_Int32
, sal_Int32
>> aAdjustValueList
;
1749 Reference
< XPropertySet
> rXPropSet( xShape
, UNO_QUERY
);
1750 Reference
< XPropertyState
> rXPropState( xShape
, UNO_QUERY
);
1751 awt::Point aStartPoint
, aEndPoint
;
1752 Reference
< XShape
> rXShapeA
;
1753 Reference
< XShape
> rXShapeB
;
1754 PropertyState eState
;
1755 ConnectorType eConnectorType
= ConnectorType_STANDARD
;
1756 if (GetProperty(rXPropSet
, "EdgeKind"))
1757 mAny
>>= eConnectorType
;
1759 switch( eConnectorType
) {
1760 case ConnectorType_CURVE
:
1761 sGeometry
= "curvedConnector";
1763 case ConnectorType_LINES
:
1764 case ConnectorType_STANDARD
:
1765 sGeometry
= "bentConnector";
1768 case ConnectorType_LINE
:
1769 sGeometry
= "straightConnector1";
1773 if (GetPropertyAndState( rXPropSet
, rXPropState
, "EdgeStartPoint", eState
) && eState
== beans::PropertyState_DIRECT_VALUE
)
1775 mAny
>>= aStartPoint
;
1776 if (GetPropertyAndState( rXPropSet
, rXPropState
, "EdgeEndPoint", eState
) && eState
== beans::PropertyState_DIRECT_VALUE
)
1779 if (GetProperty(rXPropSet
, "EdgeStartConnection"))
1781 if (GetProperty(rXPropSet
, "EdgeEndConnection"))
1784 if (GetProperty(rXPropSet
, "StartGluePointIndex"))
1785 mAny
>>= nStartGlueId
;
1786 if (nStartGlueId
!= -1)
1787 nStartGlueId
= lcl_GetGluePointId(rXShapeA
, nStartGlueId
);
1789 if (GetProperty(rXPropSet
, "EndGluePointIndex"))
1790 mAny
>>= nEndGlueId
;
1791 if (nEndGlueId
!= -1)
1792 nEndGlueId
= lcl_GetGluePointId(rXShapeB
, nEndGlueId
);
1794 // Position is relative to group in Word, but relative to anchor of group in API.
1795 if (GetDocumentType() == DOCUMENT_DOCX
&& !mbUserShapes
&& m_xParent
.is())
1797 awt::Point aParentPos
= m_xParent
->getPosition();
1798 aStartPoint
.X
-= aParentPos
.X
;
1799 aStartPoint
.Y
-= aParentPos
.Y
;
1800 aEndPoint
.X
-= aParentPos
.X
;
1801 aEndPoint
.Y
-= aParentPos
.Y
;
1803 EscherConnectorListEntry
aConnectorEntry( xShape
, aStartPoint
, rXShapeA
, aEndPoint
, rXShapeB
);
1805 if (eConnectorType
!= ConnectorType_LINE
)
1807 tools::PolyPolygon aPolyPolygon
= EscherPropertyContainer::GetPolyPolygon(xShape
);
1808 if (aPolyPolygon
.Count() > 0)
1810 tools::Polygon aPoly
= aPolyPolygon
.GetObject(0);
1811 lcl_GetConnectorAdjustValue(xShape
, aPoly
, eConnectorType
, aAdjustValueList
);
1812 nAngle
= lcl_GetAngle(aPoly
);
1813 lcl_FlipHFlipV(aPoly
, nAngle
, bFlipH
, bFlipV
);
1816 Point
center((aEndPoint
.X
+ aStartPoint
.X
) / 2, (aEndPoint
.Y
+ aStartPoint
.Y
) / 2);
1817 lcl_Rotate(nAngle
, center
, aStartPoint
);
1818 lcl_Rotate(nAngle
, center
, aEndPoint
);
1821 sGeometry
= sGeometry
+ OUString::number(aAdjustValueList
.size() + 2);
1825 tools::Rectangle
aRect( Point( aStartPoint
.X
, aStartPoint
.Y
), Point( aEndPoint
.X
, aEndPoint
.Y
) );
1826 if( aRect
.getOpenWidth() < 0 ) {
1827 aRect
.SetLeft(aEndPoint
.X
);
1828 aRect
.setWidth( aStartPoint
.X
- aEndPoint
.X
);
1829 if (eConnectorType
== ConnectorType_LINE
)
1833 if( aRect
.getOpenHeight() < 0 ) {
1834 aRect
.SetTop(aEndPoint
.Y
);
1835 aRect
.setHeight( aStartPoint
.Y
- aEndPoint
.Y
);
1836 if (eConnectorType
== ConnectorType_LINE
)
1840 // tdf#99810 connector shape (cxnSp) is not valid with namespace 'wps'
1841 const auto nShapeNode
= (mnXmlNamespace
== XML_wps
? XML_wsp
: XML_cxnSp
);
1842 pFS
->startElementNS(mnXmlNamespace
, nShapeNode
);
1844 if (mnXmlNamespace
== XML_wps
)
1846 // non visual connector shape drawing properties
1847 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvCnPr
);
1851 // non visual shape properties
1852 pFS
->startElementNS(mnXmlNamespace
, XML_nvCxnSpPr
);
1853 pFS
->startElementNS(mnXmlNamespace
, XML_cNvPr
,
1854 XML_id
, OString::number(GetNewShapeID(xShape
)),
1855 XML_name
, GetShapeName(xShape
));
1856 AddExtLst(pFS
, rXPropSet
);
1857 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
1858 // non visual connector shape drawing properties
1859 pFS
->startElementNS(mnXmlNamespace
, XML_cNvCxnSpPr
);
1861 if (GetShapeID(rXShapeA
) == -1)
1862 GetNewShapeID(rXShapeA
);
1863 if (GetShapeID(rXShapeB
) == -1)
1864 GetNewShapeID(rXShapeB
);
1865 WriteConnectorConnections(nStartGlueId
, nEndGlueId
, GetShapeID(rXShapeA
), GetShapeID(rXShapeB
));
1866 pFS
->endElementNS(mnXmlNamespace
, XML_cNvCxnSpPr
);
1867 if (GetDocumentType() == DOCUMENT_PPTX
)
1868 pFS
->singleElementNS(mnXmlNamespace
, XML_nvPr
);
1869 pFS
->endElementNS(mnXmlNamespace
, XML_nvCxnSpPr
);
1872 // visual shape properties
1873 pFS
->startElementNS(mnXmlNamespace
, XML_spPr
);
1874 WriteTransformation( xShape
, aRect
, XML_a
, bFlipH
, bFlipV
, nAngle
);
1875 // TODO: write adjustments (ppt export doesn't work well there either)
1876 WritePresetShape( sGeometry
.toUtf8(), aAdjustValueList
);
1877 Reference
< XPropertySet
> xShapeProps( xShape
, UNO_QUERY
);
1878 if( xShapeProps
.is() )
1879 WriteOutline( xShapeProps
);
1880 pFS
->endElementNS( mnXmlNamespace
, XML_spPr
);
1882 // connector shape (cxnSp) cannot contain text (txBody) (according to schema)
1883 if( nShapeNode
!= XML_cxnSp
)
1886 WriteTextBox( xShape
, mnXmlNamespace
);
1889 pFS
->endElementNS(mnXmlNamespace
, nShapeNode
);
1894 ShapeExport
& ShapeExport::WriteLineShape( const Reference
< XShape
>& xShape
)
1896 bool bFlipH
= false;
1897 bool bFlipV
= false;
1899 SAL_INFO("oox.shape", "write line shape");
1901 FSHelperPtr pFS
= GetFS();
1903 pFS
->startElementNS(mnXmlNamespace
, (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_sp
: XML_wsp
));
1905 tools::PolyPolygon aPolyPolygon
= EscherPropertyContainer::GetPolyPolygon( xShape
);
1906 if( aPolyPolygon
.Count() == 1 && aPolyPolygon
[ 0 ].GetSize() == 2)
1908 const tools::Polygon
& rPoly
= aPolyPolygon
[ 0 ];
1910 bFlipH
= ( rPoly
[ 0 ].X() > rPoly
[ 1 ].X() );
1911 bFlipV
= ( rPoly
[ 0 ].Y() > rPoly
[ 1 ].Y() );
1914 Reference
<XPropertySet
> const xShapeProps(xShape
, UNO_QUERY
);
1915 // non visual shape properties
1916 if (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
)
1918 pFS
->startElementNS(mnXmlNamespace
, XML_nvSpPr
);
1919 pFS
->startElementNS(mnXmlNamespace
, XML_cNvPr
,
1920 XML_id
, OString::number(GetNewShapeID(xShape
)),
1921 XML_name
, GetShapeName(xShape
));
1922 AddExtLst(pFS
, xShapeProps
);
1923 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
1925 pFS
->singleElementNS( mnXmlNamespace
, XML_cNvSpPr
);
1926 if (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
)
1928 WriteNonVisualProperties( xShape
);
1929 pFS
->endElementNS( mnXmlNamespace
, XML_nvSpPr
);
1932 // visual shape properties
1933 pFS
->startElementNS(mnXmlNamespace
, XML_spPr
);
1934 WriteShapeTransformation( xShape
, XML_a
, bFlipH
, bFlipV
, true);
1935 WritePresetShape( "line" );
1936 if( xShapeProps
.is() )
1937 WriteOutline( xShapeProps
);
1938 pFS
->endElementNS( mnXmlNamespace
, XML_spPr
);
1941 pFS
->startElementNS(mnXmlNamespace
, XML_style
);
1942 WriteShapeStyle( xShapeProps
);
1943 pFS
->endElementNS( mnXmlNamespace
, XML_style
);
1946 WriteTextBox( xShape
, mnXmlNamespace
);
1948 pFS
->endElementNS( mnXmlNamespace
, (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_sp
: XML_wsp
) );
1953 ShapeExport
& ShapeExport::WriteNonVisualDrawingProperties( const Reference
< XShape
>& xShape
, const char* pName
)
1955 FSHelperPtr pFS
= GetFS();
1957 Reference
<XPropertySet
> const xShapeProps(xShape
, UNO_QUERY
);
1958 pFS
->startElementNS(mnXmlNamespace
, XML_cNvPr
,
1959 XML_id
, OString::number(GetNewShapeID(xShape
)),
1961 AddExtLst(pFS
, xShapeProps
);
1962 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
1967 ShapeExport
& ShapeExport::WriteNonVisualProperties( const Reference
< XShape
>& )
1969 // Override to generate //nvPr elements.
1973 ShapeExport
& ShapeExport::WriteRectangleShape( const Reference
< XShape
>& xShape
)
1975 SAL_INFO("oox.shape", "write rectangle shape");
1977 FSHelperPtr pFS
= GetFS();
1979 pFS
->startElementNS(mnXmlNamespace
, (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_sp
: XML_wsp
));
1981 sal_Int32 nRadius
= 0;
1983 Reference
< XPropertySet
> xShapeProps( xShape
, UNO_QUERY
);
1984 if( xShapeProps
.is() )
1986 xShapeProps
->getPropertyValue( "CornerRadius" ) >>= nRadius
;
1991 nRadius
= MapSize( awt::Size( nRadius
, 0 ) ).Width
;
1993 //TODO: use nRadius value more precisely than just deciding whether to use
1994 // "rect" or "roundRect" preset shape below
1996 // non visual shape properties
1997 if (GetDocumentType() == DOCUMENT_DOCX
&& !mbUserShapes
)
1998 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvSpPr
);
1999 pFS
->startElementNS(mnXmlNamespace
, XML_nvSpPr
);
2000 pFS
->startElementNS(mnXmlNamespace
, XML_cNvPr
,
2001 XML_id
, OString::number(GetNewShapeID(xShape
)),
2002 XML_name
, GetShapeName(xShape
));
2003 AddExtLst(pFS
, xShapeProps
);
2004 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
2005 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvSpPr
);
2006 WriteNonVisualProperties( xShape
);
2007 pFS
->endElementNS( mnXmlNamespace
, XML_nvSpPr
);
2009 // visual shape properties
2010 pFS
->startElementNS(mnXmlNamespace
, XML_spPr
);
2011 WriteShapeTransformation( xShape
, XML_a
);
2012 WritePresetShape( nRadius
== 0 ? "rect" : "roundRect" );
2013 Reference
< XPropertySet
> xProps( xShape
, UNO_QUERY
);
2016 WriteFill( xProps
);
2017 WriteOutline( xProps
);
2019 pFS
->endElementNS( mnXmlNamespace
, XML_spPr
);
2022 WriteTextBox( xShape
, mnXmlNamespace
);
2024 pFS
->endElementNS( mnXmlNamespace
, (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_sp
: XML_wsp
) );
2029 typedef ShapeExport
& (ShapeExport::*ShapeConverter
)( const Reference
< XShape
>& );
2030 typedef std::unordered_map
< const char*, ShapeConverter
, rtl::CStringHash
, rtl::CStringEqual
> NameToConvertMapType
;
2032 static const NameToConvertMapType
& lcl_GetConverters()
2034 static NameToConvertMapType
const shape_converters
2036 // tdf#98736 export CaptionShape as TextShape, because it is non-ooxml shape and
2037 // we can't export this shape as CustomShape
2038 // TODO: WriteCaptionShape
2039 { "com.sun.star.drawing.CaptionShape" , &ShapeExport::WriteTextShape
},
2041 { "com.sun.star.drawing.ClosedBezierShape" , &ShapeExport::WriteClosedPolyPolygonShape
},
2042 { "com.sun.star.drawing.ConnectorShape" , &ShapeExport::WriteConnectorShape
},
2043 { "com.sun.star.drawing.CustomShape" , &ShapeExport::WriteCustomShape
},
2044 { "com.sun.star.drawing.EllipseShape" , &ShapeExport::WriteEllipseShape
},
2045 { "com.sun.star.drawing.GraphicObjectShape" , &ShapeExport::WriteGraphicObjectShape
},
2046 { "com.sun.star.drawing.LineShape" , &ShapeExport::WriteLineShape
},
2047 { "com.sun.star.drawing.MediaShape" , &ShapeExport::WriteGraphicObjectShape
},
2048 { "com.sun.star.drawing.OpenBezierShape" , &ShapeExport::WriteOpenPolyPolygonShape
},
2049 { "com.sun.star.drawing.PolyPolygonShape" , &ShapeExport::WriteClosedPolyPolygonShape
},
2050 { "com.sun.star.drawing.PolyLineShape" , &ShapeExport::WriteOpenPolyPolygonShape
},
2051 { "com.sun.star.drawing.RectangleShape" , &ShapeExport::WriteRectangleShape
},
2052 { "com.sun.star.drawing.OLE2Shape" , &ShapeExport::WriteOLE2Shape
},
2053 { "com.sun.star.drawing.TableShape" , &ShapeExport::WriteTableShape
},
2054 { "com.sun.star.drawing.TextShape" , &ShapeExport::WriteTextShape
},
2055 { "com.sun.star.drawing.GroupShape" , &ShapeExport::WriteGroupShape
},
2057 { "com.sun.star.presentation.GraphicObjectShape" , &ShapeExport::WriteGraphicObjectShape
},
2058 { "com.sun.star.presentation.MediaShape" , &ShapeExport::WriteGraphicObjectShape
},
2059 { "com.sun.star.presentation.ChartShape" , &ShapeExport::WriteOLE2Shape
},
2060 { "com.sun.star.presentation.OLE2Shape" , &ShapeExport::WriteOLE2Shape
},
2061 { "com.sun.star.presentation.TableShape" , &ShapeExport::WriteTableShape
},
2062 { "com.sun.star.presentation.TextShape" , &ShapeExport::WriteTextShape
},
2064 { "com.sun.star.presentation.DateTimeShape" , &ShapeExport::WriteTextShape
},
2065 { "com.sun.star.presentation.FooterShape" , &ShapeExport::WriteTextShape
},
2066 { "com.sun.star.presentation.HeaderShape" , &ShapeExport::WriteTextShape
},
2067 { "com.sun.star.presentation.NotesShape" , &ShapeExport::WriteTextShape
},
2068 { "com.sun.star.presentation.OutlinerShape" , &ShapeExport::WriteTextShape
},
2069 { "com.sun.star.presentation.SlideNumberShape" , &ShapeExport::WriteTextShape
},
2070 { "com.sun.star.presentation.TitleTextShape" , &ShapeExport::WriteTextShape
},
2072 return shape_converters
;
2075 ShapeExport
& ShapeExport::WriteShape( const Reference
< XShape
>& xShape
)
2078 throw lang::IllegalArgumentException();
2080 OUString sShapeType
= xShape
->getShapeType();
2081 SAL_INFO("oox.shape", "write shape: " << sShapeType
);
2082 NameToConvertMapType::const_iterator aConverter
2083 = lcl_GetConverters().find(sShapeType
.toUtf8().getStr());
2084 if (aConverter
== lcl_GetConverters().end())
2086 SAL_INFO("oox.shape", "unknown shape");
2087 return WriteUnknownShape( xShape
);
2090 if (GetDocumentType() == DOCUMENT_PPTX
)
2092 Reference
< XPropertySet
> xShapeProperties(xShape
, UNO_QUERY
);
2093 if (xShapeProperties
&& xShapeProperties
->getPropertySetInfo()
2094 && xShapeProperties
->getPropertySetInfo()->hasPropertyByName("IsPresentationObject")
2095 && xShapeProperties
->getPropertyValue("IsPresentationObject").hasValue())
2096 mbPlaceholder
= xShapeProperties
->getPropertyValue("IsPresentationObject").get
<bool>();
2099 (this->*(aConverter
->second
))( xShape
);
2104 static bool lcl_isTextBox(const Reference
<XInterface
>& xIface
)
2106 uno::Reference
<beans::XPropertySet
> xPropertySet(xIface
, uno::UNO_QUERY
);
2107 if (!xPropertySet
.is())
2109 uno::Reference
<beans::XPropertySetInfo
> xPropertySetInfo
= xPropertySet
->getPropertySetInfo();
2110 if (!xPropertySetInfo
->hasPropertyByName("TextBox"))
2112 css::uno::Any
aTextBox(xPropertySet
->getPropertyValue("TextBox"));
2113 if (!aTextBox
.hasValue())
2115 return aTextBox
.get
<bool>();
2118 ShapeExport
& ShapeExport::WriteTextBox( const Reference
< XInterface
>& xIface
, sal_Int32 nXmlNamespace
, bool bWritePropertiesAsLstStyles
)
2120 // In case this shape has an associated textbox, then export that, and we're done.
2121 if (GetDocumentType() == DOCUMENT_DOCX
&& !mbUserShapes
&& GetTextExport())
2123 if (lcl_isTextBox(xIface
))
2125 GetTextExport()->WriteTextBox(uno::Reference
<drawing::XShape
>(xIface
, uno::UNO_QUERY_THROW
));
2126 WriteText( xIface
, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace
);
2131 Reference
< XText
> xXText( xIface
, UNO_QUERY
);
2132 if( (NonEmptyText( xIface
) || GetDocumentType() == DOCUMENT_PPTX
)
2135 FSHelperPtr pFS
= GetFS();
2137 pFS
->startElementNS(nXmlNamespace
,
2138 (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_txBody
: XML_txbx
));
2139 WriteText(xIface
, /*bBodyPr=*/(GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
), /*bText=*/true,
2140 /*nXmlNamespace=*/0, /*bWritePropertiesAsLstStyles=*/bWritePropertiesAsLstStyles
);
2141 pFS
->endElementNS( nXmlNamespace
, (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_txBody
: XML_txbx
) );
2142 if (GetDocumentType() == DOCUMENT_DOCX
&& !mbUserShapes
)
2143 WriteText( xIface
, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace
);
2145 else if (GetDocumentType() == DOCUMENT_DOCX
&& !mbUserShapes
)
2146 mpFS
->singleElementNS(nXmlNamespace
, XML_bodyPr
);
2151 void ShapeExport::WriteTable( const Reference
< XShape
>& rXShape
)
2153 Reference
< XTable
> xTable
;
2154 Reference
< XPropertySet
> xPropSet( rXShape
, UNO_QUERY
);
2156 mpFS
->startElementNS(XML_a
, XML_graphic
);
2157 mpFS
->startElementNS(XML_a
, XML_graphicData
,
2158 XML_uri
, "http://schemas.openxmlformats.org/drawingml/2006/table");
2160 if ( xPropSet
.is() && ( xPropSet
->getPropertyValue( "Model" ) >>= xTable
) )
2162 mpFS
->startElementNS(XML_a
, XML_tbl
);
2163 mpFS
->startElementNS(XML_a
, XML_tblPr
);
2164 WriteShapeEffects(xPropSet
);
2165 mpFS
->endElementNS(XML_a
, XML_tblPr
);
2167 Reference
< container::XIndexAccess
> xColumns( xTable
->getColumns(), UNO_QUERY_THROW
);
2168 Reference
< container::XIndexAccess
> xRows( xTable
->getRows(), UNO_QUERY_THROW
);
2169 sal_uInt16 nRowCount
= static_cast< sal_uInt16
>( xRows
->getCount() );
2170 sal_uInt16 nColumnCount
= static_cast< sal_uInt16
>( xColumns
->getCount() );
2172 mpFS
->startElementNS(XML_a
, XML_tblGrid
);
2174 for ( sal_Int32 x
= 0; x
< nColumnCount
; x
++ )
2176 Reference
< XPropertySet
> xColPropSet( xColumns
->getByIndex( x
), UNO_QUERY_THROW
);
2177 sal_Int32
nWidth(0);
2178 xColPropSet
->getPropertyValue( "Width" ) >>= nWidth
;
2180 mpFS
->singleElementNS(XML_a
, XML_gridCol
,
2181 XML_w
, OString::number(oox::drawingml::convertHmmToEmu(nWidth
)));
2184 mpFS
->endElementNS( XML_a
, XML_tblGrid
);
2186 // map for holding the transpose index of the merged cells and pair<parentTransposeIndex, parentCell>
2187 typedef std::unordered_map
<sal_Int32
, std::pair
<sal_Int32
, Reference
< XMergeableCell
> > > transposeTableMap
;
2188 transposeTableMap mergedCellMap
;
2190 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; nRow
++ )
2192 Reference
< XPropertySet
> xRowPropSet( xRows
->getByIndex( nRow
), UNO_QUERY_THROW
);
2193 sal_Int32
nRowHeight(0);
2195 xRowPropSet
->getPropertyValue( "Height" ) >>= nRowHeight
;
2197 mpFS
->startElementNS(XML_a
, XML_tr
,
2198 XML_h
, OString::number(oox::drawingml::convertHmmToEmu(nRowHeight
)));
2199 for( sal_Int32 nColumn
= 0; nColumn
< nColumnCount
; nColumn
++ )
2201 Reference
< XMergeableCell
> xCell( xTable
->getCellByPosition( nColumn
, nRow
),
2203 sal_Int32 transposedIndexofCell
= (nRow
* nColumnCount
) + nColumn
;
2205 //assume we will open a cell, set to false below if we won't
2206 bool bCellOpened
= true;
2208 if(xCell
->getColumnSpan() > 1 && xCell
->getRowSpan() > 1)
2210 // having both : horizontal and vertical merge
2211 mpFS
->startElementNS(XML_a
, XML_tc
,
2212 XML_gridSpan
, OString::number(xCell
->getColumnSpan()),
2213 XML_rowSpan
, OString::number(xCell
->getRowSpan()));
2214 // since, XMergeableCell doesn't have the information about
2215 // cell having hMerge or vMerge.
2216 // So, Populating the merged cell map in-order to use it to
2217 // decide the attribute for the individual cell.
2218 for(sal_Int32 columnIndex
= nColumn
; columnIndex
< nColumn
+xCell
->getColumnSpan(); ++columnIndex
)
2220 for(sal_Int32 rowIndex
= nRow
; rowIndex
< nRow
+xCell
->getRowSpan(); ++rowIndex
)
2222 sal_Int32 transposeIndexForMergeCell
=
2223 (rowIndex
* nColumnCount
) + columnIndex
;
2224 mergedCellMap
[transposeIndexForMergeCell
] =
2225 std::make_pair(transposedIndexofCell
, xCell
);
2230 else if(xCell
->getColumnSpan() > 1)
2232 // having : horizontal merge
2233 mpFS
->startElementNS(XML_a
, XML_tc
,
2234 XML_gridSpan
, OString::number(xCell
->getColumnSpan()));
2235 for(sal_Int32 columnIndex
= nColumn
; columnIndex
< nColumn
+ xCell
->getColumnSpan(); ++columnIndex
) {
2236 sal_Int32 transposeIndexForMergeCell
= (nRow
*nColumnCount
) + columnIndex
;
2237 mergedCellMap
[transposeIndexForMergeCell
] =
2238 std::make_pair(transposedIndexofCell
, xCell
);
2241 else if(xCell
->getRowSpan() > 1)
2243 // having : vertical merge
2244 mpFS
->startElementNS(XML_a
, XML_tc
,
2245 XML_rowSpan
, OString::number(xCell
->getRowSpan()));
2247 for(sal_Int32 rowIndex
= nRow
; rowIndex
< nRow
+ xCell
->getRowSpan(); ++rowIndex
) {
2248 sal_Int32 transposeIndexForMergeCell
= (rowIndex
*nColumnCount
) + nColumn
;
2249 mergedCellMap
[transposeIndexForMergeCell
] =
2250 std::make_pair(transposedIndexofCell
, xCell
);
2255 // now, the cell can be an independent cell or
2256 // it can be a cell which is been merged to some parent cell
2257 if(!xCell
->isMerged())
2260 mpFS
->startElementNS(XML_a
, XML_tc
);
2264 // it a merged cell to some parent cell
2265 // find the parent cell for the current cell at hand
2266 transposeTableMap::iterator it
= mergedCellMap
.find(transposedIndexofCell
);
2267 if(it
!= mergedCellMap
.end())
2269 sal_Int32 transposeIndexOfParent
= it
->second
.first
;
2270 Reference
< XMergeableCell
> parentCell
= it
->second
.second
;
2271 // finding the row and column index for the parent cell from transposed index
2272 sal_Int32 parentColumnIndex
= transposeIndexOfParent
% nColumnCount
;
2273 sal_Int32 parentRowIndex
= transposeIndexOfParent
/ nColumnCount
;
2274 if(nColumn
== parentColumnIndex
)
2276 // the cell is vertical merge and it might have gridspan
2277 if(parentCell
->getColumnSpan() > 1)
2279 // vMerge and has gridSpan
2280 mpFS
->startElementNS(XML_a
, XML_tc
,
2281 XML_vMerge
, OString::number(1),
2282 XML_gridSpan
, OString::number(xCell
->getColumnSpan()));
2287 mpFS
->startElementNS(XML_a
, XML_tc
,
2288 XML_vMerge
, OString::number(1));
2291 else if(nRow
== parentRowIndex
)
2293 // the cell is horizontal merge and it might have rowspan
2294 if(parentCell
->getRowSpan() > 1)
2296 // hMerge and has rowspan
2297 mpFS
->startElementNS(XML_a
, XML_tc
,
2298 XML_hMerge
, OString::number(1),
2299 XML_rowSpan
, OString::number(xCell
->getRowSpan()));
2304 mpFS
->startElementNS(XML_a
, XML_tc
,
2305 XML_hMerge
, OString::number(1));
2310 // has hMerge and vMerge
2311 mpFS
->startElementNS(XML_a
, XML_tc
,
2312 XML_vMerge
, OString::number(1),
2313 XML_hMerge
, OString::number(1));
2317 bCellOpened
= false;
2323 WriteTextBox( xCell
, XML_a
);
2325 Reference
< XPropertySet
> xCellPropSet(xCell
, UNO_QUERY_THROW
);
2326 WriteTableCellProperties(xCellPropSet
);
2328 mpFS
->endElementNS( XML_a
, XML_tc
);
2332 mpFS
->endElementNS( XML_a
, XML_tr
);
2335 mpFS
->endElementNS( XML_a
, XML_tbl
);
2338 mpFS
->endElementNS( XML_a
, XML_graphicData
);
2339 mpFS
->endElementNS( XML_a
, XML_graphic
);
2342 void ShapeExport::WriteTableCellProperties(const Reference
< XPropertySet
>& xCellPropSet
)
2344 sal_Int32
nLeftMargin(0), nRightMargin(0);
2345 TextVerticalAdjust eVerticalAlignment
;
2346 const char* sVerticalAlignment
;
2348 Any aLeftMargin
= xCellPropSet
->getPropertyValue("TextLeftDistance");
2349 aLeftMargin
>>= nLeftMargin
;
2351 Any aRightMargin
= xCellPropSet
->getPropertyValue("TextRightDistance");
2352 aRightMargin
>>= nRightMargin
;
2354 Any aVerticalAlignment
= xCellPropSet
->getPropertyValue("TextVerticalAdjust");
2355 aVerticalAlignment
>>= eVerticalAlignment
;
2356 sVerticalAlignment
= GetTextVerticalAdjust(eVerticalAlignment
);
2358 sal_Int32 nRotateAngle
= 0;
2359 Any aRotateAngle
= xCellPropSet
->getPropertyValue("RotateAngle");
2360 aRotateAngle
>>= nRotateAngle
;
2361 std::optional
<OString
> aTextVerticalValue
= GetTextVerticalType(nRotateAngle
);
2363 Sequence
<PropertyValue
> aGrabBag
;
2364 if( !aTextVerticalValue
&&
2365 (xCellPropSet
->getPropertyValue("CellInteropGrabBag") >>= aGrabBag
) )
2367 for (auto const& rIt
: std::as_const(aGrabBag
))
2369 if (rIt
.Name
== "mso-tcPr-vert-value")
2371 aTextVerticalValue
= rIt
.Value
.get
<OUString
>().toUtf8();
2377 mpFS
->startElementNS(XML_a
, XML_tcPr
, XML_anchor
, sVerticalAlignment
,
2378 XML_vert
, aTextVerticalValue
,
2379 XML_marL
, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeftMargin
)), nLeftMargin
> 0),
2380 XML_marR
, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRightMargin
)), nRightMargin
> 0));
2382 // Write background fill for table cell.
2384 // tcW : Table cell width
2385 WriteTableCellBorders(xCellPropSet
);
2386 DrawingML::WriteFill(xCellPropSet
);
2387 mpFS
->endElementNS( XML_a
, XML_tcPr
);
2390 void ShapeExport::WriteBorderLine(const sal_Int32 XML_line
, const BorderLine2
& rBorderLine
)
2392 // While importing the table cell border line width, it converts EMU->Hmm then divided result by 2.
2393 // To get original value of LineWidth need to multiple by 2.
2394 sal_Int32 nBorderWidth
= rBorderLine
.LineWidth
;
2396 nBorderWidth
= oox::drawingml::convertHmmToEmu( nBorderWidth
);
2398 if ( nBorderWidth
> 0 )
2400 mpFS
->startElementNS(XML_a
, XML_line
, XML_w
, OString::number(nBorderWidth
));
2401 if ( rBorderLine
.Color
== sal_Int32( COL_AUTO
) )
2402 mpFS
->singleElementNS(XML_a
, XML_noFill
);
2404 DrawingML::WriteSolidFill( ::Color(ColorTransparency
, rBorderLine
.Color
) );
2406 OUString sBorderStyle
;
2407 sal_Int16 nStyle
= rBorderLine
.LineStyle
;
2408 mAny
.setValue(&nStyle
, cppu::UnoType
<sal_Int16
>::get());
2409 switch (*o3tl::doAccess
<sal_Int16
>(mAny
))
2411 case ::table::BorderLineStyle::SOLID
:
2412 sBorderStyle
= "solid";
2414 case ::table::BorderLineStyle::DOTTED
:
2415 sBorderStyle
= "dot";
2417 case ::table::BorderLineStyle::DASHED
:
2418 sBorderStyle
= "dash";
2420 case ::table::BorderLineStyle::DASH_DOT
:
2421 sBorderStyle
= "dashDot";
2423 case ::table::BorderLineStyle::DASH_DOT_DOT
:
2424 sBorderStyle
= "sysDashDotDot";
2427 mpFS
->singleElementNS(XML_a
, XML_prstDash
, XML_val
, sBorderStyle
);
2428 mpFS
->endElementNS(XML_a
, XML_line
);
2430 else if( nBorderWidth
== 0)
2432 mpFS
->startElementNS(XML_a
, XML_line
);
2433 mpFS
->singleElementNS(XML_a
, XML_noFill
);
2434 mpFS
->endElementNS( XML_a
, XML_line
);
2438 void ShapeExport::WriteTableCellBorders(const Reference
< XPropertySet
>& xCellPropSet
)
2440 BorderLine2 aBorderLine
;
2442 // lnL - Left Border Line Properties of table cell
2443 xCellPropSet
->getPropertyValue("LeftBorder") >>= aBorderLine
;
2444 WriteBorderLine( XML_lnL
, aBorderLine
);
2446 // lnR - Right Border Line Properties of table cell
2447 xCellPropSet
->getPropertyValue("RightBorder") >>= aBorderLine
;
2448 WriteBorderLine( XML_lnR
, aBorderLine
);
2450 // lnT - Top Border Line Properties of table cell
2451 xCellPropSet
->getPropertyValue("TopBorder") >>= aBorderLine
;
2452 WriteBorderLine( XML_lnT
, aBorderLine
);
2454 // lnB - Bottom Border Line Properties of table cell
2455 xCellPropSet
->getPropertyValue("BottomBorder") >>= aBorderLine
;
2456 WriteBorderLine( XML_lnB
, aBorderLine
);
2459 ShapeExport
& ShapeExport::WriteTableShape( const Reference
< XShape
>& xShape
)
2461 FSHelperPtr pFS
= GetFS();
2463 pFS
->startElementNS(mnXmlNamespace
, XML_graphicFrame
);
2465 pFS
->startElementNS(mnXmlNamespace
, XML_nvGraphicFramePr
);
2467 Reference
<XPropertySet
> const xShapeProps(xShape
, UNO_QUERY
);
2468 pFS
->startElementNS(mnXmlNamespace
, XML_cNvPr
,
2469 XML_id
, OString::number(GetNewShapeID(xShape
)),
2470 XML_name
, GetShapeName(xShape
));
2471 AddExtLst(pFS
, xShapeProps
);
2472 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
2474 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvGraphicFramePr
);
2476 if( GetDocumentType() == DOCUMENT_PPTX
)
2477 pFS
->singleElementNS(mnXmlNamespace
, XML_nvPr
);
2478 pFS
->endElementNS( mnXmlNamespace
, XML_nvGraphicFramePr
);
2480 WriteShapeTransformation( xShape
, mnXmlNamespace
);
2481 WriteTable( xShape
);
2483 pFS
->endElementNS( mnXmlNamespace
, XML_graphicFrame
);
2488 ShapeExport
& ShapeExport::WriteTextShape( const Reference
< XShape
>& xShape
)
2490 FSHelperPtr pFS
= GetFS();
2491 Reference
<XPropertySet
> xShapeProps(xShape
, UNO_QUERY
);
2493 pFS
->startElementNS(mnXmlNamespace
, (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_sp
: XML_wsp
));
2495 // non visual shape properties
2496 if (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
)
2498 pFS
->startElementNS(mnXmlNamespace
, XML_nvSpPr
);
2499 pFS
->startElementNS(mnXmlNamespace
, XML_cNvPr
,
2500 XML_id
, OString::number(GetNewShapeID(xShape
)),
2501 XML_name
, GetShapeName(xShape
));
2503 if (GetProperty(xShapeProps
, "URL"))
2506 if (!sURL
.isEmpty())
2508 OUString sRelId
= mpFB
->addRelation(mpFS
->getOutputStream(),
2509 oox::getRelationship(Relationship::HYPERLINK
),
2510 mpURLTransformer
->getTransformedString(sURL
),
2511 mpURLTransformer
->isExternalURL(sURL
));
2513 mpFS
->singleElementNS(XML_a
, XML_hlinkClick
, FSNS(XML_r
, XML_id
), sRelId
);
2515 AddExtLst(pFS
, xShapeProps
);
2516 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
2518 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvSpPr
, XML_txBox
, "1");
2519 if (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
)
2521 WriteNonVisualProperties( xShape
);
2522 pFS
->endElementNS( mnXmlNamespace
, XML_nvSpPr
);
2525 // visual shape properties
2526 pFS
->startElementNS(mnXmlNamespace
, XML_spPr
);
2527 WriteShapeTransformation( xShape
, XML_a
);
2528 WritePresetShape( "rect" );
2529 uno::Reference
<beans::XPropertySet
> xPropertySet(xShape
, UNO_QUERY
);
2530 if (!IsFontworkShape(xShapeProps
)) // Fontwork needs fill and outline in run properties instead.
2532 WriteBlipOrNormalFill(xPropertySet
, "Graphic", xShape
->getSize());
2533 WriteOutline(xPropertySet
);
2534 WriteShapeEffects(xPropertySet
);
2536 pFS
->endElementNS( mnXmlNamespace
, XML_spPr
);
2538 WriteTextBox( xShape
, mnXmlNamespace
);
2540 pFS
->endElementNS( mnXmlNamespace
, (GetDocumentType() != DOCUMENT_DOCX
|| mbUserShapes
? XML_sp
: XML_wsp
) );
2545 void ShapeExport::WriteMathShape(Reference
<XShape
> const& xShape
)
2547 Reference
<XPropertySet
> const xPropSet(xShape
, UNO_QUERY
);
2548 assert(xPropSet
.is());
2549 Reference
<XModel
> xMathModel
;
2550 xPropSet
->getPropertyValue("Model") >>= xMathModel
;
2551 assert(xMathModel
.is());
2552 assert(GetDocumentType() != DOCUMENT_DOCX
); // should be written in DocxAttributeOutput
2553 SAL_WARN_IF(GetDocumentType() == DOCUMENT_XLSX
, "oox.shape", "Math export to XLSX isn't tested, should it happen here?");
2555 // ECMA standard does not actually allow oMath outside of
2556 // WordProcessingML so write a MCE like PPT 2010 does
2557 mpFS
->startElementNS(XML_mc
, XML_AlternateContent
);
2558 mpFS
->startElementNS(XML_mc
, XML_Choice
,
2559 FSNS(XML_xmlns
, XML_a14
), mpFB
->getNamespaceURL(OOX_NS(a14
)),
2560 XML_Requires
, "a14");
2561 mpFS
->startElementNS(mnXmlNamespace
, XML_sp
);
2562 mpFS
->startElementNS(mnXmlNamespace
, XML_nvSpPr
);
2563 mpFS
->startElementNS(mnXmlNamespace
, XML_cNvPr
,
2564 XML_id
, OString::number(GetNewShapeID(xShape
)),
2565 XML_name
, GetShapeName(xShape
));
2566 AddExtLst(mpFS
, xPropSet
);
2567 mpFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
2568 mpFS
->singleElementNS(mnXmlNamespace
, XML_cNvSpPr
, XML_txBox
, "1");
2569 mpFS
->singleElementNS(mnXmlNamespace
, XML_nvPr
);
2570 mpFS
->endElementNS(mnXmlNamespace
, XML_nvSpPr
);
2571 mpFS
->startElementNS(mnXmlNamespace
, XML_spPr
);
2572 WriteShapeTransformation(xShape
, XML_a
);
2573 WritePresetShape("rect");
2574 mpFS
->endElementNS(mnXmlNamespace
, XML_spPr
);
2575 mpFS
->startElementNS(mnXmlNamespace
, XML_txBody
);
2576 mpFS
->startElementNS(XML_a
, XML_bodyPr
);
2577 mpFS
->endElementNS(XML_a
, XML_bodyPr
);
2578 mpFS
->startElementNS(XML_a
, XML_p
);
2579 mpFS
->startElementNS(XML_a14
, XML_m
);
2581 oox::FormulaImExportBase
*const pMagic(
2582 dynamic_cast<oox::FormulaImExportBase
*>(xMathModel
.get()));
2584 pMagic
->writeFormulaOoxml(GetFS(), GetFB()->getVersion(), GetDocumentType(),
2585 FormulaImExportBase::eFormulaAlign::INLINE
);
2587 mpFS
->endElementNS(XML_a14
, XML_m
);
2588 mpFS
->endElementNS(XML_a
, XML_p
);
2589 mpFS
->endElementNS(mnXmlNamespace
, XML_txBody
);
2590 mpFS
->endElementNS(mnXmlNamespace
, XML_sp
);
2591 mpFS
->endElementNS(XML_mc
, XML_Choice
);
2592 mpFS
->startElementNS(XML_mc
, XML_Fallback
);
2593 // TODO: export bitmap shape as fallback
2594 mpFS
->endElementNS(XML_mc
, XML_Fallback
);
2595 mpFS
->endElementNS(XML_mc
, XML_AlternateContent
);
2598 ShapeExport
& ShapeExport::WriteOLE2Shape( const Reference
< XShape
>& xShape
)
2600 Reference
< XPropertySet
> xPropSet( xShape
, UNO_QUERY
);
2604 enum { CHART
, MATH
, OTHER
} eType(OTHER
);
2606 xPropSet
->getPropertyValue("CLSID") >>= clsid
;
2607 if (!clsid
.isEmpty())
2609 SvGlobalName aClassID
;
2610 bool const isValid
= aClassID
.MakeId(clsid
);
2611 assert(isValid
); (void)isValid
;
2612 if (SotExchange::IsChart(aClassID
))
2614 else if (SotExchange::IsMath(aClassID
))
2620 Reference
< XChartDocument
> xChartDoc
;
2621 xPropSet
->getPropertyValue("Model") >>= xChartDoc
;
2622 assert(xChartDoc
.is());
2624 #if !ENABLE_WASM_STRIP_CHART
2625 // WASM_CHART change
2626 // TODO: With Chart extracted this cannot really happen since
2627 // no Chart could've been added at all
2628 ChartExport
aChartExport( mnXmlNamespace
, GetFS(), xChartDoc
, GetFB(), GetDocumentType() );
2629 aChartExport
.WriteChartObj( xShape
, GetNewShapeID( xShape
), ++mnChartCount
);
2636 WriteMathShape(xShape
);
2640 uno::Reference
<embed::XEmbeddedObject
> const xObj(
2641 xPropSet
->getPropertyValue("EmbeddedObject"), uno::UNO_QUERY
);
2645 SAL_WARN("oox.shape", "ShapeExport::WriteOLE2Shape: no object");
2647 // tdf#152436 Export the preview graphic of the object if the object is missing.
2648 SdrObject
* pSdrOLE2(SdrObject::getSdrObjectFromXShape(xShape
));
2649 if (auto pOle2Obj
= dynamic_cast<SdrOle2Obj
*>(pSdrOLE2
))
2651 const Graphic
* pGraphic
= pOle2Obj
->GetGraphic();
2653 WriteGraphicObjectShapePart(xShape
, pGraphic
);
2659 uno::Sequence
<beans::PropertyValue
> grabBag
;
2663 uno::Reference
<beans::XPropertySet
> const xParent(
2664 uno::Reference
<container::XChild
>(xObj
, uno::UNO_QUERY_THROW
)->getParent(),
2665 uno::UNO_QUERY_THROW
);
2667 xParent
->getPropertyValue("InteropGrabBag") >>= grabBag
;
2669 entryName
= uno::Reference
<embed::XEmbedPersist
>(xObj
, uno::UNO_QUERY_THROW
)->getEntryName();
2671 catch (uno::Exception
const&)
2673 TOOLS_WARN_EXCEPTION("oox.shape", "ShapeExport::WriteOLE2Shape");
2679 for (auto const& it
: std::as_const(grabBag
))
2681 if (it
.Name
== "EmbeddedObjects")
2683 uno::Sequence
<beans::PropertyValue
> objects
;
2684 it
.Value
>>= objects
;
2685 for (auto const& object
: std::as_const(objects
))
2687 if (object
.Name
== entryName
)
2689 uno::Sequence
<beans::PropertyValue
> props
;
2690 object
.Value
>>= props
;
2691 for (auto const& prop
: std::as_const(props
))
2693 if (prop
.Name
== "ProgID")
2695 prop
.Value
>>= progID
;
2706 OUString sMediaType
;
2707 OUString sRelationType
;
2709 const char * pProgID(nullptr);
2710 OString anotherProgID
;
2712 uno::Reference
<io::XInputStream
> const xInStream
=
2713 oox::GetOLEObjectStream(
2714 mpFB
->getComponentContext(), xObj
, progID
,
2715 sMediaType
, sRelationType
, sSuffix
, pProgID
);
2719 if (!xInStream
.is())
2721 xPropSet
->getPropertyValue("LinkURL") >>= sURL
;
2725 sRelId
= mpFB
->addRelation(mpFS
->getOutputStream(),
2726 oox::getRelationship(Relationship::OLEOBJECT
), sURL
, true);
2730 if (!pProgID
&& !progID
.isEmpty())
2732 anotherProgID
= OUStringToOString(progID
, RTL_TEXTENCODING_UTF8
);
2733 pProgID
= anotherProgID
.getStr();
2736 assert(!sMediaType
.isEmpty());
2737 assert(!sRelationType
.isEmpty());
2738 assert(!sSuffix
.isEmpty());
2741 = "embeddings/oleObject" + OUString::number(++m_nEmbeddedObjects
) + "." + sSuffix
;
2742 uno::Reference
<io::XOutputStream
> const xOutStream(mpFB
->openFragmentStream(
2743 OUString::createFromAscii(GetComponentDir()) + "/" + sFileName
, sMediaType
));
2744 assert(xOutStream
.is()); // no reason why that could fail
2748 ::comphelper::OStorageHelper::CopyInputToOutput(xInStream
, xOutStream
);
2750 catch (uno::Exception
const&)
2752 TOOLS_WARN_EXCEPTION("oox.shape", "ShapeExport::WriteOLEObject");
2755 sRelId
= mpFB
->addRelation(
2756 mpFS
->getOutputStream(), sRelationType
,
2757 Concat2View(OUString::createFromAscii(GetRelationCompPrefix()) + sFileName
));
2761 bool bShowAsIcon
= (xPropSet
->getPropertyValue("Aspect") >>= nAspect
)
2762 && nAspect
== embed::Aspects::MSOLE_ICON
;
2764 mpFS
->startElementNS(mnXmlNamespace
, XML_graphicFrame
);
2766 mpFS
->startElementNS(mnXmlNamespace
, XML_nvGraphicFramePr
);
2768 mpFS
->startElementNS(mnXmlNamespace
, XML_cNvPr
,
2769 XML_id
, OString::number(GetNewShapeID(xShape
)),
2770 XML_name
, GetShapeName(xShape
));
2771 AddExtLst(mpFS
, xPropSet
);
2772 mpFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
2774 mpFS
->singleElementNS(mnXmlNamespace
, XML_cNvGraphicFramePr
);
2776 if (GetDocumentType() == DOCUMENT_PPTX
)
2777 mpFS
->singleElementNS(mnXmlNamespace
, XML_nvPr
);
2778 mpFS
->endElementNS( mnXmlNamespace
, XML_nvGraphicFramePr
);
2780 WriteShapeTransformation( xShape
, mnXmlNamespace
);
2782 mpFS
->startElementNS(XML_a
, XML_graphic
);
2783 mpFS
->startElementNS(XML_a
, XML_graphicData
,
2784 XML_uri
, "http://schemas.openxmlformats.org/presentationml/2006/ole");
2787 mpFS
->startElementNS( mnXmlNamespace
, XML_oleObj
,
2788 XML_showAsIcon
, sax_fastparser::UseIf("1", bShowAsIcon
),
2789 XML_progId
, pProgID
,
2790 FSNS(XML_r
, XML_id
), sRelId
,
2795 mpFS
->startElementNS( mnXmlNamespace
, XML_oleObj
,
2796 //? XML_name, "Document",
2797 XML_showAsIcon
, sax_fastparser::UseIf("1", bShowAsIcon
),
2798 FSNS(XML_r
, XML_id
), sRelId
,
2799 // The spec says that this is a required attribute, but PowerPoint can only handle an empty value.
2804 mpFS
->singleElementNS(mnXmlNamespace
, XML_embed
);
2806 mpFS
->singleElementNS(mnXmlNamespace
, XML_link
, XML_updateAutomatic
, "1");
2809 SdrObject
* pSdrOLE2(SdrObject::getSdrObjectFromXShape(xShape
));
2810 // The spec doesn't allow <p:pic> here, but PowerPoint requires it.
2811 bool const bEcma
= mpFB
->getVersion() == oox::core::ECMA_376_1ST_EDITION
;
2813 if (auto pOle2Obj
= dynamic_cast<SdrOle2Obj
*>(pSdrOLE2
))
2815 const Graphic
* pGraphic
= pOle2Obj
->GetGraphic();
2817 WriteGraphicObjectShapePart( xShape
, pGraphic
);
2820 mpFS
->endElementNS( mnXmlNamespace
, XML_oleObj
);
2822 mpFS
->endElementNS( XML_a
, XML_graphicData
);
2823 mpFS
->endElementNS( XML_a
, XML_graphic
);
2825 mpFS
->endElementNS( mnXmlNamespace
, XML_graphicFrame
);
2830 ShapeExport
& ShapeExport::WriteUnknownShape( const Reference
< XShape
>& )
2832 // Override this method to do something useful.
2836 sal_Int32
ShapeExport::GetNewShapeID( const Reference
< XShape
>& rXShape
)
2838 return GetNewShapeID( rXShape
, GetFB() );
2841 sal_Int32
ShapeExport::GetNewShapeID( const Reference
< XShape
>& rXShape
, XmlFilterBase
* pFB
)
2846 sal_Int32 nID
= pFB
->GetUniqueId();
2848 (*mpShapeMap
)[ rXShape
] = nID
;
2853 sal_Int32
ShapeExport::GetShapeID( const Reference
< XShape
>& rXShape
)
2855 return GetShapeID( rXShape
, mpShapeMap
);
2858 sal_Int32
ShapeExport::GetShapeID( const Reference
< XShape
>& rXShape
, ShapeHashMap
* pShapeMap
)
2863 ShapeHashMap::const_iterator aIter
= pShapeMap
->find( rXShape
);
2865 if( aIter
== pShapeMap
->end() )
2868 return aIter
->second
;
2871 OUString
ShapeExport::GetShapeName(const Reference
<XShape
>& xShape
)
2873 Reference
<XPropertySet
> rXPropSet(xShape
, UNO_QUERY
);
2875 // Empty name keeps the object unnamed.
2878 if (GetProperty(rXPropSet
, "Name"))
2885 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */