Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/oox/source/export/shapes.cxx
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <config_wasm_strip.h>
21
22
#include <sal/config.h>
23
#include <sal/log.hxx>
24
25
#include <filter/msfilter/util.hxx>
26
#include <o3tl/string_view.hxx>
27
#include <o3tl/any.hxx>
28
#include <oox/core/xmlfilterbase.hxx>
29
#include <oox/export/shapes.hxx>
30
#include <oox/export/utils.hxx>
31
#include <oox/token/namespaces.hxx>
32
#include <oox/token/relationship.hxx>
33
#include <oox/token/tokens.hxx>
34
35
#include <initializer_list>
36
#include <string_view>
37
38
#include <com/sun/star/beans/PropertyValues.hpp>
39
#include <com/sun/star/beans/XPropertySet.hpp>
40
#include <com/sun/star/beans/XPropertySetInfo.hpp>
41
#include <com/sun/star/beans/XPropertyState.hpp>
42
#include <com/sun/star/container/XChild.hpp>
43
#include <com/sun/star/document/XExporter.hpp>
44
#include <com/sun/star/document/XStorageBasedDocument.hpp>
45
#include <com/sun/star/drawing/CircleKind.hpp>
46
#include <com/sun/star/drawing/FillStyle.hpp>
47
#include <com/sun/star/drawing/ConnectorType.hpp>
48
#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
49
#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
50
#include <com/sun/star/embed/Aspects.hpp>
51
#include <com/sun/star/embed/EmbedStates.hpp>
52
#include <com/sun/star/embed/XEmbeddedObject.hpp>
53
#include <com/sun/star/embed/XEmbedPersist.hpp>
54
#include <com/sun/star/frame/XStorable.hpp>
55
#include <com/sun/star/graphic/XGraphic.hpp>
56
#include <com/sun/star/i18n/ScriptType.hpp>
57
#include <com/sun/star/io/XOutputStream.hpp>
58
#include <com/sun/star/text/XSimpleText.hpp>
59
#include <com/sun/star/text/XText.hpp>
60
#include <com/sun/star/table/XTable.hpp>
61
#include <com/sun/star/table/XMergeableCell.hpp>
62
#include <com/sun/star/chart2/XChartDocument.hpp>
63
#include <com/sun/star/frame/XModel.hpp>
64
#include <com/sun/star/uno/XComponentContext.hpp>
65
#include <com/sun/star/drawing/XDrawPages.hpp>
66
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
67
#include <com/sun/star/presentation/ClickAction.hpp>
68
#include <com/sun/star/drawing/XGluePointsSupplier.hpp>
69
#include <com/sun/star/container/XIdentifierAccess.hpp>
70
#include <com/sun/star/table/BorderLineStyle.hpp>
71
#include <tools/globname.hxx>
72
#include <comphelper/classids.hxx>
73
#include <comphelper/propertysequence.hxx>
74
#include <comphelper/storagehelper.hxx>
75
#include <comphelper/memorystream.hxx>
76
#include <sot/exchange.hxx>
77
#include <utility>
78
#include <vcl/graph.hxx>
79
#include <vcl/outdev.hxx>
80
#include <filter/msfilter/escherex.hxx>
81
#include <svtools/embedhlp.hxx>
82
#include <svx/svdoashp.hxx>
83
#include <svx/svdoole2.hxx>
84
#include <comphelper/diagnose_ex.hxx>
85
#include <oox/export/chartexport.hxx>
86
#include <oox/mathml/imexport.hxx>
87
#include <basegfx/numeric/ftools.hxx>
88
#include <oox/export/DMLPresetShapeExport.hxx>
89
90
#include <frozen/bits/defines.h>
91
#include <frozen/bits/elsa_std.h>
92
#include <frozen/set.h>
93
#include <frozen/unordered_map.h>
94
95
#include <sax/fastattribs.hxx>
96
97
using namespace ::css;
98
using namespace ::css::beans;
99
using namespace ::css::uno;
100
using namespace ::css::drawing;
101
using namespace ::css::table;
102
using namespace ::css::container;
103
using namespace ::css::document;
104
using namespace ::css::text;
105
106
using ::css::io::XOutputStream;
107
using ::css::chart2::XChartDocument;
108
using ::css::frame::XModel;
109
110
using ::oox::core::XmlFilterBase;
111
using ::sax_fastparser::FSHelperPtr;
112
113
114
namespace oox {
115
116
static void lcl_ConvertProgID(std::u16string_view rProgID,
117
    OUString & o_rMediaType, OUString & o_rRelationType, OUString & o_rFileExtension)
118
0
{
119
0
    if (rProgID == u"Excel.Sheet.12")
120
0
    {
121
0
        o_rMediaType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
122
0
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
123
0
        o_rFileExtension = "xlsx";
124
0
    }
125
0
    else if (o3tl::starts_with(rProgID, u"Excel.SheetBinaryMacroEnabled.12") )
126
0
    {
127
0
        o_rMediaType = "application/vnd.ms-excel.sheet.binary.macroEnabled.12";
128
0
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
129
0
        o_rFileExtension = "xlsb";
130
0
    }
131
0
    else if (o3tl::starts_with(rProgID, u"Excel.SheetMacroEnabled.12"))
132
0
    {
133
0
        o_rMediaType = "application/vnd.ms-excel.sheet.macroEnabled.12";
134
0
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
135
0
        o_rFileExtension = "xlsm";
136
0
    }
137
0
    else if (o3tl::starts_with(rProgID, u"Excel.Sheet"))
138
0
    {
139
0
        o_rMediaType = "application/vnd.ms-excel";
140
0
        o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
141
0
        o_rFileExtension = "xls";
142
0
    }
143
0
    else if (rProgID == u"PowerPoint.Show.12")
144
0
    {
145
0
        o_rMediaType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
146
0
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
147
0
        o_rFileExtension = "pptx";
148
0
    }
149
0
    else if (rProgID == u"PowerPoint.ShowMacroEnabled.12")
150
0
    {
151
0
        o_rMediaType = "application/vnd.ms-powerpoint.presentation.macroEnabled.12";
152
0
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
153
0
        o_rFileExtension = "pptm";
154
0
    }
155
0
    else if (o3tl::starts_with(rProgID, u"PowerPoint.Show"))
156
0
    {
157
0
        o_rMediaType = "application/vnd.ms-powerpoint";
158
0
        o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
159
0
        o_rFileExtension = "ppt";
160
0
    }
161
0
    else if (o3tl::starts_with(rProgID, u"PowerPoint.Slide.12"))
162
0
    {
163
0
       o_rMediaType = "application/vnd.openxmlformats-officedocument.presentationml.slide";
164
0
       o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
165
0
       o_rFileExtension = "sldx";
166
0
    }
167
0
    else if (rProgID == u"PowerPoint.SlideMacroEnabled.12")
168
0
    {
169
0
       o_rMediaType = "application/vnd.ms-powerpoint.slide.macroEnabled.12";
170
0
       o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
171
0
       o_rFileExtension = "sldm";
172
0
    }
173
0
    else if (rProgID == u"Word.DocumentMacroEnabled.12")
174
0
    {
175
0
        o_rMediaType = "application/vnd.ms-word.document.macroEnabled.12";
176
0
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
177
0
        o_rFileExtension = "docm";
178
0
    }
179
0
    else if (rProgID == u"Word.Document.12")
180
0
    {
181
0
        o_rMediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
182
0
        o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
183
0
        o_rFileExtension = "docx";
184
0
    }
185
0
    else if (rProgID == u"Word.Document.8")
186
0
    {
187
0
        o_rMediaType = "application/msword";
188
0
        o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
189
0
        o_rFileExtension = "doc";
190
0
    }
191
0
    else if (rProgID == u"Excel.Chart.8")
192
0
    {
193
0
        o_rMediaType = "application/vnd.ms-excel";
194
0
        o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
195
0
        o_rFileExtension = "xls";
196
0
    }
197
0
    else if (rProgID == u"AcroExch.Document.11")
198
0
    {
199
0
        o_rMediaType = "application/pdf";
200
0
        o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
201
0
        o_rFileExtension = "pdf";
202
0
    }
203
0
    else
204
0
    {
205
0
        o_rMediaType = "application/vnd.openxmlformats-officedocument.oleObject";
206
0
        o_rRelationType = oox::getRelationship(Relationship::OLEOBJECT);
207
0
        o_rFileExtension = "bin";
208
0
    }
209
0
}
210
211
static uno::Reference<io::XInputStream> lcl_StoreOwnAsOOXML(
212
    uno::Reference<embed::XEmbeddedObject> const& xObj,
213
    char const*& o_rpProgID,
214
    OUString & o_rMediaType, OUString & o_rRelationType, OUString & o_rSuffix)
215
0
{
216
0
    static struct {
217
0
        struct {
218
0
            sal_uInt32 n1;
219
0
            sal_uInt16 n2, n3;
220
0
            sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15;
221
0
        } ClassId;
222
0
        char const* pFilterName;
223
0
        char const* pMediaType;
224
0
        char const* pProgID;
225
0
        char const* pSuffix;
226
0
    } const s_Mapping[] = {
227
0
        { {SO3_SW_CLASSID_60}, "MS Word 2007 XML", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "Word.Document.12", "docx" },
228
0
        { {SO3_SC_CLASSID_60}, "Calc MS Excel 2007 XML", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Excel.Sheet.12", "xlsx" },
229
0
        { {SO3_SIMPRESS_CLASSID_60}, "Impress MS PowerPoint 2007 XML", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "PowerPoint.Show.12", "pptx" },
230
        // FIXME: Draw does not appear to have a MSO format export filter?
231
//            { {SO3_SDRAW_CLASSID}, "", "", "", "" },
232
0
        { {SO3_SCH_CLASSID_60}, "unused", "", "", "" },
233
0
        { {SO3_SM_CLASSID_60}, "unused", "", "", "" },
234
0
    };
235
236
0
    const char * pFilterName(nullptr);
237
0
    SvGlobalName const classId(xObj->getClassID());
238
0
    for (auto & i : s_Mapping)
239
0
    {
240
0
        auto const& rId(i.ClassId);
241
0
        SvGlobalName const temp(rId.n1, rId.n2, rId.n3, rId.b8, rId.b9, rId.b10, rId.b11, rId.b12, rId.b13, rId.b14, rId.b15);
242
0
        if (temp == classId)
243
0
        {
244
0
            assert(SvGlobalName(SO3_SCH_CLASSID_60) != classId); // chart should be written elsewhere!
245
0
            assert(SvGlobalName(SO3_SM_CLASSID_60) != classId); // formula should be written elsewhere!
246
0
            pFilterName = i.pFilterName;
247
0
            o_rMediaType = OUString::createFromAscii(i.pMediaType);
248
0
            o_rpProgID = i.pProgID;
249
0
            o_rSuffix = OUString::createFromAscii(i.pSuffix);
250
0
            o_rRelationType = oox::getRelationship(Relationship::PACKAGE);
251
0
            break;
252
0
        }
253
0
    }
254
255
0
    if (!pFilterName)
256
0
    {
257
0
        SAL_WARN("oox.shape", "oox::GetOLEObjectStream: unknown ClassId " << classId.GetHexName());
258
0
        return nullptr;
259
0
    }
260
261
0
    if (embed::EmbedStates::LOADED == xObj->getCurrentState())
262
0
    {
263
0
        xObj->changeState(embed::EmbedStates::RUNNING);
264
0
    }
265
    // use a temp stream - while it would work to store directly to a
266
    // fragment stream, an error during export means we'd have to delete it
267
0
    rtl::Reference< comphelper::UNOMemoryStream > xTempStream = new comphelper::UNOMemoryStream();
268
0
    uno::Sequence<beans::PropertyValue> args( comphelper::InitPropertySequence({
269
0
            { "OutputStream", Any(xTempStream->getOutputStream()) },
270
0
            { "FilterName", Any(OUString::createFromAscii(pFilterName)) }
271
0
        }));
272
0
    uno::Reference<frame::XStorable> xStorable(xObj->getComponent(), uno::UNO_QUERY);
273
0
    try
274
0
    {
275
0
        xStorable->storeToURL(u"private:stream"_ustr, args);
276
0
    }
277
0
    catch (uno::Exception const&)
278
0
    {
279
0
        TOOLS_WARN_EXCEPTION("oox.shape", "oox::GetOLEObjectStream");
280
0
        return nullptr;
281
0
    }
282
0
    xTempStream->getOutputStream()->closeOutput();
283
0
    return xTempStream->getInputStream();
284
0
}
285
286
uno::Reference<io::XInputStream> GetOLEObjectStream(
287
        uno::Reference<embed::XEmbeddedObject> const& xObj,
288
        std::u16string_view i_rProgID,
289
        OUString & o_rMediaType,
290
        OUString & o_rRelationType,
291
        OUString & o_rSuffix,
292
        const char *& o_rpProgID)
293
0
{
294
0
    uno::Reference<io::XInputStream> xInStream;
295
0
    try
296
0
    {
297
0
        uno::Reference<document::XStorageBasedDocument> const xParent(
298
0
            uno::Reference<container::XChild>(xObj, uno::UNO_QUERY_THROW)->getParent(),
299
0
            uno::UNO_QUERY_THROW);
300
0
        uno::Reference<embed::XStorage> const xParentStorage(xParent->getDocumentStorage());
301
0
        OUString const entryName(
302
0
            uno::Reference<embed::XEmbedPersist>(xObj, uno::UNO_QUERY_THROW)->getEntryName());
303
304
0
        if (xParentStorage->isStreamElement(entryName))
305
0
        {
306
0
            lcl_ConvertProgID(i_rProgID, o_rMediaType, o_rRelationType, o_rSuffix);
307
0
            xInStream = xParentStorage->cloneStreamElement(entryName)->getInputStream();
308
            // TODO: make it possible to take the sMediaType from the stream
309
0
        }
310
0
        else // the object is ODF - either the whole document is
311
0
        {    // ODF, or the OLE was edited so it was converted to ODF
312
0
            xInStream = lcl_StoreOwnAsOOXML(xObj,
313
0
                    o_rpProgID, o_rMediaType, o_rRelationType, o_rSuffix);
314
0
        }
315
0
    }
316
0
    catch (uno::Exception const&)
317
0
    {
318
0
        TOOLS_WARN_EXCEPTION("oox.shape", "oox::GetOLEObjectStream");
319
0
    }
320
0
    return xInStream;
321
0
}
322
323
} // namespace oox
324
325
namespace oox::drawingml {
326
327
ShapeExport::ShapeExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, ShapeHashMap* pShapeMap, XmlFilterBase* pFB, DocumentType eDocumentType, DMLTextExport* pTextExport, bool bUserShapes )
328
0
    : DrawingML( std::move(pFS), pFB, eDocumentType, pTextExport )
329
0
    , m_nEmbeddedObjects(0)
330
0
    , mnShapeIdMax( 1 )
331
0
    , mbUserShapes( bUserShapes )
332
0
    , mnXmlNamespace( nXmlNamespace )
333
0
    , maMapModeSrc( MapUnit::Map100thMM )
334
0
    , maMapModeDest( MapUnit::MapInch, Point(), Fraction( 1, 576 ), Fraction( 1, 576 ) )
335
0
    , mpShapeMap( pShapeMap ? pShapeMap : &maShapeMap )
336
0
{
337
0
    mpURLTransformer = std::make_shared<URLTransformer>();
338
0
}
339
340
void ShapeExport::SetURLTranslator(const std::shared_ptr<URLTransformer>& pTransformer)
341
0
{
342
0
    mpURLTransformer = pTransformer;
343
0
}
344
345
awt::Size ShapeExport::MapSize( const awt::Size& rSize ) const
346
0
{
347
0
    Size aRetSize( OutputDevice::LogicToLogic( Size( rSize.Width, rSize.Height ), maMapModeSrc, maMapModeDest ) );
348
349
0
    if ( !aRetSize.Width() )
350
0
        aRetSize.AdjustWidth( 1 );
351
0
    if ( !aRetSize.Height() )
352
0
        aRetSize.AdjustHeight( 1 );
353
0
    return awt::Size( aRetSize.Width(), aRetSize.Height() );
354
0
}
355
356
static bool IsNonEmptySimpleText(const Reference<XInterface>& xIface)
357
0
{
358
0
    if (Reference<XSimpleText> xText{ xIface, UNO_QUERY })
359
0
        return xText->getString().getLength();
360
361
0
    return false;
362
0
}
363
364
bool ShapeExport::NonEmptyText( const Reference< XInterface >& xIface )
365
0
{
366
0
    Reference< XPropertySet > xPropSet( xIface, UNO_QUERY );
367
368
0
    if( xPropSet.is() )
369
0
    {
370
0
        Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
371
0
        if ( xPropSetInfo.is() )
372
0
        {
373
0
            if ( xPropSetInfo->hasPropertyByName( u"IsEmptyPresentationObject"_ustr ) )
374
0
            {
375
0
                bool bIsEmptyPresObj = false;
376
0
                if ( xPropSet->getPropertyValue( u"IsEmptyPresentationObject"_ustr ) >>= bIsEmptyPresObj )
377
0
                {
378
0
                    SAL_INFO("oox.shape", "empty presentation object " << bIsEmptyPresObj << " , props:");
379
0
                    if( bIsEmptyPresObj )
380
0
                       return true;
381
0
                }
382
0
            }
383
384
0
            if ( xPropSetInfo->hasPropertyByName( u"IsPresentationObject"_ustr ) )
385
0
            {
386
0
                bool bIsPresObj = false;
387
0
                if ( xPropSet->getPropertyValue( u"IsPresentationObject"_ustr ) >>= bIsPresObj )
388
0
                {
389
0
                    SAL_INFO("oox.shape", "presentation object " << bIsPresObj << ", props:");
390
0
                    if( bIsPresObj )
391
0
                       return true;
392
0
                }
393
0
            }
394
0
        }
395
0
    }
396
397
0
    return IsNonEmptySimpleText(xIface);
398
0
}
399
400
static void AddExtLst(FSHelperPtr const& pFS, Reference<XPropertySet> const& xShape)
401
0
{
402
0
    if (xShape->getPropertySetInfo()->hasPropertyByName(u"Decorative"_ustr)
403
0
        && xShape->getPropertyValue(u"Decorative"_ustr).get<bool>())
404
0
    {
405
0
        pFS->startElementNS(XML_a, XML_extLst);
406
//            FSNS(XML_xmlns, XML_a), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
407
0
        pFS->startElementNS(XML_a, XML_ext,
408
            // MSO uses this "URI" which is obviously not a URI
409
0
            XML_uri, "{C183D7F6-B498-43B3-948B-1728B52AA6E4}");
410
0
        pFS->singleElementNS(XML_adec, XML_decorative,
411
0
            FSNS(XML_xmlns, XML_adec), "http://schemas.microsoft.com/office/drawing/2017/decorative",
412
0
            XML_val, "1");
413
0
        pFS->endElementNS(XML_a, XML_ext);
414
0
        pFS->endElementNS(XML_a, XML_extLst);
415
0
    }
416
0
}
417
418
ShapeExport& ShapeExport::WritePolyPolygonShape( const Reference< XShape >& xShape, const bool bClosed )
419
0
{
420
0
    SAL_INFO("oox.shape", "write polypolygon shape");
421
422
0
    FSHelperPtr pFS = GetFS();
423
0
    pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp));
424
425
0
    awt::Point aPos = xShape->getPosition();
426
    // Position is relative to group for child elements in Word, but absolute in API.
427
0
    if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes && m_xParent.is())
428
0
    {
429
0
        awt::Point aParentPos = m_xParent->getPosition();
430
0
        aPos.X -= aParentPos.X;
431
0
        aPos.Y -= aParentPos.Y;
432
0
    }
433
0
    awt::Size aSize = xShape->getSize();
434
0
    tools::Rectangle aRect(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height));
435
436
#if OSL_DEBUG_LEVEL > 0
437
    tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon(xShape);
438
    awt::Size size = MapSize( awt::Size( aRect.GetWidth(), aRect.GetHeight() ) );
439
    SAL_INFO("oox.shape", "poly count " << aPolyPolygon.Count());
440
    SAL_INFO("oox.shape", "size: " << size.Width << " x " << size.Height);
441
#endif
442
443
0
    Reference<XPropertySet> const xProps(xShape, UNO_QUERY);
444
    // non visual shape properties
445
0
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
446
0
    {
447
0
        pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
448
0
        pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
449
0
                              XML_id, OString::number(GetNewShapeID(xShape)),
450
0
                              XML_name, GetShapeName(xShape));
451
0
        AddExtLst(pFS, xProps);
452
0
        pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
453
0
    }
454
0
    pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
455
0
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
456
0
    {
457
0
        WriteNonVisualProperties( xShape );
458
0
        pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
459
0
    }
460
461
    // visual shape properties
462
0
    pFS->startElementNS(mnXmlNamespace, XML_spPr);
463
0
    WriteTransformation( xShape, aRect, XML_a );
464
0
    WritePolyPolygon(xShape, bClosed);
465
0
    if( xProps.is() ) {
466
0
        if( bClosed )
467
0
            WriteFill(xProps, aSize);
468
0
        WriteOutline( xProps );
469
0
    }
470
471
0
    pFS->endElementNS( mnXmlNamespace, XML_spPr );
472
473
    // write text
474
0
    WriteTextBox( xShape, mnXmlNamespace );
475
476
0
    pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );
477
478
0
    return *this;
479
0
}
480
481
ShapeExport& ShapeExport::WriteClosedPolyPolygonShape( const Reference< XShape >& xShape )
482
0
{
483
0
    return WritePolyPolygonShape( xShape, true );
484
0
}
485
486
ShapeExport& ShapeExport::WriteOpenPolyPolygonShape( const Reference< XShape >& xShape )
487
0
{
488
0
    return WritePolyPolygonShape( xShape, false );
489
0
}
490
491
ShapeExport& ShapeExport::WriteGroupShape(const uno::Reference<drawing::XShape>& xShape)
492
0
{
493
0
    FSHelperPtr pFS = GetFS();
494
495
0
    sal_Int32 nGroupShapeToken = XML_grpSp;
496
0
    if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes)
497
0
    {
498
0
        if (!m_xParent.is())
499
0
            nGroupShapeToken = XML_wgp; // toplevel
500
0
        else
501
0
            mnXmlNamespace = XML_wpg;
502
0
    }
503
504
0
    pFS->startElementNS(mnXmlNamespace, nGroupShapeToken);
505
506
    // non visual properties
507
0
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
508
0
    {
509
0
        pFS->startElementNS(mnXmlNamespace, XML_nvGrpSpPr);
510
0
        pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
511
0
                XML_id, OString::number(GetNewShapeID(xShape)),
512
0
                XML_name, GetShapeName(xShape));
513
0
        uno::Reference<beans::XPropertySet> const xShapeProps(xShape, uno::UNO_QUERY_THROW);
514
0
        AddExtLst(pFS, xShapeProps);
515
0
        pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
516
0
        pFS->singleElementNS(mnXmlNamespace, XML_cNvGrpSpPr);
517
0
        WriteNonVisualProperties(xShape );
518
0
        pFS->endElementNS(mnXmlNamespace, XML_nvGrpSpPr);
519
0
    }
520
0
    else
521
0
        pFS->singleElementNS(mnXmlNamespace, XML_cNvGrpSpPr);
522
523
    // visual properties
524
0
    pFS->startElementNS(mnXmlNamespace, XML_grpSpPr);
525
0
    WriteShapeTransformation(xShape, XML_a, false, false, true);
526
0
    pFS->endElementNS(mnXmlNamespace, XML_grpSpPr);
527
528
0
    uno::Reference<drawing::XShapes> xGroupShape(xShape, uno::UNO_QUERY_THROW);
529
0
    uno::Reference<drawing::XShape> xParent = m_xParent;
530
0
    m_xParent = xShape;
531
0
    for (sal_Int32 i = 0; i < xGroupShape->getCount(); ++i)
532
0
    {
533
0
        uno::Reference<drawing::XShape> xChild(xGroupShape->getByIndex(i), uno::UNO_QUERY_THROW);
534
0
        sal_Int32 nSavedNamespace = mnXmlNamespace;
535
536
0
        uno::Reference<lang::XServiceInfo> xServiceInfo(xChild, uno::UNO_QUERY_THROW);
537
0
        if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes)
538
0
        {
539
            // tdf#128820: WriteGraphicObjectShapePart calls WriteTextShape for non-empty simple
540
            // text objects, which needs writing into wps::wsp element, so make sure to use wps
541
            // namespace for those objects
542
0
            if (xServiceInfo->supportsService(u"com.sun.star.drawing.GraphicObjectShape"_ustr)
543
0
                && !IsNonEmptySimpleText(xChild))
544
0
                mnXmlNamespace = XML_pic;
545
0
            else
546
0
                mnXmlNamespace = XML_wps;
547
0
        }
548
0
        WriteShape(xChild);
549
550
0
        mnXmlNamespace = nSavedNamespace;
551
0
    }
552
0
    m_xParent = std::move(xParent);
553
554
0
    pFS->endElementNS(mnXmlNamespace, nGroupShapeToken);
555
0
    return *this;
556
0
}
557
namespace
558
{
559
560
constexpr frozen::set<std::u16string_view, 57> constDenySet(
561
{
562
    u"block-arc",
563
    u"rectangle",
564
    u"ellipse",
565
    u"ring",
566
    u"can",
567
    u"cube",
568
    u"paper",
569
    u"frame",
570
    u"forbidden",
571
    u"smiley",
572
    u"sun",
573
    u"flower",
574
    u"bracket-pair",
575
    u"brace-pair",
576
    u"quad-bevel",
577
    u"round-rectangular-callout",
578
    u"rectangular-callout",
579
    u"round-callout",
580
    u"cloud-callout",
581
    u"line-callout-1",
582
    u"line-callout-2",
583
    u"line-callout-3",
584
    u"paper",
585
    u"vertical-scroll",
586
    u"horizontal-scroll",
587
    u"mso-spt34",
588
    u"mso-spt75",
589
    u"mso-spt164",
590
    u"mso-spt180",
591
    u"flowchart-process",
592
    u"flowchart-alternate-process",
593
    u"flowchart-decision",
594
    u"flowchart-data",
595
    u"flowchart-predefined-process",
596
    u"flowchart-internal-storage",
597
    u"flowchart-document",
598
    u"flowchart-multidocument",
599
    u"flowchart-terminator",
600
    u"flowchart-preparation",
601
    u"flowchart-manual-input",
602
    u"flowchart-manual-operation",
603
    u"flowchart-connector",
604
    u"flowchart-off-page-connector",
605
    u"flowchart-card",
606
    u"flowchart-punched-tape",
607
    u"flowchart-summing-junction",
608
    u"flowchart-or",
609
    u"flowchart-collate",
610
    u"flowchart-sort",
611
    u"flowchart-extract",
612
    u"flowchart-merge",
613
    u"flowchart-stored-data",
614
    u"flowchart-delay",
615
    u"flowchart-sequential-access",
616
    u"flowchart-magnetic-disk",
617
    u"flowchart-direct-access-storage",
618
    u"flowchart-display"
619
});
620
621
constexpr frozen::set<std::u16string_view, 6> constAllowSet(
622
{
623
    u"heart",
624
    u"puzzle",
625
    u"col-60da8460",
626
    u"col-502ad400",
627
    u"sinusoid",
628
    u"mso-spt100"
629
});
630
631
} // end anonymous namespace
632
633
static bool lcl_IsOnDenylist(OUString const & rShapeType)
634
0
{
635
0
    return constDenySet.find(rShapeType) != constDenySet.end();
636
0
}
637
638
static bool lcl_IsOnAllowlist(OUString const & rShapeType)
639
0
{
640
0
    return constAllowSet.find(rShapeType) != constAllowSet.end();
641
0
}
642
643
static bool lcl_GetHandlePosition( sal_Int32 &nValue, const EnhancedCustomShapeParameter &rParam, const Sequence< EnhancedCustomShapeAdjustmentValue > &rSeq)
644
0
{
645
0
    bool bAdj = false;
646
0
    if ( rParam.Value.getValueTypeClass() == TypeClass_DOUBLE )
647
0
    {
648
0
        double fValue(0.0);
649
0
        if ( rParam.Value >>= fValue )
650
0
            nValue = static_cast<sal_Int32>(fValue);
651
0
    }
652
0
    else
653
0
        rParam.Value >>= nValue;
654
655
0
    if ( rParam.Type == EnhancedCustomShapeParameterType::ADJUSTMENT)
656
0
    {
657
0
        bAdj = true;
658
0
        sal_Int32 nIdx = nValue;
659
0
        if ( nIdx < rSeq.getLength() )
660
0
        {
661
0
            if ( rSeq[ nIdx ] .Value.getValueTypeClass() == TypeClass_DOUBLE )
662
0
            {
663
0
                double fValue(0.0);
664
0
                rSeq[ nIdx ].Value >>= fValue;
665
0
                nValue = fValue;
666
667
0
            }
668
0
            else
669
0
            {
670
0
                rSeq[ nIdx ].Value >>= nValue;
671
0
            }
672
0
        }
673
0
    }
674
0
    return bAdj;
675
0
}
676
677
static void lcl_AnalyzeHandles( const uno::Sequence<beans::PropertyValues> & rHandles,
678
        std::vector< std::pair< sal_Int32, sal_Int32> > &rHandlePositionList,
679
        const Sequence< EnhancedCustomShapeAdjustmentValue > &rSeq)
680
0
{
681
0
    for ( const Sequence< PropertyValue >& rPropSeq : rHandles )
682
0
    {
683
0
        static constexpr OUStringLiteral sPosition( u"Position"  );
684
0
        bool bPosition = false;
685
0
        EnhancedCustomShapeParameterPair aPosition;
686
0
        for ( const PropertyValue& rPropVal: rPropSeq )
687
0
        {
688
0
            if ( rPropVal.Name == sPosition )
689
0
            {
690
0
                if ( rPropVal.Value >>= aPosition )
691
0
                    bPosition = true;
692
0
            }
693
0
        }
694
0
        if ( bPosition )
695
0
        {
696
0
            sal_Int32 nXPosition = 0;
697
0
            sal_Int32 nYPosition = 0;
698
            // For polar handles, nXPosition is radius and nYPosition is angle
699
0
            lcl_GetHandlePosition( nXPosition, aPosition.First , rSeq );
700
0
            lcl_GetHandlePosition( nYPosition, aPosition.Second, rSeq );
701
0
            rHandlePositionList.emplace_back( nXPosition, nYPosition );
702
0
        }
703
0
    }
704
0
}
705
706
static void lcl_AppendAdjustmentValue( std::vector< std::pair< sal_Int32, sal_Int32> > &rAvList, sal_Int32 nAdjIdx, sal_Int32 nValue )
707
0
{
708
0
    rAvList.emplace_back( nAdjIdx , nValue );
709
0
}
710
711
static sal_Int32 lcl_NormalizeAngle( sal_Int32 nAngle )
712
0
{
713
0
    nAngle = nAngle % 360;
714
0
    return nAngle < 0 ? ( nAngle + 360 ) : nAngle ;
715
0
}
716
717
static sal_Int32 lcl_CircleAngle2CustomShapeEllipseAngleOOX(const sal_Int32 nInternAngle, const sal_Int32 nWidth, const sal_Int32 nHeight)
718
0
{
719
0
    if (nWidth != 0 || nHeight != 0)
720
0
    {
721
0
        double fAngle = basegfx::deg2rad<100>(nInternAngle); // intern 1/100 deg to rad
722
0
        fAngle = atan2(nHeight * sin(fAngle), nWidth * cos(fAngle)); // circle to ellipse
723
0
        fAngle = basegfx::rad2deg<60000>(fAngle); // rad to OOXML angle unit
724
0
        sal_Int32 nAngle = basegfx::fround(fAngle); // normalize
725
0
        nAngle = nAngle % 21600000;
726
0
        return nAngle < 0 ? (nAngle + 21600000) : nAngle;
727
0
    }
728
0
    else // should be handled by caller, dummy value
729
0
        return 0;
730
0
}
731
732
static OUString lcl_GetTarget(const css::uno::Reference<css::frame::XModel>& xModel,
733
                              std::u16string_view rURL)
734
0
{
735
0
    Reference<drawing::XDrawPagesSupplier> xDPS(xModel, uno::UNO_QUERY_THROW);
736
0
    Reference<drawing::XDrawPages> xDrawPages(xDPS->getDrawPages(), uno::UNO_SET_THROW);
737
0
    sal_uInt32 nPageCount = xDrawPages->getCount();
738
0
    OUString sTarget;
739
740
0
    for (sal_uInt32 i = 0; i < nPageCount; ++i)
741
0
    {
742
0
        Reference<XDrawPage> xDrawPage;
743
0
        xDrawPages->getByIndex(i) >>= xDrawPage;
744
0
        Reference<container::XNamed> xNamed(xDrawPage, UNO_QUERY);
745
0
        if (!xNamed)
746
0
            continue;
747
0
        OUString sSlideName = "#" + xNamed->getName();
748
0
        if (rURL == sSlideName)
749
0
        {
750
0
            sTarget = "slide" + OUString::number(i + 1) + ".xml";
751
0
            break;
752
0
        }
753
0
    }
754
755
0
    return sTarget;
756
0
}
757
758
ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape )
759
0
{
760
0
    SAL_INFO("oox.shape", "write custom shape");
761
0
    Reference< XPropertySet > rXPropSet( xShape, UNO_QUERY );
762
    // First check, if this is a Fontwork-shape. For DrawingML, such a shape is a
763
    // TextBox shape with body property prstTxWarp.
764
0
    if (IsFontworkShape(rXPropSet))
765
0
    {
766
0
        ShapeExport::WriteTextShape(xShape); // qualifier to prevent PowerPointShapeExport
767
0
        return *this;
768
0
    }
769
770
0
    bool bHasGeometrySeq(false);
771
0
    Sequence< PropertyValue > aGeometrySeq;
772
0
    OUString sShapeType(u"non-primitive"_ustr); // default in ODF
773
0
    if (GetProperty(rXPropSet, u"CustomShapeGeometry"_ustr))
774
0
    {
775
0
        SAL_INFO("oox.shape", "got custom shape geometry");
776
0
        if (mAny >>= aGeometrySeq)
777
0
        {
778
0
            bHasGeometrySeq = true;
779
0
            SAL_INFO("oox.shape", "got custom shape geometry sequence");
780
0
            for (const PropertyValue& rProp : aGeometrySeq)
781
0
            {
782
0
                SAL_INFO("oox.shape", "geometry property: " << rProp.Name);
783
0
                if (rProp.Name == "Type")
784
0
                    rProp.Value >>= sShapeType;
785
0
            }
786
0
        }
787
0
    }
788
789
0
    bool bPredefinedHandlesUsed = true;
790
0
    bool bHasHandles = false;
791
792
0
    ShapeFlag nMirrorFlags = ShapeFlag::NONE;
793
0
    MSO_SPT eShapeType = EscherPropertyContainer::GetCustomShapeType( xShape, nMirrorFlags, sShapeType );
794
0
    assert(dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(xShape)) && "Not a SdrObjCustomShape (!)");
795
0
    SdrObjCustomShape& rSdrObjCustomShape(static_cast< SdrObjCustomShape& >(*SdrObject::getSdrObjectFromXShape(xShape)));
796
0
    const bool bIsDefaultObject(
797
0
        EscherPropertyContainer::IsDefaultObject(
798
0
            rSdrObjCustomShape,
799
0
            eShapeType));
800
0
    OString sPresetShape = msfilter::util::GetOOXMLPresetGeometry(sShapeType);
801
0
    SAL_INFO("oox.shape", "custom shape type: " << sShapeType << " ==> " << sPresetShape);
802
803
0
    sal_Int32 nAdjustmentValuesIndex = -1;
804
0
    awt::Rectangle aViewBox;
805
0
    uno::Sequence<beans::PropertyValues> aHandles;
806
807
0
    bool bFlipH = false;
808
0
    bool bFlipV = false;
809
810
0
    if (bHasGeometrySeq)
811
0
    {
812
0
        for (int i = 0; i < aGeometrySeq.getLength(); i++)
813
0
        {
814
0
                const PropertyValue& rProp = aGeometrySeq[ i ];
815
0
                SAL_INFO("oox.shape", "geometry property: " << rProp.Name);
816
817
0
                if ( rProp.Name == "MirroredX" )
818
0
                    rProp.Value >>= bFlipH;
819
820
0
                if ( rProp.Name == "MirroredY" )
821
0
                    rProp.Value >>= bFlipV;
822
0
                if ( rProp.Name == "AdjustmentValues" )
823
0
                    nAdjustmentValuesIndex = i;
824
0
                else if ( rProp.Name == "Handles" )
825
0
                {
826
0
                    rProp.Value >>= aHandles;
827
0
                    if ( aHandles.hasElements() )
828
0
                        bHasHandles = true;
829
0
                    if( !bIsDefaultObject )
830
0
                        bPredefinedHandlesUsed = false;
831
                    // TODO: update nAdjustmentsWhichNeedsToBeConverted here
832
0
                }
833
0
                else if ( rProp.Name == "ViewBox" )
834
0
                    rProp.Value >>= aViewBox;
835
0
        }
836
0
    }
837
838
0
    FSHelperPtr pFS = GetFS();
839
    // non visual shape properties
840
0
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
841
0
    {
842
        // get InteropGrabBag to export attributes stored in the grabbag
843
0
        uno::Sequence<beans::PropertyValue> aGrabBagProps;
844
0
        rXPropSet->getPropertyValue(u"InteropGrabBag"_ustr) >>= aGrabBagProps;
845
846
0
        bool bUseBackground = false;
847
0
        if (GetProperty(rXPropSet, u"FillUseSlideBackground"_ustr))
848
0
            mAny >>= bUseBackground;
849
0
        if (bUseBackground)
850
0
            mpFS->startElementNS(mnXmlNamespace, XML_sp, XML_useBgFill, "1");
851
0
        else
852
0
        {
853
0
            rtl::Reference<sax_fastparser::FastAttributeList> pAttrListSp
854
0
                = sax_fastparser::FastSerializerHelper::createAttrList();
855
856
0
            for (auto const& it : aGrabBagProps)
857
0
            {
858
                // export macro attribute of <sp> element
859
0
                if (it.Name == u"mso-sp-macro"_ustr)
860
0
                {
861
0
                    OUString sMacro;
862
0
                    it.Value >>= sMacro;
863
864
0
                    if (!sMacro.isEmpty())
865
0
                        pAttrListSp->add(XML_macro, sMacro);
866
0
                }
867
868
                // export textlink attribute of <sp> element
869
0
                if (it.Name == u"mso-sp-textlink"_ustr)
870
0
                {
871
0
                    OUString sTextLink;
872
0
                    it.Value >>= sTextLink;
873
874
0
                    if (!sTextLink.isEmpty())
875
0
                        pAttrListSp->add(XML_textlink, sTextLink);
876
0
                }
877
878
                // export fLocksText attribute of <sp> element
879
0
                if (it.Name == u"mso-sp-fLocksText"_ustr)
880
0
                {
881
0
                    bool bFLocksText = true; // default="true"
882
0
                    it.Value >>= bFLocksText;
883
0
                    pAttrListSp->add(XML_fLocksText, ToPsz10(bFLocksText));
884
0
                }
885
886
                // export fPublished attribute of <sp> element
887
0
                if (it.Name == u"mso-sp-fPublished"_ustr)
888
0
                {
889
0
                    bool bFPublished = false;
890
0
                    it.Value >>= bFPublished;
891
0
                    pAttrListSp->add(XML_fPublished, ToPsz10(bFPublished));
892
0
                }
893
0
            }
894
895
            // export <sp> element (with a namespace prefix)
896
0
            mpFS->startElementNS(mnXmlNamespace, XML_sp, pAttrListSp);
897
0
        }
898
899
0
        bool isVisible = true ;
900
0
        if( GetProperty(rXPropSet, u"Visible"_ustr))
901
0
        {
902
0
            mAny >>= isVisible;
903
0
        }
904
0
        pFS->startElementNS( mnXmlNamespace, XML_nvSpPr );
905
906
        // export descr attribute of <cNvPr> element
907
0
        OUString sDescr;
908
0
        if (GetProperty(rXPropSet, u"Description"_ustr))
909
0
            mAny >>= sDescr;
910
911
        // export title attribute of <cNvPr> element
912
0
        OUString sTitle;
913
0
        if (GetProperty(rXPropSet, u"Title"_ustr))
914
0
            mAny >>= sTitle;
915
916
        // export <cNvPr> element
917
0
        pFS->startElementNS(
918
0
            mnXmlNamespace, XML_cNvPr, XML_id,
919
0
            OString::number(GetShapeID(xShape) == -1 ? GetNewShapeID(xShape) : GetShapeID(xShape)),
920
0
            XML_name, GetShapeName(xShape), XML_hidden, sax_fastparser::UseIf("1", !isVisible),
921
0
            XML_descr, sax_fastparser::UseIf(sDescr, !sDescr.isEmpty()), XML_title,
922
0
            sax_fastparser::UseIf(sTitle, !sTitle.isEmpty()));
923
924
0
        rtl::Reference<sax_fastparser::FastAttributeList> pAttrListHlinkClick
925
0
            = sax_fastparser::FastSerializerHelper::createAttrList();
926
927
0
        for (auto const& it : aGrabBagProps)
928
0
        {
929
            // export tooltip attribute of <hlinkClick> element
930
0
            if (it.Name == u"mso-hlinkClick-tooltip"_ustr)
931
0
            {
932
0
                OUString sTooltip;
933
0
                it.Value >>= sTooltip;
934
935
0
                if (!sTooltip.isEmpty())
936
0
                    pAttrListHlinkClick->add(XML_tooltip, sTooltip);
937
0
            }
938
0
        }
939
940
0
        if( GetProperty(rXPropSet, u"URL"_ustr) )
941
0
        {
942
0
            OUString sURL;
943
0
            mAny >>= sURL;
944
0
            if( !sURL.isEmpty() )
945
0
            {
946
0
                OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
947
0
                        oox::getRelationship(Relationship::HYPERLINK),
948
0
                        mpURLTransformer->getTransformedString(sURL),
949
0
                        mpURLTransformer->isExternalURL(sURL));
950
951
0
                mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
952
                // pAttrListHlinkClick->add(FSNS(XML_r, XML_id), sRelId);
953
0
            }
954
0
        }
955
956
        // // export <hlinkClick> element
957
        // mpFS->singleElementNS(XML_a, XML_hlinkClick, pAttrListHlinkClick);
958
959
0
        OUString sBookmark;
960
0
        if (GetProperty(rXPropSet, u"Bookmark"_ustr))
961
0
            mAny >>= sBookmark;
962
963
0
        if (GetProperty(rXPropSet, u"OnClick"_ustr))
964
0
        {
965
0
            OUString sPPAction;
966
0
            presentation::ClickAction eClickAction = presentation::ClickAction_NONE;
967
0
            mAny >>= eClickAction;
968
0
            if (eClickAction != presentation::ClickAction_NONE)
969
0
            {
970
0
                switch (eClickAction)
971
0
                {
972
0
                    case presentation::ClickAction_STOPPRESENTATION:
973
0
                        sPPAction = "ppaction://hlinkshowjump?jump=endshow";
974
0
                        break;
975
0
                    case presentation::ClickAction_NEXTPAGE:
976
0
                        sPPAction = "ppaction://hlinkshowjump?jump=nextslide";
977
0
                        break;
978
0
                    case presentation::ClickAction_LASTPAGE:
979
0
                        sPPAction = "ppaction://hlinkshowjump?jump=lastslide";
980
0
                        break;
981
0
                    case presentation::ClickAction_PREVPAGE:
982
0
                        sPPAction = "ppaction://hlinkshowjump?jump=previousslide";
983
0
                        break;
984
0
                    case presentation::ClickAction_FIRSTPAGE:
985
0
                        sPPAction = "ppaction://hlinkshowjump?jump=firstslide";
986
0
                        break;
987
0
                    case presentation::ClickAction_BOOKMARK:
988
0
                        sBookmark = "#" + sBookmark;
989
0
                        break;
990
0
                    default:
991
0
                        break;
992
0
                }
993
0
            }
994
0
            if (!sPPAction.isEmpty())
995
0
                pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), "", XML_action,
996
0
                                     sPPAction);
997
0
        }
998
0
        if (!sBookmark.isEmpty())
999
0
        {
1000
0
            bool bExtURL = URLTransformer().isExternalURL(sBookmark);
1001
0
            sBookmark = bExtURL ? sBookmark : lcl_GetTarget(GetFB()->getModel(), sBookmark);
1002
1003
0
            OUString sRelId
1004
0
                = mpFB->addRelation(mpFS->getOutputStream(),
1005
0
                                    bExtURL ? oox::getRelationship(Relationship::HYPERLINK)
1006
0
                                            : oox::getRelationship(Relationship::SLIDE),
1007
0
                                    sBookmark, bExtURL);
1008
0
            if (bExtURL)
1009
0
                mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
1010
0
            else
1011
0
                mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
1012
0
                                      XML_action, "ppaction://hlinksldjump");
1013
0
        }
1014
0
        AddExtLst(pFS, rXPropSet);
1015
0
        pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1016
0
        pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
1017
0
        WriteNonVisualProperties( xShape );
1018
0
        pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
1019
0
    }
1020
0
    else
1021
0
    {
1022
0
        pFS->startElementNS(mnXmlNamespace, XML_wsp);
1023
0
        if (m_xParent.is())
1024
0
        {
1025
0
            pFS->startElementNS(mnXmlNamespace, XML_cNvPr, XML_id,
1026
0
                                OString::number(GetShapeID(xShape) == -1 ? GetNewShapeID(xShape)
1027
0
                                                                         : GetShapeID(xShape)),
1028
0
                                XML_name, GetShapeName(xShape));
1029
1030
0
            if (GetProperty(rXPropSet, u"Hyperlink"_ustr))
1031
0
            {
1032
0
                OUString sURL;
1033
0
                mAny >>= sURL;
1034
0
                if (!sURL.isEmpty())
1035
0
                {
1036
0
                    OUString sRelId = mpFB->addRelation(
1037
0
                        mpFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
1038
0
                        mpURLTransformer->getTransformedString(sURL),
1039
0
                        mpURLTransformer->isExternalURL(sURL));
1040
1041
0
                    mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
1042
0
                }
1043
0
            }
1044
0
            AddExtLst(pFS, rXPropSet);
1045
0
            pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1046
0
        }
1047
0
        pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
1048
0
    }
1049
1050
    // visual shape properties
1051
0
    pFS->startElementNS(mnXmlNamespace, XML_spPr);
1052
1053
    // we export non-primitive shapes to custom geometry
1054
    // we also export non-ooxml shapes which have handles/equations to custom geometry, because
1055
    // we cannot convert ODF equations to DrawingML equations. TODO: see what binary DOC export filter does.
1056
    // but our WritePolyPolygon()/WriteCustomGeometry() functions are incomplete, therefore we use a denylist
1057
    // we use a allowlist for shapes where mapping to MSO preset shape is not optimal
1058
0
    bool bCustGeom = true;
1059
0
    bool bOnDenylist = false;
1060
0
    if( sShapeType == "ooxml-non-primitive" )
1061
0
        bCustGeom = true;
1062
0
    else if( sShapeType.startsWith("ooxml") )
1063
0
        bCustGeom = false;
1064
0
    else if( lcl_IsOnAllowlist(sShapeType) )
1065
0
        bCustGeom = true;
1066
0
    else if( lcl_IsOnDenylist(sShapeType) )
1067
0
    {
1068
0
        bCustGeom = false;
1069
0
        bOnDenylist = true;
1070
0
    }
1071
1072
0
    bool bPresetWriteSuccessful = false;
1073
    // Let the custom shapes what has name and preset information in OOXML, to be written
1074
    // as preset ones with parameters. Try that with this converter class.
1075
0
    if (!sShapeType.startsWith("ooxml") && sShapeType != "non-primitive" && !mbUserShapes
1076
0
        && xShape->getShapeType() == "com.sun.star.drawing.CustomShape"
1077
0
        && !lcl_IsOnAllowlist(sShapeType))
1078
0
    {
1079
0
        DMLPresetShapeExporter aCustomShapeConverter(this, xShape);
1080
0
        bPresetWriteSuccessful = aCustomShapeConverter.WriteShape();
1081
0
    }
1082
    // If preset writing has problems try to write the shape as it done before
1083
0
    if (bPresetWriteSuccessful)
1084
0
        ;// Already written do nothing.
1085
0
    else if (bCustGeom)
1086
0
    {
1087
0
        WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
1088
0
        bool bSuccess = WriteCustomGeometry(xShape, rSdrObjCustomShape);
1089
        // In case of Writer, the parent element is <wps:spPr>, and there the <a:custGeom> element
1090
        // is not optional.
1091
0
        if (!bSuccess && GetDocumentType() == DOCUMENT_DOCX)
1092
0
        {
1093
0
            WriteEmptyCustomGeometry();
1094
0
        }
1095
0
    }
1096
0
    else if (bOnDenylist && bHasHandles && nAdjustmentValuesIndex !=-1 && !sShapeType.startsWith("mso-spt"))
1097
0
    {
1098
0
        WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
1099
0
        Sequence< EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
1100
0
        std::vector< std::pair< sal_Int32, sal_Int32> > aHandlePositionList;
1101
0
        std::vector< std::pair< sal_Int32, sal_Int32> > aAvList;
1102
0
        aGeometrySeq[ nAdjustmentValuesIndex ].Value >>= aAdjustmentSeq ;
1103
1104
0
        lcl_AnalyzeHandles( aHandles, aHandlePositionList, aAdjustmentSeq );
1105
1106
0
        sal_Int32 nXPosition = 0;
1107
0
        sal_Int32 nYPosition = 0;
1108
0
        if ( !aHandlePositionList.empty() )
1109
0
        {
1110
0
            nXPosition = aHandlePositionList[0].first ;
1111
0
            nYPosition = aHandlePositionList[0].second ;
1112
0
        }
1113
0
        switch( eShapeType )
1114
0
        {
1115
0
            case mso_sptBorderCallout1:
1116
0
            {
1117
0
                sal_Int32 adj3 =  double(nYPosition)/aViewBox.Height *100000;
1118
0
                sal_Int32 adj4 =  double(nXPosition)/aViewBox.Width *100000;
1119
0
                lcl_AppendAdjustmentValue( aAvList, 1, 18750 );
1120
0
                lcl_AppendAdjustmentValue( aAvList, 2, -8333 );
1121
0
                lcl_AppendAdjustmentValue( aAvList, 3, adj3 );
1122
0
                lcl_AppendAdjustmentValue( aAvList, 4, adj4 );
1123
0
                break;
1124
0
            }
1125
0
            case mso_sptBorderCallout2:
1126
0
            {
1127
0
                sal_Int32 adj5 =  double(nYPosition)/aViewBox.Height *100000;
1128
0
                sal_Int32 adj6 =  double(nXPosition)/aViewBox.Width *100000;
1129
0
                sal_Int32 adj3 =  18750;
1130
0
                sal_Int32 adj4 =  -16667;
1131
0
                lcl_AppendAdjustmentValue( aAvList, 1, 18750 );
1132
0
                lcl_AppendAdjustmentValue( aAvList, 2, -8333 );
1133
0
                if ( aHandlePositionList.size() > 1 )
1134
0
                {
1135
0
                    nXPosition = aHandlePositionList[1].first ;
1136
0
                    nYPosition = aHandlePositionList[1].second ;
1137
0
                    adj3 =  double(nYPosition)/aViewBox.Height *100000;
1138
0
                    adj4 =  double(nXPosition)/aViewBox.Width *100000;
1139
0
                }
1140
0
                lcl_AppendAdjustmentValue( aAvList, 3, adj3 );
1141
0
                lcl_AppendAdjustmentValue( aAvList, 4, adj4 );
1142
0
                lcl_AppendAdjustmentValue( aAvList, 5, adj5 );
1143
0
                lcl_AppendAdjustmentValue( aAvList, 6, adj6 );
1144
0
                break;
1145
0
            }
1146
0
            case mso_sptWedgeRectCallout:
1147
0
            case mso_sptWedgeRRectCallout:
1148
0
            case mso_sptWedgeEllipseCallout:
1149
0
            case mso_sptCloudCallout:
1150
0
            {
1151
0
                sal_Int32 adj1 =  (double(nXPosition)/aViewBox.Width -0.5) *100000;
1152
0
                sal_Int32 adj2 =  (double(nYPosition)/aViewBox.Height -0.5) *100000;
1153
0
                lcl_AppendAdjustmentValue( aAvList, 1, adj1 );
1154
0
                lcl_AppendAdjustmentValue( aAvList, 2, adj2 );
1155
0
                if ( eShapeType == mso_sptWedgeRRectCallout)
1156
0
                {
1157
0
                    lcl_AppendAdjustmentValue( aAvList, 3, 16667);
1158
0
                }
1159
1160
0
                break;
1161
0
            }
1162
0
            case mso_sptFoldedCorner:
1163
0
            {
1164
0
                sal_Int32 adj =  double( aViewBox.Width - nXPosition) / std::min( aViewBox.Width,aViewBox.Height ) * 100000;
1165
0
                lcl_AppendAdjustmentValue( aAvList, 0, adj );
1166
0
                break;
1167
0
            }
1168
0
            case mso_sptDonut:
1169
0
            case mso_sptSun:
1170
0
            case mso_sptMoon:
1171
0
            case mso_sptNoSmoking:
1172
0
            case mso_sptHorizontalScroll:
1173
0
            case mso_sptBevel:
1174
0
            case mso_sptBracketPair:
1175
0
            {
1176
0
                sal_Int32 adj =  double( nXPosition )/aViewBox.Width*100000 ;
1177
0
                lcl_AppendAdjustmentValue( aAvList, 0, adj );
1178
0
                break;
1179
0
            }
1180
0
            case mso_sptCan:
1181
0
            case mso_sptCube:
1182
0
            case mso_sptBracePair:
1183
0
            case mso_sptVerticalScroll:
1184
0
            {
1185
0
                sal_Int32 adj =  double( nYPosition )/aViewBox.Height *100000 ;
1186
0
                lcl_AppendAdjustmentValue( aAvList, 0, adj );
1187
0
                break;
1188
0
            }
1189
0
            case mso_sptSmileyFace:
1190
0
            {
1191
0
                sal_Int32 adj =  double( nYPosition )/aViewBox.Height *100000 - 76458.0;
1192
0
                lcl_AppendAdjustmentValue( aAvList, 0, adj );
1193
0
                break;
1194
0
            }
1195
0
            case mso_sptBlockArc:
1196
0
            {
1197
0
                sal_Int32 nRadius = 50000 * ( 1 - double(nXPosition) / 10800);
1198
0
                sal_Int32 nAngleStart = lcl_NormalizeAngle( nYPosition );
1199
0
                sal_Int32 nAngleEnd = lcl_NormalizeAngle( 180 - nAngleStart );
1200
0
                lcl_AppendAdjustmentValue( aAvList, 1, 21600000 / 360 * nAngleStart );
1201
0
                lcl_AppendAdjustmentValue( aAvList, 2, 21600000 / 360 * nAngleEnd );
1202
0
                lcl_AppendAdjustmentValue( aAvList, 3, nRadius );
1203
0
                break;
1204
0
            }
1205
            // case mso_sptNil:
1206
            // case mso_sptBentConnector3:
1207
            // case mso_sptBorderCallout3:
1208
0
            default:
1209
0
            {
1210
0
                if ( sPresetShape == "frame" )
1211
0
                {
1212
0
                    sal_Int32 adj1 =  double( nYPosition )/aViewBox.Height *100000 ;
1213
0
                    lcl_AppendAdjustmentValue( aAvList, 1, adj1 );
1214
0
                }
1215
0
                break;
1216
0
            }
1217
0
        }
1218
0
        WritePresetShape( sPresetShape  , aAvList );
1219
0
    }
1220
0
    else // preset geometry
1221
0
    {
1222
0
        WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV );
1223
0
        if( nAdjustmentValuesIndex != -1 )
1224
0
        {
1225
0
            WritePresetShape( sPresetShape, eShapeType, bPredefinedHandlesUsed,
1226
0
                              aGeometrySeq[ nAdjustmentValuesIndex ] );
1227
0
        }
1228
0
        else
1229
0
            WritePresetShape( sPresetShape );
1230
0
    }
1231
0
    if( rXPropSet.is() )
1232
0
    {
1233
0
        WriteFill(rXPropSet, xShape->getSize());
1234
0
        WriteOutline( rXPropSet );
1235
0
        WriteShapeEffects( rXPropSet );
1236
1237
0
        bool bHas3DEffectinShape = false;
1238
0
        uno::Sequence<beans::PropertyValue> grabBag;
1239
0
        rXPropSet->getPropertyValue(u"InteropGrabBag"_ustr) >>= grabBag;
1240
1241
0
        for (auto const& it : grabBag)
1242
0
            if (it.Name == "3DEffectProperties")
1243
0
                bHas3DEffectinShape = true;
1244
1245
0
        if( bHas3DEffectinShape)
1246
0
            Write3DEffects( rXPropSet, /*bIsText=*/false );
1247
0
    }
1248
1249
0
    pFS->endElementNS( mnXmlNamespace, XML_spPr );
1250
1251
0
    pFS->startElementNS(mnXmlNamespace, XML_style);
1252
0
    WriteShapeStyle( rXPropSet );
1253
0
    pFS->endElementNS( mnXmlNamespace, XML_style );
1254
1255
    // write text
1256
0
    WriteTextBox( xShape, mnXmlNamespace );
1257
1258
0
    pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );
1259
1260
0
    return *this;
1261
0
}
1262
1263
ShapeExport& ShapeExport::WriteEllipseShape( const Reference< XShape >& xShape )
1264
0
{
1265
0
    SAL_INFO("oox.shape", "write ellipse shape");
1266
1267
0
    FSHelperPtr pFS = GetFS();
1268
1269
0
    pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp));
1270
1271
    // TODO: connector ?
1272
1273
0
    Reference<XPropertySet> const xProps(xShape, UNO_QUERY);
1274
    // non visual shape properties
1275
0
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
1276
0
    {
1277
0
        pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
1278
0
        pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
1279
0
                XML_id, OString::number(GetNewShapeID(xShape)),
1280
0
                XML_name, GetShapeName(xShape));
1281
0
        AddExtLst(pFS, xProps);
1282
0
        pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1283
0
        pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr );
1284
0
        WriteNonVisualProperties( xShape );
1285
0
        pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
1286
0
    }
1287
0
    else
1288
0
        pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
1289
1290
0
    CircleKind  eCircleKind(CircleKind_FULL);
1291
0
    if (xProps.is())
1292
0
        xProps->getPropertyValue(u"CircleKind"_ustr ) >>= eCircleKind;
1293
1294
    // visual shape properties
1295
0
    pFS->startElementNS( mnXmlNamespace, XML_spPr );
1296
0
    WriteShapeTransformation( xShape, XML_a );
1297
1298
0
    if (CircleKind_FULL == eCircleKind)
1299
0
        WritePresetShape("ellipse"_ostr);
1300
0
    else
1301
0
    {
1302
0
        sal_Int32 nStartAngleIntern(9000);
1303
0
        sal_Int32 nEndAngleIntern(0);
1304
0
        if (xProps.is())
1305
0
        {
1306
0
           xProps->getPropertyValue(u"CircleStartAngle"_ustr ) >>= nStartAngleIntern;
1307
0
           xProps->getPropertyValue(u"CircleEndAngle"_ustr) >>= nEndAngleIntern;
1308
0
        }
1309
0
        std::vector< std::pair<sal_Int32,sal_Int32>> aAvList;
1310
0
        awt::Size aSize = xShape->getSize();
1311
0
        if (aSize.Width != 0 || aSize.Height != 0)
1312
0
        {
1313
            // Our arc has 90° up, OOXML has 90° down, so mirror it.
1314
            // API angles are 1/100 degree.
1315
0
            sal_Int32 nStartAngleOOXML(lcl_CircleAngle2CustomShapeEllipseAngleOOX(36000 - nEndAngleIntern, aSize.Width, aSize.Height));
1316
0
            sal_Int32 nEndAngleOOXML(lcl_CircleAngle2CustomShapeEllipseAngleOOX(36000 - nStartAngleIntern, aSize.Width, aSize.Height));
1317
0
            lcl_AppendAdjustmentValue( aAvList, 1, nStartAngleOOXML);
1318
0
            lcl_AppendAdjustmentValue( aAvList, 2, nEndAngleOOXML);
1319
0
        }
1320
0
        switch (eCircleKind)
1321
0
        {
1322
0
            case CircleKind_ARC :
1323
0
                WritePresetShape("arc"_ostr, aAvList);
1324
0
            break;
1325
0
            case CircleKind_SECTION :
1326
0
                WritePresetShape("pie"_ostr, aAvList);
1327
0
            break;
1328
0
            case CircleKind_CUT :
1329
0
                WritePresetShape("chord"_ostr, aAvList);
1330
0
            break;
1331
0
        default :
1332
0
            WritePresetShape("ellipse"_ostr);
1333
0
        }
1334
0
    }
1335
0
    if( xProps.is() )
1336
0
    {
1337
0
        if (CircleKind_ARC == eCircleKind)
1338
0
        {
1339
            // An arc in ODF is never filled, even if a fill style other than
1340
            // "none" is set. OOXML arc can be filled, so set fill explicit to
1341
            // NONE, otherwise some hidden or inherited filling is shown.
1342
0
            FillStyle eFillStyle(FillStyle_NONE);
1343
0
            uno::Any aNewValue;
1344
0
            aNewValue <<= eFillStyle;
1345
0
            xProps->setPropertyValue(u"FillStyle"_ustr, aNewValue);
1346
0
        }
1347
0
        WriteFill( xProps );
1348
0
        WriteOutline( xProps );
1349
0
    }
1350
0
    pFS->endElementNS( mnXmlNamespace, XML_spPr );
1351
1352
    // write text
1353
0
    WriteTextBox( xShape, mnXmlNamespace );
1354
1355
0
    pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );
1356
1357
0
    return *this;
1358
0
}
1359
1360
ShapeExport& ShapeExport::WriteGraphicObjectShape( const Reference< XShape >& xShape )
1361
0
{
1362
0
    WriteGraphicObjectShapePart( xShape );
1363
1364
0
    return *this;
1365
0
}
1366
1367
void ShapeExport::WriteGraphicObjectShapePart( const Reference< XShape >& xShape, const Graphic* pGraphic )
1368
0
{
1369
0
    SAL_INFO("oox.shape", "write graphic object shape");
1370
1371
0
    if (IsNonEmptySimpleText(xShape))
1372
0
    {
1373
0
        SAL_INFO("oox.shape", "graphicObject: wrote only text");
1374
1375
0
        WriteTextShape(xShape);
1376
1377
0
        return;
1378
0
    }
1379
1380
0
    SAL_INFO("oox.shape", "graphicObject without text");
1381
1382
0
    uno::Reference<graphic::XGraphic> xGraphic;
1383
0
    OUString sMediaURL;
1384
1385
0
    Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1386
1387
0
    if (pGraphic)
1388
0
    {
1389
0
        xGraphic.set(pGraphic->GetXGraphic());
1390
0
    }
1391
0
    else if (xShapeProps.is() && xShapeProps->getPropertySetInfo()->hasPropertyByName(u"Graphic"_ustr))
1392
0
    {
1393
0
        xShapeProps->getPropertyValue(u"Graphic"_ustr) >>= xGraphic;
1394
0
    }
1395
1396
    // tdf#155903 Only for PPTX, Microsoft does not support this feature in Word and Excel.
1397
0
    bool bHasMediaURL = GetDocumentType() == DOCUMENT_PPTX && xShapeProps.is()
1398
0
                        && xShapeProps->getPropertySetInfo()->hasPropertyByName(u"MediaURL"_ustr)
1399
0
                        && (xShapeProps->getPropertyValue(u"MediaURL"_ustr) >>= sMediaURL);
1400
1401
0
    if (!xGraphic.is() && !bHasMediaURL)
1402
0
    {
1403
0
        SAL_INFO("oox.shape", "no graphic or media URL found");
1404
0
        return;
1405
0
    }
1406
1407
0
    FSHelperPtr pFS = GetFS();
1408
0
    XmlFilterBase* pFB = GetFB();
1409
1410
0
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
1411
0
        pFS->startElementNS(mnXmlNamespace, XML_pic);
1412
0
    else
1413
0
        pFS->startElementNS(mnXmlNamespace, XML_pic,
1414
0
            FSNS(XML_xmlns, XML_pic), pFB->getNamespaceURL(OOX_NS(dmlPicture)));
1415
1416
0
    pFS->startElementNS(mnXmlNamespace, XML_nvPicPr);
1417
1418
0
    presentation::ClickAction eClickAction = presentation::ClickAction_NONE;
1419
0
    OUString sDescr, sURL, sBookmark, sPPAction;
1420
0
    bool bHaveDesc;
1421
1422
0
    if ( ( bHaveDesc = GetProperty( xShapeProps, u"Description"_ustr ) ) )
1423
0
        mAny >>= sDescr;
1424
0
    if ( GetProperty( xShapeProps, u"URL"_ustr ) )
1425
0
        mAny >>= sURL;
1426
0
    if (GetProperty(xShapeProps, u"Bookmark"_ustr))
1427
0
        mAny >>= sBookmark;
1428
0
    if (GetProperty(xShapeProps, u"OnClick"_ustr))
1429
0
        mAny >>= eClickAction;
1430
1431
0
    pFS->startElementNS( mnXmlNamespace, XML_cNvPr,
1432
0
                          XML_id,     OString::number(GetNewShapeID(xShape)),
1433
0
                          XML_name,   GetShapeName(xShape),
1434
0
                          XML_descr,  sax_fastparser::UseIf(sDescr, bHaveDesc));
1435
1436
0
    if (eClickAction != presentation::ClickAction_NONE)
1437
0
    {
1438
0
        switch (eClickAction)
1439
0
        {
1440
0
            case presentation::ClickAction_STOPPRESENTATION:
1441
0
                sPPAction = "ppaction://hlinkshowjump?jump=endshow";
1442
0
                break;
1443
0
            case presentation::ClickAction_NEXTPAGE:
1444
0
                sPPAction = "ppaction://hlinkshowjump?jump=nextslide";
1445
0
                break;
1446
0
            case presentation::ClickAction_LASTPAGE:
1447
0
                sPPAction = "ppaction://hlinkshowjump?jump=lastslide";
1448
0
                break;
1449
0
            case presentation::ClickAction_PREVPAGE:
1450
0
                sPPAction = "ppaction://hlinkshowjump?jump=previousslide";
1451
0
                break;
1452
0
            case presentation::ClickAction_FIRSTPAGE:
1453
0
                sPPAction = "ppaction://hlinkshowjump?jump=firstslide";
1454
0
                break;
1455
0
            case presentation::ClickAction_BOOKMARK:
1456
0
                sBookmark = "#" + sBookmark;
1457
0
                break;
1458
0
            default:
1459
0
                break;
1460
0
        }
1461
0
    }
1462
1463
    // OOXTODO: //cNvPr children: XML_extLst, XML_hlinkHover
1464
0
    if (bHasMediaURL || !sPPAction.isEmpty())
1465
0
        pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), "", XML_action,
1466
0
                             bHasMediaURL ? u"ppaction://media"_ustr : sPPAction);
1467
0
    if( !sURL.isEmpty() )
1468
0
    {
1469
0
        OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
1470
0
                oox::getRelationship(Relationship::HYPERLINK),
1471
0
                mpURLTransformer->getTransformedString(sURL),
1472
0
                mpURLTransformer->isExternalURL(sURL));
1473
1474
0
        mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
1475
0
    }
1476
1477
0
    if (!sBookmark.isEmpty())
1478
0
    {
1479
0
        bool bExtURL = URLTransformer().isExternalURL(sBookmark);
1480
0
        sBookmark = bExtURL ? sBookmark : lcl_GetTarget(GetFB()->getModel(), sBookmark);
1481
1482
0
        OUString sRelId = mpFB->addRelation(mpFS->getOutputStream(),
1483
0
                                            bExtURL ? oox::getRelationship(Relationship::HYPERLINK)
1484
0
                                                    : oox::getRelationship(Relationship::SLIDE),
1485
0
                                            sBookmark, bExtURL);
1486
1487
0
        if (bExtURL)
1488
0
            mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
1489
0
        else
1490
0
            mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId, XML_action,
1491
0
                                  "ppaction://hlinksldjump");
1492
0
    }
1493
0
    AddExtLst(pFS, xShapeProps);
1494
0
    pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1495
1496
0
    pFS->singleElementNS(mnXmlNamespace, XML_cNvPicPr
1497
                         // OOXTODO: XML_preferRelativeSize
1498
0
                        );
1499
0
    if (bHasMediaURL)
1500
0
        WriteMediaNonVisualProperties(xShape);
1501
0
    else
1502
0
        WriteNonVisualProperties(xShape);
1503
1504
0
    pFS->endElementNS( mnXmlNamespace, XML_nvPicPr );
1505
1506
0
    pFS->startElementNS(mnXmlNamespace, XML_blipFill);
1507
1508
0
    if (xGraphic.is())
1509
0
    {
1510
0
        WriteXGraphicBlip(xShapeProps, xGraphic, mbUserShapes);
1511
0
    }
1512
0
    else if (bHasMediaURL)
1513
0
    {
1514
0
        Reference<graphic::XGraphic> xFallbackGraphic;
1515
0
        if (xShapeProps->getPropertySetInfo()->hasPropertyByName(u"FallbackGraphic"_ustr))
1516
0
            xShapeProps->getPropertyValue(u"FallbackGraphic"_ustr) >>= xFallbackGraphic;
1517
1518
0
        WriteXGraphicBlip(xShapeProps, xFallbackGraphic, mbUserShapes);
1519
0
    }
1520
1521
0
    if (xGraphic.is())
1522
0
    {
1523
0
        WriteSrcRectXGraphic(xShapeProps, xGraphic);
1524
0
    }
1525
1526
    // now we stretch always when we get pGraphic (when changing that
1527
    // behavior, test n#780830 for regression, where the OLE sheet might get tiled
1528
0
    bool bStretch = false;
1529
0
    if( !pGraphic && GetProperty( xShapeProps, u"FillBitmapStretch"_ustr ) )
1530
0
        mAny >>= bStretch;
1531
1532
0
    if ( pGraphic || bStretch )
1533
0
        pFS->singleElementNS(XML_a, XML_stretch);
1534
1535
0
    if (bHasMediaURL)
1536
0
    {
1537
        // Graphic of media shapes is always stretched.
1538
0
        pFS->startElementNS(XML_a, XML_stretch);
1539
0
        pFS->singleElementNS(XML_a, XML_fillRect);
1540
0
        pFS->endElementNS(XML_a, XML_stretch);
1541
0
    }
1542
1543
0
    pFS->endElementNS( mnXmlNamespace, XML_blipFill );
1544
1545
    // visual shape properties
1546
0
    pFS->startElementNS(mnXmlNamespace, XML_spPr);
1547
0
    bool bFlipH = false;
1548
0
    if( xShapeProps->getPropertySetInfo()->hasPropertyByName(u"IsMirrored"_ustr) )
1549
0
    {
1550
0
        xShapeProps->getPropertyValue(u"IsMirrored"_ustr) >>= bFlipH;
1551
0
    }
1552
0
    WriteShapeTransformation( xShape, XML_a, bFlipH, false, false, false, true );
1553
0
    WritePresetShape( "rect"_ostr );
1554
0
    WriteFill(xShapeProps);
1555
    // graphic object can come with the frame (bnc#654525)
1556
0
    WriteOutline( xShapeProps );
1557
1558
0
    WriteShapeEffects( xShapeProps );
1559
0
    Write3DEffects( xShapeProps, /*bIsText=*/false );
1560
1561
0
    pFS->endElementNS( mnXmlNamespace, XML_spPr );
1562
1563
0
    pFS->endElementNS( mnXmlNamespace, XML_pic );
1564
0
}
1565
1566
static void lcl_Rotate(sal_Int32 nAngle, Point center, awt::Point& pt)
1567
0
{
1568
0
    sal_Int16 nCos, nSin;
1569
0
    switch (nAngle)
1570
0
    {
1571
0
        case 90:
1572
0
            nCos = 0;
1573
0
            nSin = 1;
1574
0
            break;
1575
0
        case 180:
1576
0
            nCos = -1;
1577
0
            nSin = 0;
1578
0
            break;
1579
0
        case 270:
1580
0
            nCos = 0;
1581
0
            nSin = -1;
1582
0
            break;
1583
0
        default:
1584
0
            return;
1585
0
    }
1586
0
    sal_Int32 x = pt.X - center.X();
1587
0
    sal_Int32 y = pt.Y - center.Y();
1588
0
    pt.X = center.X() + x * nCos - y * nSin;
1589
0
    pt.Y = center.Y() + y * nCos + x * nSin;
1590
0
}
1591
1592
static void lcl_FlipHFlipV(const tools::Polygon& rPoly, sal_Int32 nAngle, bool& rFlipH, bool& rFlipV)
1593
0
{
1594
0
    Point aStart = rPoly[0];
1595
0
    Point aEnd = rPoly[rPoly.GetSize() - 1];
1596
1597
0
    if (aStart.X() > aEnd.X() && aStart.Y() > aEnd.Y())
1598
0
    {
1599
0
        if (nAngle)
1600
0
        {
1601
0
            if (nAngle == 90)
1602
0
                rFlipH = true;
1603
0
            if (nAngle == 270)
1604
0
                rFlipV = true;
1605
0
        }
1606
0
        else // 0°
1607
0
        {
1608
0
            rFlipH = true;
1609
0
            rFlipV = true;
1610
0
        }
1611
0
    }
1612
1613
0
    if (aStart.X() < aEnd.X() && aStart.Y() < aEnd.Y())
1614
0
    {
1615
0
        if (nAngle)
1616
0
        {
1617
0
            if (nAngle != 270)
1618
0
            {
1619
0
                rFlipH = true;
1620
0
                rFlipV = true;
1621
0
            }
1622
0
            else
1623
0
                rFlipH = true;
1624
0
        }
1625
0
    }
1626
1627
0
    if (aStart.Y() < aEnd.Y() && aStart.X() > aEnd.X())
1628
0
    {
1629
0
        if (nAngle)
1630
0
        {
1631
0
            if (nAngle == 180)
1632
0
                rFlipV = true;
1633
0
            if (nAngle == 270)
1634
0
            {
1635
0
                rFlipV = true;
1636
0
                rFlipH = true;
1637
0
            }
1638
0
        }
1639
0
        else // 0°
1640
0
        {
1641
0
            rFlipH = true;
1642
0
        }
1643
0
    }
1644
1645
0
    if (aStart.Y() > aEnd.Y() && aStart.X() < aEnd.X())
1646
0
    {
1647
0
        if (nAngle)
1648
0
        {
1649
0
            if (nAngle == 90)
1650
0
            {
1651
0
                rFlipH = true;
1652
0
                rFlipV = true;
1653
0
            }
1654
0
            if (nAngle == 180)
1655
0
                rFlipH = true;
1656
0
        }
1657
0
        else // 0°
1658
0
            rFlipV = true;
1659
0
    }
1660
0
}
1661
1662
static sal_Int32 lcl_GetAngle(const tools::Polygon& rPoly)
1663
0
{
1664
0
    sal_Int32 nAngle;
1665
0
    Point aStartPoint = rPoly[0];
1666
0
    Point aEndPoint = rPoly[rPoly.GetSize() - 1];
1667
0
    if (aStartPoint.X() == rPoly[1].X())
1668
0
    {
1669
0
        if ((aStartPoint.X() < aEndPoint.X() && aStartPoint.Y() > aEndPoint.Y())
1670
0
            || (aStartPoint.X() > aEndPoint.X() && aStartPoint.Y() < aEndPoint.Y()))
1671
0
        {
1672
0
            nAngle = 90;
1673
0
        }
1674
0
        else
1675
0
            nAngle = 270;
1676
0
    }
1677
0
    else
1678
0
    {
1679
0
        if (aStartPoint.X() > rPoly[1].X())
1680
0
            nAngle = 180;
1681
0
        else
1682
0
            nAngle = 0;
1683
0
    }
1684
1685
0
    return nAngle;
1686
0
}
1687
1688
// Adjust value decide the position, where the connector should turn.
1689
static void lcl_GetConnectorAdjustValue(const Reference<XShape>& xShape, const tools::Polygon& rPoly,
1690
                                        ConnectorType eConnectorType,
1691
                                        std::vector<std::pair<sal_Int32, sal_Int32>>& rAvList)
1692
0
{
1693
0
    Reference<XPropertySet> xShapeProps(xShape, UNO_QUERY);
1694
0
    bool bIsOOXMLCurve(false);
1695
0
    xShapeProps->getPropertyValue(u"EdgeOOXMLCurve"_ustr) >>= bIsOOXMLCurve;
1696
0
    sal_Int32 nAdjCount = 0;
1697
0
    if (eConnectorType == ConnectorType_CURVE)
1698
0
    {
1699
0
        if (bIsOOXMLCurve)
1700
0
        {
1701
0
            nAdjCount = (rPoly.GetSize() - 4) / 3;
1702
0
        }
1703
0
        else if (rPoly.GetSize() == 4)
1704
0
        {
1705
0
            if ((rPoly[0].X() == rPoly[1].X() && rPoly[2].X() == rPoly[3].X())
1706
0
                || (rPoly[0].Y() == rPoly[1].Y() && rPoly[2].Y() == rPoly[3].Y()))
1707
0
            {
1708
0
                nAdjCount = 1; // curvedConnector3, control vectors parallel
1709
0
            }
1710
0
            else
1711
0
                nAdjCount = 0; // curvedConnector2, control vectors orthogonal
1712
0
        }
1713
0
        else if (rPoly.GetSize() > 4)
1714
0
        {
1715
0
            if ((rPoly[2].X() == rPoly[3].X() && rPoly[3].X() == rPoly[4].X())
1716
0
                || (rPoly[2].Y() == rPoly[3].Y() && rPoly[3].Y() == rPoly[4].Y()))
1717
0
            {
1718
0
                nAdjCount = 3; // curvedConnector5
1719
0
            }
1720
0
            else
1721
0
                nAdjCount = 2; // curvedConnector4
1722
0
        }
1723
0
    }
1724
0
    else
1725
0
    {
1726
0
        switch (rPoly.GetSize())
1727
0
        {
1728
0
            case 3:
1729
0
                nAdjCount = 0; // bentConnector2
1730
0
                break;
1731
0
            case 4:
1732
0
                nAdjCount = 1; // bentConnector3
1733
0
                break;
1734
0
            case 5:
1735
0
                nAdjCount = 2; // bentConnector4
1736
0
                break;
1737
0
            case 6:
1738
0
                nAdjCount = 3; // bentConnector5
1739
0
                break;
1740
0
        }
1741
0
    }
1742
1743
0
    if (nAdjCount)
1744
0
    {
1745
0
        sal_Int32 nAdjustValue;
1746
0
        Point aStart = rPoly[0];
1747
0
        Point aEnd = rPoly[rPoly.GetSize() - 1];
1748
1749
0
        for (sal_Int32 i = 1; i <= nAdjCount; ++i)
1750
0
        {
1751
0
            Point aPt = rPoly[i];
1752
1753
0
            if (aEnd.Y() == aStart.Y())
1754
0
                aEnd.setY(aStart.Y() + 1);
1755
0
            if (aEnd.X() == aStart.X())
1756
0
                aEnd.setX(aStart.X() + 1);
1757
1758
0
            bool bVertical = rPoly[1].X() - aStart.X() != 0 ? true : false;
1759
            // vertical and horizon alternate
1760
0
            if (i % 2 == 1)
1761
0
                bVertical = !bVertical;
1762
1763
0
            if (eConnectorType == ConnectorType_CURVE)
1764
0
            {
1765
0
                if (bIsOOXMLCurve)
1766
0
                {
1767
0
                    aPt = rPoly[3 *  i];
1768
0
                }
1769
0
                else
1770
0
                {
1771
0
                    awt::Size aSize = xShape->getSize();
1772
0
                    awt::Point aShapePosition = xShape->getPosition();
1773
0
                    tools::Rectangle aBoundRect = rPoly.GetBoundRect();
1774
1775
0
                    if (bVertical)
1776
0
                    {
1777
0
                        if ((aBoundRect.GetSize().Height() - aSize.Height) == 1)
1778
0
                            aPt.setY(rPoly[i + 1].Y());
1779
0
                        else if (aStart.Y() > aPt.Y())
1780
0
                            aPt.setY(aShapePosition.Y);
1781
0
                        else
1782
0
                            aPt.setY(aShapePosition.Y + aSize.Height);
1783
0
                    }
1784
0
                    else
1785
0
                    {
1786
0
                        if ((aBoundRect.GetSize().Width() - aSize.Width) == 1)
1787
0
                            aPt.setX(rPoly[i + 1].X());
1788
0
                        else if (aStart.X() > aPt.X())
1789
0
                            aPt.setX(aShapePosition.X);
1790
0
                        else
1791
0
                            aPt.setX(aShapePosition.X + aSize.Width);
1792
0
                    }
1793
0
                }
1794
0
            }
1795
1796
0
            if (bVertical)
1797
0
                nAdjustValue = ((aPt.Y() - aStart.Y()) * 100000) / (aEnd.Y() - aStart.Y());
1798
0
            else
1799
0
                nAdjustValue = ((aPt.X() - aStart.X()) * 100000) / (aEnd.X() - aStart.X());
1800
1801
0
            rAvList.emplace_back(i, nAdjustValue);
1802
0
        }
1803
0
    }
1804
0
}
1805
1806
static sal_Int32 lcl_GetGluePointId(const Reference<XShape>& xShape, sal_Int32 nGluePointId)
1807
0
{
1808
0
    if (nGluePointId > 3)
1809
0
        return nGluePointId - 4;
1810
0
    else
1811
0
    {
1812
0
        bool bFlipH = false;
1813
0
        bool bFlipV = false;
1814
0
        Reference<XPropertySet> xShapeProps(xShape, UNO_QUERY);
1815
0
        if (xShapeProps.is() && xShapeProps->getPropertySetInfo()
1816
0
                && xShapeProps->getPropertySetInfo()->hasPropertyByName(u"CustomShapeGeometry"_ustr))
1817
0
        {
1818
0
            Sequence<PropertyValue> aGeometrySeq;
1819
0
            xShapeProps->getPropertyValue(u"CustomShapeGeometry"_ustr) >>= aGeometrySeq;
1820
0
            for (int i = 0; i < aGeometrySeq.getLength(); i++)
1821
0
            {
1822
0
                const PropertyValue& rProp = aGeometrySeq[i];
1823
0
                if (rProp.Name == "MirroredX")
1824
0
                    rProp.Value >>= bFlipH;
1825
1826
0
                if (rProp.Name == "MirroredY")
1827
0
                    rProp.Value >>= bFlipV;
1828
0
            }
1829
0
        }
1830
1831
0
        if ((!bFlipH && !bFlipV) || (bFlipH && bFlipV))
1832
0
        {
1833
            // change id of the bounding box (1 <-> 3)
1834
0
            if (nGluePointId == 1)
1835
0
                nGluePointId = 3; // Right
1836
0
            else if (nGluePointId == 3)
1837
0
                nGluePointId = 1; // Left
1838
0
        }
1839
0
    }
1840
1841
0
    return nGluePointId;
1842
0
}
1843
1844
ShapeExport& ShapeExport::WriteConnectorShape( const Reference< XShape >& xShape )
1845
0
{
1846
0
    bool bFlipH = false;
1847
0
    bool bFlipV = false;
1848
0
    sal_Int32 nAngle = 0;
1849
0
    sal_Int32 nStartGlueId = 0;
1850
0
    sal_Int32 nEndGlueId = 0;
1851
1852
0
    SAL_INFO("oox.shape", "write connector shape");
1853
1854
0
    FSHelperPtr pFS = GetFS();
1855
1856
0
    OUString sGeometry;
1857
0
    std::vector<std::pair<sal_Int32, sal_Int32>> aAdjustValueList;
1858
0
    Reference< XPropertySet > rXPropSet( xShape, UNO_QUERY );
1859
0
    Reference< XPropertyState > rXPropState( xShape, UNO_QUERY );
1860
0
    awt::Point aStartPoint, aEndPoint;
1861
0
    Reference< XShape > rXShapeA;
1862
0
    Reference< XShape > rXShapeB;
1863
0
    PropertyState eState;
1864
0
    ConnectorType eConnectorType = ConnectorType_STANDARD;
1865
0
    if (GetProperty(rXPropSet, u"EdgeKind"_ustr))
1866
0
        mAny >>= eConnectorType;
1867
1868
0
    switch( eConnectorType ) {
1869
0
        case ConnectorType_CURVE:
1870
0
            sGeometry = "curvedConnector";
1871
0
            break;
1872
0
        case ConnectorType_LINES:
1873
0
        case ConnectorType_STANDARD:
1874
0
            sGeometry = "bentConnector";
1875
0
            break;
1876
0
        default:
1877
0
        case ConnectorType_LINE:
1878
0
            sGeometry = "straightConnector1";
1879
0
            break;
1880
0
    }
1881
1882
0
    if (GetPropertyAndState( rXPropSet, rXPropState, u"EdgeStartPoint"_ustr, eState ) && eState == beans::PropertyState_DIRECT_VALUE )
1883
0
    {
1884
0
        mAny >>= aStartPoint;
1885
0
        if (GetPropertyAndState( rXPropSet, rXPropState, u"EdgeEndPoint"_ustr, eState ) && eState == beans::PropertyState_DIRECT_VALUE )
1886
0
            mAny >>= aEndPoint;
1887
0
    }
1888
0
    if (GetProperty(rXPropSet, u"EdgeStartConnection"_ustr))
1889
0
        mAny >>= rXShapeA;
1890
0
    if (GetProperty(rXPropSet, u"EdgeEndConnection"_ustr))
1891
0
        mAny >>= rXShapeB;
1892
1893
    // Position is relative to group in Word, but relative to anchor of group in API.
1894
0
    if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes && m_xParent.is())
1895
0
    {
1896
0
        awt::Point aParentPos = m_xParent->getPosition();
1897
0
        aStartPoint.X -= aParentPos.X;
1898
0
        aStartPoint.Y -= aParentPos.Y;
1899
0
        aEndPoint.X -= aParentPos.X;
1900
0
        aEndPoint.Y -= aParentPos.Y;
1901
0
    }
1902
0
    EscherConnectorListEntry aConnectorEntry( xShape, aStartPoint, rXShapeA, aEndPoint, rXShapeB );
1903
1904
0
    if (GetProperty(rXPropSet, u"StartGluePointIndex"_ustr))
1905
0
    {
1906
0
        mAny >>= nStartGlueId;
1907
0
        nStartGlueId = (nStartGlueId != -1) ? lcl_GetGluePointId(rXShapeA, nStartGlueId)
1908
0
                                            : (aConnectorEntry.mXConnectToA.is()
1909
0
                                                   ? aConnectorEntry.GetConnectorRule(true)
1910
0
                                                   : -1);
1911
0
    }
1912
1913
0
    if (GetProperty(rXPropSet, u"EndGluePointIndex"_ustr))
1914
0
    {
1915
0
        mAny >>= nEndGlueId;
1916
0
        nEndGlueId = (nEndGlueId != -1) ? lcl_GetGluePointId(rXShapeB, nEndGlueId)
1917
0
                                        : (aConnectorEntry.mXConnectToB.is()
1918
0
                                               ? aConnectorEntry.GetConnectorRule(false)
1919
0
                                               : -1);
1920
0
    }
1921
1922
0
    if (eConnectorType != ConnectorType_LINE)
1923
0
    {
1924
0
        tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon(xShape);
1925
0
        if (aPolyPolygon.Count() > 0)
1926
0
        {
1927
0
            const tools::Polygon& aPoly = aPolyPolygon.GetObject(0);
1928
0
            lcl_GetConnectorAdjustValue(xShape, aPoly, eConnectorType, aAdjustValueList);
1929
0
            nAngle = lcl_GetAngle(aPoly);
1930
0
            lcl_FlipHFlipV(aPoly, nAngle, bFlipH, bFlipV);
1931
0
            if (nAngle)
1932
0
            {
1933
0
                Point center((aEndPoint.X + aStartPoint.X) / 2, (aEndPoint.Y + aStartPoint.Y) / 2);
1934
0
                lcl_Rotate(nAngle, center, aStartPoint);
1935
0
                lcl_Rotate(nAngle, center, aEndPoint);
1936
0
                nAngle *= 60000;
1937
0
            }
1938
0
            sGeometry = sGeometry + OUString::number(aAdjustValueList.size() + 2);
1939
0
        }
1940
0
    }
1941
1942
0
    tools::Rectangle aRect( Point( aStartPoint.X, aStartPoint.Y ), Point( aEndPoint.X, aEndPoint.Y ) );
1943
0
    if( aRect.getOpenWidth() < 0 ) {
1944
0
        aRect.SetLeft(aEndPoint.X);
1945
0
        aRect.setWidth( aStartPoint.X - aEndPoint.X );
1946
0
        if (eConnectorType == ConnectorType_LINE)
1947
0
            bFlipH = true;
1948
0
    }
1949
1950
0
    if( aRect.getOpenHeight() < 0 ) {
1951
0
        aRect.SetTop(aEndPoint.Y);
1952
0
        aRect.setHeight( aStartPoint.Y - aEndPoint.Y );
1953
0
        if (eConnectorType == ConnectorType_LINE)
1954
0
            bFlipV = true;
1955
0
    }
1956
1957
    // tdf#99810 connector shape (cxnSp) is not valid with namespace 'wps'
1958
0
    const auto nShapeNode = (mnXmlNamespace == XML_wps ? XML_wsp : XML_cxnSp);
1959
0
    pFS->startElementNS(mnXmlNamespace, nShapeNode);
1960
1961
0
    if (mnXmlNamespace == XML_wps)
1962
0
    {
1963
        // non visual connector shape drawing properties
1964
0
        pFS->singleElementNS(mnXmlNamespace, XML_cNvCnPr);
1965
0
    }
1966
0
    else
1967
0
    {
1968
        // non visual shape properties
1969
0
        pFS->startElementNS(mnXmlNamespace, XML_nvCxnSpPr);
1970
0
        pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
1971
0
            XML_id, OString::number(GetNewShapeID(xShape)),
1972
0
            XML_name, GetShapeName(xShape));
1973
0
        AddExtLst(pFS, rXPropSet);
1974
0
        pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1975
        // non visual connector shape drawing properties
1976
0
        pFS->startElementNS(mnXmlNamespace, XML_cNvCxnSpPr);
1977
1978
0
        if (GetShapeID(rXShapeA) == -1)
1979
0
            GetNewShapeID(rXShapeA);
1980
0
        if (GetShapeID(rXShapeB) == -1)
1981
0
            GetNewShapeID(rXShapeB);
1982
0
        WriteConnectorConnections(nStartGlueId, nEndGlueId, GetShapeID(rXShapeA), GetShapeID(rXShapeB));
1983
0
        pFS->endElementNS(mnXmlNamespace, XML_cNvCxnSpPr);
1984
0
        if (GetDocumentType() == DOCUMENT_PPTX)
1985
0
            pFS->singleElementNS(mnXmlNamespace, XML_nvPr);
1986
0
        pFS->endElementNS(mnXmlNamespace, XML_nvCxnSpPr);
1987
0
    }
1988
1989
    // visual shape properties
1990
0
    pFS->startElementNS(mnXmlNamespace, XML_spPr);
1991
0
    WriteTransformation( xShape, aRect, XML_a, bFlipH, bFlipV, nAngle );
1992
    // TODO: write adjustments (ppt export doesn't work well there either)
1993
0
    WritePresetShape( sGeometry.toUtf8(), aAdjustValueList);
1994
0
    Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1995
0
    if( xShapeProps.is() )
1996
0
        WriteOutline( xShapeProps );
1997
0
    pFS->endElementNS( mnXmlNamespace, XML_spPr );
1998
1999
    // connector shape (cxnSp) cannot contain text (txBody) (according to schema)
2000
0
    if( nShapeNode != XML_cxnSp )
2001
0
    {
2002
        // write text
2003
0
        WriteTextBox( xShape, mnXmlNamespace );
2004
0
    }
2005
2006
0
    pFS->endElementNS(mnXmlNamespace, nShapeNode);
2007
2008
0
    return *this;
2009
0
}
2010
2011
ShapeExport& ShapeExport::WriteLineShape( const Reference< XShape >& xShape )
2012
0
{
2013
0
    bool bFlipH = false;
2014
0
    bool bFlipV = false;
2015
2016
0
    SAL_INFO("oox.shape", "write line shape");
2017
2018
0
    FSHelperPtr pFS = GetFS();
2019
2020
0
    pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp));
2021
2022
0
    tools::PolyPolygon aPolyPolygon = EscherPropertyContainer::GetPolyPolygon( xShape );
2023
0
    if( aPolyPolygon.Count() == 1 && aPolyPolygon[ 0 ].GetSize() == 2)
2024
0
    {
2025
0
        const tools::Polygon& rPoly = aPolyPolygon[ 0 ];
2026
2027
0
        bFlipH = ( rPoly[ 0 ].X() > rPoly[ 1 ].X() );
2028
0
        bFlipV = ( rPoly[ 0 ].Y() > rPoly[ 1 ].Y() );
2029
0
    }
2030
2031
0
    Reference<XPropertySet> const xShapeProps(xShape, UNO_QUERY);
2032
    // non visual shape properties
2033
0
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
2034
0
    {
2035
0
        pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
2036
0
        pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
2037
0
                              XML_id, OString::number(GetNewShapeID(xShape)),
2038
0
                              XML_name, GetShapeName(xShape));
2039
0
        AddExtLst(pFS, xShapeProps);
2040
0
        pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2041
0
    }
2042
0
    pFS->singleElementNS( mnXmlNamespace, XML_cNvSpPr );
2043
0
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
2044
0
    {
2045
0
        WriteNonVisualProperties( xShape );
2046
0
        pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
2047
0
    }
2048
2049
    // visual shape properties
2050
0
    pFS->startElementNS(mnXmlNamespace, XML_spPr);
2051
0
    WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV, true);
2052
0
    WritePresetShape( "line"_ostr );
2053
0
    if( xShapeProps.is() )
2054
0
        WriteOutline( xShapeProps );
2055
0
    pFS->endElementNS( mnXmlNamespace, XML_spPr );
2056
2057
    //write style
2058
0
    pFS->startElementNS(mnXmlNamespace, XML_style);
2059
0
    WriteShapeStyle( xShapeProps );
2060
0
    pFS->endElementNS( mnXmlNamespace, XML_style );
2061
2062
    // write text
2063
0
    WriteTextBox( xShape, mnXmlNamespace );
2064
2065
0
    pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );
2066
2067
0
    return *this;
2068
0
}
2069
2070
ShapeExport& ShapeExport::WriteNonVisualDrawingProperties( const Reference< XShape >& xShape, const char* pName )
2071
0
{
2072
0
    FSHelperPtr pFS = GetFS();
2073
2074
0
    Reference<XPropertySet> const xShapeProps(xShape, UNO_QUERY);
2075
0
    pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
2076
0
                              XML_id, OString::number(GetNewShapeID(xShape)),
2077
0
                              XML_name, pName );
2078
0
    AddExtLst(pFS, xShapeProps);
2079
0
    pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2080
2081
0
    return *this;
2082
0
}
2083
2084
ShapeExport& ShapeExport::WriteNonVisualProperties( const Reference< XShape >& )
2085
0
{
2086
    // Override to generate //nvPr elements.
2087
0
    return *this;
2088
0
}
2089
2090
ShapeExport& ShapeExport::WriteRectangleShape( const Reference< XShape >& xShape )
2091
0
{
2092
0
    SAL_INFO("oox.shape", "write rectangle shape");
2093
2094
0
    FSHelperPtr pFS = GetFS();
2095
2096
0
    pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp));
2097
2098
0
    sal_Int32 nRadius = 0;
2099
2100
0
    Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
2101
0
    if( xShapeProps.is() )
2102
0
    {
2103
0
        xShapeProps->getPropertyValue( u"CornerRadius"_ustr ) >>= nRadius;
2104
0
    }
2105
2106
0
    if( nRadius )
2107
0
    {
2108
0
        nRadius = MapSize( awt::Size( nRadius, 0 ) ).Width;
2109
0
    }
2110
    //TODO: use nRadius value more precisely than just deciding whether to use
2111
    // "rect" or "roundRect" preset shape below
2112
2113
    // non visual shape properties
2114
0
    if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes)
2115
0
        pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
2116
0
    pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
2117
0
    pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
2118
0
                          XML_id, OString::number(GetNewShapeID(xShape)),
2119
0
                          XML_name, GetShapeName(xShape));
2120
0
    AddExtLst(pFS, xShapeProps);
2121
0
    pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2122
0
    pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
2123
0
    WriteNonVisualProperties( xShape );
2124
0
    pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
2125
2126
    // visual shape properties
2127
0
    pFS->startElementNS(mnXmlNamespace, XML_spPr);
2128
0
    WriteShapeTransformation( xShape, XML_a );
2129
0
    WritePresetShape( nRadius == 0 ? "rect" : "roundRect" );
2130
0
    Reference< XPropertySet > xProps( xShape, UNO_QUERY );
2131
0
    if( xProps.is() )
2132
0
    {
2133
0
        WriteFill( xProps );
2134
0
        WriteOutline( xProps );
2135
0
    }
2136
0
    pFS->endElementNS( mnXmlNamespace, XML_spPr );
2137
2138
    // write text
2139
0
    WriteTextBox( xShape, mnXmlNamespace );
2140
2141
0
    pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );
2142
2143
0
    return *this;
2144
0
}
2145
2146
2147
typedef ShapeExport& (ShapeExport::*ShapeConverter)( const Reference< XShape >& );
2148
typedef std::unordered_map< const char*, ShapeConverter, rtl::CStringHash, rtl::CStringEqual> NameToConvertMapType;
2149
2150
namespace
2151
{
2152
2153
constexpr auto constMap = frozen::make_unordered_map<std::u16string_view, ShapeConverter>(
2154
{
2155
    { u"com.sun.star.drawing.CaptionShape", &ShapeExport::WriteTextShape },
2156
    { u"com.sun.star.drawing.ClosedBezierShape", &ShapeExport::WriteClosedPolyPolygonShape },
2157
    { u"com.sun.star.drawing.ConnectorShape", &ShapeExport::WriteConnectorShape },
2158
    { u"com.sun.star.drawing.CustomShape", &ShapeExport::WriteCustomShape },
2159
    { u"com.sun.star.drawing.EllipseShape", &ShapeExport::WriteEllipseShape },
2160
    { u"com.sun.star.drawing.GraphicObjectShape", &ShapeExport::WriteGraphicObjectShape },
2161
    { u"com.sun.star.drawing.LineShape", &ShapeExport::WriteLineShape },
2162
    { u"com.sun.star.drawing.MediaShape", &ShapeExport::WriteGraphicObjectShape },
2163
    { u"com.sun.star.drawing.OpenBezierShape", &ShapeExport::WriteOpenPolyPolygonShape },
2164
    { u"com.sun.star.drawing.PolyPolygonShape", &ShapeExport::WriteClosedPolyPolygonShape },
2165
    { u"com.sun.star.drawing.PolyLineShape", &ShapeExport::WriteOpenPolyPolygonShape },
2166
    { u"com.sun.star.drawing.RectangleShape", &ShapeExport::WriteRectangleShape },
2167
    { u"com.sun.star.drawing.OLE2Shape", &ShapeExport::WriteOLE2Shape },
2168
    { u"com.sun.star.drawing.TableShape", &ShapeExport::WriteTableShape },
2169
    { u"com.sun.star.drawing.TextShape", &ShapeExport::WriteTextShape },
2170
    { u"com.sun.star.drawing.GroupShape", &ShapeExport::WriteGroupShape },
2171
    { u"com.sun.star.presentation.GraphicObjectShape", &ShapeExport::WriteGraphicObjectShape },
2172
    { u"com.sun.star.presentation.MediaShape", &ShapeExport::WriteGraphicObjectShape },
2173
    { u"com.sun.star.presentation.ChartShape", &ShapeExport::WriteOLE2Shape },
2174
    { u"com.sun.star.presentation.OLE2Shape", &ShapeExport::WriteOLE2Shape },
2175
    { u"com.sun.star.presentation.TableShape", &ShapeExport::WriteTableShape },
2176
    { u"com.sun.star.presentation.TextShape", &ShapeExport::WriteTextShape },
2177
    { u"com.sun.star.presentation.DateTimeShape", &ShapeExport::WriteTextShape },
2178
    { u"com.sun.star.presentation.FooterShape", &ShapeExport::WriteTextShape },
2179
    { u"com.sun.star.presentation.HeaderShape", &ShapeExport::WriteTextShape },
2180
    { u"com.sun.star.presentation.NotesShape", &ShapeExport::WriteTextShape },
2181
    { u"com.sun.star.presentation.OutlinerShape", &ShapeExport::WriteTextShape },
2182
    { u"com.sun.star.presentation.SlideNumberShape", &ShapeExport::WriteTextShape },
2183
    { u"com.sun.star.presentation.TitleTextShape", &ShapeExport::WriteTextShape },
2184
});
2185
2186
} // end anonymous namespace
2187
2188
2189
bool ShapeExport::IsShapeTypeKnown(const Reference<XShape>& xShape)
2190
0
{
2191
0
    if (!xShape)
2192
0
        return false;
2193
2194
0
    const OUString sShapeType = xShape->getShapeType();
2195
0
    return constMap.contains(sShapeType);
2196
0
}
2197
2198
ShapeExport& ShapeExport::WriteShape( const Reference< XShape >& xShape )
2199
0
{
2200
0
    if (!xShape)
2201
0
        throw lang::IllegalArgumentException();
2202
2203
0
    OUString sShapeType = xShape->getShapeType();
2204
0
    SAL_INFO("oox.shape", "write shape: " << sShapeType);
2205
0
    auto aConverterIterator = constMap.find(sShapeType);
2206
0
    if (aConverterIterator == constMap.end())
2207
0
    {
2208
0
        SAL_INFO("oox.shape", "unknown shape");
2209
0
        return WriteUnknownShape( xShape );
2210
0
    }
2211
2212
0
    if (GetDocumentType() == DOCUMENT_PPTX)
2213
0
    {
2214
0
        Reference< XPropertySet > xShapeProperties(xShape, UNO_QUERY);
2215
0
        if (xShapeProperties && xShapeProperties->getPropertySetInfo()
2216
0
            && xShapeProperties->getPropertySetInfo()->hasPropertyByName(u"IsPresentationObject"_ustr)
2217
0
            && xShapeProperties->getPropertyValue(u"IsPresentationObject"_ustr).hasValue())
2218
0
            mbPlaceholder = xShapeProperties->getPropertyValue(u"IsPresentationObject"_ustr).get<bool>();
2219
0
    }
2220
2221
0
    (this->*(aConverterIterator->second))(xShape);
2222
2223
0
    return *this;
2224
0
}
2225
2226
static bool lcl_isTextBox(const Reference<XInterface>& xIface)
2227
0
{
2228
0
    uno::Reference<beans::XPropertySet> xPropertySet(xIface, uno::UNO_QUERY);
2229
0
    if (!xPropertySet.is())
2230
0
        return false;
2231
0
    uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
2232
0
    if (!xPropertySetInfo->hasPropertyByName(u"TextBox"_ustr))
2233
0
       return false;
2234
0
    css::uno::Any aTextBox(xPropertySet->getPropertyValue(u"TextBox"_ustr));
2235
0
    if (!aTextBox.hasValue())
2236
0
       return false;
2237
0
    return aTextBox.get<bool>();
2238
0
}
2239
2240
ShapeExport& ShapeExport::WriteTextBox( const Reference< XInterface >& xIface, sal_Int32 nXmlNamespace, bool bWritePropertiesAsLstStyles )
2241
0
{
2242
    // In case this shape has an associated textbox, then export that, and we're done.
2243
0
    if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes && GetTextExport())
2244
0
    {
2245
0
        if (lcl_isTextBox(xIface))
2246
0
        {
2247
0
            GetTextExport()->WriteTextBox(uno::Reference<drawing::XShape>(xIface, uno::UNO_QUERY_THROW));
2248
0
            WriteText( xIface, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace );
2249
0
            return *this;
2250
0
        }
2251
0
    }
2252
2253
0
    Reference< XText > xXText( xIface, UNO_QUERY );
2254
0
    if( (NonEmptyText( xIface ) || GetDocumentType() == DOCUMENT_PPTX)
2255
0
        && xXText.is() )
2256
0
    {
2257
0
        FSHelperPtr pFS = GetFS();
2258
2259
0
        pFS->startElementNS(nXmlNamespace,
2260
0
                            (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_txBody : XML_txbx));
2261
0
        WriteText(xIface, /*bBodyPr=*/(GetDocumentType() != DOCUMENT_DOCX || mbUserShapes), /*bText=*/true,
2262
0
                  /*nXmlNamespace=*/0, /*bWritePropertiesAsLstStyles=*/bWritePropertiesAsLstStyles);
2263
0
        pFS->endElementNS( nXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_txBody : XML_txbx) );
2264
0
        if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes)
2265
0
            WriteText( xIface, /*bBodyPr=*/true, /*bText=*/false, /*nXmlNamespace=*/nXmlNamespace );
2266
0
    }
2267
0
    else if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes)
2268
0
        mpFS->singleElementNS(nXmlNamespace, XML_bodyPr);
2269
2270
0
    return *this;
2271
0
}
2272
2273
void ShapeExport::WriteTable( const Reference< XShape >& rXShape  )
2274
0
{
2275
0
    Reference< XTable > xTable;
2276
0
    Reference< XPropertySet > xPropSet( rXShape, UNO_QUERY );
2277
2278
0
    mpFS->startElementNS(XML_a, XML_graphic);
2279
0
    mpFS->startElementNS(XML_a, XML_graphicData,
2280
0
                         XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/table");
2281
2282
0
    if ( xPropSet.is() && ( xPropSet->getPropertyValue( u"Model"_ustr ) >>= xTable ) )
2283
0
    {
2284
0
        mpFS->startElementNS(XML_a, XML_tbl);
2285
0
        mpFS->startElementNS(XML_a, XML_tblPr);
2286
0
        WriteShapeEffects(xPropSet);
2287
0
        mpFS->endElementNS(XML_a, XML_tblPr);
2288
2289
0
        Reference< container::XIndexAccess > xColumns( xTable->getColumns(), UNO_QUERY_THROW );
2290
0
        Reference< container::XIndexAccess > xRows( xTable->getRows(), UNO_QUERY_THROW );
2291
0
        sal_uInt16 nRowCount = static_cast< sal_uInt16 >( xRows->getCount() );
2292
0
        sal_uInt16 nColumnCount = static_cast< sal_uInt16 >( xColumns->getCount() );
2293
2294
0
        mpFS->startElementNS(XML_a, XML_tblGrid);
2295
2296
0
        for ( sal_Int32 x = 0; x < nColumnCount; x++ )
2297
0
        {
2298
0
            Reference< XPropertySet > xColPropSet( xColumns->getByIndex( x ), UNO_QUERY_THROW );
2299
0
            sal_Int32 nWidth(0);
2300
0
            xColPropSet->getPropertyValue( u"Width"_ustr ) >>= nWidth;
2301
2302
0
            mpFS->singleElementNS(XML_a, XML_gridCol,
2303
0
                                  XML_w, OString::number(oox::drawingml::convertHmmToEmu(nWidth)));
2304
0
        }
2305
2306
0
        mpFS->endElementNS( XML_a, XML_tblGrid );
2307
2308
        // map for holding the transpose index of the merged cells and pair<parentTransposeIndex, parentCell>
2309
0
        typedef std::unordered_map<sal_Int32, std::pair<sal_Int32, Reference< XMergeableCell> > > transposeTableMap;
2310
0
        transposeTableMap mergedCellMap;
2311
2312
0
        for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
2313
0
        {
2314
0
            Reference< XPropertySet > xRowPropSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
2315
0
            sal_Int32 nRowHeight(0);
2316
2317
0
            xRowPropSet->getPropertyValue( u"Height"_ustr ) >>= nRowHeight;
2318
2319
0
            mpFS->startElementNS(XML_a, XML_tr,
2320
0
                XML_h, OString::number(oox::drawingml::convertHmmToEmu(nRowHeight)));
2321
0
            for( sal_Int32 nColumn = 0; nColumn < nColumnCount; nColumn++ )
2322
0
            {
2323
0
                Reference< XMergeableCell > xCell( xTable->getCellByPosition( nColumn, nRow ),
2324
0
                                                   UNO_QUERY_THROW );
2325
0
                sal_Int32 transposedIndexofCell = (nRow * nColumnCount) + nColumn;
2326
2327
                //assume we will open a cell, set to false below if we won't
2328
0
                bool bCellOpened = true;
2329
2330
0
                if(xCell->getColumnSpan() > 1 && xCell->getRowSpan() > 1)
2331
0
                {
2332
                    // having both : horizontal and vertical merge
2333
0
                    mpFS->startElementNS(XML_a, XML_tc,
2334
0
                                         XML_gridSpan, OString::number(xCell->getColumnSpan()),
2335
0
                                         XML_rowSpan, OString::number(xCell->getRowSpan()));
2336
                    // since, XMergeableCell doesn't have the information about
2337
                    // cell having hMerge or vMerge.
2338
                    // So, Populating the merged cell map in-order to use it to
2339
                    // decide the attribute for the individual cell.
2340
0
                    for(sal_Int32 columnIndex = nColumn; columnIndex < nColumn+xCell->getColumnSpan(); ++columnIndex)
2341
0
                    {
2342
0
                        for(sal_Int32 rowIndex = nRow; rowIndex < nRow+xCell->getRowSpan(); ++rowIndex)
2343
0
                        {
2344
0
                            sal_Int32 transposeIndexForMergeCell =
2345
0
                                (rowIndex * nColumnCount) + columnIndex;
2346
0
                            mergedCellMap[transposeIndexForMergeCell] =
2347
0
                                std::make_pair(transposedIndexofCell, xCell);
2348
0
                        }
2349
0
                    }
2350
2351
0
                }
2352
0
                else if(xCell->getColumnSpan() > 1)
2353
0
                {
2354
                    // having : horizontal merge
2355
0
                    mpFS->startElementNS(XML_a, XML_tc,
2356
0
                                         XML_gridSpan, OString::number(xCell->getColumnSpan()));
2357
0
                    for(sal_Int32 columnIndex = nColumn; columnIndex < nColumn + xCell->getColumnSpan(); ++columnIndex) {
2358
0
                        sal_Int32 transposeIndexForMergeCell = (nRow*nColumnCount) + columnIndex;
2359
0
                        mergedCellMap[transposeIndexForMergeCell] =
2360
0
                            std::make_pair(transposedIndexofCell, xCell);
2361
0
                    }
2362
0
                }
2363
0
                else if(xCell->getRowSpan() > 1)
2364
0
                {
2365
                    // having : vertical merge
2366
0
                    mpFS->startElementNS(XML_a, XML_tc,
2367
0
                                         XML_rowSpan, OString::number(xCell->getRowSpan()));
2368
2369
0
                    for(sal_Int32 rowIndex = nRow; rowIndex < nRow + xCell->getRowSpan(); ++rowIndex) {
2370
0
                        sal_Int32 transposeIndexForMergeCell = (rowIndex*nColumnCount) + nColumn;
2371
0
                        mergedCellMap[transposeIndexForMergeCell] =
2372
0
                            std::make_pair(transposedIndexofCell, xCell);
2373
0
                    }
2374
0
                }
2375
0
                else
2376
0
                {
2377
                    // now, the cell can be an independent cell or
2378
                    // it can be a cell which is been merged to some parent cell
2379
0
                    if(!xCell->isMerged())
2380
0
                    {
2381
                        // independent cell
2382
0
                        mpFS->startElementNS(XML_a, XML_tc);
2383
0
                    }
2384
0
                    else
2385
0
                    {
2386
                        // it a merged cell to some parent cell
2387
                        // find the parent cell for the current cell at hand
2388
0
                        transposeTableMap::iterator it = mergedCellMap.find(transposedIndexofCell);
2389
0
                        if(it != mergedCellMap.end())
2390
0
                        {
2391
0
                            sal_Int32 transposeIndexOfParent = it->second.first;
2392
0
                            Reference< XMergeableCell > parentCell = it->second.second;
2393
                            // finding the row and column index for the parent cell from transposed index
2394
0
                            sal_Int32 parentColumnIndex = transposeIndexOfParent % nColumnCount;
2395
0
                            sal_Int32 parentRowIndex = transposeIndexOfParent / nColumnCount;
2396
0
                            if(nColumn == parentColumnIndex)
2397
0
                            {
2398
                                // the cell is vertical merge and it might have gridspan
2399
0
                                if(parentCell->getColumnSpan() > 1)
2400
0
                                {
2401
                                    // vMerge and has gridSpan
2402
0
                                    mpFS->startElementNS(XML_a, XML_tc,
2403
0
                                                         XML_vMerge, OString::number(1),
2404
0
                                                         XML_gridSpan, OString::number(xCell->getColumnSpan()));
2405
0
                                }
2406
0
                                else
2407
0
                                {
2408
                                    // only vMerge
2409
0
                                    mpFS->startElementNS(XML_a, XML_tc,
2410
0
                                                         XML_vMerge, OString::number(1));
2411
0
                                }
2412
0
                            }
2413
0
                            else if(nRow == parentRowIndex)
2414
0
                            {
2415
                                // the cell is horizontal merge and it might have rowspan
2416
0
                                if(parentCell->getRowSpan() > 1)
2417
0
                                {
2418
                                    // hMerge and has rowspan
2419
0
                                    mpFS->startElementNS(XML_a, XML_tc,
2420
0
                                                         XML_hMerge, OString::number(1),
2421
0
                                                         XML_rowSpan, OString::number(xCell->getRowSpan()));
2422
0
                                }
2423
0
                                else
2424
0
                                {
2425
                                    // only hMerge
2426
0
                                    mpFS->startElementNS(XML_a, XML_tc,
2427
0
                                                         XML_hMerge, OString::number(1));
2428
0
                                }
2429
0
                            }
2430
0
                            else
2431
0
                            {
2432
                                // has hMerge and vMerge
2433
0
                                mpFS->startElementNS(XML_a, XML_tc,
2434
0
                                                     XML_vMerge, OString::number(1),
2435
0
                                                     XML_hMerge, OString::number(1));
2436
0
                            }
2437
0
                        }
2438
0
                        else
2439
0
                            bCellOpened = false;
2440
0
                    }
2441
0
                }
2442
2443
0
                if (bCellOpened)
2444
0
                {
2445
0
                    WriteTextBox( xCell, XML_a );
2446
2447
0
                    Reference< XPropertySet > xCellPropSet(xCell, UNO_QUERY_THROW);
2448
0
                    WriteTableCellProperties(xCellPropSet);
2449
2450
0
                    mpFS->endElementNS( XML_a, XML_tc );
2451
0
                }
2452
0
            }
2453
2454
0
            mpFS->endElementNS( XML_a, XML_tr );
2455
0
        }
2456
2457
0
        mpFS->endElementNS( XML_a, XML_tbl );
2458
0
    }
2459
2460
0
    mpFS->endElementNS( XML_a, XML_graphicData );
2461
0
    mpFS->endElementNS( XML_a, XML_graphic );
2462
0
}
2463
2464
void ShapeExport::WriteTableCellProperties(const Reference< XPropertySet>& xCellPropSet)
2465
0
{
2466
0
    sal_Int32 nLeftMargin(0), nRightMargin(0);
2467
0
    sal_Int32 nTopMargin(0);
2468
0
    sal_Int32 nBottomMargin(0);
2469
0
    TextVerticalAdjust eVerticalAlignment;
2470
0
    const char* sVerticalAlignment;
2471
2472
0
    Any aLeftMargin = xCellPropSet->getPropertyValue(u"TextLeftDistance"_ustr);
2473
0
    aLeftMargin >>= nLeftMargin;
2474
2475
0
    Any aRightMargin = xCellPropSet->getPropertyValue(u"TextRightDistance"_ustr);
2476
0
    aRightMargin >>= nRightMargin;
2477
2478
0
    Any aTopMargin = xCellPropSet->getPropertyValue(u"TextUpperDistance"_ustr);
2479
0
    aTopMargin >>= nTopMargin;
2480
2481
0
    Any aBottomMargin = xCellPropSet->getPropertyValue(u"TextLowerDistance"_ustr);
2482
0
    aBottomMargin >>= nBottomMargin;
2483
2484
0
    Any aVerticalAlignment = xCellPropSet->getPropertyValue(u"TextVerticalAdjust"_ustr);
2485
0
    aVerticalAlignment >>= eVerticalAlignment;
2486
0
    sVerticalAlignment = GetTextVerticalAdjust(eVerticalAlignment);
2487
2488
0
    sal_Int32 nRotateAngle = 0;
2489
0
    Any aRotateAngle = xCellPropSet->getPropertyValue(u"RotateAngle"_ustr);
2490
0
    aRotateAngle >>= nRotateAngle;
2491
0
    std::optional<OString> aTextVerticalValue = GetTextVerticalType(nRotateAngle);
2492
2493
0
    Sequence<PropertyValue> aGrabBag;
2494
0
    if( !aTextVerticalValue &&
2495
0
        (xCellPropSet->getPropertyValue(u"CellInteropGrabBag"_ustr) >>= aGrabBag) )
2496
0
    {
2497
0
        for (auto const& rIt : aGrabBag)
2498
0
        {
2499
0
            if (rIt.Name == "mso-tcPr-vert-value")
2500
0
            {
2501
0
                aTextVerticalValue = rIt.Value.get<OUString>().toUtf8();
2502
0
                break;
2503
0
            }
2504
0
        }
2505
0
    }
2506
2507
0
    mpFS->startElementNS(XML_a, XML_tcPr, XML_anchor, sVerticalAlignment,
2508
0
    XML_vert, aTextVerticalValue,
2509
0
    XML_marL, OString::number(oox::drawingml::convertHmmToEmu(nLeftMargin)),
2510
0
    XML_marR, OString::number(oox::drawingml::convertHmmToEmu(nRightMargin)),
2511
0
    XML_marT, OString::number(oox::drawingml::convertHmmToEmu(nTopMargin)),
2512
0
    XML_marB, OString::number(oox::drawingml::convertHmmToEmu(nBottomMargin)));
2513
2514
    // Write background fill for table cell.
2515
    // TODO
2516
    // tcW : Table cell width
2517
0
    WriteTableCellBorders(xCellPropSet);
2518
0
    DrawingML::WriteFill(xCellPropSet);
2519
0
    mpFS->endElementNS( XML_a, XML_tcPr );
2520
0
}
2521
2522
void ShapeExport::WriteBorderLine(const sal_Int32 xml_line_element, const BorderLine2& rBorderLine)
2523
0
{
2524
// While importing the table cell border line width, it converts EMU->Hmm then divided result by 2.
2525
// To get original value of LineWidth need to multiple by 2.
2526
0
    sal_Int32 nBorderWidth = rBorderLine.LineWidth;
2527
0
    nBorderWidth *= 2;
2528
0
    nBorderWidth = oox::drawingml::convertHmmToEmu( nBorderWidth );
2529
2530
0
    if ( nBorderWidth > 0 )
2531
0
    {
2532
0
        mpFS->startElementNS(XML_a, xml_line_element, XML_w, OString::number(nBorderWidth));
2533
0
        if ( rBorderLine.Color == sal_Int32( COL_AUTO ) )
2534
0
            mpFS->singleElementNS(XML_a, XML_noFill);
2535
0
        else
2536
0
        {
2537
0
            ::Color nColor(ColorTransparency, rBorderLine.Color);
2538
0
            if (nColor.IsTransparent())
2539
0
                DrawingML::WriteSolidFill( nColor, nColor.GetAlpha() );
2540
0
            else
2541
0
                DrawingML::WriteSolidFill( nColor );
2542
0
        }
2543
2544
0
        OUString sBorderStyle;
2545
0
        sal_Int16 nStyle = rBorderLine.LineStyle;
2546
0
        mAny.setValue(&nStyle, cppu::UnoType<sal_Int16>::get());
2547
0
        switch (*o3tl::doAccess<sal_Int16>(mAny))
2548
0
        {
2549
0
            case ::table::BorderLineStyle::SOLID:
2550
0
                sBorderStyle = "solid";
2551
0
                break;
2552
0
            case ::table::BorderLineStyle::DOTTED:
2553
0
                sBorderStyle = "dot";
2554
0
                break;
2555
0
            case ::table::BorderLineStyle::DASHED:
2556
0
                sBorderStyle = "dash";
2557
0
                break;
2558
0
            case ::table::BorderLineStyle::DASH_DOT:
2559
0
                sBorderStyle = "dashDot";
2560
0
                break;
2561
0
            case ::table::BorderLineStyle::DASH_DOT_DOT:
2562
0
                sBorderStyle = "sysDashDotDot";
2563
0
                break;
2564
0
        }
2565
0
        mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, sBorderStyle);
2566
0
        mpFS->endElementNS(XML_a, xml_line_element);
2567
0
    }
2568
0
    else if( nBorderWidth == 0)
2569
0
    {
2570
0
        mpFS->startElementNS(XML_a, xml_line_element);
2571
0
        mpFS->singleElementNS(XML_a, XML_noFill);
2572
0
        mpFS->endElementNS(XML_a, xml_line_element);
2573
0
    }
2574
0
}
2575
2576
void ShapeExport::WriteTableCellBorders(const Reference< XPropertySet>& xCellPropSet)
2577
0
{
2578
0
    BorderLine2 aBorderLine;
2579
2580
// lnL - Left Border Line Properties of table cell
2581
0
    xCellPropSet->getPropertyValue(u"LeftBorder"_ustr) >>= aBorderLine;
2582
0
    WriteBorderLine( XML_lnL, aBorderLine );
2583
2584
// lnR - Right Border Line Properties of table cell
2585
0
    xCellPropSet->getPropertyValue(u"RightBorder"_ustr) >>= aBorderLine;
2586
0
    WriteBorderLine( XML_lnR, aBorderLine );
2587
2588
// lnT - Top Border Line Properties of table cell
2589
0
    xCellPropSet->getPropertyValue(u"TopBorder"_ustr) >>= aBorderLine;
2590
0
    WriteBorderLine( XML_lnT, aBorderLine );
2591
2592
// lnB - Bottom Border Line Properties of table cell
2593
0
    xCellPropSet->getPropertyValue(u"BottomBorder"_ustr) >>= aBorderLine;
2594
0
    WriteBorderLine( XML_lnB, aBorderLine );
2595
0
}
2596
2597
ShapeExport& ShapeExport::WriteTableShape( const Reference< XShape >& xShape )
2598
0
{
2599
0
    FSHelperPtr pFS = GetFS();
2600
2601
0
    pFS->startElementNS(mnXmlNamespace, XML_graphicFrame);
2602
2603
0
    pFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr);
2604
2605
0
    Reference<XPropertySet> const xShapeProps(xShape, UNO_QUERY);
2606
0
    pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
2607
0
                          XML_id, OString::number(GetNewShapeID(xShape)),
2608
0
                          XML_name,   GetShapeName(xShape));
2609
0
    AddExtLst(pFS, xShapeProps);
2610
0
    pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2611
2612
0
    pFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr);
2613
2614
0
    if( GetDocumentType() == DOCUMENT_PPTX )
2615
0
        pFS->singleElementNS(mnXmlNamespace, XML_nvPr);
2616
0
    pFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
2617
2618
0
    WriteShapeTransformation( xShape, mnXmlNamespace );
2619
0
    WriteTable( xShape );
2620
2621
0
    pFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
2622
2623
0
    return *this;
2624
0
}
2625
2626
ShapeExport& ShapeExport::WriteTextShape( const Reference< XShape >& xShape )
2627
0
{
2628
0
    FSHelperPtr pFS = GetFS();
2629
0
    Reference<XPropertySet> xShapeProps(xShape, UNO_QUERY);
2630
2631
0
    pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp));
2632
2633
    // non visual shape properties
2634
0
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
2635
0
    {
2636
0
        pFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
2637
0
        pFS->startElementNS(mnXmlNamespace, XML_cNvPr,
2638
0
                              XML_id, OString::number(GetNewShapeID(xShape)),
2639
0
                              XML_name, GetShapeName(xShape));
2640
0
        OUString sURL;
2641
0
        if (GetProperty(xShapeProps, u"URL"_ustr))
2642
0
            mAny >>= sURL;
2643
2644
0
        if (!sURL.isEmpty())
2645
0
        {
2646
0
            OUString sRelId = mpFB->addRelation(mpFS->getOutputStream(),
2647
0
                    oox::getRelationship(Relationship::HYPERLINK),
2648
0
                    mpURLTransformer->getTransformedString(sURL),
2649
0
                    mpURLTransformer->isExternalURL(sURL));
2650
2651
0
            mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
2652
0
        }
2653
0
        AddExtLst(pFS, xShapeProps);
2654
0
        pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2655
0
    }
2656
0
    pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr, XML_txBox, "1");
2657
0
    if (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes)
2658
0
    {
2659
0
        WriteNonVisualProperties( xShape );
2660
0
        pFS->endElementNS( mnXmlNamespace, XML_nvSpPr );
2661
0
    }
2662
2663
    // visual shape properties
2664
0
    pFS->startElementNS(mnXmlNamespace, XML_spPr);
2665
0
    WriteShapeTransformation( xShape, XML_a );
2666
0
    WritePresetShape( "rect"_ostr );
2667
0
    uno::Reference<beans::XPropertySet> xPropertySet(xShape, UNO_QUERY);
2668
0
    if (!IsFontworkShape(xShapeProps)) // Fontwork needs fill and outline in run properties instead.
2669
0
    {
2670
0
        WriteBlipOrNormalFill(xPropertySet, u"Graphic"_ustr, xShape->getSize());
2671
0
        WriteOutline(xPropertySet);
2672
0
        WriteShapeEffects(xPropertySet);
2673
0
    }
2674
0
    pFS->endElementNS( mnXmlNamespace, XML_spPr );
2675
2676
0
    WriteTextBox( xShape, mnXmlNamespace );
2677
2678
0
    pFS->endElementNS( mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX || mbUserShapes ? XML_sp : XML_wsp) );
2679
2680
0
    return *this;
2681
0
}
2682
2683
void ShapeExport::WriteMathShape(Reference<XShape> const& xShape)
2684
0
{
2685
0
    Reference<XPropertySet> const xPropSet(xShape, UNO_QUERY);
2686
0
    assert(xPropSet.is());
2687
0
    Reference<XModel> xMathModel;
2688
0
    xPropSet->getPropertyValue(u"Model"_ustr) >>= xMathModel;
2689
0
    assert(xMathModel.is());
2690
0
    assert(GetDocumentType() != DOCUMENT_DOCX); // should be written in DocxAttributeOutput
2691
0
    SAL_WARN_IF(GetDocumentType() == DOCUMENT_XLSX, "oox.shape", "Math export to XLSX isn't tested, should it happen here?");
2692
0
    const OString cNvPr_id = OString::number(GetNewShapeID(xShape));
2693
0
    const OUString shapeName = GetShapeName(xShape);
2694
2695
    // ECMA standard does not actually allow oMath outside of
2696
    // WordProcessingML so write a MCE like PPT 2010 does
2697
0
    mpFS->startElementNS(XML_mc, XML_AlternateContent);
2698
0
    mpFS->startElementNS(XML_mc, XML_Choice,
2699
0
        FSNS(XML_xmlns, XML_a14), mpFB->getNamespaceURL(OOX_NS(a14)),
2700
0
        XML_Requires, "a14");
2701
0
    mpFS->startElementNS(mnXmlNamespace, XML_sp);
2702
0
    mpFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
2703
0
    mpFS->startElementNS(mnXmlNamespace, XML_cNvPr, XML_id, cNvPr_id, XML_name, shapeName);
2704
0
    AddExtLst(mpFS, xPropSet);
2705
0
    mpFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2706
0
    mpFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr, XML_txBox, "1");
2707
0
    mpFS->singleElementNS(mnXmlNamespace, XML_nvPr);
2708
0
    mpFS->endElementNS(mnXmlNamespace, XML_nvSpPr);
2709
0
    mpFS->startElementNS(mnXmlNamespace, XML_spPr);
2710
0
    WriteShapeTransformation(xShape, XML_a);
2711
0
    WritePresetShape("rect"_ostr);
2712
0
    mpFS->endElementNS(mnXmlNamespace, XML_spPr);
2713
0
    mpFS->startElementNS(mnXmlNamespace, XML_txBody);
2714
0
    mpFS->startElementNS(XML_a, XML_bodyPr);
2715
0
    mpFS->endElementNS(XML_a, XML_bodyPr);
2716
0
    mpFS->startElementNS(XML_a, XML_p);
2717
0
    mpFS->startElementNS(XML_a14, XML_m);
2718
2719
0
    oox::FormulaImExportBase *const pMagic(
2720
0
        dynamic_cast<oox::FormulaImExportBase*>(xMathModel.get()));
2721
0
    assert(pMagic);
2722
0
    pMagic->writeFormulaOoxml(GetFS(), GetFB()->getVersion(), GetDocumentType(),
2723
0
        FormulaImExportBase::eFormulaAlign::INLINE);
2724
2725
0
    mpFS->endElementNS(XML_a14, XML_m);
2726
0
    mpFS->endElementNS(XML_a, XML_p);
2727
0
    mpFS->endElementNS(mnXmlNamespace, XML_txBody);
2728
0
    mpFS->endElementNS(mnXmlNamespace, XML_sp);
2729
0
    mpFS->endElementNS(XML_mc, XML_Choice);
2730
0
    mpFS->startElementNS(XML_mc, XML_Fallback);
2731
2732
0
    svt::EmbeddedObjectRef ref(
2733
0
        xPropSet->getPropertyValue(u"EmbeddedObject"_ustr).query<css::embed::XEmbeddedObject>(),
2734
0
        embed::Aspects::MSOLE_CONTENT);
2735
0
    if (auto* graphic = ref.GetGraphic(); graphic && graphic->GetType() != GraphicType::NONE)
2736
0
    {
2737
0
        if (OUString r_id = writeGraphicToStorage(*graphic); !r_id.isEmpty())
2738
0
        {
2739
0
            mpFS->startElementNS(mnXmlNamespace, XML_sp);
2740
0
            mpFS->startElementNS(mnXmlNamespace, XML_nvSpPr);
2741
0
            mpFS->startElementNS(mnXmlNamespace, XML_cNvPr, XML_id, cNvPr_id, XML_name, shapeName);
2742
0
            AddExtLst(mpFS, xPropSet);
2743
0
            mpFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2744
0
            mpFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr, XML_txBox, "1");
2745
0
            mpFS->singleElementNS(mnXmlNamespace, XML_nvPr);
2746
0
            mpFS->endElementNS(mnXmlNamespace, XML_nvSpPr);
2747
0
            mpFS->startElementNS(mnXmlNamespace, XML_spPr);
2748
0
            WriteShapeTransformation(xShape, XML_a);
2749
0
            WritePresetShape("rect"_ostr);
2750
0
            mpFS->startElementNS(XML_a, XML_blipFill);
2751
0
            mpFS->singleElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), r_id);
2752
0
            mpFS->startElementNS(XML_a, XML_stretch);
2753
0
            mpFS->singleElementNS(XML_a, XML_fillRect);
2754
0
            mpFS->endElementNS(XML_a, XML_stretch);
2755
0
            mpFS->endElementNS(XML_a, XML_blipFill);
2756
0
            mpFS->endElementNS(mnXmlNamespace, XML_spPr);
2757
0
            mpFS->endElementNS(mnXmlNamespace, XML_sp);
2758
0
        }
2759
0
    }
2760
2761
0
    mpFS->endElementNS(XML_mc, XML_Fallback);
2762
0
    mpFS->endElementNS(XML_mc, XML_AlternateContent);
2763
0
}
2764
2765
ShapeExport& ShapeExport::WriteOLE2Shape( const Reference< XShape >& xShape )
2766
0
{
2767
0
    Reference< XPropertySet > xPropSet( xShape, UNO_QUERY );
2768
0
    if (!xPropSet.is())
2769
0
        return *this;
2770
2771
0
    enum { CHART, MATH, OTHER } eType(OTHER);
2772
0
    OUString clsid;
2773
0
    xPropSet->getPropertyValue(u"CLSID"_ustr) >>= clsid;
2774
0
    if (!clsid.isEmpty())
2775
0
    {
2776
0
        SvGlobalName aClassID;
2777
0
        bool const isValid = aClassID.MakeId(clsid);
2778
0
        assert(isValid); (void)isValid;
2779
0
        if (SotExchange::IsChart(aClassID))
2780
0
            eType = CHART;
2781
0
        else if (SotExchange::IsMath(aClassID))
2782
0
            eType = MATH;
2783
0
    }
2784
2785
0
    if (CHART == eType)
2786
0
    {
2787
0
        Reference< XChartDocument > xChartDoc;
2788
0
        xPropSet->getPropertyValue(u"Model"_ustr) >>= xChartDoc;
2789
0
        assert(xChartDoc.is());
2790
        //export the chart
2791
0
#if !ENABLE_WASM_STRIP_CHART
2792
        // WASM_CHART change
2793
        // TODO: With Chart extracted this cannot really happen since
2794
        // no Chart could've been added at all
2795
0
        ChartExport aChartExport( mnXmlNamespace, GetFS(), xChartDoc, GetFB(), GetDocumentType() );
2796
0
        aChartExport.WriteChartObj( xShape, GetNewShapeID( xShape ), ++mnChartCount );
2797
0
#endif
2798
0
        return *this;
2799
0
    }
2800
2801
0
    if (MATH == eType)
2802
0
    {
2803
0
        WriteMathShape(xShape);
2804
0
        return *this;
2805
0
    }
2806
2807
0
    uno::Reference<embed::XEmbeddedObject> const xObj(
2808
0
        xPropSet->getPropertyValue(u"EmbeddedObject"_ustr), uno::UNO_QUERY);
2809
2810
0
    if (!xObj.is())
2811
0
    {
2812
0
        SAL_WARN("oox.shape", "ShapeExport::WriteOLE2Shape: no object");
2813
2814
        // tdf#152436 Export the preview graphic of the object if the object is missing.
2815
0
        SdrObject* pSdrOLE2(SdrObject::getSdrObjectFromXShape(xShape));
2816
0
        if (auto pOle2Obj = dynamic_cast<SdrOle2Obj*>(pSdrOLE2))
2817
0
        {
2818
0
            const Graphic* pGraphic = pOle2Obj->GetGraphic();
2819
0
            if (pGraphic)
2820
0
                WriteGraphicObjectShapePart(xShape, pGraphic);
2821
0
        }
2822
2823
0
        return *this;
2824
0
    }
2825
2826
0
    uno::Sequence<beans::PropertyValue> grabBag;
2827
0
    OUString entryName;
2828
0
    try
2829
0
    {
2830
0
        uno::Reference<beans::XPropertySet> const xParent(
2831
0
            uno::Reference<container::XChild>(xObj, uno::UNO_QUERY_THROW)->getParent(),
2832
0
            uno::UNO_QUERY_THROW);
2833
2834
0
        xParent->getPropertyValue(u"InteropGrabBag"_ustr) >>= grabBag;
2835
2836
0
        entryName = uno::Reference<embed::XEmbedPersist>(xObj, uno::UNO_QUERY_THROW)->getEntryName();
2837
0
    }
2838
0
    catch (uno::Exception const&)
2839
0
    {
2840
0
        TOOLS_WARN_EXCEPTION("oox.shape", "ShapeExport::WriteOLE2Shape");
2841
0
        return *this;
2842
0
    }
2843
2844
0
    OUString progID;
2845
2846
0
    for (auto const& it : grabBag)
2847
0
    {
2848
0
        if (it.Name == "EmbeddedObjects")
2849
0
        {
2850
0
            uno::Sequence<beans::PropertyValue> objects;
2851
0
            it.Value >>= objects;
2852
0
            for (auto const& object : objects)
2853
0
            {
2854
0
                if (object.Name == entryName)
2855
0
                {
2856
0
                    uno::Sequence<beans::PropertyValue> props;
2857
0
                    object.Value >>= props;
2858
0
                    for (auto const& prop : props)
2859
0
                    {
2860
0
                        if (prop.Name == "ProgID")
2861
0
                        {
2862
0
                            prop.Value >>= progID;
2863
0
                            break;
2864
0
                        }
2865
0
                    }
2866
0
                    break;
2867
0
                }
2868
0
            }
2869
0
            break;
2870
0
        }
2871
0
    }
2872
2873
0
    OUString sMediaType;
2874
0
    OUString sRelationType;
2875
0
    OUString sSuffix;
2876
0
    const char * pProgID(nullptr);
2877
0
    OString anotherProgID;
2878
2879
0
    uno::Reference<io::XInputStream> const xInStream =
2880
0
        oox::GetOLEObjectStream(
2881
0
            xObj, progID,
2882
0
            sMediaType, sRelationType, sSuffix, pProgID);
2883
2884
0
    OUString sURL;
2885
0
    OUString sRelId;
2886
0
    if (!xInStream.is())
2887
0
    {
2888
0
        xPropSet->getPropertyValue(u"LinkURL"_ustr) >>= sURL;
2889
0
        if (sURL.isEmpty())
2890
0
            return *this;
2891
2892
0
        sRelId = mpFB->addRelation(mpFS->getOutputStream(),
2893
0
                                   oox::getRelationship(Relationship::OLEOBJECT), sURL, true);
2894
0
    }
2895
0
    else
2896
0
    {
2897
0
        if (!pProgID && !progID.isEmpty())
2898
0
        {
2899
0
            anotherProgID = OUStringToOString(progID, RTL_TEXTENCODING_UTF8);
2900
0
            pProgID = anotherProgID.getStr();
2901
0
        }
2902
2903
0
        assert(!sMediaType.isEmpty());
2904
0
        assert(!sRelationType.isEmpty());
2905
0
        assert(!sSuffix.isEmpty());
2906
2907
0
        OUString sNumber = OUString::number(++m_nEmbeddedObjects);
2908
0
        OUString sFileName = u"embeddings/oleObject"_ustr + sNumber + u"."_ustr + sSuffix;
2909
0
        OUString sFilePath = GetComponentDir() + u"/"_ustr + sFileName;
2910
0
        uno::Reference<io::XOutputStream> const xOutStream(mpFB->openFragmentStream(sFilePath, sMediaType));
2911
0
        assert(xOutStream.is()); // no reason why that could fail
2912
2913
0
        try
2914
0
        {
2915
0
            ::comphelper::OStorageHelper::CopyInputToOutput(xInStream, xOutStream);
2916
0
        }
2917
0
        catch (uno::Exception const&)
2918
0
        {
2919
0
            TOOLS_WARN_EXCEPTION("oox.shape", "ShapeExport::WriteOLEObject");
2920
0
        }
2921
2922
0
        sRelId = mpFB->addRelation(
2923
0
            mpFS->getOutputStream(), sRelationType,
2924
0
            Concat2View(GetRelationCompPrefix() + sFileName));
2925
0
    }
2926
2927
0
    sal_Int64 nAspect;
2928
0
    bool bShowAsIcon = (xPropSet->getPropertyValue(u"Aspect"_ustr) >>= nAspect)
2929
0
                       && nAspect == embed::Aspects::MSOLE_ICON;
2930
2931
0
    mpFS->startElementNS(mnXmlNamespace, XML_graphicFrame);
2932
2933
0
    mpFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr);
2934
2935
0
    mpFS->startElementNS(mnXmlNamespace, XML_cNvPr,
2936
0
                           XML_id,     OString::number(GetNewShapeID(xShape)),
2937
0
                           XML_name,   GetShapeName(xShape));
2938
0
    AddExtLst(mpFS, xPropSet);
2939
0
    mpFS->endElementNS(mnXmlNamespace, XML_cNvPr);
2940
2941
0
    mpFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr);
2942
2943
0
    if (GetDocumentType() == DOCUMENT_PPTX)
2944
0
        mpFS->singleElementNS(mnXmlNamespace, XML_nvPr);
2945
0
    mpFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
2946
2947
0
    WriteShapeTransformation( xShape, mnXmlNamespace );
2948
2949
0
    mpFS->startElementNS(XML_a, XML_graphic);
2950
0
    mpFS->startElementNS(XML_a, XML_graphicData,
2951
0
                         XML_uri, "http://schemas.openxmlformats.org/presentationml/2006/ole");
2952
0
    if (pProgID)
2953
0
    {
2954
0
        mpFS->startElementNS( mnXmlNamespace, XML_oleObj,
2955
0
                          XML_showAsIcon, sax_fastparser::UseIf("1", bShowAsIcon),
2956
0
                          XML_progId, pProgID,
2957
0
                          FSNS(XML_r, XML_id), sRelId,
2958
0
                          XML_spid, "" );
2959
0
    }
2960
0
    else
2961
0
    {
2962
0
        mpFS->startElementNS( mnXmlNamespace, XML_oleObj,
2963
//?                                              XML_name, "Document",
2964
0
                          XML_showAsIcon, sax_fastparser::UseIf("1", bShowAsIcon),
2965
0
                          FSNS(XML_r, XML_id), sRelId,
2966
                          // The spec says that this is a required attribute, but PowerPoint can only handle an empty value.
2967
0
                          XML_spid, "" );
2968
0
    }
2969
2970
0
    if (sURL.isEmpty())
2971
0
        mpFS->singleElementNS(mnXmlNamespace, XML_embed);
2972
0
    else
2973
0
        mpFS->singleElementNS(mnXmlNamespace, XML_link, XML_updateAutomatic, "1");
2974
2975
    // pic element
2976
0
    SdrObject* pSdrOLE2(SdrObject::getSdrObjectFromXShape(xShape));
2977
0
    if (auto pOle2Obj = dynamic_cast<SdrOle2Obj*>(pSdrOLE2))
2978
0
    {
2979
0
        const Graphic* pGraphic = pOle2Obj->GetGraphic();
2980
0
        if (pGraphic)
2981
0
            WriteGraphicObjectShapePart(xShape, pGraphic);
2982
0
    }
2983
2984
0
    mpFS->endElementNS( mnXmlNamespace, XML_oleObj );
2985
2986
0
    mpFS->endElementNS( XML_a, XML_graphicData );
2987
0
    mpFS->endElementNS( XML_a, XML_graphic );
2988
2989
0
    mpFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
2990
2991
0
    return *this;
2992
0
}
2993
2994
ShapeExport& ShapeExport::WriteUnknownShape( const Reference< XShape >& )
2995
0
{
2996
    // Override this method to do something useful.
2997
0
    return *this;
2998
0
}
2999
3000
sal_Int32 ShapeExport::GetNewShapeID( const Reference< XShape >& rXShape )
3001
0
{
3002
0
    return GetNewShapeID( rXShape, GetFB() );
3003
0
}
3004
3005
sal_Int32 ShapeExport::GetNewShapeID( const Reference< XShape >& rXShape, XmlFilterBase* pFB )
3006
0
{
3007
0
    if( !rXShape.is() )
3008
0
        return -1;
3009
3010
0
    sal_Int32 nID = pFB->GetUniqueId();
3011
3012
0
    (*mpShapeMap)[ rXShape ] = nID;
3013
3014
0
    return nID;
3015
0
}
3016
3017
sal_Int32 ShapeExport::GetShapeID( const Reference< XShape >& rXShape )
3018
0
{
3019
0
    return GetShapeID( rXShape, mpShapeMap );
3020
0
}
3021
3022
sal_Int32 ShapeExport::GetShapeID( const Reference< XShape >& rXShape, ShapeHashMap* pShapeMap )
3023
0
{
3024
0
    if( !rXShape.is() )
3025
0
        return -1;
3026
3027
0
    ShapeHashMap::const_iterator aIter = pShapeMap->find( rXShape );
3028
3029
0
    if( aIter == pShapeMap->end() )
3030
0
        return -1;
3031
3032
0
    return aIter->second;
3033
0
}
3034
3035
OUString ShapeExport::GetShapeName(const Reference<XShape>& xShape)
3036
0
{
3037
0
    Reference<XPropertySet> rXPropSet(xShape, UNO_QUERY);
3038
3039
    // Empty name keeps the object unnamed.
3040
0
    OUString sName;
3041
3042
0
    if (GetProperty(rXPropSet, u"Name"_ustr))
3043
0
        mAny >>= sName;
3044
0
    return sName;
3045
0
}
3046
3047
}
3048
3049
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */