Coverage Report

Created: 2026-03-31 11:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/oox/source/drawingml/diagram/diagram.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 <oox/drawingml/diagram/diagram.hxx>
21
#include "diagram.hxx"
22
#include <com/sun/star/awt/Point.hpp>
23
#include <com/sun/star/awt/Size.hpp>
24
#include <com/sun/star/beans/XPropertySet.hpp>
25
#include <com/sun/star/drawing/XShape.hpp>
26
#include <com/sun/star/drawing/XShapes.hpp>
27
#include <com/sun/star/xml/dom/XDocument.hpp>
28
#include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
29
#include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
30
#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
31
#include <sal/log.hxx>
32
#include <editeng/unoprnms.hxx>
33
#include <drawingml/fillproperties.hxx>
34
#include <drawingml/customshapeproperties.hxx>
35
#include <o3tl/unit_conversion.hxx>
36
#include <oox/drawingml/theme.hxx>
37
#include <oox/token/namespaces.hxx>
38
#include <basegfx/matrix/b2dhommatrix.hxx>
39
#include <svx/svdpage.hxx>
40
#include <oox/ppt/pptimport.hxx>
41
#include <comphelper/xmltools.hxx>
42
#include "diagramlayoutatoms.hxx"
43
#include "layoutatomvisitors.hxx"
44
#include "diagramfragmenthandler.hxx"
45
#include <comphelper/processfactory.hxx>
46
#include <com/sun/star/io/TempFile.hpp>
47
#include <oox/export/drawingml.hxx>
48
49
#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
50
#include <com/sun/star/xml/sax/Writer.hpp>
51
#include <oox/core/fastparser.hxx>
52
#include <unotools/streamwrap.hxx>
53
#include <tools/stream.hxx>
54
55
#ifdef DBG_UTIL
56
#include <osl/file.hxx>
57
#include <o3tl/environment.hxx>
58
#include <comphelper/storagehelper.hxx>
59
#include <com/sun/star/embed/XRelationshipAccess.hpp>
60
#endif
61
62
using namespace ::com::sun::star;
63
64
namespace oox::drawingml {
65
66
static void sortChildrenByZOrder(const ShapePtr& pShape)
67
290
{
68
290
    std::vector<ShapePtr>& rChildren = pShape->getChildren();
69
70
    // Offset the children from their default z-order stacking, if necessary.
71
544
    for (size_t i = 0; i < rChildren.size(); ++i)
72
254
        rChildren[i]->setZOrder(i);
73
74
544
    for (size_t i = 0; i < rChildren.size(); ++i)
75
254
    {
76
254
        const ShapePtr& pChild = rChildren[i];
77
254
        sal_Int32 nZOrderOff = pChild->getZOrderOff();
78
254
        if (nZOrderOff <= 0)
79
254
            continue;
80
81
        // Increase my ZOrder by nZOrderOff.
82
0
        pChild->setZOrder(pChild->getZOrder() + nZOrderOff);
83
0
        pChild->setZOrderOff(0);
84
85
0
        for (sal_Int32 j = 0; j < nZOrderOff; ++j)
86
0
        {
87
0
            size_t nIndex = i + j + 1;
88
0
            if (nIndex >= rChildren.size())
89
0
                break;
90
91
            // Decrease the ZOrder of the next nZOrderOff elements by one.
92
0
            const ShapePtr& pNext = rChildren[nIndex];
93
0
            pNext->setZOrder(pNext->getZOrder() - 1);
94
0
        }
95
0
    }
96
97
    // Now that the ZOrders are adjusted, sort the children.
98
290
    std::sort(rChildren.begin(), rChildren.end(),
99
290
              [](const ShapePtr& a, const ShapePtr& b) { return a->getZOrder() < b->getZOrder(); });
100
101
    // Apply also for children.
102
290
    for (const auto& rChild : rChildren)
103
254
        sortChildrenByZOrder(rChild);
104
290
}
105
106
/// Removes empty group shapes, now that their spacing influenced the layout.
107
static void removeUnneededGroupShapes(const ShapePtr& pShape)
108
290
{
109
290
    std::vector<ShapePtr>& rChildren = pShape->getChildren();
110
111
290
    std::erase_if(rChildren,
112
290
                                   [](const ShapePtr& aChild) {
113
254
                                       return aChild->getServiceName()
114
254
                                                  == "com.sun.star.drawing.GroupShape"
115
30
                                              && aChild->getChildren().empty();
116
254
                                   });
117
118
290
    for (const auto& pChild : rChildren)
119
254
    {
120
254
        removeUnneededGroupShapes(pChild);
121
254
    }
122
290
}
123
124
void SmartArtDiagram::createShapeHierarchyFromModel( const ShapePtr & pParentShape, bool bCreate )
125
107
{
126
107
    if (pParentShape->getSize().Width == 0 || pParentShape->getSize().Height == 0)
127
107
        SAL_WARN("oox.drawingml", "SmartArtDiagram cannot be correctly laid out. Size: "
128
107
            << pParentShape->getSize().Width << "x" << pParentShape->getSize().Height);
129
130
107
    pParentShape->setChildSize(pParentShape->getSize());
131
132
107
    const svx::diagram::Point* pRootPoint = mpData->getRootPoint();
133
107
    if (bCreate && mpLayout->getNode() && pRootPoint)
134
36
    {
135
        // create Shape hierarchy
136
36
        ShapeCreationVisitor aCreationVisitor(*this, pRootPoint, pParentShape);
137
36
        mpLayout->getNode()->setExistingShape(pParentShape);
138
36
        mpLayout->getNode()->accept(aCreationVisitor);
139
140
        // layout shapes - now all shapes are created
141
36
        ShapeLayoutingVisitor aLayoutingVisitor(*this, pRootPoint);
142
36
        mpLayout->getNode()->accept(aLayoutingVisitor);
143
144
36
        sortChildrenByZOrder(pParentShape);
145
36
        removeUnneededGroupShapes(pParentShape);
146
36
    }
147
148
107
    ShapePtr pBackground = std::make_shared<Shape>("com.sun.star.drawing.CustomShape");
149
107
    pBackground->setSubType(XML_rect);
150
107
    pBackground->getCustomShapeProperties()->setShapePresetType(XML_rect);
151
107
    pBackground->setSize(pParentShape->getSize());
152
107
    if (mpData->getBackgroundShapeFillProperties())
153
107
        pBackground->getFillProperties() = *mpData->getBackgroundShapeFillProperties();
154
155
    // create and set ModelID for BackgroundShape to allow later association
156
107
    getData()->setBackgroundShapeModelID(OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8));
157
107
    pBackground->setDiagramDataModelID(getData()->getBackgroundShapeModelID());
158
159
107
    auto& aChildren = pParentShape->getChildren();
160
107
    aChildren.insert(aChildren.begin(), pBackground);
161
107
}
162
163
uno::Reference<xml::dom::XDocument> SmartArtDiagram::convertAndSet(std::u16string_view rDOM, svx::diagram::DomMapFlag aDomMapFlag)
164
0
{
165
    // construct MemoryStream and OStreamWrapper
166
0
    const OString sUtf8(OUStringToOString(rDOM, RTL_TEXTENCODING_UTF8));
167
0
    SvMemoryStream aStream(const_cast<char*>(sUtf8.getStr()), sUtf8.getLength(), StreamMode::READ);
168
0
    rtl::Reference<utl::OStreamWrapper> pStreamWrapper = new utl::OStreamWrapper(aStream);
169
170
    // create the dom parser & create DomTree
171
0
    uno::Reference<xml::dom::XDocumentBuilder> xDomBuilder(xml::dom::DocumentBuilder::create(comphelper::getProcessComponentContext()));
172
0
    uno::Reference<xml::dom::XDocument> aDomTree(xDomBuilder->parse(pStreamWrapper->getInputStream()));
173
174
    // set DomTree locally
175
0
    setOOXDomValue(aDomMapFlag, uno::Any(aDomTree));
176
0
    return aDomTree;
177
0
}
178
179
SmartArtDiagram::SmartArtDiagram()
180
370
: maDiagramFontHeights()
181
370
, mpData(std::make_shared<DiagramData_oox>())
182
370
, mpLayout(std::make_shared<DiagramLayout>(*this))
183
370
, maStyles()
184
370
, maColors()
185
370
, maDiagramPRDomMap()
186
370
{
187
370
}
188
189
SmartArtDiagram::SmartArtDiagram(SmartArtDiagram const& rSource)
190
0
: maDiagramFontHeights()
191
0
, mpData(rSource.mpData ? new DiagramData_oox(*rSource.mpData) : nullptr)
192
0
, mpLayout(rSource.mpLayout)
193
0
, maStyles(rSource.maStyles)
194
0
, maColors(rSource.maColors)
195
0
, maDiagramPRDomMap(rSource.maDiagramPRDomMap)
196
0
{
197
0
}
198
199
SmartArtDiagram::SmartArtDiagram(const boost::property_tree::ptree& rDiagramModel)
200
0
: maDiagramFontHeights()
201
0
, mpData(std::make_shared<DiagramData_oox>(rDiagramModel))
202
0
, mpLayout(std::make_shared<DiagramLayout>(*this))
203
0
, maStyles()
204
0
, maColors()
205
0
, maDiagramPRDomMap()
206
0
{
207
#ifdef DBG_UTIL
208
    mpData->dump();
209
#endif
210
0
    const OUString aOOXLayoutDOM(OUString::fromUtf8(rDiagramModel.get("OOXLayout", "")));
211
0
    const OUString aOOXStyleDOM(OUString::fromUtf8(rDiagramModel.get("OOXStyle", "")));
212
0
    const OUString aOOXColorDOM(OUString::fromUtf8(rDiagramModel.get("OOXColor", "")));
213
214
0
    if (!aOOXLayoutDOM.isEmpty() || !aOOXStyleDOM.isEmpty() || !aOOXColorDOM.isEmpty())
215
0
    {
216
        // we need a PowerPointImport for the FragmentHandlers, so create a single
217
        // temporary one. Use this for all possible DomTrees
218
0
        rtl::Reference<oox::ppt::PowerPointImport> xPPTImport(new oox::ppt::PowerPointImport(comphelper::getProcessComponentContext()));
219
220
0
        if (!aOOXLayoutDOM.isEmpty())
221
0
        {
222
            // create and set DomTree locally
223
0
            uno::Reference<xml::dom::XDocument> xDom(convertAndSet(aOOXLayoutDOM, svx::diagram::DomMapFlag::OOXLayout));
224
225
            // import DomTree to mpLayout
226
0
            uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(xDom, uno::UNO_QUERY_THROW);
227
0
            rtl::Reference< core::FragmentHandler > xRefLayout(new DiagramLayoutFragmentHandler(*this, *xPPTImport, "internal", mpLayout));
228
0
            xPPTImport->importFragment(xRefLayout, xSerializer);
229
0
        }
230
231
0
        if (!aOOXStyleDOM.isEmpty())
232
0
        {
233
            // create and set DomTree locally
234
0
            uno::Reference<xml::dom::XDocument> xDom(convertAndSet(aOOXStyleDOM, svx::diagram::DomMapFlag::OOXStyle));
235
236
            // import DomTree to maStyles
237
0
            uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(xDom, uno::UNO_QUERY_THROW);
238
0
            rtl::Reference< core::FragmentHandler > xRefLayout(new DiagramQStylesFragmentHandler(*xPPTImport, "internal", maStyles));
239
0
            xPPTImport->importFragment(xRefLayout, xSerializer);
240
0
        }
241
242
0
        if (!aOOXColorDOM.isEmpty())
243
0
        {
244
            // create and set DomTree locally
245
0
            uno::Reference<xml::dom::XDocument> xDom(convertAndSet(aOOXColorDOM, svx::diagram::DomMapFlag::OOXColor));
246
247
            // import DomTree to maColors
248
0
            uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(xDom, uno::UNO_QUERY_THROW);
249
0
            rtl::Reference< core::FragmentHandler > xRefLayout(new ColorFragmentHandler(*xPPTImport, "internal", maColors));
250
0
            xPPTImport->importFragment(xRefLayout, xSerializer);
251
0
        }
252
0
    }
253
0
}
254
255
SmartArtDiagram::~SmartArtDiagram()
256
370
{
257
370
}
258
259
uno::Any SmartArtDiagram::getOOXDomValue(svx::diagram::DomMapFlag aDomMapFlag) const
260
0
{
261
0
    const DiagramPRDomMap::const_iterator aHit = maDiagramPRDomMap.find(aDomMapFlag);
262
263
0
    if (aHit != maDiagramPRDomMap.end())
264
0
        return aHit->second;
265
266
0
    return uno::Any();
267
0
}
268
269
void SmartArtDiagram::setOOXDomValue(svx::diagram::DomMapFlag aDomMapFlag, const uno::Any& rValue)
270
1.77k
{
271
1.77k
    maDiagramPRDomMap[aDomMapFlag] = rValue;
272
1.77k
}
273
274
void SmartArtDiagram::resetOOXDomValues(svx::diagram::DomMapFlags aDomMapFlags)
275
0
{
276
0
    for (const auto& rEntry : aDomMapFlags)
277
0
    {
278
0
        maDiagramPRDomMap.erase(rEntry);
279
280
0
        if (maDiagramPRDomMap.empty())
281
0
            return;
282
0
    }
283
0
}
284
285
bool SmartArtDiagram::checkMinimalDataDoms() const
286
0
{
287
    // check if re-creation is activated
288
0
    static bool bActivateAdvancedDiagramFeatures(nullptr != std::getenv("ACTIVATE_ADVANCED_DIAGRAM_FEATURES"));
289
290
0
    if (!bActivateAdvancedDiagramFeatures && maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXData))
291
0
        return false;
292
293
0
    if (maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXLayout))
294
0
        return false;
295
296
0
    if (maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXStyle))
297
0
        return false;
298
299
0
    if (maDiagramPRDomMap.end() == maDiagramPRDomMap.find(svx::diagram::DomMapFlag::OOXColor))
300
0
        return false;
301
302
0
    return true;
303
0
}
304
305
void SmartArtDiagram::writeDiagramOOXData(DrawingML& rOriginalDrawingML, uno::Reference<io::XOutputStream>& xOutputStream, std::u16string_view rDrawingRelId) const
306
0
{
307
0
    if (!xOutputStream)
308
0
        return;
309
310
    // re-create OOXData DomFile from model data
311
0
    sax_fastparser::FSHelperPtr aFS = std::make_shared<sax_fastparser::FastSerializerHelper>(xOutputStream, true);
312
0
    getData()->writeDiagramData(rOriginalDrawingML, aFS, rDrawingRelId);
313
314
    // this call is *important*, without it xDocBuilder->parse below fails and some strange
315
    // and wrong assertion gets thrown in ~FastSerializerHelper that  shall get called
316
0
    xOutputStream->closeOutput();
317
318
#ifdef DBG_UTIL
319
    uno::Reference< embed::XRelationshipAccess > xRelations( xOutputStream, uno::UNO_QUERY );
320
    if( xRelations.is() )
321
    {
322
        const uno::Sequence<uno::Sequence<beans::StringPair>> aSeqs = xRelations->getAllRelationships();
323
        for (const uno::Sequence<beans::StringPair>& aSeq : aSeqs)
324
        {
325
            SAL_INFO("oox", "RelationData:");
326
            for (const beans::StringPair& aPair : aSeq)
327
                SAL_INFO("oox", "  Key: " << aPair.First << ", Value: " << aPair.Second);
328
        }
329
    }
330
331
    const OUString env(o3tl::getEnvironment(u"DIAGRAM_DUMP_PATH"_ustr));
332
    if(!env.isEmpty())
333
    {
334
        OUString url;
335
        ::osl::FileBase::getFileURLFromSystemPath(env, url);
336
        SvFileStream aOutStream(url + "data_T.xml", StreamMode::WRITE|StreamMode::TRUNC);
337
        uno::Reference<io::XStream> xOutStream(new utl::OStreamWrapper(aOutStream));
338
        uno::Reference<io::XStream> xInStream(xOutputStream, uno::UNO_QUERY);
339
        comphelper::OStorageHelper::CopyInputToOutput(xInStream->getInputStream(), xOutStream->getOutputStream());
340
    }
341
#endif
342
0
}
343
344
void SmartArtDiagram::writeDiagramOOXDrawing(DrawingML& rOriginalDrawingML, uno::Reference<io::XOutputStream>& xOutputStream) const
345
0
{
346
0
    if (!xOutputStream)
347
0
        return;
348
349
    // re-create OOXDrawing DomFile from model data
350
0
    SAL_INFO("oox", "DiagramReCreate: creating DomMapFlag::OOXDrawing");
351
0
    sax_fastparser::FSHelperPtr aFS = std::make_shared<sax_fastparser::FastSerializerHelper>(xOutputStream, true);
352
0
    getData()->writeDiagramReplacement(rOriginalDrawingML, aFS);
353
354
    // this call is *important*, without it xDocBuilder->parse below fails and some strange
355
    // and wrong assertion gets thrown in ~FastSerializerHelper that  shall get called
356
0
    xOutputStream->closeOutput();
357
358
#ifdef DBG_UTIL
359
    uno::Reference< embed::XRelationshipAccess > xRelations( xOutputStream, uno::UNO_QUERY );
360
    if( xRelations.is() )
361
    {
362
        const uno::Sequence<uno::Sequence<beans::StringPair>> aSeqs = xRelations->getAllRelationships();
363
        for (const uno::Sequence<beans::StringPair>& aSeq : aSeqs)
364
        {
365
            SAL_INFO("oox", "RelationDrawing:");
366
            for (const beans::StringPair& aPair : aSeq)
367
                SAL_INFO("oox", "  Key: " << aPair.First << ", Value: " << aPair.Second);
368
        }
369
    }
370
371
    const OUString env(o3tl::getEnvironment(u"DIAGRAM_DUMP_PATH"_ustr));
372
    if(!env.isEmpty())
373
    {
374
        OUString url;
375
        ::osl::FileBase::getFileURLFromSystemPath(env, url);
376
        SvFileStream aOutStream(url + "drawing_T.xml", StreamMode::WRITE|StreamMode::TRUNC);
377
        uno::Reference<io::XStream> xOutStream(new utl::OStreamWrapper(aOutStream));
378
        uno::Reference<io::XStream> xInStream(xOutputStream, uno::UNO_QUERY);
379
        comphelper::OStorageHelper::CopyInputToOutput(xInStream->getInputStream(), xOutStream->getOutputStream());
380
    }
381
#endif
382
0
}
383
384
void SmartArtDiagram::addDomTreeToModelData(svx::diagram::DomMapFlag aId, std::u16string_view aName, boost::property_tree::ptree& rTarget) const
385
0
{
386
0
    uno::Reference<xml::dom::XDocument> aDomTree;
387
0
    getOOXDomValue(aId) >>= aDomTree;
388
389
0
    if (aDomTree)
390
0
    {
391
        // serialize DomTree to a MemoryStream
392
0
        SvMemoryStream aStream( 1024, 1024 );
393
0
        rtl::Reference<utl::OStreamWrapper> pStreamWrapper = new utl::OStreamWrapper( aStream );
394
0
        uno::Reference<xml::sax::XSAXSerializable> serializer;
395
0
        uno::Reference<xml::sax::XWriter> writer = xml::sax::Writer::create(comphelper::getProcessComponentContext());
396
0
        serializer.set(aDomTree, uno::UNO_QUERY);
397
0
        writer->setOutputStream(pStreamWrapper->getOutputStream());
398
0
        serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW), uno::Sequence<beans::StringPair>());
399
400
        // put into string
401
0
        const OUString aContent(static_cast<const char*>(aStream.GetData()), aStream.TellEnd(), RTL_TEXTENCODING_UTF8);
402
403
        // add to ModelData
404
0
        const OString sUtf8(OUStringToOString(aName, RTL_TEXTENCODING_UTF8));
405
0
        rTarget.put(sUtf8.getStr(), aContent);
406
0
    }
407
0
}
408
409
void SmartArtDiagram::addDiagramModelData(boost::property_tree::ptree& rTarget) const
410
0
{
411
    // add Point and Connection data
412
0
    getData()->addDiagramModelData(rTarget);
413
414
    // What DomMaps are needed?
415
    //
416
    // With the above OOXData is covered. OOXDataImageRels/OOXDataHlinkRels also,
417
    // these may/will be re-created when OOX export and a new OOXDataDomTree
418
    // needs to be created.
419
    // Similar with OOXDrawing: This contains parts of ModelData, e.g. Text and
420
    // Attributes represented by the XShapes/Sdrobjects, so for internal formats
421
    // this is not needed to be saved. This also true for OOXDrawingImageRels
422
    // and OOXDrawingHlinkRels.
423
    // We *do* import OOXLayoutDomTree/ModelInfo and this is used in the layouting
424
    // mechanism (reLayout), but it is not changed. We could add an export of that
425
    // for internal formats, but since it's not changed it just needs to be
426
    // preserved, either for internal use or export to OOX formats.
427
    // OOXStyle and OOXColor are imported only on oox import side, partially held
428
    // for initial import at Diagram classes. Also never changed, but maybe needed
429
    // for export to OOX formats. Not sure about that since Style and Color is
430
    // part of XShape/SdrObject Model Hierarchy, so exports to OOX should be possible
431
    // without these, but maybe MSO wants that data...
432
    //
433
    // OOXLayout = 3,
434
    // OOXStyle = 4,
435
    // OOXColor = 5,
436
0
    addDomTreeToModelData(svx::diagram::DomMapFlag::OOXLayout, u"OOXLayout", rTarget);
437
0
    addDomTreeToModelData(svx::diagram::DomMapFlag::OOXStyle, u"OOXStyle", rTarget);
438
0
    addDomTreeToModelData(svx::diagram::DomMapFlag::OOXColor, u"OOXColor", rTarget);
439
0
}
440
441
using ShapePairs
442
    = std::map<std::shared_ptr<drawingml::Shape>, uno::Reference<drawing::XShape>>;
443
444
void SmartArtDiagram::syncDiagramFontHeights()
445
107
{
446
    // Each name represents a group of shapes, for which the font height should have the same
447
    // scaling.
448
107
    for (const auto& rNameAndPairs : maDiagramFontHeights)
449
7
    {
450
        // Find out the minimum scale within this group.
451
7
        const ShapePairs& rShapePairs = rNameAndPairs.second;
452
7
        double fMinFontScale = 100.0;
453
7
        double fMinSpacingScale = 100.0;
454
7
        for (const auto& rShapePair : rShapePairs)
455
0
        {
456
0
            uno::Reference<beans::XPropertySet> xPropertySet(rShapePair.second, uno::UNO_QUERY);
457
0
            if (xPropertySet.is())
458
0
            {
459
0
                double fFontScale = 0.0;
460
0
                double fSpacingScale = 0.0;
461
0
                xPropertySet->getPropertyValue(u"TextFitToSizeFontScale"_ustr) >>= fFontScale;
462
0
                xPropertySet->getPropertyValue(u"TextFitToSizeSpacingScale"_ustr) >>= fSpacingScale;
463
464
0
                if (fFontScale > 0 && fSpacingScale > 0
465
0
                    && (fFontScale < fMinFontScale || (fFontScale == fMinFontScale && fSpacingScale < fMinSpacingScale)))
466
0
                {
467
0
                    fMinFontScale = fFontScale;
468
0
                    fMinSpacingScale = fSpacingScale;
469
0
                }
470
0
            }
471
0
        }
472
473
        // Set that minimum scale for all members of the group.
474
7
        if (fMinFontScale < 100.0 || fMinSpacingScale < 100.0)
475
0
        {
476
0
            for (const auto& rShapePair : rShapePairs)
477
0
            {
478
0
                uno::Reference<beans::XPropertySet> xPropertySet(rShapePair.second, uno::UNO_QUERY);
479
0
                if (xPropertySet.is())
480
0
                {
481
0
                    xPropertySet->setPropertyValue(u"TextFitToSizeFontScale"_ustr, uno::Any(fMinFontScale));
482
0
                    xPropertySet->setPropertyValue(u"TextFitToSizeSpacingScale"_ustr, uno::Any(fMinSpacingScale));
483
0
                }
484
0
            }
485
0
        }
486
7
    }
487
488
    // no longer needed after processing
489
107
    maDiagramFontHeights.clear();
490
107
}
491
492
static uno::Reference<xml::dom::XDocument> loadFragment(
493
    core::XmlFilterBase& rFilter,
494
    const OUString& rFragmentPath )
495
1.05k
{
496
    // load diagramming fragments into DOM representation, that later
497
    // gets serialized back to SAX events and parsed
498
1.05k
    return rFilter.importFragment( rFragmentPath );
499
1.05k
}
500
501
static uno::Reference<xml::dom::XDocument> loadFragment(
502
    core::XmlFilterBase& rFilter,
503
    const rtl::Reference< core::FragmentHandler >& rxHandler )
504
1.05k
{
505
1.05k
    return loadFragment( rFilter, rxHandler->getFragmentPath() );
506
1.05k
}
507
508
static void importFragment( core::XmlFilterBase& rFilter,
509
                     const uno::Reference<xml::dom::XDocument>& rXDom,
510
                     svx::diagram::DomMapFlag aDomMapFlag,
511
                     const DiagramPtr& pDiagram,
512
                     const rtl::Reference< core::FragmentHandler >& rxHandler )
513
1.05k
{
514
1.05k
    pDiagram->setOOXDomValue(aDomMapFlag, uno::Any(rXDom));
515
516
1.05k
    uno::Reference<xml::sax::XFastSAXSerializable> xSerializer(
517
1.05k
        rXDom, uno::UNO_QUERY_THROW);
518
519
    // now serialize DOM tree into internal data structures
520
1.05k
    rFilter.importFragment( rxHandler, xSerializer );
521
1.05k
}
522
523
namespace
524
{
525
/**
526
 * A fragment handler that just counts the number of <dsp:sp> elements in a
527
 * fragment.
528
 */
529
class DiagramShapeCounter : public oox::core::FragmentHandler2
530
{
531
public:
532
    DiagramShapeCounter(oox::core::XmlFilterBase& rFilter, const OUString& rFragmentPath,
533
                        sal_Int32& nCounter);
534
    oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement,
535
                                                 const AttributeList& rAttribs) override;
536
537
private:
538
    sal_Int32& m_nCounter;
539
};
540
541
DiagramShapeCounter::DiagramShapeCounter(oox::core::XmlFilterBase& rFilter,
542
                                         const OUString& rFragmentPath, sal_Int32& nCounter)
543
286
    : FragmentHandler2(rFilter, rFragmentPath)
544
286
    , m_nCounter(nCounter)
545
286
{
546
286
}
547
548
oox::core::ContextHandlerRef DiagramShapeCounter::onCreateContext(sal_Int32 nElement,
549
                                                                  const AttributeList& /*rAttribs*/)
550
1.13k
{
551
1.13k
    switch (nElement)
552
1.13k
    {
553
131
        case DSP_TOKEN(drawing):
554
262
        case DSP_TOKEN(spTree):
555
262
            return this;
556
643
        case DSP_TOKEN(sp):
557
643
            ++m_nCounter;
558
643
            break;
559
232
        default:
560
232
            break;
561
1.13k
    }
562
563
875
    return nullptr;
564
1.13k
}
565
}
566
567
void loadDiagram( ShapePtr const & pShape,
568
                  core::XmlFilterBase& rFilter,
569
                  const OUString& rDataModelPath,
570
                  const OUString& rLayoutPath,
571
                  const OUString& rQStylePath,
572
                  const OUString& rColorStylePath,
573
                  const oox::core::Relations& rRelations )
574
370
{
575
370
    DiagramPtr pDiagram = std::make_shared<SmartArtDiagram>();
576
577
370
    try
578
370
    {
579
        // set DiagramFontHeights at filter
580
370
        rFilter.setDiagramFontHeights(&pDiagram->getDiagramFontHeights());
581
582
        // data
583
370
        if( !rDataModelPath.isEmpty() )
584
366
        {
585
366
            rtl::Reference< core::FragmentHandler > xRefDataModel(
586
366
                    new DiagramDataFragmentHandler( rFilter, rDataModelPath, pDiagram->getData() ));
587
588
366
            importFragment(rFilter,
589
366
                           loadFragment(rFilter,xRefDataModel),
590
366
                           svx::diagram::DomMapFlag::OOXData,
591
366
                           pDiagram,
592
366
                           xRefDataModel);
593
594
366
            uno::Sequence<uno::Sequence<uno::Any>> aDataImageRelsMap(
595
366
                pShape->resolveRelationshipsOfTypeFromOfficeDoc(
596
366
                    rFilter, xRefDataModel->getFragmentPath(), u"image"));
597
366
            uno::Sequence<uno::Sequence<uno::Any>> aDataHlinkRelsMap(
598
366
                pShape->resolveRelationshipsOfTypeFromOfficeDoc(
599
366
                    rFilter, xRefDataModel->getFragmentPath(), u"hlink"));
600
601
366
            pDiagram->setOOXDomValue(svx::diagram::DomMapFlag::OOXDataImageRels,
602
366
                                     uno::Any(aDataImageRelsMap));
603
366
            pDiagram->setOOXDomValue(svx::diagram::DomMapFlag::OOXDataHlinkRels,
604
366
                                     uno::Any(aDataHlinkRelsMap));
605
606
            // Pass the info to pShape
607
366
            for (auto const& extDrawing : pDiagram->getData()->getExtDrawings())
608
286
            {
609
286
                OUString aFragmentPath = rRelations.getFragmentPathFromRelId(extDrawing);
610
                // Ignore RelIds which don't resolve to a fragment path.
611
286
                if (aFragmentPath.isEmpty())
612
0
                    continue;
613
614
286
                sal_Int32 nCounter = 0;
615
286
                rtl::Reference<core::FragmentHandler> xCounter(
616
286
                    new DiagramShapeCounter(rFilter, aFragmentPath, nCounter));
617
286
                rFilter.importFragment(xCounter);
618
                // Ignore ext drawings which don't actually have any shapes.
619
286
                if (nCounter == 0)
620
155
                    continue;
621
622
131
                pShape->addExtDrawingRelId(extDrawing);
623
131
            }
624
366
        }
625
626
        // Layout: always import to allow editing in the future. It's needed for
627
        // DiagramHelper_oox::reLayout to re-create the oox::Shape(s) for the
628
        // model. Without importing these the diagram model will be not complete.
629
        // NOTE: This also adds the DomMaps to rMainDomMap, so the lines
630
        //     DiagramDomMap& rMainDomMap = pDiagram->getDomMap();
631
        //     rMainDomMap[u"OOXLayout"_ustr] = loadFragment(rFilter,rLayoutPath);
632
        //     rMainDomMap[u"OOXStyle"_ustr] = loadFragment(rFilter,rQStylePath);
633
        // which were used before if !pShape->getExtDrawings().empty() are not
634
        // needed
635
370
        if (!rLayoutPath.isEmpty())
636
313
        {
637
313
            rtl::Reference< core::FragmentHandler > xRefLayout(
638
313
                    new DiagramLayoutFragmentHandler( *pDiagram, rFilter, rLayoutPath, pDiagram->getLayout()));
639
640
313
            importFragment(rFilter,
641
313
                    loadFragment(rFilter,xRefLayout),
642
313
                    svx::diagram::DomMapFlag::OOXLayout,
643
313
                    pDiagram,
644
313
                    xRefLayout);
645
313
        }
646
647
        // Style: same as for Layout (above)
648
370
        if( !rQStylePath.isEmpty() )
649
236
        {
650
236
            rtl::Reference< core::FragmentHandler > xRefQStyle(
651
236
                    new DiagramQStylesFragmentHandler( rFilter, rQStylePath, pDiagram->getStyles() ));
652
653
236
            importFragment(rFilter,
654
236
                    loadFragment(rFilter,xRefQStyle),
655
236
                    svx::diagram::DomMapFlag::OOXStyle,
656
236
                    pDiagram,
657
236
                    xRefQStyle);
658
236
        }
659
660
        // colors
661
370
        if( !rColorStylePath.isEmpty() )
662
141
        {
663
141
            rtl::Reference< core::FragmentHandler > xRefColorStyle(
664
141
                new ColorFragmentHandler( rFilter, rColorStylePath, pDiagram->getColors() ));
665
666
141
            importFragment(rFilter,
667
141
                loadFragment(rFilter,xRefColorStyle),
668
141
                svx::diagram::DomMapFlag::OOXColor,
669
141
                pDiagram,
670
141
                xRefColorStyle);
671
141
        }
672
673
370
        if( !pDiagram->getData()->getExtDrawings().empty() )
674
98
        {
675
98
            const DiagramColorMap::const_iterator aColor = pDiagram->getColors().find(u"node0"_ustr);
676
98
            if( aColor != pDiagram->getColors().end() && !aColor->second.maTextFillColors.empty())
677
0
            {
678
                // TODO(F1): well, actually, there might be *several* color
679
                // definitions in it, after all it's called list.
680
0
                pShape->setFontRefColorForNodes(DiagramColor::getColorByIndex(aColor->second.maTextFillColors, -1));
681
0
            }
682
98
        }
683
684
        // collect data, init maps
685
        // for Diagram import, do - for now - NOT clear all oox::drawingml::Shape
686
370
        pDiagram->getData()->buildDiagramDataModel(false);
687
#ifdef DBG_UTIL
688
        pDiagram->getData()->dump();
689
#endif
690
691
        // diagram loaded. now lump together & attach to shape
692
        // create own geometry if extLst is not present (no geometric
693
        // representation is available in file). This will - if false -
694
        // just create the BackgroundShape.
695
        // NOTE: Need to use pShape->getExtDrawings() here, this is the
696
        // already *filtered* version, see usage of DiagramShapeCounter
697
        // above. Moving to local bool, there might more conditions show
698
        // up
699
370
        static bool bIgnoreExtDrawings(nullptr != std::getenv("DIAGRAM_IGNORE_EXTDRAWINGS"));
700
370
        const bool bCreate(bIgnoreExtDrawings || pShape->getExtDrawings().empty());
701
370
        pDiagram->createShapeHierarchyFromModel(pShape, bCreate);
702
703
        // Get the oox::Theme definition and - if available - move/secure the
704
        // original ImportData directly to the Diagram ModelData
705
370
        std::shared_ptr<::oox::drawingml::Theme> aTheme(rFilter.getCurrentThemePtr());
706
370
        if(aTheme)
707
14
            pDiagram->getData()->setThemeDocument(aTheme->getFragment());
708
709
        // Prepare support for the advanced DiagramHelper using Diagram & Theme data
710
        // This is where pDiagram is moved to where it will stay, else it will get
711
        // cleaned up (what is intended)
712
370
        pShape->prepareDiagramHelper(pDiagram, rFilter.getCurrentThemePtr());
713
370
    }
714
370
    catch (...)
715
370
    {
716
        // unset DiagramFontHeights at filter if there was a failure
717
        // to avoid dangling pointer
718
263
        rFilter.setDiagramFontHeights(nullptr);
719
263
        throw;
720
263
    }
721
370
}
722
723
const oox::drawingml::Color&
724
DiagramColor::getColorByIndex(const std::vector<oox::drawingml::Color>& rColors, sal_Int32 nIndex)
725
488
{
726
488
    assert(!rColors.empty());
727
488
    if (nIndex == -1)
728
0
    {
729
0
        return rColors[rColors.size() - 1];
730
0
    }
731
732
488
    return rColors[nIndex % rColors.size()];
733
488
}
734
}
735
736
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */