Coverage Report

Created: 2026-03-31 11:00

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