Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / oox / source / export / shapes.cxx
blob10f75166113a2b5d2c9803111a010e6c47090030
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 <unotools/mediadescriptor.hxx>
24 #include <filter/msfilter/util.hxx>
25 #include "oox/core/xmlfilterbase.hxx"
26 #include "oox/export/shapes.hxx"
27 #include "oox/export/utils.hxx"
28 #include <oox/token/namespaces.hxx>
29 #include <oox/token/relationship.hxx>
30 #include <oox/token/tokens.hxx>
32 #include <cstdio>
33 #include <initializer_list>
35 #include <com/sun/star/awt/CharSet.hpp>
36 #include <com/sun/star/awt/FontDescriptor.hpp>
37 #include <com/sun/star/awt/FontSlant.hpp>
38 #include <com/sun/star/awt/FontWeight.hpp>
39 #include <com/sun/star/awt/FontUnderline.hpp>
40 #include <com/sun/star/awt/Gradient.hpp>
41 #include <com/sun/star/beans/PropertyValues.hpp>
42 #include <com/sun/star/beans/XPropertySet.hpp>
43 #include <com/sun/star/beans/XPropertySetInfo.hpp>
44 #include <com/sun/star/beans/XPropertyState.hpp>
45 #include <com/sun/star/container/XChild.hpp>
46 #include <com/sun/star/container/XEnumerationAccess.hpp>
47 #include <com/sun/star/document/XExporter.hpp>
48 #include <com/sun/star/document/XStorageBasedDocument.hpp>
49 #include <com/sun/star/drawing/FillStyle.hpp>
50 #include <com/sun/star/drawing/BitmapMode.hpp>
51 #include <com/sun/star/drawing/ConnectorType.hpp>
52 #include <com/sun/star/drawing/LineDash.hpp>
53 #include <com/sun/star/drawing/LineJoint.hpp>
54 #include <com/sun/star/drawing/LineStyle.hpp>
55 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
56 #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
57 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
58 #include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
59 #include <com/sun/star/embed/EmbedStates.hpp>
60 #include <com/sun/star/embed/XEmbeddedObject.hpp>
61 #include <com/sun/star/embed/XEmbedPersist.hpp>
62 #include <com/sun/star/frame/XStorable.hpp>
63 #include <com/sun/star/i18n/ScriptType.hpp>
64 #include <com/sun/star/io/XOutputStream.hpp>
65 #include <com/sun/star/style/ParagraphAdjust.hpp>
66 #include <com/sun/star/text/XSimpleText.hpp>
67 #include <com/sun/star/text/XText.hpp>
68 #include <com/sun/star/text/XTextContent.hpp>
69 #include <com/sun/star/text/XTextDocument.hpp>
70 #include <com/sun/star/text/XTextField.hpp>
71 #include <com/sun/star/text/XTextRange.hpp>
72 #include <com/sun/star/table/XTable.hpp>
73 #include <com/sun/star/table/XColumnRowRange.hpp>
74 #include <com/sun/star/table/XCellRange.hpp>
75 #include <com/sun/star/table/XMergeableCell.hpp>
76 #include <com/sun/star/chart2/XChartDocument.hpp>
77 #include <com/sun/star/frame/XModel.hpp>
78 #include <com/sun/star/table/BorderLine2.hpp>
79 #include <tools/stream.hxx>
80 #include <tools/globname.hxx>
81 #include <comphelper/classids.hxx>
82 #include <comphelper/storagehelper.hxx>
83 #include <sot/exchange.hxx>
84 #include <vcl/cvtgrf.hxx>
85 #include <unotools/fontcvt.hxx>
86 #include <vcl/graph.hxx>
87 #include <vcl/outdev.hxx>
88 #include <svtools/grfmgr.hxx>
89 #include <rtl/strbuf.hxx>
90 #include <sfx2/app.hxx>
91 #include <svl/languageoptions.hxx>
92 #include <filter/msfilter/escherex.hxx>
93 #include <svx/svdoashp.hxx>
94 #include <svx/svdoole2.hxx>
95 #include <editeng/svxenum.hxx>
96 #include <svx/unoapi.hxx>
97 #include <oox/export/chartexport.hxx>
98 #include <oox/mathml/export.hxx>
100 using namespace ::css;
101 using namespace ::css::beans;
102 using namespace ::css::uno;
103 using namespace ::css::drawing;
104 using namespace ::css::i18n;
105 using namespace ::css::table;
106 using namespace ::css::container;
107 using namespace ::css::document;
108 using namespace ::css::text;
110 using ::css::io::XOutputStream;
111 using ::css::chart2::XChartDocument;
112 using ::css::frame::XModel;
114 using ::oox::core::XmlFilterBase;
115 using ::sax_fastparser::FSHelperPtr;
117 #define IDS(x) OString(OStringLiteral(#x " ") + OString::number( mnShapeIdMax++ )).getStr()
119 namespace oox {
121 static void lcl_ConvertProgID(OUString const& rProgID,
122 OUString & o_rMediaType, OUString & o_rRelationType, OUString & o_rFileExtension)
124 if (rProgID == "Excel.Sheet.12")
126 o_rMediaType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
127 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
128 o_rFileExtension = "xlsx";
130 else if (rProgID.startsWith("Excel.SheetBinaryMacroEnabled.12") )
132 o_rMediaType = "application/vnd.ms-excel.sheet.binary.macroEnabled.12";
133 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
134 o_rFileExtension = "xlsb";
136 else if (rProgID.startsWith("Excel.SheetMacroEnabled.12"))
138 o_rMediaType = "application/vnd.ms-excel.sheet.macroEnabled.12";
139 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
140 o_rFileExtension = "xlsm";
142 else if (rProgID.startsWith("Excel.Sheet"))
144 o_rMediaType = "application/vnd.ms-excel";
145 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
146 o_rFileExtension = "xls";
148 else if (rProgID == "PowerPoint.Show.12")
150 o_rMediaType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
151 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
152 o_rFileExtension = "pptx";
154 else if (rProgID == "PowerPoint.ShowMacroEnabled.12")
156 o_rMediaType = "application/vnd.ms-powerpoint.presentation.macroEnabled.12";
157 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
158 o_rFileExtension = "pptm";
160 else if (rProgID.startsWith("PowerPoint.Show"))
162 o_rMediaType = "application/vnd.ms-powerpoint";
163 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
164 o_rFileExtension = "ppt";
166 else if (rProgID.startsWith("PowerPoint.Slide.12"))
168 o_rMediaType = "application/vnd.openxmlformats-officedocument.presentationml.slide";
169 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
170 o_rFileExtension = "sldx";
172 else if (rProgID == "PowerPoint.SlideMacroEnabled.12")
174 o_rMediaType = "application/vnd.ms-powerpoint.slide.macroEnabled.12";
175 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
176 o_rFileExtension = "sldm";
178 else if (rProgID == "Word.DocumentMacroEnabled.12")
180 o_rMediaType = "application/vnd.ms-word.document.macroEnabled.12";
181 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
182 o_rFileExtension = "docm";
184 else if (rProgID == "Word.Document.12")
186 o_rMediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
187 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
188 o_rFileExtension = "docx";
190 else if (rProgID == "Word.Document.8")
192 o_rMediaType = "application/msword";
193 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
194 o_rFileExtension = "doc";
196 else if (rProgID == "Excel.Chart.8")
198 o_rMediaType = "application/vnd.ms-excel";
199 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
200 o_rFileExtension = "xls";
202 else if (rProgID == "AcroExch.Document.11")
204 o_rMediaType = "application/pdf";
205 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
206 o_rFileExtension = "pdf";
208 else
210 o_rMediaType = "application/vnd.openxmlformats-officedocument.oleObject";
211 o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
212 o_rFileExtension = "bin";
216 static uno::Reference<io::XInputStream> lcl_StoreOwnAsOOXML(
217 uno::Reference<uno::XComponentContext> const& xContext,
218 uno::Reference<embed::XEmbeddedObject> const& xObj,
219 char const*& o_rpProgID,
220 OUString & o_rMediaType, OUString & o_rRelationType, OUString & o_rSuffix)
222 static struct {
223 struct {
224 sal_uInt32 n1;
225 sal_uInt16 n2, n3;
226 sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15;
227 } ClassId;
228 char const* pFilterName;
229 char const* pMediaType;
230 char const* pProgID;
231 char const* pSuffix;
232 } s_Mapping[] = {
233 { {SO3_SW_CLASSID_60}, "MS Word 2007 XML", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "Word.Document.12", "docx" },
234 { {SO3_SC_CLASSID_60}, "Calc MS Excel 2007 XML", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Excel.Sheet.12", "xlsx" },
235 { {SO3_SIMPRESS_CLASSID_60}, "Impress MS PowerPoint 2007 XML", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "PowerPoint.Show.12", "pptx" },
236 // FIXME: Draw does not appear to have a MSO format export filter?
237 // { {SO3_SDRAW_CLASSID}, "", "", "", "" },
238 { {SO3_SCH_CLASSID_60}, "unused", "", "", "" },
239 { {SO3_SM_CLASSID_60}, "unused", "", "", "" },
242 const char * pFilterName(nullptr);
243 SvGlobalName const classId(xObj->getClassID());
244 for (auto & i : s_Mapping)
246 auto const& rId(i.ClassId);
247 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);
248 if (temp == classId)
250 assert(SvGlobalName(SO3_SCH_CLASSID_60) != classId); // chart should be written elsewhere!
251 assert(SvGlobalName(SO3_SM_CLASSID_60) != classId); // formula should be written elsewhere!
252 pFilterName = i.pFilterName;
253 o_rMediaType = OUString::createFromAscii(i.pMediaType);
254 o_rpProgID = i.pProgID;
255 o_rSuffix = OUString::createFromAscii(i.pSuffix);
256 o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
257 break;
261 if (!pFilterName)
263 SAL_WARN("oox.shape", "oox::GetOLEObjectStream: unknown ClassId " << classId.GetHexName());
264 return nullptr;
267 if (embed::EmbedStates::LOADED == xObj->getCurrentState())
269 xObj->changeState(embed::EmbedStates::RUNNING);
271 // use a temp stream - while it would work to store directly to a
272 // fragment stream, an error during export means we'd have to delete it
273 uno::Reference<io::XStream> const xTempStream(
274 xContext->getServiceManager()->createInstanceWithContext(
275 "com.sun.star.comp.MemoryStream", xContext),
276 uno::UNO_QUERY_THROW);
277 uno::Sequence<beans::PropertyValue> args(2);
278 args[0].Name = "OutputStream";
279 args[0].Value <<= xTempStream->getOutputStream();
280 args[1].Name = "FilterName";
281 args[1].Value <<= OUString::createFromAscii(pFilterName);
282 uno::Reference<frame::XStorable> xStorable(xObj->getComponent(), uno::UNO_QUERY);
285 xStorable->storeToURL("private:stream", args);
287 catch (uno::Exception const& e)
289 SAL_WARN("oox.shape", "oox::GetOLEObjectStream: exception: \"" << e.Message << "\"");
290 return nullptr;
292 xTempStream->getOutputStream()->closeOutput();
293 return xTempStream->getInputStream();
296 uno::Reference<io::XInputStream> GetOLEObjectStream(
297 uno::Reference<uno::XComponentContext> const& xContext,
298 uno::Reference<embed::XEmbeddedObject> const& xObj,
299 OUString const& i_rProgID,
300 OUString & o_rMediaType,
301 OUString & o_rRelationType,
302 OUString & o_rSuffix,
303 const char *& o_rpProgID)
305 uno::Reference<io::XInputStream> xInStream;
308 uno::Reference<document::XStorageBasedDocument> const xParent(
309 uno::Reference<container::XChild>(xObj, uno::UNO_QUERY_THROW)->getParent(),
310 uno::UNO_QUERY_THROW);
311 uno::Reference<embed::XStorage> const xParentStorage(xParent->getDocumentStorage());
312 OUString const entryName(
313 uno::Reference<embed::XEmbedPersist>(xObj, uno::UNO_QUERY_THROW)->getEntryName());
315 if (xParentStorage->isStreamElement(entryName))
317 lcl_ConvertProgID(i_rProgID, o_rMediaType, o_rRelationType, o_rSuffix);
318 xInStream = xParentStorage->cloneStreamElement(entryName)->getInputStream();
319 // TODO: make it possible to take the sMediaType from the stream
321 else // the object is ODF - either the whole document is
322 { // ODF, or the OLE was edited so it was converted to ODF
323 xInStream = lcl_StoreOwnAsOOXML(xContext, xObj,
324 o_rpProgID, o_rMediaType, o_rRelationType, o_rSuffix);
327 catch (uno::Exception const& e)
329 SAL_WARN("oox.shape", "oox::GetOLEObjectStream: exception: " << e.Message);
331 return xInStream;
334 } // namespace oox
336 namespace oox { namespace drawingml {
338 URLTransformer::~URLTransformer()
342 OUString URLTransformer::getTransformedString(const OUString& rString) const
344 return rString;
347 bool URLTransformer::isExternalURL(const OUString& /*rURL*/) const
349 return true;
352 #define GETA(propName) \
353 GetProperty( rXPropSet, #propName)
355 #define GETAD(propName) \
356 ( GetPropertyAndState( rXPropSet, rXPropState, #propName, eState ) && eState == beans::PropertyState_DIRECT_VALUE )
358 #define GET(variable, propName) \
359 if ( GETA(propName) ) \
360 mAny >>= variable;
362 ShapeExport::ShapeExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, ShapeHashMap* pShapeMap, XmlFilterBase* pFB, DocumentType eDocumentType, DMLTextExport* pTextExport )
363 : DrawingML( pFS, pFB, eDocumentType, pTextExport )
364 , m_nEmbeddedObjects(0)
365 , mnShapeIdMax( 1 )
366 , mnPictureIdMax( 1 )
367 , mnXmlNamespace( nXmlNamespace )
368 , maFraction( 1, 576 )
369 , maMapModeSrc( MapUnit::Map100thMM )
370 , maMapModeDest( MapUnit::MapInch, Point(), maFraction, maFraction )
371 , mpShapeMap( pShapeMap ? pShapeMap : &maShapeMap )
373 mpURLTransformer.reset(new URLTransformer);
376 void ShapeExport::SetURLTranslator(const std::shared_ptr<URLTransformer>& pTransformer)
378 mpURLTransformer = pTransformer;
381 awt::Size ShapeExport::MapSize( const awt::Size& rSize ) const
383 Size aRetSize( OutputDevice::LogicToLogic( Size( rSize.Width, rSize.Height ), maMapModeSrc, maMapModeDest ) );
385 if ( !aRetSize.Width() )
386 aRetSize.Width()++;
387 if ( !aRetSize.Height() )
388 aRetSize.Height()++;
389 return awt::Size( aRetSize.Width(), aRetSize.Height() );
392 bool ShapeExport::NonEmptyText( const Reference< XInterface >& xIface )
394 Reference< XPropertySet > xPropSet( xIface, UNO_QUERY );
396 if( xPropSet.is() )
398 Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
399 if ( xPropSetInfo.is() )
401 if ( xPropSetInfo->hasPropertyByName( "IsEmptyPresentationObject" ) )
403 bool bIsEmptyPresObj = false;
404 if ( xPropSet->getPropertyValue( "IsEmptyPresentationObject" ) >>= bIsEmptyPresObj )
406 SAL_INFO("oox.shape", "empty presentation object " << bIsEmptyPresObj << " , props:");
407 if( bIsEmptyPresObj )
408 return true;
412 if ( xPropSetInfo->hasPropertyByName( "IsPresentationObject" ) )
414 bool bIsPresObj = false;
415 if ( xPropSet->getPropertyValue( "IsPresentationObject" ) >>= bIsPresObj )
417 SAL_INFO("oox.shape", "presentation object " << bIsPresObj << ", props:");
418 if( bIsPresObj )
419 return true;
425 Reference< XSimpleText > xText( xIface, UNO_QUERY );
427 if( xText.is() )
428 return xText->getString().getLength();
430 return false;
433 ShapeExport& ShapeExport::WritePolyPolygonShape( const Reference< XShape >& xShape, bool bClosed )
435 SAL_INFO("oox.shape", "write polypolygon shape");
437 FSHelperPtr pFS = GetFS();
438 pFS->startElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp), FSEND );
440 tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon( xShape );
441 tools::Rectangle aRect( aPolyPolygon.GetBoundRect() );
443 #if OSL_DEBUG_LEVEL > 0
444 awt::Size size = MapSize( awt::Size( aRect.GetWidth(), aRect.GetHeight() ) );
445 SAL_INFO("oox.shape", "poly count " << aPolyPolygon.Count());
446 SAL_INFO("oox.shape", "size: " << size.Width << " x " << size.Height);
447 #endif
449 // non visual shape properties
450 if (GetDocumentType() != DOCUMENT_DOCX)
452 pFS->startElementNS( mnXmlNamespace, XML_nvSpPr, FSEND );
453 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
454 XML_id, I32S( GetNewShapeID( xShape ) ),
455 XML_name, IDS( Freeform ),
456 FSEND );
458 pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr, FSEND );
459 if (GetDocumentType() != DOCUMENT_DOCX)
461 WriteNonVisualProperties( xShape );
462 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
465 // visual shape properties
466 pFS->startElementNS( mnXmlNamespace, XML_spPr, FSEND );
467 WriteTransformation( aRect, XML_a );
468 WritePolyPolygon( aPolyPolygon );
469 Reference< XPropertySet > xProps( xShape, UNO_QUERY );
470 if( xProps.is() ) {
471 if( bClosed )
472 WriteFill( xProps );
473 WriteOutline( xProps );
476 pFS->endElementNS( mnXmlNamespace, XML_spPr );
478 // write text
479 WriteTextBox( xShape, mnXmlNamespace );
481 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp) );
483 return *this;
486 ShapeExport& ShapeExport::WriteClosedPolyPolygonShape( const Reference< XShape >& xShape )
488 return WritePolyPolygonShape( xShape, true );
491 ShapeExport& ShapeExport::WriteOpenPolyPolygonShape( const Reference< XShape >& xShape )
493 return WritePolyPolygonShape( xShape, false );
496 ShapeExport& ShapeExport::WriteGroupShape(const uno::Reference<drawing::XShape>& xShape)
498 FSHelperPtr pFS = GetFS();
499 bool bToplevel = !m_xParent.is();
500 if (!bToplevel)
501 mnXmlNamespace = XML_wpg;
502 pFS->startElementNS(mnXmlNamespace, (bToplevel ? XML_wgp : XML_grpSp), FSEND);
504 // non visual properties
505 pFS->singleElementNS(mnXmlNamespace, XML_cNvGrpSpPr, FSEND);
507 // visual properties
508 pFS->startElementNS(mnXmlNamespace, XML_grpSpPr, FSEND);
509 WriteShapeTransformation(xShape, XML_a);
510 pFS->endElementNS(mnXmlNamespace, XML_grpSpPr);
512 uno::Reference<drawing::XShapes> xGroupShape(xShape, uno::UNO_QUERY_THROW);
513 uno::Reference<drawing::XShape> xParent = m_xParent;
514 m_xParent = xShape;
515 for (sal_Int32 i = 0; i < xGroupShape->getCount(); ++i)
517 uno::Reference<drawing::XShape> xChild(xGroupShape->getByIndex(i), uno::UNO_QUERY_THROW);
518 sal_Int32 nSavedNamespace = mnXmlNamespace;
520 uno::Reference<lang::XServiceInfo> xServiceInfo(xChild, uno::UNO_QUERY_THROW);
521 if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
522 mnXmlNamespace = XML_pic;
523 else
524 mnXmlNamespace = XML_wps;
525 WriteShape(xChild);
527 mnXmlNamespace = nSavedNamespace;
529 m_xParent = xParent;
531 pFS->endElementNS(mnXmlNamespace, (bToplevel ? XML_wgp : XML_grpSp));
532 return *this;
535 static bool lcl_IsOnBlacklist(OUString& rShapeType)
537 static const std::initializer_list<OUStringLiteral> vBlacklist = {
538 "block-arc",
539 "rectangle",
540 "ellipse",
541 "ring",
542 "can",
543 "cube",
544 "paper",
545 "frame",
546 "smiley",
547 "sun",
548 "flower",
549 "bracket-pair",
550 "brace-pair",
551 "col-60da8460",
552 "col-502ad400",
553 "quad-bevel",
554 "round-rectangular-callout",
555 "rectangular-callout",
556 "round-callout",
557 "cloud-callout",
558 "line-callout-1",
559 "line-callout-2",
560 "line-callout-3",
561 "paper",
562 "vertical-scroll",
563 "horizontal-scroll",
564 "mso-spt34",
565 "mso-spt75",
566 "mso-spt164",
567 "mso-spt180",
568 "flowchart-process",
569 "flowchart-alternate-process",
570 "flowchart-decision",
571 "flowchart-data",
572 "flowchart-predefined-process",
573 "flowchart-internal-storage",
574 "flowchart-document",
575 "flowchart-multidocument",
576 "flowchart-terminator",
577 "flowchart-preparation",
578 "flowchart-manual-input",
579 "flowchart-manual-operation",
580 "flowchart-connector",
581 "flowchart-off-page-connector",
582 "flowchart-card",
583 "flowchart-punched-tape",
584 "flowchart-summing-junction",
585 "flowchart-or",
586 "flowchart-collate",
587 "flowchart-sort",
588 "flowchart-extract",
589 "flowchart-merge",
590 "flowchart-stored-data",
591 "flowchart-delay",
592 "flowchart-sequential-access",
593 "flowchart-magnetic-disk",
594 "flowchart-direct-access-storage",
595 "flowchart-display"
598 return std::find(vBlacklist.begin(), vBlacklist.end(), rShapeType) != vBlacklist.end();
601 static bool lcl_IsOnWhitelist(OUString& rShapeType)
603 static const std::initializer_list<OUStringLiteral> vWhitelist = {
604 "forbidden",
605 "heart",
606 "puzzle"
609 return std::find(vWhitelist.begin(), vWhitelist.end(), rShapeType) != vWhitelist.end();
612 bool lcl_GetHandlePosition( sal_Int32 &nValue, const EnhancedCustomShapeParameter &rParam, Sequence< EnhancedCustomShapeAdjustmentValue > &rSeq)
614 bool bAdj = false;
615 if ( rParam.Value.getValueTypeClass() == TypeClass_DOUBLE )
617 double fValue(0.0);
618 if ( rParam.Value >>= fValue )
619 nValue = (sal_Int32)fValue;
621 else
622 rParam.Value >>= nValue;
624 if ( rParam.Type == EnhancedCustomShapeParameterType::ADJUSTMENT)
626 bAdj = true;
627 sal_Int32 nIdx = nValue;
628 if ( nIdx < rSeq.getLength() )
630 if ( rSeq[ nIdx ] .Value.getValueTypeClass() == TypeClass_DOUBLE )
632 double fValue(0.0);
633 rSeq[ nIdx ].Value >>= fValue;
634 nValue = fValue;
637 else
639 rSeq[ nIdx ].Value >>= nValue;
643 return bAdj;
646 void lcl_AnalyzeHandles( const uno::Sequence<beans::PropertyValues> & rHandles,
647 std::vector< std::pair< sal_Int32, sal_Int32> > &rHandlePositionList,
648 Sequence< EnhancedCustomShapeAdjustmentValue > &rSeq)
650 sal_uInt16 k;
651 sal_uInt16 nHandles = rHandles.getLength();
652 for ( k = 0; k < nHandles ; k++ )
654 const OUString sSwitched( "Switched" );
655 const OUString sPosition( "Position" );
656 bool bSwitched = false;
657 bool bPosition = false;
658 EnhancedCustomShapeParameterPair aPosition;
659 EnhancedCustomShapeParameterPair aPolar;
660 const Sequence< PropertyValue >& rPropSeq = rHandles[ k ];
661 for ( const PropertyValue& rPropVal: rPropSeq )
663 if ( rPropVal.Name.equals( sPosition ) )
665 if ( rPropVal.Value >>= aPosition )
666 bPosition = true;
668 else if ( rPropVal.Name.equals( sSwitched ) )
670 rPropVal.Value >>= bSwitched ;
673 if ( bPosition )
675 sal_Int32 nXPosition = 0;
676 sal_Int32 nYPosition = 0;
677 // For polar handles, nXPosition is radius and nYPosition is angle
678 lcl_GetHandlePosition( nXPosition, aPosition.First , rSeq );
679 lcl_GetHandlePosition( nYPosition, aPosition.Second, rSeq );
680 rHandlePositionList.push_back( std::pair<sal_Int32, sal_Int32> ( nXPosition, nYPosition ) );
685 void lcl_AppendAdjustmentValue( std::vector< std::pair< sal_Int32, sal_Int32> > &rAvList, sal_Int32 nAdjIdx, sal_Int32 nValue )
687 rAvList.push_back( std::pair<sal_Int32, sal_Int32> ( nAdjIdx , nValue ) );
690 sal_Int32 lcl_NormalizeAngle( sal_Int32 nAngle )
692 nAngle = nAngle % 360;
693 return nAngle < 0 ? ( nAngle + 360 ) : nAngle ;
696 ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape )
698 SAL_INFO("oox.shape", "write custom shape");
700 Reference< XPropertySet > rXPropSet( xShape, UNO_QUERY );
701 bool bPredefinedHandlesUsed = true;
702 bool bHasHandles = false;
704 OUString sShapeType;
705 sal_uInt32 nMirrorFlags = 0;
706 MSO_SPT eShapeType = EscherPropertyContainer::GetCustomShapeType( xShape, nMirrorFlags, sShapeType );
707 SdrObjCustomShape* pShape = static_cast<SdrObjCustomShape*>( GetSdrObjectFromXShape( xShape ) );
708 bool bIsDefaultObject = EscherPropertyContainer::IsDefaultObject( pShape, eShapeType );
709 const char* sPresetShape = msfilter::util::GetOOXMLPresetGeometry( USS( sShapeType ) );
710 SAL_INFO("oox.shape", "custom shape type: " << sShapeType << " ==> " << sPresetShape);
711 Sequence< PropertyValue > aGeometrySeq;
712 sal_Int32 nAdjustmentValuesIndex = -1;
713 awt::Rectangle aViewBox;
714 uno::Sequence<beans::PropertyValues> aHandles;
716 bool bFlipH = false;
717 bool bFlipV = false;
719 if( GETA( CustomShapeGeometry ) ) {
720 SAL_INFO("oox.shape", "got custom shape geometry");
721 if( mAny >>= aGeometrySeq ) {
723 SAL_INFO("oox.shape", "got custom shape geometry sequence");
724 for( int i = 0; i < aGeometrySeq.getLength(); i++ ) {
725 const PropertyValue& rProp = aGeometrySeq[ i ];
726 SAL_INFO("oox.shape", "geometry property: " << rProp.Name);
728 if ( rProp.Name == "MirroredX" )
729 rProp.Value >>= bFlipH;
731 if ( rProp.Name == "MirroredY" )
732 rProp.Value >>= bFlipV;
733 if ( rProp.Name == "AdjustmentValues" )
734 nAdjustmentValuesIndex = i;
735 else if ( rProp.Name == "Handles" )
737 rProp.Value >>= aHandles;
738 if ( aHandles.getLength() )
739 bHasHandles = true;
740 if( !bIsDefaultObject )
741 bPredefinedHandlesUsed = false;
742 // TODO: update nAdjustmentsWhichNeedsToBeConverted here
744 else if ( rProp.Name == "PresetTextWarp" )
746 rProp.Value >>= m_presetWarp;
748 else if ( rProp.Name == "ViewBox" )
749 rProp.Value >>= aViewBox;
754 FSHelperPtr pFS = GetFS();
755 pFS->startElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp), FSEND );
757 // non visual shape properties
758 if (GetDocumentType() != DOCUMENT_DOCX)
760 bool isVisible = true ;
761 if( GETA (Visible))
763 mAny >>= isVisible;
765 pFS->startElementNS( mnXmlNamespace, XML_nvSpPr, FSEND );
766 pFS->startElementNS( mnXmlNamespace, XML_cNvPr,
767 XML_id, I32S( GetNewShapeID( xShape ) ),
768 XML_name, IDS( CustomShape ),
769 XML_hidden, isVisible ? nullptr : "1",
770 FSEND );
772 if( GETA( URL ) )
774 OUString sURL;
775 mAny >>= sURL;
776 if( !sURL.isEmpty() )
778 OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
779 oox::getRelationship(Relationship::HYPERLINK),
780 mpURLTransformer->getTransformedString(sURL),
781 mpURLTransformer->isExternalURL(sURL));
783 mpFS->singleElementNS( XML_a, XML_hlinkClick,
784 FSNS( XML_r,XML_id ), USS( sRelId ),
785 FSEND );
788 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
789 pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr, FSEND );
790 WriteNonVisualProperties( xShape );
791 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
793 else
794 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr, FSEND);
796 // visual shape properties
797 pFS->startElementNS( mnXmlNamespace, XML_spPr, FSEND );
798 // moon is flipped in MSO, and mso-spt89 (right up arrow) is mapped to leftUpArrow
799 if ( sShapeType == "moon" || sShapeType == "mso-spt89" )
800 bFlipH = !bFlipH;
802 // we export non-primitive shapes to custom geometry
803 // we also export non-ooxml shapes which have handles/equations to custom geometry, because
804 // we cannot convert ODF equations to DrawingML equations. TODO: see what binary DOC export filter does.
805 // but our WritePolyPolygon()/WriteCustomGeometry() functions are incomplete, therefore we use a blacklist
806 // we use a whitelist for shapes where mapping to MSO preset shape is not optimal
807 bool bCustGeom = true;
808 bool bOnBlacklist = false;
809 if( sShapeType == "ooxml-non-primitive" )
810 bCustGeom = true;
811 else if( sShapeType.startsWith("ooxml") )
812 bCustGeom = false;
813 else if( lcl_IsOnWhitelist(sShapeType) )
814 bCustGeom = true;
815 else if( lcl_IsOnBlacklist(sShapeType) )
817 bCustGeom = false;
818 bOnBlacklist = true;
820 else if( bHasHandles )
821 bCustGeom = true;
823 if (bHasHandles && bCustGeom && pShape)
825 WriteShapeTransformation( xShape, XML_a ); // do not flip, polypolygon coordinates are flipped already
826 tools::PolyPolygon aPolyPolygon( pShape->GetLineGeometry(true) );
827 sal_Int32 nRotation = 0;
828 // The RotateAngle property's value is independent from any flipping, and that's exactly what we need here.
829 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
830 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
831 if (xPropertySetInfo->hasPropertyByName("RotateAngle"))
832 xPropertySet->getPropertyValue("RotateAngle") >>= nRotation;
833 if (nRotation != 0)
834 aPolyPolygon.Rotate(Point(0,0), static_cast<sal_uInt16>(3600-nRotation/10));
835 WritePolyPolygon( aPolyPolygon );
837 else if (bCustGeom)
839 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
840 bool bSuccess = WriteCustomGeometry( xShape, pShape );
841 if (!bSuccess)
842 WritePresetShape( sPresetShape );
844 else if (bOnBlacklist && bHasHandles && nAdjustmentValuesIndex !=-1 && !sShapeType.startsWith("mso-spt"))
846 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
847 Sequence< EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
848 std::vector< std::pair< sal_Int32, sal_Int32> > aHandlePositionList;
849 std::vector< std::pair< sal_Int32, sal_Int32> > aAvList;
850 aGeometrySeq[ nAdjustmentValuesIndex ].Value >>= aAdjustmentSeq ;
852 lcl_AnalyzeHandles( aHandles, aHandlePositionList, aAdjustmentSeq );
854 sal_Int32 nXPosition = 0;
855 sal_Int32 nYPosition = 0;
856 if ( !aHandlePositionList.empty() )
858 nXPosition = aHandlePositionList[0].first ;
859 nYPosition = aHandlePositionList[0].second ;
861 switch( eShapeType )
863 case mso_sptBorderCallout1:
865 sal_Int32 adj3 = double(nYPosition)/aViewBox.Height *100000;
866 sal_Int32 adj4 = double(nXPosition)/aViewBox.Width *100000;
867 lcl_AppendAdjustmentValue( aAvList, 1, 18750 );
868 lcl_AppendAdjustmentValue( aAvList, 2, -8333 );
869 lcl_AppendAdjustmentValue( aAvList, 3, adj3 );
870 lcl_AppendAdjustmentValue( aAvList, 4, adj4 );
871 break;
873 case mso_sptBorderCallout2:
875 sal_Int32 adj5 = double(nYPosition)/aViewBox.Height *100000;
876 sal_Int32 adj6 = double(nXPosition)/aViewBox.Width *100000;
877 sal_Int32 adj3 = 18750;
878 sal_Int32 adj4 = -16667;
879 lcl_AppendAdjustmentValue( aAvList, 1, 18750 );
880 lcl_AppendAdjustmentValue( aAvList, 2, -8333 );
881 if ( aHandlePositionList.size() > 1 )
883 nXPosition = aHandlePositionList[1].first ;
884 nYPosition = aHandlePositionList[1].second ;
885 adj3 = double(nYPosition)/aViewBox.Height *100000;
886 adj4 = double(nXPosition)/aViewBox.Width *100000;
888 lcl_AppendAdjustmentValue( aAvList, 3, adj3 );
889 lcl_AppendAdjustmentValue( aAvList, 4, adj4 );
890 lcl_AppendAdjustmentValue( aAvList, 5, adj5 );
891 lcl_AppendAdjustmentValue( aAvList, 6, adj6 );
892 break;
894 case mso_sptWedgeRectCallout:
895 case mso_sptWedgeRRectCallout:
896 case mso_sptWedgeEllipseCallout:
897 case mso_sptCloudCallout:
899 sal_Int32 adj1 = (double(nXPosition)/aViewBox.Width -0.5) *100000;
900 sal_Int32 adj2 = (double(nYPosition)/aViewBox.Height -0.5) *100000;
901 lcl_AppendAdjustmentValue( aAvList, 1, adj1 );
902 lcl_AppendAdjustmentValue( aAvList, 2, adj2 );
903 if ( eShapeType == mso_sptWedgeRRectCallout)
905 lcl_AppendAdjustmentValue( aAvList, 3, 16667);
908 break;
910 case mso_sptFoldedCorner:
912 sal_Int32 adj = double( aViewBox.Width - nXPosition) / std::min( aViewBox.Width,aViewBox.Height ) * 100000;
913 lcl_AppendAdjustmentValue( aAvList, 0, adj );
914 break;
916 case mso_sptDonut:
917 case mso_sptSun:
918 case mso_sptMoon:
919 case mso_sptHorizontalScroll:
920 case mso_sptBevel:
921 case mso_sptBracketPair:
923 sal_Int32 adj = double( nXPosition )/aViewBox.Width*100000 ;
924 lcl_AppendAdjustmentValue( aAvList, 0, adj );
925 break;
927 case mso_sptCan:
928 case mso_sptCube:
929 case mso_sptBracePair:
930 case mso_sptVerticalScroll:
932 sal_Int32 adj = double( nYPosition )/aViewBox.Height *100000 ;
933 lcl_AppendAdjustmentValue( aAvList, 0, adj );
934 break;
936 case mso_sptSmileyFace:
938 sal_Int32 adj = double( nYPosition )/aViewBox.Height *100000 - 76458.0;
939 lcl_AppendAdjustmentValue( aAvList, 0, adj );
940 break;
942 case mso_sptBlockArc:
944 sal_Int32 nRadius = 50000 * ( 1 - double(nXPosition) / 10800);
945 sal_Int32 nAngleStart = lcl_NormalizeAngle( nYPosition );
946 sal_Int32 nAngleEnd = lcl_NormalizeAngle( 180 - nAngleStart );
947 lcl_AppendAdjustmentValue( aAvList, 1, 21600000 / 360 * nAngleStart );
948 lcl_AppendAdjustmentValue( aAvList, 2, 21600000 / 360 * nAngleEnd );
949 lcl_AppendAdjustmentValue( aAvList, 3, nRadius );
950 break;
952 // case mso_sptNil:
953 // case mso_sptBentConnector3:
954 // case mso_sptBorderCallout3:
955 default:
957 if (!strcmp( sPresetShape, "frame" ))
959 sal_Int32 adj1 = double( nYPosition )/aViewBox.Height *100000 ;
960 lcl_AppendAdjustmentValue( aAvList, 1, adj1 );
962 break;
965 WritePresetShape( sPresetShape , aAvList );
967 else // preset geometry
969 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
970 if( nAdjustmentValuesIndex != -1 )
972 sal_Int32 nAdjustmentsWhichNeedsToBeConverted = 0;
973 WritePresetShape( sPresetShape, eShapeType, bPredefinedHandlesUsed,
974 nAdjustmentsWhichNeedsToBeConverted, aGeometrySeq[ nAdjustmentValuesIndex ] );
976 else
977 WritePresetShape( sPresetShape );
979 if( rXPropSet.is() )
981 WriteFill( rXPropSet );
982 WriteOutline( rXPropSet );
983 WriteShapeEffects( rXPropSet );
984 WriteShape3DEffects( rXPropSet );
987 pFS->endElementNS( mnXmlNamespace, XML_spPr );
989 pFS->startElementNS( mnXmlNamespace, XML_style, FSEND );
990 WriteShapeStyle( rXPropSet );
991 pFS->endElementNS( mnXmlNamespace, XML_style );
993 // write text
994 WriteTextBox( xShape, mnXmlNamespace );
996 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp) );
998 return *this;
1001 ShapeExport& ShapeExport::WriteEllipseShape( const Reference< XShape >& xShape )
1003 SAL_INFO("oox.shape", "write ellipse shape");
1005 FSHelperPtr pFS = GetFS();
1007 pFS->startElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp), FSEND );
1009 // TODO: arc, section, cut, connector
1011 // non visual shape properties
1012 if (GetDocumentType() != DOCUMENT_DOCX)
1014 pFS->startElementNS( mnXmlNamespace, XML_nvSpPr, FSEND );
1015 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
1016 XML_id, I32S( GetNewShapeID( xShape ) ),
1017 XML_name, IDS( Ellipse ),
1018 FSEND );
1019 pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr, FSEND );
1020 WriteNonVisualProperties( xShape );
1021 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
1023 else
1024 pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr, FSEND);
1026 // visual shape properties
1027 pFS->startElementNS( mnXmlNamespace, XML_spPr, FSEND );
1028 WriteShapeTransformation( xShape, XML_a );
1029 WritePresetShape( "ellipse" );
1030 Reference< XPropertySet > xProps( xShape, UNO_QUERY );
1031 if( xProps.is() )
1033 WriteFill( xProps );
1034 WriteOutline( xProps );
1036 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1038 // write text
1039 WriteTextBox( xShape, mnXmlNamespace );
1041 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp) );
1043 return *this;
1046 ShapeExport& ShapeExport::WriteGraphicObjectShape( const Reference< XShape >& xShape )
1048 WriteGraphicObjectShapePart( xShape );
1050 return *this;
1053 void ShapeExport::WriteGraphicObjectShapePart( const Reference< XShape >& xShape, const Graphic* pGraphic )
1055 SAL_INFO("oox.shape", "write graphic object shape");
1057 if( NonEmptyText( xShape ) )
1059 // avoid treating all 'IsPresentationObject' objects as having text.
1060 Reference< XSimpleText > xText( xShape, UNO_QUERY );
1062 if( xText.is() && !xText->getString().isEmpty() )
1064 SAL_INFO("oox.shape", "graphicObject: wrote only text");
1066 WriteTextShape( xShape );
1068 return;
1072 SAL_INFO("oox.shape", "graphicObject without text");
1074 OUString sGraphicURL;
1075 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1076 if( !pGraphic && ( !xShapeProps.is() || !( xShapeProps->getPropertyValue( "GraphicURL" ) >>= sGraphicURL ) ) )
1078 SAL_INFO("oox.shape", "no graphic URL found");
1079 return;
1082 FSHelperPtr pFS = GetFS();
1083 XmlFilterBase* pFB = GetFB();
1085 if (GetDocumentType() != DOCUMENT_DOCX)
1086 pFS->startElementNS( mnXmlNamespace, XML_pic, FSEND );
1087 else
1088 pFS->startElementNS( mnXmlNamespace, XML_pic,
1089 FSNS(XML_xmlns, XML_pic), OUStringToOString(pFB->getNamespaceURL(OOX_NS(dmlPicture)), RTL_TEXTENCODING_UTF8).getStr(),
1090 FSEND );
1092 pFS->startElementNS( mnXmlNamespace, XML_nvPicPr, FSEND );
1094 OUString sName, sDescr;
1095 bool bHaveName, bHaveDesc;
1097 if ( ( bHaveName= GetProperty( xShapeProps, "Name" ) ) )
1098 mAny >>= sName;
1099 if ( ( bHaveDesc = GetProperty( xShapeProps, "Description" ) ) )
1100 mAny >>= sDescr;
1102 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
1103 XML_id, I32S( GetNewShapeID( xShape ) ),
1104 XML_name, bHaveName ? USS( sName ) : OString( "Picture " + OString::number( mnPictureIdMax++ )).getStr(),
1105 XML_descr, bHaveDesc ? USS( sDescr ) : nullptr,
1106 FSEND );
1107 // OOXTODO: //cNvPr children: XML_extLst, XML_hlinkClick, XML_hlinkHover
1109 pFS->singleElementNS( mnXmlNamespace, XML_cNvPicPr,
1110 // OOXTODO: XML_preferRelativeSize
1111 FSEND );
1113 WriteNonVisualProperties( xShape );
1115 pFS->endElementNS( mnXmlNamespace, XML_nvPicPr );
1117 pFS->startElementNS( mnXmlNamespace, XML_blipFill, FSEND );
1119 WriteBlip( xShapeProps, sGraphicURL, false, pGraphic );
1121 WriteSrcRect( xShapeProps, sGraphicURL );
1123 // now we stretch always when we get pGraphic (when changing that
1124 // behavior, test n#780830 for regression, where the OLE sheet might get tiled
1125 bool bStretch = false;
1126 if( !pGraphic && GetProperty( xShapeProps, "FillBitmapStretch" ) )
1127 mAny >>= bStretch;
1129 if ( pGraphic || bStretch )
1130 pFS->singleElementNS( XML_a, XML_stretch, FSEND );
1132 pFS->endElementNS( mnXmlNamespace, XML_blipFill );
1134 // visual shape properties
1135 pFS->startElementNS( mnXmlNamespace, XML_spPr, FSEND );
1136 WriteShapeTransformation( xShape, XML_a );
1137 WritePresetShape( "rect" );
1138 // graphic object can come with the frame (bnc#654525)
1139 WriteOutline( xShapeProps );
1141 WriteShapeEffects( xShapeProps );
1142 WriteShape3DEffects( xShapeProps );
1144 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1146 pFS->endElementNS( mnXmlNamespace, XML_pic );
1149 ShapeExport& ShapeExport::WriteConnectorShape( const Reference< XShape >& xShape )
1151 bool bFlipH = false;
1152 bool bFlipV = false;
1154 SAL_INFO("oox.shape", "write connector shape");
1156 FSHelperPtr pFS = GetFS();
1158 const char* sGeometry = "line";
1159 Reference< XPropertySet > rXPropSet( xShape, UNO_QUERY );
1160 Reference< XPropertyState > rXPropState( xShape, UNO_QUERY );
1161 awt::Point aStartPoint, aEndPoint;
1162 Reference< XShape > rXShapeA;
1163 Reference< XShape > rXShapeB;
1164 PropertyState eState;
1165 ConnectorType eConnectorType;
1166 if( GETAD( EdgeKind ) ) {
1167 mAny >>= eConnectorType;
1169 switch( eConnectorType ) {
1170 case ConnectorType_CURVE:
1171 sGeometry = "curvedConnector3";
1172 break;
1173 case ConnectorType_STANDARD:
1174 sGeometry = "bentConnector3";
1175 break;
1176 default:
1177 case ConnectorType_LINE:
1178 case ConnectorType_LINES:
1179 sGeometry = "straightConnector1";
1180 break;
1183 if( GETAD( EdgeStartPoint ) ) {
1184 mAny >>= aStartPoint;
1185 if( GETAD( EdgeEndPoint ) ) {
1186 mAny >>= aEndPoint;
1189 GET( rXShapeA, EdgeStartConnection );
1190 GET( rXShapeB, EdgeEndConnection );
1192 EscherConnectorListEntry aConnectorEntry( xShape, aStartPoint, rXShapeA, aEndPoint, rXShapeB );
1194 tools::Rectangle aRect( Point( aStartPoint.X, aStartPoint.Y ), Point( aEndPoint.X, aEndPoint.Y ) );
1195 if( aRect.getWidth() < 0 ) {
1196 bFlipH = true;
1197 aRect.setX( aEndPoint.X );
1198 aRect.setWidth( aStartPoint.X - aEndPoint.X );
1201 if( aRect.getHeight() < 0 ) {
1202 bFlipV = true;
1203 aRect.setY( aEndPoint.Y );
1204 aRect.setHeight( aStartPoint.Y - aEndPoint.Y );
1207 pFS->startElementNS( mnXmlNamespace, XML_cxnSp, FSEND );
1209 // non visual shape properties
1210 pFS->startElementNS( mnXmlNamespace, XML_nvCxnSpPr, FSEND );
1211 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
1212 XML_id, I32S( GetNewShapeID( xShape ) ),
1213 XML_name, IDS( Line ),
1214 FSEND );
1215 // non visual connector shape drawing properties
1216 pFS->startElementNS( mnXmlNamespace, XML_cNvCxnSpPr, FSEND );
1217 WriteConnectorConnections( aConnectorEntry, GetShapeID( rXShapeA ), GetShapeID( rXShapeB ) );
1218 pFS->endElementNS( mnXmlNamespace, XML_cNvCxnSpPr );
1219 pFS->singleElementNS( mnXmlNamespace, XML_nvPr, FSEND );
1220 pFS->endElementNS( mnXmlNamespace, XML_nvCxnSpPr );
1222 // visual shape properties
1223 pFS->startElementNS( mnXmlNamespace, XML_spPr, FSEND );
1224 WriteTransformation( aRect, XML_a, bFlipH, bFlipV );
1225 // TODO: write adjustments (ppt export doesn't work well there either)
1226 WritePresetShape( sGeometry );
1227 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1228 if( xShapeProps.is() )
1229 WriteOutline( xShapeProps );
1230 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1232 // write text
1233 WriteTextBox( xShape, mnXmlNamespace );
1235 pFS->endElementNS( mnXmlNamespace, XML_cxnSp );
1237 return *this;
1240 ShapeExport& ShapeExport::WriteLineShape( const Reference< XShape >& xShape )
1242 bool bFlipH = false;
1243 bool bFlipV = false;
1245 SAL_INFO("oox.shape", "write line shape");
1247 FSHelperPtr pFS = GetFS();
1249 pFS->startElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp), FSEND );
1251 tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon( xShape );
1252 if( aPolyPolygon.Count() == 1 && aPolyPolygon[ 0 ].GetSize() == 2)
1254 const tools::Polygon& rPoly = aPolyPolygon[ 0 ];
1256 bFlipH = ( rPoly[ 0 ].X() > rPoly[ 1 ].X() );
1257 bFlipV = ( rPoly[ 0 ].Y() > rPoly[ 1 ].Y() );
1260 // non visual shape properties
1261 if (GetDocumentType() != DOCUMENT_DOCX)
1263 pFS->startElementNS( mnXmlNamespace, XML_nvSpPr, FSEND );
1264 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
1265 XML_id, I32S( GetNewShapeID( xShape ) ),
1266 XML_name, IDS( Line ),
1267 FSEND );
1269 pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr, FSEND );
1270 if (GetDocumentType() != DOCUMENT_DOCX)
1272 WriteNonVisualProperties( xShape );
1273 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
1276 // visual shape properties
1277 pFS->startElementNS( mnXmlNamespace, XML_spPr, FSEND );
1278 WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV, true);
1279 WritePresetShape( "line" );
1280 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1281 if( xShapeProps.is() )
1282 WriteOutline( xShapeProps );
1283 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1285 //write style
1286 pFS->startElementNS( mnXmlNamespace, XML_style, FSEND );
1287 WriteShapeStyle( xShapeProps );
1288 pFS->endElementNS( mnXmlNamespace, XML_style );
1290 // write text
1291 WriteTextBox( xShape, mnXmlNamespace );
1293 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp) );
1295 return *this;
1298 ShapeExport& ShapeExport::WriteNonVisualDrawingProperties( const Reference< XShape >& xShape, const char* pName )
1300 GetFS()->singleElementNS( mnXmlNamespace, XML_cNvPr,
1301 XML_id, I32S( GetNewShapeID( xShape ) ),
1302 XML_name, pName,
1303 FSEND );
1305 return *this;
1308 ShapeExport& ShapeExport::WriteNonVisualProperties( const Reference< XShape >& )
1310 // Override to generate //nvPr elements.
1311 return *this;
1314 ShapeExport& ShapeExport::WriteRectangleShape( const Reference< XShape >& xShape )
1316 SAL_INFO("oox.shape", "write rectangle shape");
1318 FSHelperPtr pFS = GetFS();
1320 pFS->startElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp), FSEND );
1322 sal_Int32 nRadius = 0;
1324 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1325 if( xShapeProps.is() )
1327 xShapeProps->getPropertyValue( "CornerRadius" ) >>= nRadius;
1330 if( nRadius )
1332 nRadius = MapSize( awt::Size( nRadius, 0 ) ).Width;
1334 //TODO: use nRadius value more precisely than just deciding whether to use
1335 // "rect" or "roundRect" preset shape below
1337 // non visual shape properties
1338 if (GetDocumentType() == DOCUMENT_DOCX)
1339 pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr, FSEND );
1340 pFS->startElementNS( mnXmlNamespace, XML_nvSpPr, FSEND );
1341 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
1342 XML_id, I32S( GetNewShapeID( xShape ) ),
1343 XML_name, IDS( Rectangle ),
1344 FSEND );
1345 pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr, FSEND );
1346 WriteNonVisualProperties( xShape );
1347 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
1349 // visual shape properties
1350 pFS->startElementNS( mnXmlNamespace, XML_spPr, FSEND );
1351 WriteShapeTransformation( xShape, XML_a );
1352 WritePresetShape( nRadius == 0 ? "rect" : "roundRect" );
1353 Reference< XPropertySet > xProps( xShape, UNO_QUERY );
1354 if( xProps.is() )
1356 WriteFill( xProps );
1357 WriteOutline( xProps );
1359 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1361 // write text
1362 WriteTextBox( xShape, mnXmlNamespace );
1364 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp) );
1366 return *this;
1369 typedef ShapeExport& (ShapeExport::*ShapeConverter)( const Reference< XShape >& );
1370 typedef std::unordered_map< const char*, ShapeConverter, rtl::CStringHash, rtl::CStringEqual> NameToConvertMapType;
1372 static const NameToConvertMapType& lcl_GetConverters(DocumentType eDocumentType)
1374 static bool shape_map_inited = false;
1375 static NameToConvertMapType shape_converters;
1376 if( shape_map_inited )
1378 return shape_converters;
1381 shape_converters[ "com.sun.star.drawing.ClosedBezierShape" ] = &ShapeExport::WriteClosedPolyPolygonShape;
1382 shape_converters[ "com.sun.star.drawing.ConnectorShape" ] = &ShapeExport::WriteConnectorShape;
1383 shape_converters[ "com.sun.star.drawing.CustomShape" ] = &ShapeExport::WriteCustomShape;
1384 shape_converters[ "com.sun.star.drawing.EllipseShape" ] = &ShapeExport::WriteEllipseShape;
1385 shape_converters[ "com.sun.star.drawing.GraphicObjectShape" ] = &ShapeExport::WriteGraphicObjectShape;
1386 shape_converters[ "com.sun.star.drawing.LineShape" ] = &ShapeExport::WriteLineShape;
1387 shape_converters[ "com.sun.star.drawing.OpenBezierShape" ] = &ShapeExport::WriteOpenPolyPolygonShape;
1388 shape_converters[ "com.sun.star.drawing.PolyPolygonShape" ] = &ShapeExport::WriteClosedPolyPolygonShape;
1389 shape_converters[ "com.sun.star.drawing.PolyLineShape" ] = &ShapeExport::WriteClosedPolyPolygonShape;
1390 shape_converters[ "com.sun.star.drawing.RectangleShape" ] = &ShapeExport::WriteRectangleShape;
1391 shape_converters[ "com.sun.star.drawing.OLE2Shape" ] = &ShapeExport::WriteOLE2Shape;
1392 shape_converters[ "com.sun.star.drawing.TableShape" ] = &ShapeExport::WriteTableShape;
1393 shape_converters[ "com.sun.star.drawing.TextShape" ] = &ShapeExport::WriteTextShape;
1395 shape_converters[ "com.sun.star.presentation.GraphicObjectShape" ] = &ShapeExport::WriteGraphicObjectShape;
1396 shape_converters[ "com.sun.star.presentation.OLE2Shape" ] = &ShapeExport::WriteOLE2Shape;
1397 shape_converters[ "com.sun.star.presentation.TableShape" ] = &ShapeExport::WriteTableShape;
1398 shape_converters[ "com.sun.star.presentation.TextShape" ] = &ShapeExport::WriteTextShape;
1400 shape_converters[ "com.sun.star.presentation.DateTimeShape" ] = &ShapeExport::WriteTextShape;
1401 shape_converters[ "com.sun.star.presentation.FooterShape" ] = &ShapeExport::WriteTextShape;
1402 shape_converters[ "com.sun.star.presentation.HeaderShape" ] = &ShapeExport::WriteTextShape;
1403 shape_converters[ "com.sun.star.presentation.NotesShape" ] = &ShapeExport::WriteTextShape;
1404 shape_converters[ "com.sun.star.presentation.OutlinerShape" ] = &ShapeExport::WriteTextShape;
1405 shape_converters[ "com.sun.star.presentation.SlideNumberShape" ] = &ShapeExport::WriteTextShape;
1406 shape_converters[ "com.sun.star.presentation.TitleTextShape" ] = &ShapeExport::WriteTextShape;
1407 if (eDocumentType == DOCUMENT_DOCX)
1408 shape_converters[ "com.sun.star.drawing.GroupShape" ] = &ShapeExport::WriteGroupShape;
1409 shape_map_inited = true;
1411 return shape_converters;
1414 ShapeExport& ShapeExport::WriteShape( const Reference< XShape >& xShape )
1416 OUString sShapeType = xShape->getShapeType();
1417 SAL_INFO("oox.shape", "write shape: " << sShapeType);
1418 NameToConvertMapType::const_iterator aConverter = lcl_GetConverters(GetDocumentType()).find( USS( sShapeType ) );
1419 if( aConverter == lcl_GetConverters(GetDocumentType()).end() )
1421 SAL_INFO("oox.shape", "unknown shape");
1422 return WriteUnknownShape( xShape );
1424 (this->*(aConverter->second))( xShape );
1426 return *this;
1429 ShapeExport& ShapeExport::WriteTextBox( const Reference< XInterface >& xIface, sal_Int32 nXmlNamespace )
1431 // In case this shape has an associated textbox, then export that, and we're done.
1432 if (GetDocumentType() == DOCUMENT_DOCX && GetTextExport())
1434 uno::Reference<beans::XPropertySet> xPropertySet(xIface, uno::UNO_QUERY);
1435 if (xPropertySet.is())
1437 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
1438 if (xPropertySetInfo->hasPropertyByName("TextBox") && xPropertySet->getPropertyValue("TextBox").get<bool>())
1440 GetTextExport()->WriteTextBox(uno::Reference<drawing::XShape>(xIface, uno::UNO_QUERY_THROW));
1441 WriteText( xIface, m_presetWarp, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace );
1442 return *this;
1447 if( NonEmptyText( xIface ) )
1449 FSHelperPtr pFS = GetFS();
1451 pFS->startElementNS( nXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_txBody : XML_txbx), FSEND );
1452 WriteText( xIface, m_presetWarp, /*bBodyPr=*/(GetDocumentType() != DOCUMENT_DOCX) );
1453 pFS->endElementNS( nXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_txBody : XML_txbx) );
1454 if (GetDocumentType() == DOCUMENT_DOCX)
1455 WriteText( xIface, m_presetWarp, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace );
1457 else if (GetDocumentType() == DOCUMENT_DOCX)
1458 mpFS->singleElementNS(nXmlNamespace, XML_bodyPr, FSEND);
1460 return *this;
1463 void ShapeExport::WriteTable( const Reference< XShape >& rXShape )
1465 Reference< XTable > xTable;
1466 Reference< XPropertySet > xPropSet( rXShape, UNO_QUERY );
1468 mpFS->startElementNS( XML_a, XML_graphic, FSEND );
1469 mpFS->startElementNS( XML_a, XML_graphicData, XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/table", FSEND );
1471 if ( xPropSet.is() && ( xPropSet->getPropertyValue( "Model" ) >>= xTable ) )
1473 mpFS->startElementNS( XML_a, XML_tbl, FSEND );
1474 mpFS->singleElementNS( XML_a, XML_tblPr, FSEND );
1476 Reference< container::XIndexAccess > xColumns( xTable->getColumns(), UNO_QUERY_THROW );
1477 Reference< container::XIndexAccess > xRows( xTable->getRows(), UNO_QUERY_THROW );
1478 sal_uInt16 nRowCount = static_cast< sal_uInt16 >( xRows->getCount() );
1479 sal_uInt16 nColumnCount = static_cast< sal_uInt16 >( xColumns->getCount() );
1481 mpFS->startElementNS( XML_a, XML_tblGrid, FSEND );
1483 for ( sal_Int32 x = 0; x < nColumnCount; x++ )
1485 Reference< XPropertySet > xColPropSet( xColumns->getByIndex( x ), UNO_QUERY_THROW );
1486 sal_Int32 nWidth(0);
1487 xColPropSet->getPropertyValue( "Width" ) >>= nWidth;
1489 mpFS->singleElementNS( XML_a, XML_gridCol, XML_w, I64S(oox::drawingml::convertHmmToEmu(nWidth)), FSEND );
1492 mpFS->endElementNS( XML_a, XML_tblGrid );
1494 // map for holding the transpose index of the merged cells and pair<parentTransposeIndex, parentCell>
1495 typedef std::unordered_map<sal_Int32, std::pair<sal_Int32, Reference< XMergeableCell> > > transposeTableMap;
1496 transposeTableMap mergedCellMap;
1498 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
1500 Reference< XPropertySet > xRowPropSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
1501 sal_Int32 nRowHeight(0);
1503 xRowPropSet->getPropertyValue( "Height" ) >>= nRowHeight;
1505 mpFS->startElementNS( XML_a, XML_tr, XML_h, I64S( oox::drawingml::convertHmmToEmu( nRowHeight ) ), FSEND );
1506 for( sal_Int32 nColumn = 0; nColumn < nColumnCount; nColumn++ )
1508 Reference< XMergeableCell > xCell( xTable->getCellByPosition( nColumn, nRow ),
1509 UNO_QUERY_THROW );
1510 sal_Int32 transposedIndexofCell = (nRow * nColumnCount) + nColumn;
1512 //assume we will open a cell, set to false below if we won't
1513 bool bCellOpened = true;
1515 if(xCell->getColumnSpan() > 1 && xCell->getRowSpan() > 1)
1517 // having both : horizontal and vertical merge
1518 mpFS->startElementNS(XML_a, XML_tc, XML_gridSpan,
1519 I32S(xCell->getColumnSpan()),
1520 XML_rowSpan, I32S(xCell->getRowSpan()),
1521 FSEND);
1522 // since, XMergeableCell doesn't have the information about
1523 // cell having hMerge or vMerge.
1524 // So, Populating the merged cell map in-order to use it to
1525 // decide the attribute for the individual cell.
1526 for(sal_Int32 columnIndex = nColumn; columnIndex < nColumn+xCell->getColumnSpan(); ++columnIndex)
1528 for(sal_Int32 rowIndex = nRow; rowIndex < nRow+xCell->getRowSpan(); ++rowIndex)
1530 sal_Int32 transposeIndexForMergeCell =
1531 (rowIndex * nColumnCount) + columnIndex;
1532 mergedCellMap[transposeIndexForMergeCell] =
1533 std::make_pair(transposedIndexofCell, xCell);
1538 else if(xCell->getColumnSpan() > 1)
1540 // having : horizontal merge
1541 mpFS->startElementNS(XML_a, XML_tc, XML_gridSpan,
1542 I32S(xCell->getColumnSpan()), FSEND);
1543 for(sal_Int32 columnIndex = nColumn; columnIndex < nColumn + xCell->getColumnSpan(); ++columnIndex) {
1544 sal_Int32 transposeIndexForMergeCell = (nRow*nColumnCount) + columnIndex;
1545 mergedCellMap[transposeIndexForMergeCell] =
1546 std::make_pair(transposedIndexofCell, xCell);
1549 else if(xCell->getRowSpan() > 1)
1551 // having : vertical merge
1552 mpFS->startElementNS(XML_a, XML_tc, XML_rowSpan,
1553 I32S(xCell->getRowSpan()), FSEND);
1555 for(sal_Int32 rowIndex = nRow; rowIndex < nRow + xCell->getRowSpan(); ++rowIndex) {
1556 sal_Int32 transposeIndexForMergeCell = (rowIndex*nColumnCount) + nColumn;
1557 mergedCellMap[transposeIndexForMergeCell] =
1558 std::make_pair(transposedIndexofCell, xCell);
1561 else
1563 // now, the cell can be an independent cell or
1564 // it can be a cell which is been merged to some parent cell
1565 if(!xCell->isMerged())
1567 // independent cell
1568 mpFS->startElementNS( XML_a, XML_tc, FSEND );
1570 else
1572 // it a merged cell to some parent cell
1573 // find the parent cell for the current cell at hand
1574 transposeTableMap::iterator it = mergedCellMap.find(transposedIndexofCell);
1575 if(it != mergedCellMap.end())
1577 sal_Int32 transposeIndexOfParent = it->second.first;
1578 Reference< XMergeableCell > parentCell = it->second.second;
1579 // finding the row and column index for the parent cell from transposed index
1580 sal_Int32 parentColumnIndex = transposeIndexOfParent % nColumnCount;
1581 sal_Int32 parentRowIndex = transposeIndexOfParent / nColumnCount;
1582 if(nColumn == parentColumnIndex)
1584 // the cell is vertical merge and it might have gridspan
1585 if(parentCell->getColumnSpan() > 1)
1587 // vMerge and has gridSpan
1588 mpFS->startElementNS( XML_a, XML_tc,
1589 XML_vMerge, I32S(1),
1590 XML_gridSpan, I32S(xCell->getColumnSpan()),
1591 FSEND );
1593 else
1595 // only vMerge
1596 mpFS->startElementNS( XML_a, XML_tc,
1597 XML_vMerge, I32S(1), FSEND );
1600 else if(nRow == parentRowIndex)
1602 // the cell is horizontal merge and it might have rowspan
1603 if(parentCell->getRowSpan() > 1)
1605 // hMerge and has rowspan
1606 mpFS->startElementNS( XML_a, XML_tc,
1607 XML_hMerge, I32S(1),
1608 XML_rowSpan, I32S(xCell->getRowSpan()),
1609 FSEND );
1611 else
1613 // only hMerge
1614 mpFS->startElementNS( XML_a, XML_tc,
1615 XML_hMerge, I32S(1), FSEND );
1618 else
1620 // has hMerge and vMerge
1621 mpFS->startElementNS( XML_a, XML_tc,
1622 XML_vMerge, I32S(1),
1623 XML_hMerge, I32S(1),
1624 FSEND );
1627 else
1628 bCellOpened = false;
1632 if (bCellOpened)
1634 WriteTextBox( xCell, XML_a );
1636 Reference< XPropertySet > xCellPropSet(xCell, UNO_QUERY_THROW);
1637 WriteTableCellProperties(xCellPropSet);
1639 mpFS->endElementNS( XML_a, XML_tc );
1643 mpFS->endElementNS( XML_a, XML_tr );
1646 mpFS->endElementNS( XML_a, XML_tbl );
1649 mpFS->endElementNS( XML_a, XML_graphicData );
1650 mpFS->endElementNS( XML_a, XML_graphic );
1653 void ShapeExport::WriteTableCellProperties(const Reference< XPropertySet>& xCellPropSet)
1655 sal_Int32 nLeftMargin(0), nRightMargin(0);
1657 Any aLeftMargin = xCellPropSet->getPropertyValue("TextLeftDistance");
1658 aLeftMargin >>= nLeftMargin;
1660 Any aRightMargin = xCellPropSet->getPropertyValue("TextRightDistance");
1661 aRightMargin >>= nRightMargin;
1663 mpFS->startElementNS( XML_a, XML_tcPr,
1664 XML_marL, nLeftMargin > 0 ? I32S( oox::drawingml::convertHmmToEmu( nLeftMargin ) ) : nullptr,
1665 XML_marR, nRightMargin > 0 ? I32S( oox::drawingml::convertHmmToEmu( nRightMargin ) ): nullptr,
1666 FSEND );
1668 // Write background fill for table cell.
1669 // TODO
1670 // tcW : Table cell width
1671 WriteTableCellBorders(xCellPropSet);
1672 DrawingML::WriteFill(xCellPropSet);
1673 mpFS->endElementNS( XML_a, XML_tcPr );
1676 void ShapeExport::WriteTableCellBorders(const Reference< XPropertySet>& xCellPropSet)
1678 BorderLine2 aBorderLine;
1680 // lnL - Left Border Line Properties of table cell
1681 xCellPropSet->getPropertyValue("LeftBorder") >>= aBorderLine;
1682 sal_Int32 nLeftBorder = aBorderLine.LineWidth;
1683 util::Color aLeftBorderColor = aBorderLine.Color;
1685 // While importing the table cell border line width, it converts EMU->Hmm then divided result by 2.
1686 // To get original value of LineWidth need to multiple by 2.
1687 nLeftBorder = nLeftBorder*2;
1688 nLeftBorder = oox::drawingml::convertHmmToEmu( nLeftBorder );
1690 if(nLeftBorder > 0)
1692 mpFS->startElementNS( XML_a, XML_lnL, XML_w, I32S(nLeftBorder), FSEND );
1693 DrawingML::WriteSolidFill(aLeftBorderColor);
1694 mpFS->endElementNS( XML_a, XML_lnL );
1697 // lnR - Right Border Line Properties of table cell
1698 xCellPropSet->getPropertyValue("RightBorder") >>= aBorderLine;
1699 sal_Int32 nRightBorder = aBorderLine.LineWidth;
1700 util::Color aRightBorderColor = aBorderLine.Color;
1701 nRightBorder = nRightBorder * 2 ;
1702 nRightBorder = oox::drawingml::convertHmmToEmu( nRightBorder );
1704 if(nRightBorder > 0)
1706 mpFS->startElementNS( XML_a, XML_lnR, XML_w, I32S(nRightBorder), FSEND);
1707 DrawingML::WriteSolidFill(aRightBorderColor);
1708 mpFS->endElementNS( XML_a, XML_lnR);
1711 // lnT - Top Border Line Properties of table cell
1712 xCellPropSet->getPropertyValue("TopBorder") >>= aBorderLine;
1713 sal_Int32 nTopBorder = aBorderLine.LineWidth;
1714 util::Color aTopBorderColor = aBorderLine.Color;
1715 nTopBorder = nTopBorder * 2;
1716 nTopBorder = oox::drawingml::convertHmmToEmu( nTopBorder );
1718 if(nTopBorder > 0)
1720 mpFS->startElementNS( XML_a, XML_lnT, XML_w, I32S(nTopBorder), FSEND);
1721 DrawingML::WriteSolidFill(aTopBorderColor);
1722 mpFS->endElementNS( XML_a, XML_lnT);
1725 // lnB - Bottom Border Line Properties of table cell
1726 xCellPropSet->getPropertyValue("BottomBorder") >>= aBorderLine;
1727 sal_Int32 nBottomBorder = aBorderLine.LineWidth;
1728 util::Color aBottomBorderColor = aBorderLine.Color;
1729 nBottomBorder = nBottomBorder * 2;
1730 nBottomBorder = oox::drawingml::convertHmmToEmu( nBottomBorder );
1732 if(nBottomBorder > 0)
1734 mpFS->startElementNS( XML_a, XML_lnB, XML_w, I32S(nBottomBorder), FSEND);
1735 DrawingML::WriteSolidFill(aBottomBorderColor);
1736 mpFS->endElementNS( XML_a, XML_lnB);
1740 ShapeExport& ShapeExport::WriteTableShape( const Reference< XShape >& xShape )
1742 FSHelperPtr pFS = GetFS();
1744 pFS->startElementNS( mnXmlNamespace, XML_graphicFrame, FSEND );
1746 pFS->startElementNS( mnXmlNamespace, XML_nvGraphicFramePr, FSEND );
1748 pFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
1749 XML_id, I32S( GetNewShapeID( xShape ) ),
1750 XML_name, IDS(Table),
1751 FSEND );
1753 pFS->singleElementNS( mnXmlNamespace, XML_cNvGraphicFramePr,
1754 FSEND );
1756 if( GetDocumentType() == DOCUMENT_PPTX )
1757 pFS->singleElementNS( mnXmlNamespace, XML_nvPr,
1758 FSEND );
1759 pFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
1761 WriteShapeTransformation( xShape, mnXmlNamespace );
1762 WriteTable( xShape );
1764 pFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
1766 return *this;
1769 ShapeExport& ShapeExport::WriteTextShape( const Reference< XShape >& xShape )
1771 FSHelperPtr pFS = GetFS();
1773 pFS->startElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp), FSEND );
1775 // non visual shape properties
1776 if (GetDocumentType() != DOCUMENT_DOCX)
1778 pFS->startElementNS( mnXmlNamespace, XML_nvSpPr, FSEND );
1779 WriteNonVisualDrawingProperties( xShape, IDS( TextShape ) );
1781 pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr, XML_txBox, "1", FSEND );
1782 if (GetDocumentType() != DOCUMENT_DOCX)
1784 WriteNonVisualProperties( xShape );
1785 pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
1788 // visual shape properties
1789 pFS->startElementNS( mnXmlNamespace, XML_spPr, FSEND );
1790 WriteShapeTransformation( xShape, XML_a );
1791 WritePresetShape( "rect" );
1792 uno::Reference<beans::XPropertySet> xPropertySet(xShape, UNO_QUERY);
1793 WriteBlipOrNormalFill(xPropertySet, "GraphicURL");
1794 WriteOutline(xPropertySet);
1795 pFS->endElementNS( mnXmlNamespace, XML_spPr );
1797 WriteTextBox( xShape, mnXmlNamespace );
1799 pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp) );
1801 return *this;
1804 void ShapeExport::WriteMathShape(Reference<XShape> const& xShape)
1806 Reference<XPropertySet> const xPropSet(xShape, UNO_QUERY);
1807 assert(xPropSet.is());
1808 Reference<XModel> xMathModel;
1809 xPropSet->getPropertyValue("Model") >>= xMathModel;
1810 assert(xMathModel.is());
1811 assert(GetDocumentType() != DOCUMENT_DOCX); // should be written in DocxAttributeOutput
1812 SAL_WARN_IF(GetDocumentType() == DOCUMENT_XLSX, "oox.shape", "Math export to XLSX isn't tested, should it happen here?");
1814 // ECMA standard does not actually allow oMath outside of
1815 // WordProcessingML so write a MCE like PPT 2010 does
1816 mpFS->startElementNS(XML_mc, XML_AlternateContent, FSEND);
1817 mpFS->startElementNS(XML_mc, XML_Choice,
1818 FSNS(XML_xmlns, XML_a14), OUStringToOString(mpFB->getNamespaceURL(OOX_NS(a14)), RTL_TEXTENCODING_UTF8).getStr(),
1819 XML_Requires, "a14",
1820 FSEND);
1821 mpFS->startElementNS(mnXmlNamespace, XML_sp, FSEND);
1822 mpFS->startElementNS(mnXmlNamespace, XML_nvSpPr, FSEND);
1823 mpFS->singleElementNS(mnXmlNamespace, XML_cNvPr,
1824 XML_id, OString::number(GetNewShapeID(xShape)).getStr(),
1825 XML_name, OString("Formula " + OString::number(mnShapeIdMax++)).getStr(),
1826 FSEND);
1827 mpFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr, XML_txBox, "1", FSEND);
1828 mpFS->singleElementNS(mnXmlNamespace, XML_nvPr, FSEND);
1829 mpFS->endElementNS(mnXmlNamespace, XML_nvSpPr);
1830 mpFS->startElementNS(mnXmlNamespace, XML_spPr, FSEND);
1831 WriteShapeTransformation(xShape, XML_a);
1832 WritePresetShape("rect");
1833 mpFS->endElementNS(mnXmlNamespace, XML_spPr);
1834 mpFS->startElementNS(mnXmlNamespace, XML_txBody, FSEND);
1835 mpFS->startElementNS(XML_a, XML_bodyPr, FSEND);
1836 mpFS->endElementNS(XML_a, XML_bodyPr);
1837 mpFS->startElementNS(XML_a, XML_p, FSEND);
1838 mpFS->startElementNS(XML_a14, XML_m, FSEND);
1840 oox::FormulaExportBase *const pMagic(dynamic_cast<oox::FormulaExportBase*>(xMathModel.get()));
1841 assert(pMagic);
1842 pMagic->writeFormulaOoxml(GetFS(), GetFB()->getVersion(), GetDocumentType());
1844 mpFS->endElementNS(XML_a14, XML_m);
1845 mpFS->endElementNS(XML_a, XML_p);
1846 mpFS->endElementNS(mnXmlNamespace, XML_txBody);
1847 mpFS->endElementNS(mnXmlNamespace, XML_sp);
1848 mpFS->endElementNS(XML_mc, XML_Choice);
1849 mpFS->startElementNS(XML_mc, XML_Fallback, FSEND);
1850 // TODO: export bitmap shape as fallback
1851 mpFS->endElementNS(XML_mc, XML_Fallback);
1852 mpFS->endElementNS(XML_mc, XML_AlternateContent);
1855 ShapeExport& ShapeExport::WriteOLE2Shape( const Reference< XShape >& xShape )
1857 Reference< XPropertySet > xPropSet( xShape, UNO_QUERY );
1858 if (!xPropSet.is())
1859 return *this;
1861 enum { CHART, MATH, OTHER } eType(OTHER);
1862 OUString clsid;
1863 xPropSet->getPropertyValue("CLSID") >>= clsid;
1864 if (!clsid.isEmpty())
1866 SvGlobalName aClassID;
1867 bool const isValid = aClassID.MakeId(clsid);
1868 assert(isValid); (void)isValid;
1869 if (SotExchange::IsChart(aClassID))
1870 eType = CHART;
1871 else if (SotExchange::IsMath(aClassID))
1872 eType = MATH;
1875 if (CHART == eType)
1877 Reference< XChartDocument > xChartDoc;
1878 xPropSet->getPropertyValue("Model") >>= xChartDoc;
1879 assert(xChartDoc.is());
1880 //export the chart
1881 Reference< XModel > xModel( xChartDoc, UNO_QUERY );
1882 ChartExport aChartExport( mnXmlNamespace, GetFS(), xModel, GetFB(), GetDocumentType() );
1883 static sal_Int32 nChartCount = 0;
1884 aChartExport.WriteChartObj( xShape, ++nChartCount );
1885 return *this;
1888 if (MATH == eType)
1890 WriteMathShape(xShape);
1891 return *this;
1894 uno::Reference<embed::XEmbeddedObject> const xObj(
1895 xPropSet->getPropertyValue("EmbeddedObject"), uno::UNO_QUERY);
1897 if (!xObj.is())
1899 SAL_WARN("oox.shape", "ShapeExport::WriteOLE2Shape: no object");
1900 return *this;
1903 uno::Sequence<beans::PropertyValue> grabBag;
1904 OUString entryName;
1907 uno::Reference<beans::XPropertySet> const xParent(
1908 uno::Reference<container::XChild>(xObj, uno::UNO_QUERY_THROW)->getParent(),
1909 uno::UNO_QUERY_THROW);
1911 xParent->getPropertyValue("InteropGrabBag") >>= grabBag;
1913 entryName = uno::Reference<embed::XEmbedPersist>(xObj, uno::UNO_QUERY)->getEntryName();
1915 catch (uno::Exception const& e)
1917 SAL_WARN("oox.shape", "ShapeExport::WriteOLE2Shape: exception: " << e.Message);
1918 return *this;
1921 OUString progID;
1923 for (auto const& it : grabBag)
1925 if (it.Name == "EmbeddedObjects")
1927 uno::Sequence<beans::PropertyValue> objects;
1928 it.Value >>= objects;
1929 for (auto const& object : objects)
1931 if (object.Name == entryName)
1933 uno::Sequence<beans::PropertyValue> props;
1934 object.Value >>= props;
1935 for (auto const& prop : props)
1937 if (prop.Name == "ProgID")
1939 prop.Value >>= progID;
1940 break;
1943 break;
1946 break;
1950 OUString sMediaType;
1951 OUString sRelationType;
1952 OUString sSuffix;
1953 const char * pProgID(nullptr);
1955 uno::Reference<io::XInputStream> const xInStream =
1956 oox::GetOLEObjectStream(
1957 mpFB->getComponentContext(), xObj, progID,
1958 sMediaType, sRelationType, sSuffix, pProgID);
1960 if (!xInStream.is())
1962 return *this;
1965 OString anotherProgID;
1966 if (!pProgID && !progID.isEmpty())
1968 anotherProgID = OUStringToOString(progID, RTL_TEXTENCODING_UTF8);
1969 pProgID = anotherProgID.getStr();
1972 assert(!sMediaType.isEmpty());
1973 assert(!sRelationType.isEmpty());
1974 assert(!sSuffix.isEmpty());
1976 OUString sFileName = "embeddings/oleObject" + OUString::number(++m_nEmbeddedObjects) + "." + sSuffix;
1977 uno::Reference<io::XOutputStream> const xOutStream(
1978 mpFB->openFragmentStream(
1979 OUString::createFromAscii(GetComponentDir()) + "/" + sFileName,
1980 sMediaType));
1981 assert(xOutStream.is()); // no reason why that could fail
1983 try {
1984 ::comphelper::OStorageHelper::CopyInputToOutput(xInStream, xOutStream);
1985 } catch (uno::Exception const& e) {
1986 SAL_WARN("oox.shape", "ShapeExport::WriteOLEObject: exception: " << e.Message);
1989 OUString const sRelId = mpFB->addRelation(
1990 mpFS->getOutputStream(), sRelationType,
1991 OUString::createFromAscii(GetRelationCompPrefix()) + sFileName);
1993 mpFS->startElementNS( mnXmlNamespace, XML_graphicFrame, FSEND );
1995 mpFS->startElementNS( mnXmlNamespace, XML_nvGraphicFramePr, FSEND );
1997 mpFS->singleElementNS( mnXmlNamespace, XML_cNvPr,
1998 XML_id, I32S( GetNewShapeID( xShape ) ),
1999 XML_name, IDS(Object),
2000 FSEND );
2002 mpFS->singleElementNS( mnXmlNamespace, XML_cNvGraphicFramePr,
2003 FSEND );
2005 if (GetDocumentType() == DOCUMENT_PPTX)
2006 mpFS->singleElementNS( mnXmlNamespace, XML_nvPr,
2007 FSEND );
2008 mpFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
2010 WriteShapeTransformation( xShape, mnXmlNamespace );
2012 mpFS->startElementNS( XML_a, XML_graphic, FSEND );
2013 mpFS->startElementNS( XML_a, XML_graphicData,
2014 XML_uri, "http://schemas.openxmlformats.org/presentationml/2006/ole",
2015 FSEND );
2016 if (pProgID)
2018 mpFS->startElementNS( mnXmlNamespace, XML_oleObj,
2019 XML_progId, pProgID,
2020 FSNS(XML_r, XML_id), USS( sRelId ),
2021 XML_spid, "",
2022 FSEND );
2024 else
2026 mpFS->startElementNS( mnXmlNamespace, XML_oleObj,
2027 //? XML_name, "Document",
2028 FSNS(XML_r, XML_id), USS( sRelId ),
2029 // The spec says that this is a required attribute, but PowerPoint can only handle an empty value.
2030 XML_spid, "",
2031 FSEND );
2034 mpFS->singleElementNS( mnXmlNamespace, XML_embed, FSEND );
2036 // pic element
2037 SdrObject* pSdrOLE2( GetSdrObjectFromXShape( xShape ) );
2038 // The spec doesn't allow <p:pic> here, but PowerPoint requires it.
2039 bool bEcma = mpFB->getVersion() == oox::core::ECMA_DIALECT;
2040 if (pSdrOLE2 && dynamic_cast<const SdrOle2Obj*>( pSdrOLE2) != nullptr && bEcma)
2042 const Graphic* pGraphic = static_cast<SdrOle2Obj*>(pSdrOLE2)->GetGraphic();
2043 if (pGraphic)
2044 WriteGraphicObjectShapePart( xShape, pGraphic );
2047 mpFS->endElementNS( mnXmlNamespace, XML_oleObj );
2049 mpFS->endElementNS( XML_a, XML_graphicData );
2050 mpFS->endElementNS( XML_a, XML_graphic );
2052 mpFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
2054 return *this;
2057 ShapeExport& ShapeExport::WriteUnknownShape( const Reference< XShape >& )
2059 // Override this method to do something useful.
2060 return *this;
2063 size_t ShapeExport::ShapeHash::operator()( const Reference < XShape >& rXShape ) const
2065 return rXShape->getShapeType().hashCode();
2068 sal_Int32 ShapeExport::GetNewShapeID( const Reference< XShape >& rXShape )
2070 return GetNewShapeID( rXShape, GetFB() );
2073 sal_Int32 ShapeExport::GetNewShapeID( const Reference< XShape >& rXShape, XmlFilterBase* pFB )
2075 if( !rXShape.is() )
2076 return -1;
2078 sal_Int32 nID = pFB->GetUniqueId();
2080 (*mpShapeMap)[ rXShape ] = nID;
2082 return nID;
2085 sal_Int32 ShapeExport::GetShapeID( const Reference< XShape >& rXShape )
2087 return GetShapeID( rXShape, mpShapeMap );
2090 sal_Int32 ShapeExport::GetShapeID( const Reference< XShape >& rXShape, ShapeHashMap* pShapeMap )
2092 if( !rXShape.is() )
2093 return -1;
2095 ShapeHashMap::const_iterator aIter = pShapeMap->find( rXShape );
2097 if( aIter == pShapeMap->end() )
2098 return -1;
2100 return aIter->second;
2105 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */