Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / oox / source / export / shapes.cxx
blobebab46728bef655cc698137577ca0fd821022dd4
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 <sal/config.h>
22 #include <config_global.h>
23 #include <sal/log.hxx>
24 #include <unotools/mediadescriptor.hxx>
25 #include <filter/msfilter/util.hxx>
26 #include <oox/core/xmlfilterbase.hxx>
27 #include <oox/export/shapes.hxx>
28 #include <oox/export/utils.hxx>
29 #include <oox/token/namespaces.hxx>
30 #include <oox/token/relationship.hxx>
31 #include <oox/token/tokens.hxx>
33 #include <cstdio>
34 #include <initializer_list>
36 #include <com/sun/star/awt/CharSet.hpp>
37 #include <com/sun/star/awt/FontDescriptor.hpp>
38 #include <com/sun/star/awt/FontSlant.hpp>
39 #include <com/sun/star/awt/FontWeight.hpp>
40 #include <com/sun/star/awt/FontUnderline.hpp>
41 #include <com/sun/star/awt/Gradient.hpp>
42 #include <com/sun/star/beans/PropertyValues.hpp>
43 #include <com/sun/star/beans/XPropertySet.hpp>
44 #include <com/sun/star/beans/XPropertySetInfo.hpp>
45 #include <com/sun/star/beans/XPropertyState.hpp>
46 #include <com/sun/star/container/XChild.hpp>
47 #include <com/sun/star/container/XEnumerationAccess.hpp>
48 #include <com/sun/star/document/XExporter.hpp>
49 #include <com/sun/star/document/XStorageBasedDocument.hpp>
50 #include <com/sun/star/drawing/CircleKind.hpp>
51 #include <com/sun/star/drawing/FillStyle.hpp>
52 #include <com/sun/star/drawing/BitmapMode.hpp>
53 #include <com/sun/star/drawing/ConnectorType.hpp>
54 #include <com/sun/star/drawing/LineDash.hpp>
55 #include <com/sun/star/drawing/LineJoint.hpp>
56 #include <com/sun/star/drawing/LineStyle.hpp>
57 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
58 #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
59 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
60 #include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
61 #include <com/sun/star/embed/EmbedStates.hpp>
62 #include <com/sun/star/embed/XEmbeddedObject.hpp>
63 #include <com/sun/star/embed/XEmbedPersist.hpp>
64 #include <com/sun/star/frame/XStorable.hpp>
65 #include <com/sun/star/graphic/XGraphic.hpp>
66 #include <com/sun/star/i18n/ScriptType.hpp>
67 #include <com/sun/star/io/XOutputStream.hpp>
68 #include <com/sun/star/style/ParagraphAdjust.hpp>
69 #include <com/sun/star/text/XSimpleText.hpp>
70 #include <com/sun/star/text/XText.hpp>
71 #include <com/sun/star/text/XTextContent.hpp>
72 #include <com/sun/star/text/XTextDocument.hpp>
73 #include <com/sun/star/text/XTextField.hpp>
74 #include <com/sun/star/text/XTextRange.hpp>
75 #include <com/sun/star/table/XTable.hpp>
76 #include <com/sun/star/table/XColumnRowRange.hpp>
77 #include <com/sun/star/table/XCellRange.hpp>
78 #include <com/sun/star/table/XMergeableCell.hpp>
79 #include <com/sun/star/chart2/XChartDocument.hpp>
80 #include <com/sun/star/frame/XModel.hpp>
81 #include <com/sun/star/uno/XComponentContext.hpp>
82 #include <tools/stream.hxx>
83 #include <tools/globname.hxx>
84 #include <comphelper/classids.hxx>
85 #include <comphelper/propertysequence.hxx>
86 #include <comphelper/storagehelper.hxx>
87 #include <sot/exchange.hxx>
88 #include <utility>
89 #include <vcl/cvtgrf.hxx>
90 #include <unotools/fontcvt.hxx>
91 #include <vcl/graph.hxx>
92 #include <vcl/outdev.hxx>
93 #include <vcl/GraphicObject.hxx>
94 #include <rtl/strbuf.hxx>
95 #include <sfx2/app.hxx>
96 #include <svl/languageoptions.hxx>
97 #include <filter/msfilter/escherex.hxx>
98 #include <svx/svdoashp.hxx>
99 #include <svx/svdoole2.hxx>
100 #include <tools/diagnose_ex.h>
101 #include <editeng/svxenum.hxx>
102 #include <svx/unoapi.hxx>
103 #include <oox/export/chartexport.hxx>
104 #include <oox/mathml/export.hxx>
105 #include <drawingml/presetgeometrynames.hxx>
106 #include <basegfx/numeric/ftools.hxx>
108 using namespace ::css;
109 using namespace ::css::beans;
110 using namespace ::css::uno;
111 using namespace ::css::drawing;
112 using namespace ::css::i18n;
113 using namespace ::css::table;
114 using namespace ::css::container;
115 using namespace ::css::document;
116 using namespace ::css::text;
118 using ::css::io::XOutputStream;
119 using ::css::chart2::XChartDocument;
120 using ::css::frame::XModel;
122 using ::oox::core::XmlFilterBase;
123 using ::sax_fastparser::FSHelperPtr;
125 #define IDS(x) OString(#x " " + OString::number(mnShapeIdMax++)).getStr()
127 namespace oox {
129 static void lcl_ConvertProgID(OUString const& rProgID,
130 OUString & o_rMediaType, OUString & o_rRelationType, OUString & o_rFileExtension)
132 if (rProgID == "Excel.Sheet.12")
134 o_rMediaType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
135 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
136 o_rFileExtension = "xlsx";
138 else if (rProgID.startsWith("Excel.SheetBinaryMacroEnabled.12") )
140 o_rMediaType = "application/vnd.ms-excel.sheet.binary.macroEnabled.12";
141 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
142 o_rFileExtension = "xlsb";
144 else if (rProgID.startsWith("Excel.SheetMacroEnabled.12"))
146 o_rMediaType = "application/vnd.ms-excel.sheet.macroEnabled.12";
147 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
148 o_rFileExtension = "xlsm";
150 else if (rProgID.startsWith("Excel.Sheet"))
152 o_rMediaType = "application/vnd.ms-excel";
153 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
154 o_rFileExtension = "xls";
156 else if (rProgID == "PowerPoint.Show.12")
158 o_rMediaType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
159 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
160 o_rFileExtension = "pptx";
162 else if (rProgID == "PowerPoint.ShowMacroEnabled.12")
164 o_rMediaType = "application/vnd.ms-powerpoint.presentation.macroEnabled.12";
165 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
166 o_rFileExtension = "pptm";
168 else if (rProgID.startsWith("PowerPoint.Show"))
170 o_rMediaType = "application/vnd.ms-powerpoint";
171 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
172 o_rFileExtension = "ppt";
174 else if (rProgID.startsWith("PowerPoint.Slide.12"))
176 o_rMediaType = "application/vnd.openxmlformats-officedocument.presentationml.slide";
177 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
178 o_rFileExtension = "sldx";
180 else if (rProgID == "PowerPoint.SlideMacroEnabled.12")
182 o_rMediaType = "application/vnd.ms-powerpoint.slide.macroEnabled.12";
183 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
184 o_rFileExtension = "sldm";
186 else if (rProgID == "Word.DocumentMacroEnabled.12")
188 o_rMediaType = "application/vnd.ms-word.document.macroEnabled.12";
189 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
190 o_rFileExtension = "docm";
192 else if (rProgID == "Word.Document.12")
194 o_rMediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
195 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
196 o_rFileExtension = "docx";
198 else if (rProgID == "Word.Document.8")
200 o_rMediaType = "application/msword";
201 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
202 o_rFileExtension = "doc";
204 else if (rProgID == "Excel.Chart.8")
206 o_rMediaType = "application/vnd.ms-excel";
207 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
208 o_rFileExtension = "xls";
210 else if (rProgID == "AcroExch.Document.11")
212 o_rMediaType = "application/pdf";
213 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
214 o_rFileExtension = "pdf";
216 else
218 o_rMediaType = "application/vnd.openxmlformats-officedocument.oleObject";
219 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
220 o_rFileExtension = "bin";
224 static uno::Reference<io::XInputStream> lcl_StoreOwnAsOOXML(
225 uno::Reference<uno::XComponentContext> const& xContext,
226 uno::Reference<embed::XEmbeddedObject> const& xObj,
227 char const*& o_rpProgID,
228 OUString & o_rMediaType, OUString & o_rRelationType, OUString & o_rSuffix)
230 static struct {
231 struct {
232 sal_uInt32 n1;
233 sal_uInt16 n2, n3;
234 sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15;
235 } ClassId;
236 char const* pFilterName;
237 char const* pMediaType;
238 char const* pProgID;
239 char const* pSuffix;
240 } const s_Mapping[] = {
241 { {SO3_SW_CLASSID_60}, "MS Word 2007 XML", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "Word.Document.12", "docx" },
242 { {SO3_SC_CLASSID_60}, "Calc MS Excel 2007 XML", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Excel.Sheet.12", "xlsx" },
243 { {SO3_SIMPRESS_CLASSID_60}, "Impress MS PowerPoint 2007 XML", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "PowerPoint.Show.12", "pptx" },
244 // FIXME: Draw does not appear to have a MSO format export filter?
245 // { {SO3_SDRAW_CLASSID}, "", "", "", "" },
246 { {SO3_SCH_CLASSID_60}, "unused", "", "", "" },
247 { {SO3_SM_CLASSID_60}, "unused", "", "", "" },
250 const char * pFilterName(nullptr);
251 SvGlobalName const classId(xObj->getClassID());
252 for (auto & i : s_Mapping)
254 auto const& rId(i.ClassId);
255 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);
256 if (temp == classId)
258 assert(SvGlobalName(SO3_SCH_CLASSID_60) != classId); // chart should be written elsewhere!
259 assert(SvGlobalName(SO3_SM_CLASSID_60) != classId); // formula should be written elsewhere!
260 pFilterName = i.pFilterName;
261 o_rMediaType = OUString::createFromAscii(i.pMediaType);
262 o_rpProgID = i.pProgID;
263 o_rSuffix = OUString::createFromAscii(i.pSuffix);
264 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
265 break;
269 if (!pFilterName)
271 SAL_WARN("oox.shape", "oox::GetOLEObjectStream: unknown ClassId " << classId.GetHexName());
272 return nullptr;
275 if (embed::EmbedStates::LOADED == xObj->getCurrentState())
277 xObj->changeState(embed::EmbedStates::RUNNING);
279 // use a temp stream - while it would work to store directly to a
280 // fragment stream, an error during export means we'd have to delete it
281 uno::Reference<io::XStream> const xTempStream(
282 xContext->getServiceManager()->createInstanceWithContext(
283 "com.sun.star.comp.MemoryStream", xContext),
284 uno::UNO_QUERY_THROW);
285 uno::Sequence<beans::PropertyValue> args( comphelper::InitPropertySequence({
286 { "OutputStream", Any(xTempStream->getOutputStream()) },
287 { "FilterName", Any(OUString::createFromAscii(pFilterName)) }
288 }));
289 uno::Reference<frame::XStorable> xStorable(xObj->getComponent(), uno::UNO_QUERY);
292 xStorable->storeToURL("private:stream", args);
294 catch (uno::Exception const&)
296 TOOLS_WARN_EXCEPTION("oox.shape", "oox::GetOLEObjectStream");
297 return nullptr;
299 xTempStream->getOutputStream()->closeOutput();
300 return xTempStream->getInputStream();
303 uno::Reference<io::XInputStream> GetOLEObjectStream(
304 uno::Reference<uno::XComponentContext> const& xContext,
305 uno::Reference<embed::XEmbeddedObject> const& xObj,
306 OUString const& i_rProgID,
307 OUString & o_rMediaType,
308 OUString & o_rRelationType,
309 OUString & o_rSuffix,
310 const char *& o_rpProgID)
312 uno::Reference<io::XInputStream> xInStream;
315 uno::Reference<document::XStorageBasedDocument> const xParent(
316 uno::Reference<container::XChild>(xObj, uno::UNO_QUERY_THROW)->getParent(),
317 uno::UNO_QUERY_THROW);
318 uno::Reference<embed::XStorage> const xParentStorage(xParent->getDocumentStorage());
319 OUString const entryName(
320 uno::Reference<embed::XEmbedPersist>(xObj, uno::UNO_QUERY_THROW)->getEntryName());
322 if (xParentStorage->isStreamElement(entryName))
324 lcl_ConvertProgID(i_rProgID, o_rMediaType, o_rRelationType, o_rSuffix);
325 xInStream = xParentStorage->cloneStreamElement(entryName)->getInputStream();
326 // TODO: make it possible to take the sMediaType from the stream
328 else // the object is ODF - either the whole document is
329 { // ODF, or the OLE was edited so it was converted to ODF
330 xInStream = lcl_StoreOwnAsOOXML(xContext, xObj,
331 o_rpProgID, o_rMediaType, o_rRelationType, o_rSuffix);
334 catch (uno::Exception const&)
336 TOOLS_WARN_EXCEPTION("oox.shape", "oox::GetOLEObjectStream");
338 return xInStream;
341 } // namespace oox
343 namespace oox { namespace drawingml {
345 #define GETA(propName) \
346 GetProperty( rXPropSet, #propName)
348 #define GETAD(propName) \
349 ( GetPropertyAndState( rXPropSet, rXPropState, #propName, eState ) && eState == beans::PropertyState_DIRECT_VALUE )
351 #define GET(variable, propName) \
352 if ( GETA(propName) ) \
353 mAny >>= variable;
355 ShapeExport::ShapeExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, ShapeHashMap* pShapeMap, XmlFilterBase* pFB, DocumentType eDocumentType, DMLTextExport* pTextExport )
356 : DrawingML( std::move(pFS), pFB, eDocumentType, pTextExport )
357 , m_nEmbeddedObjects(0)
358 , mnShapeIdMax( 1 )
359 , mnPictureIdMax( 1 )
360 , mnXmlNamespace( nXmlNamespace )
361 , maMapModeSrc( MapUnit::Map100thMM )
362 , maMapModeDest( MapUnit::MapInch, Point(), Fraction( 1, 576 ), Fraction( 1, 576 ) )
363 , mpShapeMap( pShapeMap ? pShapeMap : &maShapeMap )
365 mpURLTransformer.reset(new URLTransformer);
368 void ShapeExport::SetURLTranslator(const std::shared_ptr<URLTransformer>& pTransformer)
370 mpURLTransformer = pTransformer;
373 awt::Size ShapeExport::MapSize( const awt::Size& rSize ) const
375 Size aRetSize( OutputDevice::LogicToLogic( Size( rSize.Width, rSize.Height ), maMapModeSrc, maMapModeDest ) );
377 if ( !aRetSize.Width() )
378 aRetSize.AdjustWidth( 1 );
379 if ( !aRetSize.Height() )
380 aRetSize.AdjustHeight( 1 );
381 return awt::Size( aRetSize.Width(), aRetSize.Height() );
384 bool ShapeExport::NonEmptyText( const Reference< XInterface >& xIface )
386 Reference< XPropertySet > xPropSet( xIface, UNO_QUERY );
388 if( xPropSet.is() )
390 Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
391 if ( xPropSetInfo.is() )
393 if ( xPropSetInfo->hasPropertyByName( "IsEmptyPresentationObject" ) )
395 bool bIsEmptyPresObj = false;
396 if ( xPropSet->getPropertyValue( "IsEmptyPresentationObject" ) >>= bIsEmptyPresObj )
398 SAL_INFO("oox.shape", "empty presentation object " << bIsEmptyPresObj << " , props:");
399 if( bIsEmptyPresObj )
400 return true;
404 if ( xPropSetInfo->hasPropertyByName( "IsPresentationObject" ) )
406 bool bIsPresObj = false;
407 if ( xPropSet->getPropertyValue( "IsPresentationObject" ) >>= bIsPresObj )
409 SAL_INFO("oox.shape", "presentation object " << bIsPresObj << ", props:");
410 if( bIsPresObj )
411 return true;
417 Reference< XSimpleText > xText( xIface, UNO_QUERY );
419 if( xText.is() )
420 return xText->getString().getLength();
422 return false;
425 ShapeExport& ShapeExport::WritePolyPolygonShape( const Reference< XShape >& xShape, const bool bClosed )
427 SAL_INFO("oox.shape", "write polypolygon shape");
429 FSHelperPtr pFS = GetFS();
430 pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp));
432 tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon( xShape );
433 tools::Rectangle aRect( aPolyPolygon.GetBoundRect() );
435 #if OSL_DEBUG_LEVEL > 0
436 awt::Size size = MapSize( awt::Size( aRect.GetWidth(), aRect.GetHeight() ) );
437 SAL_INFO("oox.shape", "poly count " << aPolyPolygon.Count());
438 SAL_INFO("oox.shape", "size: " << size.Width << " x " << size.Height);
439 #endif
441 // non visual shape properties
442 if (GetDocumentType() != DOCUMENT_DOCX)
444 pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
445 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
446 XML_id, OString::number(GetNewShapeID(xShape)),
447 XML_name, IDS( Freeform ) );
449 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
450 if (GetDocumentType() != DOCUMENT_DOCX)
452 WriteNonVisualProperties( xShape );
453 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
456 // visual shape properties
457 pFS->startElementNS(mnXmlNamespace, XML_spPr);
458 WriteTransformation( aRect, XML_a );
459 WritePolyPolygon( aPolyPolygon, bClosed );
460 Reference< XPropertySet > xProps( xShape, UNO_QUERY );
461 if( xProps.is() ) {
462 if( bClosed )
463 WriteFill( xProps );
464 WriteOutline( xProps );
467 pFS->endElementNS( mnXmlNamespace, XML_spPr );
469 // write text
470 WriteTextBox( xShape, mnXmlNamespace );
472 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp) );
474 return *this;
477 ShapeExport& ShapeExport::WriteClosedPolyPolygonShape( const Reference< XShape >& xShape )
479 return WritePolyPolygonShape( xShape, true );
482 ShapeExport& ShapeExport::WriteOpenPolyPolygonShape( const Reference< XShape >& xShape )
484 return WritePolyPolygonShape( xShape, false );
487 ShapeExport& ShapeExport::WriteGroupShape(const uno::Reference<drawing::XShape>& xShape)
489 FSHelperPtr pFS = GetFS();
491 sal_Int32 nGroupShapeToken = XML_grpSp;
492 if (GetDocumentType() == DOCUMENT_DOCX)
494 if (!m_xParent.is())
495 nGroupShapeToken = XML_wgp; // toplevel
496 else
497 mnXmlNamespace = XML_wpg;
500 pFS->startElementNS(mnXmlNamespace, nGroupShapeToken);
502 // non visual properties
503 if (GetDocumentType() != DOCUMENT_DOCX)
505 pFS->startElementNS(mnXmlNamespace, XML_nvGrpSpPr);
506 pFS->singleElementNS(mnXmlNamespace, XML_cNvPr,
507 XML_id, OString::number(GetNewShapeID(xShape)),
508 XML_name, IDS(Group));
509 pFS->singleElementNS(mnXmlNamespace, XML_cNvGrpSpPr);
510 WriteNonVisualProperties(xShape );
511 pFS->endElementNS(mnXmlNamespace, XML_nvGrpSpPr);
513 else
514 pFS->singleElementNS(mnXmlNamespace, XML_cNvGrpSpPr);
516 // visual properties
517 pFS->startElementNS(mnXmlNamespace, XML_grpSpPr);
518 WriteShapeTransformation(xShape, XML_a, false, false, true);
519 pFS->endElementNS(mnXmlNamespace, XML_grpSpPr);
521 uno::Reference<drawing::XShapes> xGroupShape(xShape, uno::UNO_QUERY_THROW);
522 uno::Reference<drawing::XShape> xParent = m_xParent;
523 m_xParent = xShape;
524 for (sal_Int32 i = 0; i < xGroupShape->getCount(); ++i)
526 uno::Reference<drawing::XShape> xChild(xGroupShape->getByIndex(i), uno::UNO_QUERY_THROW);
527 sal_Int32 nSavedNamespace = mnXmlNamespace;
529 uno::Reference<lang::XServiceInfo> xServiceInfo(xChild, uno::UNO_QUERY_THROW);
530 if (GetDocumentType() == DOCUMENT_DOCX)
532 if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
533 mnXmlNamespace = XML_pic;
534 else
535 mnXmlNamespace = XML_wps;
537 WriteShape(xChild);
539 mnXmlNamespace = nSavedNamespace;
541 m_xParent = xParent;
543 pFS->endElementNS(mnXmlNamespace, nGroupShapeToken);
544 return *this;
547 static bool lcl_IsOnBlacklist(OUString const & rShapeType)
549 static const std::initializer_list<OUStringLiteral> vBlacklist = {
550 "block-arc",
551 "rectangle",
552 "ellipse",
553 "ring",
554 "can",
555 "cube",
556 "paper",
557 "frame",
558 "smiley",
559 "sun",
560 "flower",
561 "bracket-pair",
562 "brace-pair",
563 "col-60da8460",
564 "col-502ad400",
565 "quad-bevel",
566 "round-rectangular-callout",
567 "rectangular-callout",
568 "round-callout",
569 "cloud-callout",
570 "line-callout-1",
571 "line-callout-2",
572 "line-callout-3",
573 "paper",
574 "vertical-scroll",
575 "horizontal-scroll",
576 "mso-spt34",
577 "mso-spt75",
578 "mso-spt164",
579 "mso-spt180",
580 "flowchart-process",
581 "flowchart-alternate-process",
582 "flowchart-decision",
583 "flowchart-data",
584 "flowchart-predefined-process",
585 "flowchart-internal-storage",
586 "flowchart-document",
587 "flowchart-multidocument",
588 "flowchart-terminator",
589 "flowchart-preparation",
590 "flowchart-manual-input",
591 "flowchart-manual-operation",
592 "flowchart-connector",
593 "flowchart-off-page-connector",
594 "flowchart-card",
595 "flowchart-punched-tape",
596 "flowchart-summing-junction",
597 "flowchart-or",
598 "flowchart-collate",
599 "flowchart-sort",
600 "flowchart-extract",
601 "flowchart-merge",
602 "flowchart-stored-data",
603 "flowchart-delay",
604 "flowchart-sequential-access",
605 "flowchart-magnetic-disk",
606 "flowchart-direct-access-storage",
607 "flowchart-display"
610 return std::find(vBlacklist.begin(), vBlacklist.end(), rShapeType) != vBlacklist.end();
613 static bool lcl_IsOnWhitelist(OUString const & rShapeType)
615 static const std::initializer_list<OUStringLiteral> vWhitelist = {
616 "forbidden",
617 "heart",
618 "puzzle"
621 return std::find(vWhitelist.begin(), vWhitelist.end(), rShapeType) != vWhitelist.end();
624 static bool lcl_GetHandlePosition( sal_Int32 &nValue, const EnhancedCustomShapeParameter &rParam, Sequence< EnhancedCustomShapeAdjustmentValue > &rSeq)
626 bool bAdj = false;
627 if ( rParam.Value.getValueTypeClass() == TypeClass_DOUBLE )
629 double fValue(0.0);
630 if ( rParam.Value >>= fValue )
631 nValue = static_cast<sal_Int32>(fValue);
633 else
634 rParam.Value >>= nValue;
636 if ( rParam.Type == EnhancedCustomShapeParameterType::ADJUSTMENT)
638 bAdj = true;
639 sal_Int32 nIdx = nValue;
640 if ( nIdx < rSeq.getLength() )
642 if ( rSeq[ nIdx ] .Value.getValueTypeClass() == TypeClass_DOUBLE )
644 double fValue(0.0);
645 rSeq[ nIdx ].Value >>= fValue;
646 nValue = fValue;
649 else
651 rSeq[ nIdx ].Value >>= nValue;
655 return bAdj;
658 static void lcl_AnalyzeHandles( const uno::Sequence<beans::PropertyValues> & rHandles,
659 std::vector< std::pair< sal_Int32, sal_Int32> > &rHandlePositionList,
660 Sequence< EnhancedCustomShapeAdjustmentValue > &rSeq)
662 for ( const Sequence< PropertyValue >& rPropSeq : rHandles )
664 const OUString sPosition( "Position" );
665 bool bPosition = false;
666 EnhancedCustomShapeParameterPair aPosition;
667 EnhancedCustomShapeParameterPair aPolar;
668 for ( const PropertyValue& rPropVal: rPropSeq )
670 if ( rPropVal.Name == sPosition )
672 if ( rPropVal.Value >>= aPosition )
673 bPosition = true;
676 if ( bPosition )
678 sal_Int32 nXPosition = 0;
679 sal_Int32 nYPosition = 0;
680 // For polar handles, nXPosition is radius and nYPosition is angle
681 lcl_GetHandlePosition( nXPosition, aPosition.First , rSeq );
682 lcl_GetHandlePosition( nYPosition, aPosition.Second, rSeq );
683 rHandlePositionList.emplace_back( nXPosition, nYPosition );
688 static void lcl_AppendAdjustmentValue( std::vector< std::pair< sal_Int32, sal_Int32> > &rAvList, sal_Int32 nAdjIdx, sal_Int32 nValue )
690 rAvList.emplace_back( nAdjIdx , nValue );
693 static sal_Int32 lcl_NormalizeAngle( sal_Int32 nAngle )
695 nAngle = nAngle % 360;
696 return nAngle < 0 ? ( nAngle + 360 ) : nAngle ;
699 static sal_Int32 lcl_CircleAngle2CustomShapeEllipseAngleOOX(const sal_Int32 nInternAngle, const sal_Int32 nWidth, const sal_Int32 nHeight)
701 if (nWidth != 0 || nHeight != 0)
703 double fAngle = basegfx::deg2rad(nInternAngle / 100.0); // intern 1/100 deg to degree to rad
704 fAngle = atan2(nHeight * sin(fAngle), nWidth * cos(fAngle)); // circle to ellipse
705 fAngle = basegfx::rad2deg(fAngle) * 60000.0; // rad to degree to OOXML angle unit
706 sal_Int32 nAngle = basegfx::fround(fAngle); // normalize
707 nAngle = nAngle % 21600000;
708 return nAngle < 0 ? (nAngle + 21600000) : nAngle;
710 else // should be handled by caller, dummy value
711 return 0;
714 ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape )
716 // First check, if this is a Fontwork-shape. For DrawingML, such a shape is a
717 // TextBox shape with body property prstTxWarp.
718 SAL_INFO("oox.shape", "write custom shape");
719 Reference< XPropertySet > rXPropSet( xShape, UNO_QUERY );
720 bool bIsFontworkShape(false);
721 bool bHasGeometrySeq(false);
722 Sequence< PropertyValue > aGeometrySeq;
723 OUString sShapeType;
724 if (GETA(CustomShapeGeometry))
726 SAL_INFO("oox.shape", "got custom shape geometry");
727 if (mAny >>= aGeometrySeq)
729 bHasGeometrySeq = true;
730 SAL_INFO("oox.shape", "got custom shape geometry sequence");
731 for (const PropertyValue& rProp : std::as_const(aGeometrySeq))
733 SAL_INFO("oox.shape", "geometry property: " << rProp.Name);
734 if (rProp.Name == "TextPath")
736 uno::Sequence<beans::PropertyValue> aTextPathSeq;
737 rProp.Value >>= aTextPathSeq;
738 for (const PropertyValue& rTextProp : std::as_const(aTextPathSeq))
740 if (rTextProp.Name == "TextPath")
742 rTextProp.Value >>= bIsFontworkShape;
746 else if (rProp.Name == "Type")
747 rProp.Value >>= sShapeType;
751 if (bIsFontworkShape)
753 // write the correct type to m_presetWarp, WriteTextShape() needs it
754 // to set TextWarp.
755 m_presetWarp = PresetGeometryTypeNames::GetMsoName(sShapeType);
756 ShapeExport::WriteTextShape(xShape); // qualifier to prevent PowerPointShapeExport
757 return *this;
761 bool bPredefinedHandlesUsed = true;
762 bool bHasHandles = false;
764 ShapeFlag nMirrorFlags = ShapeFlag::NONE;
765 MSO_SPT eShapeType = EscherPropertyContainer::GetCustomShapeType( xShape, nMirrorFlags, sShapeType );
766 OSL_ENSURE(nullptr != dynamic_cast< SdrObjCustomShape* >(GetSdrObjectFromXShape(xShape)), "Not a SdrObjCustomShape (!)");
767 SdrObjCustomShape& rSdrObjCustomShape(static_cast< SdrObjCustomShape& >(*GetSdrObjectFromXShape(xShape)));
768 const bool bIsDefaultObject(
769 EscherPropertyContainer::IsDefaultObject(
770 rSdrObjCustomShape,
771 eShapeType));
772 const char* sPresetShape = msfilter::util::GetOOXMLPresetGeometry(sShapeType.toUtf8().getStr());
773 SAL_INFO("oox.shape", "custom shape type: " << sShapeType << " ==> " << sPresetShape);
775 sal_Int32 nAdjustmentValuesIndex = -1;
776 awt::Rectangle aViewBox;
777 uno::Sequence<beans::PropertyValues> aHandles;
779 bool bFlipH = false;
780 bool bFlipV = false;
782 // Avoid interference of preset type to the next shape
783 m_presetWarp = "";
785 if (bHasGeometrySeq)
787 for (int i = 0; i < aGeometrySeq.getLength(); i++)
789 const PropertyValue& rProp = aGeometrySeq[ i ];
790 SAL_INFO("oox.shape", "geometry property: " << rProp.Name);
792 if ( rProp.Name == "MirroredX" )
793 rProp.Value >>= bFlipH;
795 if ( rProp.Name == "MirroredY" )
796 rProp.Value >>= bFlipV;
797 if ( rProp.Name == "AdjustmentValues" )
798 nAdjustmentValuesIndex = i;
799 else if ( rProp.Name == "Handles" )
801 rProp.Value >>= aHandles;
802 if ( aHandles.hasElements() )
803 bHasHandles = true;
804 if( !bIsDefaultObject )
805 bPredefinedHandlesUsed = false;
806 // TODO: update nAdjustmentsWhichNeedsToBeConverted here
808 else if ( rProp.Name == "PresetTextWarp" )
810 rProp.Value >>= m_presetWarp;
812 else if ( rProp.Name == "ViewBox" )
813 rProp.Value >>= aViewBox;
817 FSHelperPtr pFS = GetFS();
818 pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp));
820 // non visual shape properties
821 if (GetDocumentType() != DOCUMENT_DOCX)
823 bool isVisible = true ;
824 if( GETA (Visible))
826 mAny >>= isVisible;
828 pFS->startElementNS( mnXmlNamespace, XML_nvSpPr );
829 pFS->startElementNS( mnXmlNamespace, XML_cNvPr,
830 XML_id, OString::number(GetNewShapeID(xShape)),
831 XML_name, IDS( CustomShape ),
832 XML_hidden, isVisible ? nullptr : "1" );
834 if( GETA( URL ) )
836 OUString sURL;
837 mAny >>= sURL;
838 if( !sURL.isEmpty() )
840 OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
841 oox::getRelationship(Relationship::HYPERLINK),
842 mpURLTransformer->getTransformedString(sURL),
843 mpURLTransformer->isExternalURL(sURL));
845 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId.toUtf8());
848 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
849 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
850 WriteNonVisualProperties( xShape );
851 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
853 else
854 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
856 // visual shape properties
857 pFS->startElementNS(mnXmlNamespace, XML_spPr);
858 // moon is flipped in MSO, and mso-spt89 (right up arrow) is mapped to leftUpArrow
859 if ( sShapeType == "moon" || sShapeType == "mso-spt89" )
860 bFlipH = !bFlipH;
862 // we export non-primitive shapes to custom geometry
863 // we also export non-ooxml shapes which have handles/equations to custom geometry, because
864 // we cannot convert ODF equations to DrawingML equations. TODO: see what binary DOC export filter does.
865 // but our WritePolyPolygon()/WriteCustomGeometry() functions are incomplete, therefore we use a blacklist
866 // we use a whitelist for shapes where mapping to MSO preset shape is not optimal
867 bool bCustGeom = true;
868 bool bOnBlacklist = false;
869 if( sShapeType == "ooxml-non-primitive" )
870 bCustGeom = true;
871 else if( sShapeType.startsWith("ooxml") )
872 bCustGeom = false;
873 else if( lcl_IsOnWhitelist(sShapeType) )
874 bCustGeom = true;
875 else if( lcl_IsOnBlacklist(sShapeType) )
877 bCustGeom = false;
878 bOnBlacklist = true;
880 else if( bHasHandles )
881 bCustGeom = true;
883 if (bHasHandles && bCustGeom)
885 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV, false, true );// do not flip, polypolygon coordinates are flipped already
886 tools::PolyPolygon aPolyPolygon( rSdrObjCustomShape.GetLineGeometry(true) );
887 sal_Int32 nRotation = 0;
888 // The RotateAngle property's value is independent from any flipping, and that's exactly what we need here.
889 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
890 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
891 if (xPropertySetInfo->hasPropertyByName("RotateAngle"))
892 xPropertySet->getPropertyValue("RotateAngle") >>= nRotation;
893 // Remove rotation
894 bool bInvertRotation = bFlipH != bFlipV;
895 if (nRotation != 0)
896 aPolyPolygon.Rotate(Point(0,0), static_cast<sal_uInt16>(bInvertRotation ? nRotation/10 : 3600-nRotation/10));
897 WritePolyPolygon( aPolyPolygon, false );
899 else if (bCustGeom)
901 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
902 bool bSuccess = WriteCustomGeometry(xShape, rSdrObjCustomShape);
903 if (!bSuccess)
904 WritePresetShape( sPresetShape );
906 else if (bOnBlacklist && bHasHandles && nAdjustmentValuesIndex !=-1 && !sShapeType.startsWith("mso-spt"))
908 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
909 Sequence< EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
910 std::vector< std::pair< sal_Int32, sal_Int32> > aHandlePositionList;
911 std::vector< std::pair< sal_Int32, sal_Int32> > aAvList;
912 aGeometrySeq[ nAdjustmentValuesIndex ].Value >>= aAdjustmentSeq ;
914 lcl_AnalyzeHandles( aHandles, aHandlePositionList, aAdjustmentSeq );
916 sal_Int32 nXPosition = 0;
917 sal_Int32 nYPosition = 0;
918 if ( !aHandlePositionList.empty() )
920 nXPosition = aHandlePositionList[0].first ;
921 nYPosition = aHandlePositionList[0].second ;
923 switch( eShapeType )
925 case mso_sptBorderCallout1:
927 sal_Int32 adj3 = double(nYPosition)/aViewBox.Height *100000;
928 sal_Int32 adj4 = double(nXPosition)/aViewBox.Width *100000;
929 lcl_AppendAdjustmentValue( aAvList, 1, 18750 );
930 lcl_AppendAdjustmentValue( aAvList, 2, -8333 );
931 lcl_AppendAdjustmentValue( aAvList, 3, adj3 );
932 lcl_AppendAdjustmentValue( aAvList, 4, adj4 );
933 break;
935 case mso_sptBorderCallout2:
937 sal_Int32 adj5 = double(nYPosition)/aViewBox.Height *100000;
938 sal_Int32 adj6 = double(nXPosition)/aViewBox.Width *100000;
939 sal_Int32 adj3 = 18750;
940 sal_Int32 adj4 = -16667;
941 lcl_AppendAdjustmentValue( aAvList, 1, 18750 );
942 lcl_AppendAdjustmentValue( aAvList, 2, -8333 );
943 if ( aHandlePositionList.size() > 1 )
945 nXPosition = aHandlePositionList[1].first ;
946 nYPosition = aHandlePositionList[1].second ;
947 adj3 = double(nYPosition)/aViewBox.Height *100000;
948 adj4 = double(nXPosition)/aViewBox.Width *100000;
950 lcl_AppendAdjustmentValue( aAvList, 3, adj3 );
951 lcl_AppendAdjustmentValue( aAvList, 4, adj4 );
952 lcl_AppendAdjustmentValue( aAvList, 5, adj5 );
953 lcl_AppendAdjustmentValue( aAvList, 6, adj6 );
954 break;
956 case mso_sptWedgeRectCallout:
957 case mso_sptWedgeRRectCallout:
958 case mso_sptWedgeEllipseCallout:
959 case mso_sptCloudCallout:
961 sal_Int32 adj1 = (double(nXPosition)/aViewBox.Width -0.5) *100000;
962 sal_Int32 adj2 = (double(nYPosition)/aViewBox.Height -0.5) *100000;
963 lcl_AppendAdjustmentValue( aAvList, 1, adj1 );
964 lcl_AppendAdjustmentValue( aAvList, 2, adj2 );
965 if ( eShapeType == mso_sptWedgeRRectCallout)
967 lcl_AppendAdjustmentValue( aAvList, 3, 16667);
970 break;
972 case mso_sptFoldedCorner:
974 sal_Int32 adj = double( aViewBox.Width - nXPosition) / std::min( aViewBox.Width,aViewBox.Height ) * 100000;
975 lcl_AppendAdjustmentValue( aAvList, 0, adj );
976 break;
978 case mso_sptDonut:
979 case mso_sptSun:
980 case mso_sptMoon:
981 case mso_sptHorizontalScroll:
982 case mso_sptBevel:
983 case mso_sptBracketPair:
985 sal_Int32 adj = double( nXPosition )/aViewBox.Width*100000 ;
986 lcl_AppendAdjustmentValue( aAvList, 0, adj );
987 break;
989 case mso_sptCan:
990 case mso_sptCube:
991 case mso_sptBracePair:
992 case mso_sptVerticalScroll:
994 sal_Int32 adj = double( nYPosition )/aViewBox.Height *100000 ;
995 lcl_AppendAdjustmentValue( aAvList, 0, adj );
996 break;
998 case mso_sptSmileyFace:
1000 sal_Int32 adj = double( nYPosition )/aViewBox.Height *100000 - 76458.0;
1001 lcl_AppendAdjustmentValue( aAvList, 0, adj );
1002 break;
1004 case mso_sptBlockArc:
1006 sal_Int32 nRadius = 50000 * ( 1 - double(nXPosition) / 10800);
1007 sal_Int32 nAngleStart = lcl_NormalizeAngle( nYPosition );
1008 sal_Int32 nAngleEnd = lcl_NormalizeAngle( 180 - nAngleStart );
1009 lcl_AppendAdjustmentValue( aAvList, 1, 21600000 / 360 * nAngleStart );
1010 lcl_AppendAdjustmentValue( aAvList, 2, 21600000 / 360 * nAngleEnd );
1011 lcl_AppendAdjustmentValue( aAvList, 3, nRadius );
1012 break;
1014 // case mso_sptNil:
1015 // case mso_sptBentConnector3:
1016 // case mso_sptBorderCallout3:
1017 default:
1019 if (!strcmp( sPresetShape, "frame" ))
1021 sal_Int32 adj1 = double( nYPosition )/aViewBox.Height *100000 ;
1022 lcl_AppendAdjustmentValue( aAvList, 1, adj1 );
1024 break;
1027 WritePresetShape( sPresetShape , aAvList );
1029 else // preset geometry
1031 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
1032 if( nAdjustmentValuesIndex != -1 )
1034 WritePresetShape( sPresetShape, eShapeType, bPredefinedHandlesUsed,
1035 aGeometrySeq[ nAdjustmentValuesIndex ] );
1037 else
1038 WritePresetShape( sPresetShape );
1040 if( rXPropSet.is() )
1042 WriteFill( rXPropSet );
1043 WriteOutline( rXPropSet );
1044 WriteShapeEffects( rXPropSet );
1045 WriteShape3DEffects( rXPropSet );
1048 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1050 pFS->startElementNS(mnXmlNamespace, XML_style);
1051 WriteShapeStyle( rXPropSet );
1052 pFS->endElementNS( mnXmlNamespace, XML_style );
1054 // write text
1055 WriteTextBox( xShape, mnXmlNamespace );
1057 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp) );
1059 return *this;
1062 ShapeExport& ShapeExport::WriteEllipseShape( const Reference< XShape >& xShape )
1064 SAL_INFO("oox.shape", "write ellipse shape");
1066 FSHelperPtr pFS = GetFS();
1068 pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp));
1070 // TODO: connector ?
1072 // non visual shape properties
1073 if (GetDocumentType() != DOCUMENT_DOCX)
1075 pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
1076 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
1077 XML_id, OString::number(GetNewShapeID(xShape)),
1078 XML_name, IDS( Ellipse ) );
1079 pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr );
1080 WriteNonVisualProperties( xShape );
1081 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
1083 else
1084 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
1086 Reference< XPropertySet > xProps( xShape, UNO_QUERY );
1087 CircleKind eCircleKind(CircleKind_FULL);
1088 if (xProps.is())
1089 xProps->getPropertyValue("CircleKind" ) >>= eCircleKind;
1091 // visual shape properties
1092 pFS->startElementNS( mnXmlNamespace, XML_spPr );
1093 WriteShapeTransformation( xShape, XML_a );
1095 if (CircleKind_FULL == eCircleKind)
1096 WritePresetShape("ellipse");
1097 else
1099 sal_Int32 nStartAngleIntern(9000);
1100 sal_Int32 nEndAngleIntern(0);
1101 if (xProps.is())
1103 xProps->getPropertyValue("CircleStartAngle" ) >>= nStartAngleIntern;
1104 xProps->getPropertyValue("CircleEndAngle") >>= nEndAngleIntern;
1106 std::vector< std::pair<sal_Int32,sal_Int32>> aAvList;
1107 awt::Size aSize = xShape->getSize();
1108 if (aSize.Width != 0 || aSize.Height != 0)
1110 // Our arc has 90° up, OOXML has 90° down, so mirror it.
1111 // API angles are 1/100 degree.
1112 sal_Int32 nStartAngleOOXML(lcl_CircleAngle2CustomShapeEllipseAngleOOX(36000 - nEndAngleIntern, aSize.Width, aSize.Height));
1113 sal_Int32 nEndAngleOOXML(lcl_CircleAngle2CustomShapeEllipseAngleOOX(36000 - nStartAngleIntern, aSize.Width, aSize.Height));
1114 lcl_AppendAdjustmentValue( aAvList, 1, nStartAngleOOXML);
1115 lcl_AppendAdjustmentValue( aAvList, 2, nEndAngleOOXML);
1117 switch (eCircleKind)
1119 case CircleKind_ARC :
1120 WritePresetShape("arc", aAvList);
1121 break;
1122 case CircleKind_SECTION :
1123 WritePresetShape("pie", aAvList);
1124 break;
1125 case CircleKind_CUT :
1126 WritePresetShape("chord", aAvList);
1127 break;
1128 default :
1129 WritePresetShape("ellipse");
1132 if( xProps.is() )
1134 if (CircleKind_ARC == eCircleKind)
1136 // An arc in ODF is never filled, even if a fill style other than
1137 // "none" is set. OOXML arc can be filled, so set fill explicit to
1138 // NONE, otherwise some hidden or inherited filling is shown.
1139 FillStyle eFillStyle(FillStyle_NONE);
1140 uno::Any aNewValue;
1141 aNewValue <<= eFillStyle;
1142 xProps->setPropertyValue("FillStyle", aNewValue);
1144 WriteFill( xProps );
1145 WriteOutline( xProps );
1147 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1149 // write text
1150 WriteTextBox( xShape, mnXmlNamespace );
1152 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp) );
1154 return *this;
1157 ShapeExport& ShapeExport::WriteGraphicObjectShape( const Reference< XShape >& xShape )
1159 WriteGraphicObjectShapePart( xShape );
1161 return *this;
1164 void ShapeExport::WriteGraphicObjectShapePart( const Reference< XShape >& xShape, const Graphic* pGraphic )
1166 SAL_INFO("oox.shape", "write graphic object shape");
1168 if( NonEmptyText( xShape ) )
1170 // avoid treating all 'IsPresentationObject' objects as having text.
1171 Reference< XSimpleText > xText( xShape, UNO_QUERY );
1173 if( xText.is() && !xText->getString().isEmpty() )
1175 SAL_INFO("oox.shape", "graphicObject: wrote only text");
1177 WriteTextShape( xShape );
1179 return;
1183 SAL_INFO("oox.shape", "graphicObject without text");
1185 uno::Reference<graphic::XGraphic> xGraphic;
1186 OUString sMediaURL;
1188 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1190 if (pGraphic)
1192 xGraphic.set(pGraphic->GetXGraphic());
1194 else if (xShapeProps.is() && xShapeProps->getPropertySetInfo()->hasPropertyByName("Graphic"))
1196 xShapeProps->getPropertyValue("Graphic") >>= xGraphic;
1199 bool bHasMediaURL = xShapeProps.is() && xShapeProps->getPropertySetInfo()->hasPropertyByName("MediaURL") && (xShapeProps->getPropertyValue("MediaURL") >>= sMediaURL);
1201 if (!xGraphic.is() && !bHasMediaURL)
1203 SAL_INFO("oox.shape", "no graphic or media URL found");
1204 return;
1207 FSHelperPtr pFS = GetFS();
1208 XmlFilterBase* pFB = GetFB();
1210 if (GetDocumentType() != DOCUMENT_DOCX)
1211 pFS->startElementNS(mnXmlNamespace, XML_pic);
1212 else
1213 pFS->startElementNS(mnXmlNamespace, XML_pic,
1214 FSNS(XML_xmlns, XML_pic), pFB->getNamespaceURL(OOX_NS(dmlPicture)).toUtf8());
1216 pFS->startElementNS(mnXmlNamespace, XML_nvPicPr);
1218 OUString sName, sDescr, sURL;
1219 bool bHaveName, bHaveDesc, bHaveURL;
1221 if ( ( bHaveName= GetProperty( xShapeProps, "Name" ) ) )
1222 mAny >>= sName;
1223 if ( ( bHaveDesc = GetProperty( xShapeProps, "Description" ) ) )
1224 mAny >>= sDescr;
1225 if ( ( bHaveURL = GetProperty( xShapeProps, "URL" ) ) )
1226 mAny >>= sURL;
1228 pFS->startElementNS( mnXmlNamespace, XML_cNvPr,
1229 XML_id, OString::number(GetNewShapeID(xShape)),
1230 XML_name, bHaveName
1231 ? sName.toUtf8()
1232 : OString("Picture " + OString::number(mnPictureIdMax++)),
1233 XML_descr, bHaveDesc ? sDescr.toUtf8().getStr() : nullptr );
1235 // OOXTODO: //cNvPr children: XML_extLst, XML_hlinkHover
1236 if (bHasMediaURL)
1237 pFS->singleElementNS(XML_a, XML_hlinkClick,
1238 FSNS(XML_r, XML_id), "",
1239 XML_action, "ppaction://media");
1240 if( !sURL.isEmpty() )
1242 OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
1243 oox::getRelationship(Relationship::HYPERLINK),
1244 mpURLTransformer->getTransformedString(sURL),
1245 mpURLTransformer->isExternalURL(sURL));
1247 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId.toUtf8());
1249 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1251 pFS->singleElementNS(mnXmlNamespace, XML_cNvPicPr
1252 // OOXTODO: XML_preferRelativeSize
1254 if (bHasMediaURL)
1255 WriteMediaNonVisualProperties(xShape);
1256 else
1257 WriteNonVisualProperties(xShape);
1259 pFS->endElementNS( mnXmlNamespace, XML_nvPicPr );
1261 pFS->startElementNS(mnXmlNamespace, XML_blipFill);
1263 if (xGraphic.is())
1265 WriteXGraphicBlip(xShapeProps, xGraphic, false);
1267 else if (bHasMediaURL)
1269 Reference<graphic::XGraphic> xFallbackGraphic;
1270 if (xShapeProps->getPropertySetInfo()->hasPropertyByName("FallbackGraphic"))
1271 xShapeProps->getPropertyValue("FallbackGraphic") >>= xFallbackGraphic;
1273 WriteXGraphicBlip(xShapeProps, xFallbackGraphic, false);
1276 if (xGraphic.is())
1278 WriteSrcRectXGraphic(xShapeProps, xGraphic);
1281 // now we stretch always when we get pGraphic (when changing that
1282 // behavior, test n#780830 for regression, where the OLE sheet might get tiled
1283 bool bStretch = false;
1284 if( !pGraphic && GetProperty( xShapeProps, "FillBitmapStretch" ) )
1285 mAny >>= bStretch;
1287 if ( pGraphic || bStretch )
1288 pFS->singleElementNS(XML_a, XML_stretch);
1290 pFS->endElementNS( mnXmlNamespace, XML_blipFill );
1292 // visual shape properties
1293 pFS->startElementNS(mnXmlNamespace, XML_spPr);
1294 bool bFlipH = false;
1295 if( xShapeProps->getPropertySetInfo()->hasPropertyByName("IsMirrored") )
1297 xShapeProps->getPropertyValue("IsMirrored") >>= bFlipH;
1299 WriteShapeTransformation( xShape, XML_a, bFlipH, false, false, false, true );
1300 WritePresetShape( "rect" );
1301 // graphic object can come with the frame (bnc#654525)
1302 WriteOutline( xShapeProps );
1304 WriteShapeEffects( xShapeProps );
1305 WriteShape3DEffects( xShapeProps );
1307 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1309 pFS->endElementNS( mnXmlNamespace, XML_pic );
1312 ShapeExport& ShapeExport::WriteConnectorShape( const Reference< XShape >& xShape )
1314 bool bFlipH = false;
1315 bool bFlipV = false;
1317 SAL_INFO("oox.shape", "write connector shape");
1319 FSHelperPtr pFS = GetFS();
1321 const char* sGeometry = "line";
1322 Reference< XPropertySet > rXPropSet( xShape, UNO_QUERY );
1323 Reference< XPropertyState > rXPropState( xShape, UNO_QUERY );
1324 awt::Point aStartPoint, aEndPoint;
1325 Reference< XShape > rXShapeA;
1326 Reference< XShape > rXShapeB;
1327 PropertyState eState;
1328 ConnectorType eConnectorType;
1329 if( GETAD( EdgeKind ) ) {
1330 mAny >>= eConnectorType;
1332 switch( eConnectorType ) {
1333 case ConnectorType_CURVE:
1334 sGeometry = "curvedConnector3";
1335 break;
1336 case ConnectorType_STANDARD:
1337 sGeometry = "bentConnector3";
1338 break;
1339 default:
1340 case ConnectorType_LINE:
1341 case ConnectorType_LINES:
1342 sGeometry = "straightConnector1";
1343 break;
1346 if( GETAD( EdgeStartPoint ) ) {
1347 mAny >>= aStartPoint;
1348 if( GETAD( EdgeEndPoint ) ) {
1349 mAny >>= aEndPoint;
1352 GET( rXShapeA, EdgeStartConnection );
1353 GET( rXShapeB, EdgeEndConnection );
1355 EscherConnectorListEntry aConnectorEntry( xShape, aStartPoint, rXShapeA, aEndPoint, rXShapeB );
1357 tools::Rectangle aRect( Point( aStartPoint.X, aStartPoint.Y ), Point( aEndPoint.X, aEndPoint.Y ) );
1358 if( aRect.getWidth() < 0 ) {
1359 bFlipH = true;
1360 aRect.setX( aEndPoint.X );
1361 aRect.setWidth( aStartPoint.X - aEndPoint.X );
1364 if( aRect.getHeight() < 0 ) {
1365 bFlipV = true;
1366 aRect.setY( aEndPoint.Y );
1367 aRect.setHeight( aStartPoint.Y - aEndPoint.Y );
1370 pFS->startElementNS(mnXmlNamespace, XML_cxnSp);
1372 // non visual shape properties
1373 pFS->startElementNS(mnXmlNamespace, XML_nvCxnSpPr);
1374 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
1375 XML_id, OString::number(GetNewShapeID(xShape)),
1376 XML_name, IDS( Line ) );
1377 // non visual connector shape drawing properties
1378 pFS->startElementNS(mnXmlNamespace, XML_cNvCxnSpPr);
1379 WriteConnectorConnections( aConnectorEntry, GetShapeID( rXShapeA ), GetShapeID( rXShapeB ) );
1380 pFS->endElementNS( mnXmlNamespace, XML_cNvCxnSpPr );
1381 pFS->singleElementNS(mnXmlNamespace, XML_nvPr);
1382 pFS->endElementNS( mnXmlNamespace, XML_nvCxnSpPr );
1384 // visual shape properties
1385 pFS->startElementNS(mnXmlNamespace, XML_spPr);
1386 WriteTransformation( aRect, XML_a, bFlipH, bFlipV );
1387 // TODO: write adjustments (ppt export doesn't work well there either)
1388 WritePresetShape( sGeometry );
1389 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1390 if( xShapeProps.is() )
1391 WriteOutline( xShapeProps );
1392 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1394 // write text
1395 WriteTextBox( xShape, mnXmlNamespace );
1397 pFS->endElementNS( mnXmlNamespace, XML_cxnSp );
1399 return *this;
1402 ShapeExport& ShapeExport::WriteLineShape( const Reference< XShape >& xShape )
1404 bool bFlipH = false;
1405 bool bFlipV = false;
1407 SAL_INFO("oox.shape", "write line shape");
1409 FSHelperPtr pFS = GetFS();
1411 pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp));
1413 tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon( xShape );
1414 if( aPolyPolygon.Count() == 1 && aPolyPolygon[ 0 ].GetSize() == 2)
1416 const tools::Polygon& rPoly = aPolyPolygon[ 0 ];
1418 bFlipH = ( rPoly[ 0 ].X() > rPoly[ 1 ].X() );
1419 bFlipV = ( rPoly[ 0 ].Y() > rPoly[ 1 ].Y() );
1422 // non visual shape properties
1423 if (GetDocumentType() != DOCUMENT_DOCX)
1425 pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
1426 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
1427 XML_id, OString::number(GetNewShapeID(xShape)),
1428 XML_name, IDS( Line ) );
1430 pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr );
1431 if (GetDocumentType() != DOCUMENT_DOCX)
1433 WriteNonVisualProperties( xShape );
1434 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
1437 // visual shape properties
1438 pFS->startElementNS(mnXmlNamespace, XML_spPr);
1439 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV, true);
1440 WritePresetShape( "line" );
1441 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1442 if( xShapeProps.is() )
1443 WriteOutline( xShapeProps );
1444 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1446 //write style
1447 pFS->startElementNS(mnXmlNamespace, XML_style);
1448 WriteShapeStyle( xShapeProps );
1449 pFS->endElementNS( mnXmlNamespace, XML_style );
1451 // write text
1452 WriteTextBox( xShape, mnXmlNamespace );
1454 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp) );
1456 return *this;
1459 ShapeExport& ShapeExport::WriteNonVisualDrawingProperties( const Reference< XShape >& xShape, const char* pName )
1461 GetFS()->singleElementNS( mnXmlNamespace, XML_cNvPr,
1462 XML_id, OString::number(GetNewShapeID(xShape)),
1463 XML_name, pName );
1465 return *this;
1468 ShapeExport& ShapeExport::WriteNonVisualProperties( const Reference< XShape >& )
1470 // Override to generate //nvPr elements.
1471 return *this;
1474 ShapeExport& ShapeExport::WriteRectangleShape( const Reference< XShape >& xShape )
1476 SAL_INFO("oox.shape", "write rectangle shape");
1478 FSHelperPtr pFS = GetFS();
1480 pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp));
1482 sal_Int32 nRadius = 0;
1484 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1485 if( xShapeProps.is() )
1487 xShapeProps->getPropertyValue( "CornerRadius" ) >>= nRadius;
1490 if( nRadius )
1492 nRadius = MapSize( awt::Size( nRadius, 0 ) ).Width;
1494 //TODO: use nRadius value more precisely than just deciding whether to use
1495 // "rect" or "roundRect" preset shape below
1497 // non visual shape properties
1498 if (GetDocumentType() == DOCUMENT_DOCX)
1499 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
1500 pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
1501 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
1502 XML_id, OString::number(GetNewShapeID(xShape)),
1503 XML_name, IDS( Rectangle ) );
1504 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
1505 WriteNonVisualProperties( xShape );
1506 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
1508 // visual shape properties
1509 pFS->startElementNS(mnXmlNamespace, XML_spPr);
1510 WriteShapeTransformation( xShape, XML_a );
1511 WritePresetShape( nRadius == 0 ? "rect" : "roundRect" );
1512 Reference< XPropertySet > xProps( xShape, UNO_QUERY );
1513 if( xProps.is() )
1515 WriteFill( xProps );
1516 WriteOutline( xProps );
1518 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1520 // write text
1521 WriteTextBox( xShape, mnXmlNamespace );
1523 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp) );
1525 return *this;
1528 typedef ShapeExport& (ShapeExport::*ShapeConverter)( const Reference< XShape >& );
1529 typedef std::unordered_map< const char*, ShapeConverter, rtl::CStringHash, rtl::CStringEqual> NameToConvertMapType;
1531 static const NameToConvertMapType& lcl_GetConverters()
1533 static NameToConvertMapType const shape_converters
1535 { "com.sun.star.drawing.ClosedBezierShape" , &ShapeExport::WriteClosedPolyPolygonShape },
1536 { "com.sun.star.drawing.ConnectorShape" , &ShapeExport::WriteConnectorShape },
1537 { "com.sun.star.drawing.CustomShape" , &ShapeExport::WriteCustomShape },
1538 { "com.sun.star.drawing.EllipseShape" , &ShapeExport::WriteEllipseShape },
1539 { "com.sun.star.drawing.GraphicObjectShape" , &ShapeExport::WriteGraphicObjectShape },
1540 { "com.sun.star.drawing.LineShape" , &ShapeExport::WriteLineShape },
1541 { "com.sun.star.drawing.OpenBezierShape" , &ShapeExport::WriteOpenPolyPolygonShape },
1542 { "com.sun.star.drawing.PolyPolygonShape" , &ShapeExport::WriteClosedPolyPolygonShape },
1543 { "com.sun.star.drawing.PolyLineShape" , &ShapeExport::WriteOpenPolyPolygonShape },
1544 { "com.sun.star.drawing.RectangleShape" , &ShapeExport::WriteRectangleShape },
1545 { "com.sun.star.drawing.OLE2Shape" , &ShapeExport::WriteOLE2Shape },
1546 { "com.sun.star.drawing.TableShape" , &ShapeExport::WriteTableShape },
1547 { "com.sun.star.drawing.TextShape" , &ShapeExport::WriteTextShape },
1548 { "com.sun.star.drawing.GroupShape" , &ShapeExport::WriteGroupShape },
1550 { "com.sun.star.presentation.GraphicObjectShape" , &ShapeExport::WriteGraphicObjectShape },
1551 { "com.sun.star.presentation.MediaShape" , &ShapeExport::WriteGraphicObjectShape },
1552 { "com.sun.star.presentation.OLE2Shape" , &ShapeExport::WriteOLE2Shape },
1553 { "com.sun.star.presentation.TableShape" , &ShapeExport::WriteTableShape },
1554 { "com.sun.star.presentation.TextShape" , &ShapeExport::WriteTextShape },
1556 { "com.sun.star.presentation.DateTimeShape" , &ShapeExport::WriteTextShape },
1557 { "com.sun.star.presentation.FooterShape" , &ShapeExport::WriteTextShape },
1558 { "com.sun.star.presentation.HeaderShape" , &ShapeExport::WriteTextShape },
1559 { "com.sun.star.presentation.NotesShape" , &ShapeExport::WriteTextShape },
1560 { "com.sun.star.presentation.OutlinerShape" , &ShapeExport::WriteTextShape },
1561 { "com.sun.star.presentation.SlideNumberShape" , &ShapeExport::WriteTextShape },
1562 { "com.sun.star.presentation.TitleTextShape" , &ShapeExport::WriteTextShape },
1564 return shape_converters;
1567 ShapeExport& ShapeExport::WriteShape( const Reference< XShape >& xShape )
1569 OUString sShapeType = xShape->getShapeType();
1570 SAL_INFO("oox.shape", "write shape: " << sShapeType);
1571 NameToConvertMapType::const_iterator aConverter
1572 = lcl_GetConverters().find(sShapeType.toUtf8().getStr());
1573 if (aConverter == lcl_GetConverters().end())
1575 SAL_INFO("oox.shape", "unknown shape");
1576 return WriteUnknownShape( xShape );
1578 (this->*(aConverter->second))( xShape );
1580 return *this;
1583 ShapeExport& ShapeExport::WriteTextBox( const Reference< XInterface >& xIface, sal_Int32 nXmlNamespace )
1585 // In case this shape has an associated textbox, then export that, and we're done.
1586 if (GetDocumentType() == DOCUMENT_DOCX && GetTextExport())
1588 uno::Reference<beans::XPropertySet> xPropertySet(xIface, uno::UNO_QUERY);
1589 if (xPropertySet.is())
1591 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
1592 if (xPropertySetInfo->hasPropertyByName("TextBox") && xPropertySet->getPropertyValue("TextBox").get<bool>())
1594 GetTextExport()->WriteTextBox(uno::Reference<drawing::XShape>(xIface, uno::UNO_QUERY_THROW));
1595 WriteText( xIface, m_presetWarp, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace );
1596 return *this;
1601 Reference< XText > xXText( xIface, UNO_QUERY );
1602 if( NonEmptyText( xIface ) && xXText.is() )
1604 FSHelperPtr pFS = GetFS();
1606 pFS->startElementNS(nXmlNamespace,
1607 (GetDocumentType() != DOCUMENT_DOCX ? XML_txBody : XML_txbx));
1608 WriteText( xIface, m_presetWarp, /*bBodyPr=*/(GetDocumentType() != DOCUMENT_DOCX) );
1609 pFS->endElementNS( nXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_txBody : XML_txbx) );
1610 if (GetDocumentType() == DOCUMENT_DOCX)
1611 WriteText( xIface, m_presetWarp, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace );
1613 else if (GetDocumentType() == DOCUMENT_DOCX)
1614 mpFS->singleElementNS(nXmlNamespace, XML_bodyPr);
1616 return *this;
1619 void ShapeExport::WriteTable( const Reference< XShape >& rXShape )
1621 Reference< XTable > xTable;
1622 Reference< XPropertySet > xPropSet( rXShape, UNO_QUERY );
1624 mpFS->startElementNS(XML_a, XML_graphic);
1625 mpFS->startElementNS(XML_a, XML_graphicData,
1626 XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/table");
1628 if ( xPropSet.is() && ( xPropSet->getPropertyValue( "Model" ) >>= xTable ) )
1630 mpFS->startElementNS(XML_a, XML_tbl);
1631 mpFS->singleElementNS(XML_a, XML_tblPr);
1633 Reference< container::XIndexAccess > xColumns( xTable->getColumns(), UNO_QUERY_THROW );
1634 Reference< container::XIndexAccess > xRows( xTable->getRows(), UNO_QUERY_THROW );
1635 sal_uInt16 nRowCount = static_cast< sal_uInt16 >( xRows->getCount() );
1636 sal_uInt16 nColumnCount = static_cast< sal_uInt16 >( xColumns->getCount() );
1638 mpFS->startElementNS(XML_a, XML_tblGrid);
1640 for ( sal_Int32 x = 0; x < nColumnCount; x++ )
1642 Reference< XPropertySet > xColPropSet( xColumns->getByIndex( x ), UNO_QUERY_THROW );
1643 sal_Int32 nWidth(0);
1644 xColPropSet->getPropertyValue( "Width" ) >>= nWidth;
1646 mpFS->singleElementNS(XML_a, XML_gridCol,
1647 XML_w, OString::number(oox::drawingml::convertHmmToEmu(nWidth)));
1650 mpFS->endElementNS( XML_a, XML_tblGrid );
1652 // map for holding the transpose index of the merged cells and pair<parentTransposeIndex, parentCell>
1653 typedef std::unordered_map<sal_Int32, std::pair<sal_Int32, Reference< XMergeableCell> > > transposeTableMap;
1654 transposeTableMap mergedCellMap;
1656 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
1658 Reference< XPropertySet > xRowPropSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
1659 sal_Int32 nRowHeight(0);
1661 xRowPropSet->getPropertyValue( "Height" ) >>= nRowHeight;
1663 mpFS->startElementNS(XML_a, XML_tr,
1664 XML_h, OString::number(oox::drawingml::convertHmmToEmu(nRowHeight)));
1665 for( sal_Int32 nColumn = 0; nColumn < nColumnCount; nColumn++ )
1667 Reference< XMergeableCell > xCell( xTable->getCellByPosition( nColumn, nRow ),
1668 UNO_QUERY_THROW );
1669 sal_Int32 transposedIndexofCell = (nRow * nColumnCount) + nColumn;
1671 //assume we will open a cell, set to false below if we won't
1672 bool bCellOpened = true;
1674 if(xCell->getColumnSpan() > 1 && xCell->getRowSpan() > 1)
1676 // having both : horizontal and vertical merge
1677 mpFS->startElementNS(XML_a, XML_tc,
1678 XML_gridSpan, OString::number(xCell->getColumnSpan()),
1679 XML_rowSpan, OString::number(xCell->getRowSpan()));
1680 // since, XMergeableCell doesn't have the information about
1681 // cell having hMerge or vMerge.
1682 // So, Populating the merged cell map in-order to use it to
1683 // decide the attribute for the individual cell.
1684 for(sal_Int32 columnIndex = nColumn; columnIndex < nColumn+xCell->getColumnSpan(); ++columnIndex)
1686 for(sal_Int32 rowIndex = nRow; rowIndex < nRow+xCell->getRowSpan(); ++rowIndex)
1688 sal_Int32 transposeIndexForMergeCell =
1689 (rowIndex * nColumnCount) + columnIndex;
1690 mergedCellMap[transposeIndexForMergeCell] =
1691 std::make_pair(transposedIndexofCell, xCell);
1696 else if(xCell->getColumnSpan() > 1)
1698 // having : horizontal merge
1699 mpFS->startElementNS(XML_a, XML_tc,
1700 XML_gridSpan, OString::number(xCell->getColumnSpan()));
1701 for(sal_Int32 columnIndex = nColumn; columnIndex < nColumn + xCell->getColumnSpan(); ++columnIndex) {
1702 sal_Int32 transposeIndexForMergeCell = (nRow*nColumnCount) + columnIndex;
1703 mergedCellMap[transposeIndexForMergeCell] =
1704 std::make_pair(transposedIndexofCell, xCell);
1707 else if(xCell->getRowSpan() > 1)
1709 // having : vertical merge
1710 mpFS->startElementNS(XML_a, XML_tc,
1711 XML_rowSpan, OString::number(xCell->getRowSpan()));
1713 for(sal_Int32 rowIndex = nRow; rowIndex < nRow + xCell->getRowSpan(); ++rowIndex) {
1714 sal_Int32 transposeIndexForMergeCell = (rowIndex*nColumnCount) + nColumn;
1715 mergedCellMap[transposeIndexForMergeCell] =
1716 std::make_pair(transposedIndexofCell, xCell);
1719 else
1721 // now, the cell can be an independent cell or
1722 // it can be a cell which is been merged to some parent cell
1723 if(!xCell->isMerged())
1725 // independent cell
1726 mpFS->startElementNS(XML_a, XML_tc);
1728 else
1730 // it a merged cell to some parent cell
1731 // find the parent cell for the current cell at hand
1732 transposeTableMap::iterator it = mergedCellMap.find(transposedIndexofCell);
1733 if(it != mergedCellMap.end())
1735 sal_Int32 transposeIndexOfParent = it->second.first;
1736 Reference< XMergeableCell > parentCell = it->second.second;
1737 // finding the row and column index for the parent cell from transposed index
1738 sal_Int32 parentColumnIndex = transposeIndexOfParent % nColumnCount;
1739 sal_Int32 parentRowIndex = transposeIndexOfParent / nColumnCount;
1740 if(nColumn == parentColumnIndex)
1742 // the cell is vertical merge and it might have gridspan
1743 if(parentCell->getColumnSpan() > 1)
1745 // vMerge and has gridSpan
1746 mpFS->startElementNS(XML_a, XML_tc,
1747 XML_vMerge, OString::number(1),
1748 XML_gridSpan, OString::number(xCell->getColumnSpan()));
1750 else
1752 // only vMerge
1753 mpFS->startElementNS(XML_a, XML_tc,
1754 XML_vMerge, OString::number(1));
1757 else if(nRow == parentRowIndex)
1759 // the cell is horizontal merge and it might have rowspan
1760 if(parentCell->getRowSpan() > 1)
1762 // hMerge and has rowspan
1763 mpFS->startElementNS(XML_a, XML_tc,
1764 XML_hMerge, OString::number(1),
1765 XML_rowSpan, OString::number(xCell->getRowSpan()));
1767 else
1769 // only hMerge
1770 mpFS->startElementNS(XML_a, XML_tc,
1771 XML_hMerge, OString::number(1));
1774 else
1776 // has hMerge and vMerge
1777 mpFS->startElementNS(XML_a, XML_tc,
1778 XML_vMerge, OString::number(1),
1779 XML_hMerge, OString::number(1));
1782 else
1783 bCellOpened = false;
1787 if (bCellOpened)
1789 WriteTextBox( xCell, XML_a );
1791 Reference< XPropertySet > xCellPropSet(xCell, UNO_QUERY_THROW);
1792 WriteTableCellProperties(xCellPropSet);
1794 mpFS->endElementNS( XML_a, XML_tc );
1798 mpFS->endElementNS( XML_a, XML_tr );
1801 mpFS->endElementNS( XML_a, XML_tbl );
1804 mpFS->endElementNS( XML_a, XML_graphicData );
1805 mpFS->endElementNS( XML_a, XML_graphic );
1808 void ShapeExport::WriteTableCellProperties(const Reference< XPropertySet>& xCellPropSet)
1810 sal_Int32 nLeftMargin(0), nRightMargin(0);
1812 Any aLeftMargin = xCellPropSet->getPropertyValue("TextLeftDistance");
1813 aLeftMargin >>= nLeftMargin;
1815 Any aRightMargin = xCellPropSet->getPropertyValue("TextRightDistance");
1816 aRightMargin >>= nRightMargin;
1818 mpFS->startElementNS(XML_a, XML_tcPr,
1819 XML_marL, nLeftMargin > 0 ? OString::number(oox::drawingml::convertHmmToEmu(nLeftMargin)).getStr() : nullptr,
1820 XML_marR, nRightMargin > 0 ? OString::number(oox::drawingml::convertHmmToEmu(nRightMargin)).getStr() : nullptr);
1822 // Write background fill for table cell.
1823 // TODO
1824 // tcW : Table cell width
1825 WriteTableCellBorders(xCellPropSet);
1826 DrawingML::WriteFill(xCellPropSet);
1827 mpFS->endElementNS( XML_a, XML_tcPr );
1830 void ShapeExport::WriteBorderLine(const sal_Int32 XML_line, const BorderLine2& rBorderLine)
1832 // While importing the table cell border line width, it converts EMU->Hmm then divided result by 2.
1833 // To get original value of LineWidth need to multiple by 2.
1834 sal_Int32 nBorderWidth = rBorderLine.LineWidth;
1835 nBorderWidth *= 2;
1836 nBorderWidth = oox::drawingml::convertHmmToEmu( nBorderWidth );
1838 if ( nBorderWidth > 0 )
1840 mpFS->startElementNS(XML_a, XML_line, XML_w, OString::number(nBorderWidth));
1841 if ( rBorderLine.Color == sal_Int32( COL_AUTO ) )
1842 mpFS->singleElementNS(XML_a, XML_noFill);
1843 else
1844 DrawingML::WriteSolidFill( ::Color(rBorderLine.Color) );
1845 mpFS->endElementNS( XML_a, XML_line );
1849 void ShapeExport::WriteTableCellBorders(const Reference< XPropertySet>& xCellPropSet)
1851 BorderLine2 aBorderLine;
1853 // lnL - Left Border Line Properties of table cell
1854 xCellPropSet->getPropertyValue("LeftBorder") >>= aBorderLine;
1855 WriteBorderLine( XML_lnL, aBorderLine );
1857 // lnR - Right Border Line Properties of table cell
1858 xCellPropSet->getPropertyValue("RightBorder") >>= aBorderLine;
1859 WriteBorderLine( XML_lnR, aBorderLine );
1861 // lnT - Top Border Line Properties of table cell
1862 xCellPropSet->getPropertyValue("TopBorder") >>= aBorderLine;
1863 WriteBorderLine( XML_lnT, aBorderLine );
1865 // lnB - Bottom Border Line Properties of table cell
1866 xCellPropSet->getPropertyValue("BottomBorder") >>= aBorderLine;
1867 WriteBorderLine( XML_lnB, aBorderLine );
1870 ShapeExport& ShapeExport::WriteTableShape( const Reference< XShape >& xShape )
1872 FSHelperPtr pFS = GetFS();
1874 pFS->startElementNS(mnXmlNamespace, XML_graphicFrame);
1876 pFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr);
1878 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
1879 XML_id, OString::number(GetNewShapeID(xShape)),
1880 XML_name, IDS(Table) );
1882 pFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr);
1884 if( GetDocumentType() == DOCUMENT_PPTX )
1885 pFS->singleElementNS(mnXmlNamespace, XML_nvPr);
1886 pFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
1888 WriteShapeTransformation( xShape, mnXmlNamespace );
1889 WriteTable( xShape );
1891 pFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
1893 return *this;
1896 ShapeExport& ShapeExport::WriteTextShape( const Reference< XShape >& xShape )
1898 bool bIsFontworkShape(m_presetWarp.startsWith("text") && m_presetWarp != "textNoShape");
1899 FSHelperPtr pFS = GetFS();
1900 Reference<XPropertySet> xShapeProps(xShape, UNO_QUERY);
1901 pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp));
1903 // non visual shape properties
1904 if (GetDocumentType() != DOCUMENT_DOCX)
1906 pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
1907 pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
1908 XML_id, OString::number(GetNewShapeID(xShape)),
1909 XML_name, IDS(TextShape));
1910 OUString sURL;
1911 if (GetProperty(xShapeProps, "URL"))
1912 mAny >>= sURL;
1914 if (!sURL.isEmpty())
1916 OUString sRelId = mpFB->addRelation(mpFS->getOutputStream(),
1917 oox::getRelationship(Relationship::HYPERLINK),
1918 mpURLTransformer->getTransformedString(sURL),
1919 mpURLTransformer->isExternalURL(sURL));
1921 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId.toUtf8());
1923 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1925 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr, XML_txBox, "1");
1926 if (GetDocumentType() != DOCUMENT_DOCX)
1928 WriteNonVisualProperties( xShape );
1929 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
1932 // visual shape properties
1933 pFS->startElementNS(mnXmlNamespace, XML_spPr);
1934 WriteShapeTransformation( xShape, XML_a );
1935 WritePresetShape( "rect" );
1936 uno::Reference<beans::XPropertySet> xPropertySet(xShape, UNO_QUERY);
1937 if (!bIsFontworkShape) // Fontwork needs fill and outline on char instead.
1939 WriteBlipOrNormalFill(xPropertySet, "Graphic");
1940 WriteOutline(xPropertySet);
1942 WriteShapeEffects(xPropertySet);
1943 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1945 WriteTextBox( xShape, mnXmlNamespace );
1947 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp) );
1949 return *this;
1952 void ShapeExport::WriteMathShape(Reference<XShape> const& xShape)
1954 Reference<XPropertySet> const xPropSet(xShape, UNO_QUERY);
1955 assert(xPropSet.is());
1956 Reference<XModel> xMathModel;
1957 xPropSet->getPropertyValue("Model") >>= xMathModel;
1958 assert(xMathModel.is());
1959 assert(GetDocumentType() != DOCUMENT_DOCX); // should be written in DocxAttributeOutput
1960 SAL_WARN_IF(GetDocumentType() == DOCUMENT_XLSX, "oox.shape", "Math export to XLSX isn't tested, should it happen here?");
1962 // ECMA standard does not actually allow oMath outside of
1963 // WordProcessingML so write a MCE like PPT 2010 does
1964 mpFS->startElementNS(XML_mc, XML_AlternateContent);
1965 mpFS->startElementNS(XML_mc, XML_Choice,
1966 FSNS(XML_xmlns, XML_a14), mpFB->getNamespaceURL(OOX_NS(a14)).toUtf8(),
1967 XML_Requires, "a14");
1968 mpFS->startElementNS(mnXmlNamespace, XML_sp);
1969 mpFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
1970 mpFS->singleElementNS(mnXmlNamespace, XML_cNvPr,
1971 XML_id, OString::number(GetNewShapeID(xShape)),
1972 XML_name, IDS(Formula));
1973 mpFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr, XML_txBox, "1");
1974 mpFS->singleElementNS(mnXmlNamespace, XML_nvPr);
1975 mpFS->endElementNS(mnXmlNamespace, XML_nvSpPr);
1976 mpFS->startElementNS(mnXmlNamespace, XML_spPr);
1977 WriteShapeTransformation(xShape, XML_a);
1978 WritePresetShape("rect");
1979 mpFS->endElementNS(mnXmlNamespace, XML_spPr);
1980 mpFS->startElementNS(mnXmlNamespace, XML_txBody);
1981 mpFS->startElementNS(XML_a, XML_bodyPr);
1982 mpFS->endElementNS(XML_a, XML_bodyPr);
1983 mpFS->startElementNS(XML_a, XML_p);
1984 mpFS->startElementNS(XML_a14, XML_m);
1986 oox::FormulaExportBase *const pMagic(dynamic_cast<oox::FormulaExportBase*>(xMathModel.get()));
1987 assert(pMagic);
1988 pMagic->writeFormulaOoxml(GetFS(), GetFB()->getVersion(), GetDocumentType());
1990 mpFS->endElementNS(XML_a14, XML_m);
1991 mpFS->endElementNS(XML_a, XML_p);
1992 mpFS->endElementNS(mnXmlNamespace, XML_txBody);
1993 mpFS->endElementNS(mnXmlNamespace, XML_sp);
1994 mpFS->endElementNS(XML_mc, XML_Choice);
1995 mpFS->startElementNS(XML_mc, XML_Fallback);
1996 // TODO: export bitmap shape as fallback
1997 mpFS->endElementNS(XML_mc, XML_Fallback);
1998 mpFS->endElementNS(XML_mc, XML_AlternateContent);
2001 ShapeExport& ShapeExport::WriteOLE2Shape( const Reference< XShape >& xShape )
2003 Reference< XPropertySet > xPropSet( xShape, UNO_QUERY );
2004 if (!xPropSet.is())
2005 return *this;
2007 enum { CHART, MATH, OTHER } eType(OTHER);
2008 OUString clsid;
2009 xPropSet->getPropertyValue("CLSID") >>= clsid;
2010 if (!clsid.isEmpty())
2012 SvGlobalName aClassID;
2013 bool const isValid = aClassID.MakeId(clsid);
2014 assert(isValid); (void)isValid;
2015 if (SotExchange::IsChart(aClassID))
2016 eType = CHART;
2017 else if (SotExchange::IsMath(aClassID))
2018 eType = MATH;
2021 if (CHART == eType)
2023 Reference< XChartDocument > xChartDoc;
2024 xPropSet->getPropertyValue("Model") >>= xChartDoc;
2025 assert(xChartDoc.is());
2026 //export the chart
2027 ChartExport aChartExport( mnXmlNamespace, GetFS(), xChartDoc, GetFB(), GetDocumentType() );
2028 static sal_Int32 nChartCount = 0;
2029 aChartExport.WriteChartObj( xShape, GetNewShapeID( xShape ), ++nChartCount );
2030 return *this;
2033 if (MATH == eType)
2035 WriteMathShape(xShape);
2036 return *this;
2039 uno::Reference<embed::XEmbeddedObject> const xObj(
2040 xPropSet->getPropertyValue("EmbeddedObject"), uno::UNO_QUERY);
2042 if (!xObj.is())
2044 SAL_WARN("oox.shape", "ShapeExport::WriteOLE2Shape: no object");
2045 return *this;
2048 uno::Sequence<beans::PropertyValue> grabBag;
2049 OUString entryName;
2052 uno::Reference<beans::XPropertySet> const xParent(
2053 uno::Reference<container::XChild>(xObj, uno::UNO_QUERY_THROW)->getParent(),
2054 uno::UNO_QUERY_THROW);
2056 xParent->getPropertyValue("InteropGrabBag") >>= grabBag;
2058 entryName = uno::Reference<embed::XEmbedPersist>(xObj, uno::UNO_QUERY_THROW)->getEntryName();
2060 catch (uno::Exception const&)
2062 TOOLS_WARN_EXCEPTION("oox.shape", "ShapeExport::WriteOLE2Shape");
2063 return *this;
2066 OUString progID;
2068 for (auto const& it : std::as_const(grabBag))
2070 if (it.Name == "EmbeddedObjects")
2072 uno::Sequence<beans::PropertyValue> objects;
2073 it.Value >>= objects;
2074 for (auto const& object : std::as_const(objects))
2076 if (object.Name == entryName)
2078 uno::Sequence<beans::PropertyValue> props;
2079 object.Value >>= props;
2080 for (auto const& prop : std::as_const(props))
2082 if (prop.Name == "ProgID")
2084 prop.Value >>= progID;
2085 break;
2088 break;
2091 break;
2095 OUString sMediaType;
2096 OUString sRelationType;
2097 OUString sSuffix;
2098 const char * pProgID(nullptr);
2100 uno::Reference<io::XInputStream> const xInStream =
2101 oox::GetOLEObjectStream(
2102 mpFB->getComponentContext(), xObj, progID,
2103 sMediaType, sRelationType, sSuffix, pProgID);
2105 if (!xInStream.is())
2107 return *this;
2110 OString anotherProgID;
2111 if (!pProgID && !progID.isEmpty())
2113 anotherProgID = OUStringToOString(progID, RTL_TEXTENCODING_UTF8);
2114 pProgID = anotherProgID.getStr();
2117 assert(!sMediaType.isEmpty());
2118 assert(!sRelationType.isEmpty());
2119 assert(!sSuffix.isEmpty());
2121 OUString sFileName = "embeddings/oleObject" + OUString::number(++m_nEmbeddedObjects) + "." + sSuffix;
2122 uno::Reference<io::XOutputStream> const xOutStream(
2123 mpFB->openFragmentStream(
2124 OUString::createFromAscii(GetComponentDir()) + "/" + sFileName,
2125 sMediaType));
2126 assert(xOutStream.is()); // no reason why that could fail
2128 try {
2129 ::comphelper::OStorageHelper::CopyInputToOutput(xInStream, xOutStream);
2130 } catch (uno::Exception const&) {
2131 TOOLS_WARN_EXCEPTION("oox.shape", "ShapeExport::WriteOLEObject");
2134 OUString const sRelId = mpFB->addRelation(
2135 mpFS->getOutputStream(), sRelationType,
2136 OUString::createFromAscii(GetRelationCompPrefix()) + sFileName);
2138 mpFS->startElementNS(mnXmlNamespace, XML_graphicFrame);
2140 mpFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr);
2142 mpFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
2143 XML_id, OString::number(GetNewShapeID(xShape)),
2144 XML_name, IDS(Object) );
2146 mpFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr);
2148 if (GetDocumentType() == DOCUMENT_PPTX)
2149 mpFS->singleElementNS(mnXmlNamespace, XML_nvPr);
2150 mpFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
2152 WriteShapeTransformation( xShape, mnXmlNamespace );
2154 mpFS->startElementNS(XML_a, XML_graphic);
2155 mpFS->startElementNS(XML_a, XML_graphicData,
2156 XML_uri, "http://schemas.openxmlformats.org/presentationml/2006/ole");
2157 if (pProgID)
2159 mpFS->startElementNS( mnXmlNamespace, XML_oleObj,
2160 XML_progId, pProgID,
2161 FSNS(XML_r, XML_id), sRelId.toUtf8(),
2162 XML_spid, "" );
2164 else
2166 mpFS->startElementNS( mnXmlNamespace, XML_oleObj,
2167 //? XML_name, "Document",
2168 FSNS(XML_r, XML_id), sRelId.toUtf8(),
2169 // The spec says that this is a required attribute, but PowerPoint can only handle an empty value.
2170 XML_spid, "" );
2173 mpFS->singleElementNS( mnXmlNamespace, XML_embed );
2175 // pic element
2176 SdrObject* pSdrOLE2( GetSdrObjectFromXShape( xShape ) );
2177 // The spec doesn't allow <p:pic> here, but PowerPoint requires it.
2178 bool bEcma = mpFB->getVersion() == oox::core::ECMA_DIALECT;
2179 if (dynamic_cast<const SdrOle2Obj*>( pSdrOLE2) && bEcma)
2181 const Graphic* pGraphic = static_cast<SdrOle2Obj*>(pSdrOLE2)->GetGraphic();
2182 if (pGraphic)
2183 WriteGraphicObjectShapePart( xShape, pGraphic );
2186 mpFS->endElementNS( mnXmlNamespace, XML_oleObj );
2188 mpFS->endElementNS( XML_a, XML_graphicData );
2189 mpFS->endElementNS( XML_a, XML_graphic );
2191 mpFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
2193 return *this;
2196 ShapeExport& ShapeExport::WriteUnknownShape( const Reference< XShape >& )
2198 // Override this method to do something useful.
2199 return *this;
2202 sal_Int32 ShapeExport::GetNewShapeID( const Reference< XShape >& rXShape )
2204 return GetNewShapeID( rXShape, GetFB() );
2207 sal_Int32 ShapeExport::GetNewShapeID( const Reference< XShape >& rXShape, XmlFilterBase* pFB )
2209 if( !rXShape.is() )
2210 return -1;
2212 sal_Int32 nID = pFB->GetUniqueId();
2214 (*mpShapeMap)[ rXShape ] = nID;
2216 return nID;
2219 sal_Int32 ShapeExport::GetShapeID( const Reference< XShape >& rXShape )
2221 return GetShapeID( rXShape, mpShapeMap );
2224 sal_Int32 ShapeExport::GetShapeID( const Reference< XShape >& rXShape, ShapeHashMap* pShapeMap )
2226 if( !rXShape.is() )
2227 return -1;
2229 ShapeHashMap::const_iterator aIter = pShapeMap->find( rXShape );
2231 if( aIter == pShapeMap->end() )
2232 return -1;
2234 return aIter->second;
2239 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */