Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/oox/source/drawingml/fontworkhelpers.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <drawingml/fontworkhelpers.hxx>
21
22
#include <basegfx/utils/bgradient.hxx>
23
#include <basegfx/utils/gradienttools.hxx>
24
#include <comphelper/propertysequence.hxx>
25
#include <comphelper/propertyvalue.hxx>
26
#include <comphelper/sequence.hxx>
27
#include <comphelper/sequenceashashmap.hxx>
28
#include <docmodel/uno/UnoComplexColor.hxx>
29
#include <docmodel/uno/UnoGradientTools.hxx>
30
#include <drawingml/customshapeproperties.hxx>
31
#include <drawingml/presetgeometrynames.hxx>
32
#include <oox/drawingml/drawingmltypes.hxx>
33
#include <oox/helper/grabbagstack.hxx>
34
#include <sal/log.hxx>
35
#include <svx/msdffdef.hxx>
36
#include <tools/color.hxx>
37
#include <tools/helpers.hxx>
38
39
#include <com/sun/star/awt/Gradient2.hpp>
40
#include <com/sun/star/beans/PropertyAttribute.hpp>
41
#include <com/sun/star/beans/PropertyValue.hpp>
42
#include <com/sun/star/beans/XPropertySet.hpp>
43
#include <com/sun/star/container/XEnumeration.hpp>
44
#include <com/sun/star/container/XEnumerationAccess.hpp>
45
#include <com/sun/star/drawing/DashStyle.hpp>
46
#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
47
#include <com/sun/star/drawing/EnhancedCustomShapeTextPathMode.hpp>
48
#include <com/sun/star/drawing/FillStyle.hpp>
49
#include <com/sun/star/drawing/LineCap.hpp>
50
#include <com/sun/star/drawing/LineDash.hpp>
51
#include <com/sun/star/drawing/LineJoint.hpp>
52
#include <com/sun/star/drawing/LineStyle.hpp>
53
#include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
54
#include <com/sun/star/drawing/XShape.hpp>
55
#include <com/sun/star/text/XText.hpp>
56
#include <com/sun/star/text/XTextRange.hpp>
57
#include <com/sun/star/util/XComplexColor.hpp>
58
59
#include <array>
60
#include <map>
61
62
using namespace com::sun::star;
63
64
void FontworkHelpers::resetPropertyValueInVec(std::vector<beans::PropertyValue>& rPropVec,
65
                                              const OUString& rName)
66
0
{
67
0
    auto aIterator = std::find_if(
68
0
        rPropVec.begin(), rPropVec.end(),
69
0
        [rName](const beans::PropertyValue& rValue) { return rValue.Name == rName; });
70
71
0
    if (aIterator != rPropVec.end())
72
0
        rPropVec.erase(aIterator);
73
0
}
74
75
void FontworkHelpers::putCustomShapeIntoTextPathMode(
76
    const css::uno::Reference<drawing::XShape>& xShape,
77
    const oox::drawingml::CustomShapePropertiesPtr& pCustomShapePropertiesPtr,
78
    const OUString& sMSPresetType, const bool bFromWordArt)
79
0
{
80
0
    if (!xShape.is() || !pCustomShapePropertiesPtr || sMSPresetType == u"textNoShape")
81
0
        return;
82
83
0
    uno::Reference<drawing::XEnhancedCustomShapeDefaulter> xDefaulter(xShape, uno::UNO_QUERY);
84
0
    if (!xDefaulter.is())
85
0
        return;
86
87
0
    uno::Reference<beans::XPropertySet> xSet(xShape, uno::UNO_QUERY);
88
0
    if (!xSet.is())
89
0
        return;
90
91
    // The DrawingML shapes from the presetTextWarpDefinitions are mapped to the definitions
92
    // in svx/../EnhancedCustomShapeGeometry.cxx, which are used for WordArt shapes from
93
    // binary MS Office. Therefore all adjustment values need to be adapted.
94
0
    const OUString sFontworkType = PresetGeometryTypeNames::GetFontworkType(sMSPresetType);
95
0
    auto aAdjGdList = pCustomShapePropertiesPtr->getAdjustmentGuideList();
96
0
    uno::Sequence<drawing::EnhancedCustomShapeAdjustmentValue> aAdjustment(
97
0
        !aAdjGdList.empty() ? aAdjGdList.size() : 1);
98
0
    auto pAdjustment = aAdjustment.getArray();
99
0
    int nIndex = 0;
100
0
    for (const auto& aEntry : aAdjGdList)
101
0
    {
102
0
        double fValue = aEntry.maFormula.toDouble();
103
        // then: polar-handle, else: XY-handle
104
        // There exist only 8 polar-handles at all in presetTextWarp.
105
0
        if ((sFontworkType == "fontwork-arch-down-curve")
106
0
            || (sFontworkType == "fontwork-arch-down-pour" && aEntry.maName == "adj1")
107
0
            || (sFontworkType == "fontwork-arch-up-curve")
108
0
            || (sFontworkType == "fontwork-arch-up-pour" && aEntry.maName == "adj1")
109
0
            || (sFontworkType == "fontwork-open-circle-curve")
110
0
            || (sFontworkType == "fontwork-open-circle-pour" && aEntry.maName == "adj1")
111
0
            || (sFontworkType == "fontwork-circle-curve")
112
0
            || (sFontworkType == "fontwork-circle-pour" && aEntry.maName == "adj1"))
113
0
        {
114
            // DrawingML has 1/60000 degree unit, but WordArt simple degree. Range [0..360[
115
            // or range ]-180..180] doesn't matter, because only cos(angle) and
116
            // sin(angle) are used.
117
0
            fValue = NormAngle360(fValue / 60000.0);
118
0
        }
119
0
        else
120
0
        {
121
            // DrawingML writes adjustment guides as relative value with 100% = 100000,
122
            // but WordArt definitions use values absolute in viewBox 0 0 21600 21600,
123
            // so scale with 21600/100000 = 0.216, with two exceptions:
124
            // X-handles of waves describe increase/decrease relative to horizontal center.
125
            // The gdRefR of pour-shapes is not relative to viewBox but to radius.
126
0
            if ((sFontworkType == "mso-spt158" && aEntry.maName == "adj2") // textDoubleWave1
127
0
                || (sFontworkType == "fontwork-wave" && aEntry.maName == "adj2") // textWave1
128
0
                || (sFontworkType == "mso-spt157" && aEntry.maName == "adj2") // textWave2
129
0
                || (sFontworkType == "mso-spt159" && aEntry.maName == "adj2")) // textWave4
130
0
            {
131
0
                fValue = (fValue + 50000.0) * 0.216;
132
0
            }
133
0
            else if ((sFontworkType == "fontwork-arch-down-pour" && aEntry.maName == "adj2")
134
0
                     || (sFontworkType == "fontwork-arch-up-pour" && aEntry.maName == "adj2")
135
0
                     || (sFontworkType == "fontwork-open-circle-pour" && aEntry.maName == "adj2")
136
0
                     || (sFontworkType == "fontwork-circle-pour" && aEntry.maName == "adj2"))
137
0
            {
138
0
                fValue *= 0.108;
139
0
            }
140
0
            else
141
0
            {
142
0
                fValue *= 0.216;
143
0
            }
144
0
        }
145
146
0
        pAdjustment[nIndex].Value <<= fValue;
147
0
        pAdjustment[nIndex++].State = css::beans::PropertyState_DIRECT_VALUE;
148
0
    }
149
150
    // Set attributes in CustomShapeGeometry
151
0
    xDefaulter->createCustomShapeDefaults(sFontworkType);
152
153
0
    auto aGeomPropSeq = xSet->getPropertyValue(u"CustomShapeGeometry"_ustr)
154
0
                            .get<uno::Sequence<beans::PropertyValue>>();
155
0
    auto aGeomPropVec
156
0
        = comphelper::sequenceToContainer<std::vector<beans::PropertyValue>>(aGeomPropSeq);
157
158
    // Reset old properties
159
0
    static constexpr OUString sTextPath(u"TextPath"_ustr);
160
0
    static constexpr OUString sAdjustmentValues(u"AdjustmentValues"_ustr);
161
0
    static constexpr OUString sPresetTextWarp(u"PresetTextWarp"_ustr);
162
163
0
    resetPropertyValueInVec(aGeomPropVec, u"CoordinateSize"_ustr);
164
0
    resetPropertyValueInVec(aGeomPropVec, u"Equations"_ustr);
165
0
    resetPropertyValueInVec(aGeomPropVec, u"Path"_ustr);
166
0
    resetPropertyValueInVec(aGeomPropVec, sAdjustmentValues);
167
0
    resetPropertyValueInVec(aGeomPropVec, u"ViewBox"_ustr);
168
0
    resetPropertyValueInVec(aGeomPropVec, u"Handles"_ustr);
169
0
    resetPropertyValueInVec(aGeomPropVec, sTextPath);
170
0
    resetPropertyValueInVec(aGeomPropVec, sPresetTextWarp);
171
172
0
    bool bScaleX(false);
173
0
    if (!bFromWordArt
174
0
        && (sMSPresetType == u"textArchDown" || sMSPresetType == u"textArchUp"
175
0
            || sMSPresetType == u"textCircle" || sMSPresetType == u"textButton"))
176
0
    {
177
0
        bScaleX = true;
178
0
    }
179
180
    // Apply new properties
181
0
    uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
182
0
        { { sTextPath, uno::Any(true) },
183
0
          { u"TextPathMode"_ustr, uno::Any(drawing::EnhancedCustomShapeTextPathMode_PATH) },
184
0
          { u"ScaleX"_ustr, uno::Any(bScaleX) } }));
185
0
    aGeomPropVec.push_back(comphelper::makePropertyValue(sTextPath, aPropertyValues));
186
187
0
    aGeomPropVec.push_back(comphelper::makePropertyValue(sPresetTextWarp, sMSPresetType));
188
189
0
    if (!aAdjGdList.empty())
190
0
    {
191
0
        aGeomPropVec.push_back(comphelper::makePropertyValue(sAdjustmentValues, aAdjustment));
192
0
    }
193
194
0
    xSet->setPropertyValue(u"CustomShapeGeometry"_ustr,
195
0
                           uno::Any(comphelper::containerToSequence(aGeomPropVec)));
196
0
}
197
198
OString FontworkHelpers::GetVMLFontworkShapetypeMarkup(const MSO_SPT eShapeType)
199
0
{
200
    // The markup is taken from VML in DOCX documents. Using the generated 'vml-shape-types' file
201
    // does not work.
202
203
0
    static const std::map<MSO_SPT, OString> aTypeToMarkupMap{
204
0
        { mso_sptTextSimple,
205
0
          "<v:shapetype id=\"_x0000_t24\" coordsize=\"21600,21600\" o:spt=\"24\" adj=\"10800\" "
206
0
          "path=\"m@7,l@8,m@5,21600l@6,21600e\"><v:formulas><v:f eqn=\"sum #0 0 10800\"/><v:f "
207
0
          "eqn=\"prod #0 2 1\"/><v:f eqn=\"sum 21600 0 @1\"/><v:f eqn=\"sum 0 0 @2\"/><v:f "
208
0
          "eqn=\"sum 21600 0 @3\"/><v:f eqn=\"if @0 @3 0\"/><v:f eqn=\"if @0 21600 @1\"/><v:f "
209
0
          "eqn=\"if @0 0 @2\"/><v:f eqn=\"if @0 @4 21600\"/><v:f eqn=\"mid @5 @6\"/><v:f eqn=\"mid "
210
0
          "@8 @5\"/><v:f eqn=\"mid @7 @8\"/><v:f eqn=\"mid @6 @7\"/><v:f eqn=\"sum @6 0 "
211
0
          "@5\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
212
0
          "o:connectlocs=\"@9,0;@10,10800;@11,21600;@12,10800\" "
213
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
214
0
          "position=\"#0,bottomRight\" xrange=\"6629,14971\"/></v:handles><o:lock v:ext=\"edit\" "
215
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
216
0
        { mso_sptTextOctagon,
217
0
          "<v:shapetype id=\"_x0000_t25\" coordsize=\"21600,21600\" o:spt=\"25\" adj=\"4800\" "
218
0
          "path=\"m0@0l7200,r7200,l21600@0m0@1l7200,21600r7200,l21600@1e\"><v:formulas><v:f "
219
0
          "eqn=\"val #0\"/><v:f eqn=\"sum 21600 0 @0\"/></v:formulas><v:path textpathok=\"t\" "
220
0
          "o:connecttype=\"rect\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
221
0
          "position=\"topLeft,#0\" yrange=\"3086,10800\"/></v:handles><o:lock v:ext=\"edit\" "
222
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
223
0
        { mso_sptTextHexagon,
224
0
          "<v:shapetype id=\"_x0000_t26\" coordsize=\"21600,21600\" o:spt=\"26\" adj=\"10800\" "
225
0
          "path=\"m0@0l10800,,21600@0m,21600r10800,l21600,21600e\"><v:formulas><v:f eqn=\"val "
226
0
          "#0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum @1 10800 0\"/><v:f eqn=\"sum 21600 0 "
227
0
          "@1\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
228
0
          "o:connectlocs=\"10800,0;5400,@1;10800,21600;16200,@1\" "
229
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
230
0
          "position=\"topLeft,#0\" yrange=\"0,21600\"/></v:handles><o:lock v:ext=\"edit\" "
231
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
232
0
        { mso_sptTextCurve,
233
0
          "<v:shapetype id=\"_x0000_t27\" coordsize=\"21600,21600\" o:spt=\"27\" adj=\"3086\" "
234
0
          "path=\"m,qy10800@0,21600,m0@1qy10800,21600,21600@1e\"><v:formulas><v:f eqn=\"val "
235
0
          "#0\"/><v:f eqn=\"sum 21600 0 #0\"/><v:f eqn=\"prod @1 1 2\"/><v:f eqn=\"sum @2 10800 "
236
0
          "0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
237
0
          "o:connectlocs=\"10800,@0;0,@2;10800,21600;21600,@2\" "
238
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
239
0
          "position=\"center,#0\" yrange=\"0,7200\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
240
0
          "shapetype=\"t\"/></v:shapetype>" },
241
0
        { mso_sptTextWave,
242
0
          "<v:shapetype id=\"_x0000_t28\" coordsize=\"21600,21600\" o:spt=\"28\" "
243
0
          "adj=\"2809,10800\" "
244
0
          "path=\"m@25@0c@26@3@27@1@28@0m@21@4c@22@5@23@6@24@4e\"><v:formulas><v:f eqn=\"val "
245
0
          "#0\"/><v:f eqn=\"prod @0 41 9\"/><v:f eqn=\"prod @0 23 9\"/><v:f eqn=\"sum 0 0 "
246
0
          "@2\"/><v:f eqn=\"sum 21600 0 #0\"/><v:f eqn=\"sum 21600 0 @1\"/><v:f eqn=\"sum 21600 0 "
247
0
          "@3\"/><v:f eqn=\"sum #1 0 10800\"/><v:f eqn=\"sum 21600 0 #1\"/><v:f eqn=\"prod @8 2 "
248
0
          "3\"/><v:f eqn=\"prod @8 4 3\"/><v:f eqn=\"prod @8 2 1\"/><v:f eqn=\"sum 21600 0 "
249
0
          "@9\"/><v:f eqn=\"sum 21600 0 @10\"/><v:f eqn=\"sum 21600 0 @11\"/><v:f eqn=\"prod #1 2 "
250
0
          "3\"/><v:f eqn=\"prod #1 4 3\"/><v:f eqn=\"prod #1 2 1\"/><v:f eqn=\"sum 21600 0 "
251
0
          "@15\"/><v:f eqn=\"sum 21600 0 @16\"/><v:f eqn=\"sum 21600 0 @17\"/><v:f eqn=\"if @7 @14 "
252
0
          "0\"/><v:f eqn=\"if @7 @13 @15\"/><v:f eqn=\"if @7 @12 @16\"/><v:f eqn=\"if @7 21600 "
253
0
          "@17\"/><v:f eqn=\"if @7 0 @20\"/><v:f eqn=\"if @7 @9 @19\"/><v:f eqn=\"if @7 @10 "
254
0
          "@18\"/><v:f eqn=\"if @7 @11 21600\"/><v:f eqn=\"sum @24 0 @21\"/><v:f eqn=\"sum @4 0 "
255
0
          "@0\"/><v:f eqn=\"max @21 @25\"/><v:f eqn=\"min @24 @28\"/><v:f eqn=\"prod @0 2 "
256
0
          "1\"/><v:f eqn=\"sum 21600 0 @33\"/><v:f eqn=\"mid @26 @27\"/><v:f eqn=\"mid @24 "
257
0
          "@28\"/><v:f eqn=\"mid @22 @23\"/><v:f eqn=\"mid @21 @25\"/></v:formulas><v:path "
258
0
          "textpathok=\"t\" o:connecttype=\"custom\" "
259
0
          "o:connectlocs=\"@35,@0;@38,10800;@37,@4;@36,10800\" "
260
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\" "
261
0
          "xscale=\"t\"/><v:handles><v:h position=\"topLeft,#0\" yrange=\"0,4459\"/><v:h "
262
0
          "position=\"#1,bottomRight\" xrange=\"8640,12960\"/></v:handles><o:lock v:ext=\"edit\" "
263
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
264
0
        { mso_sptTextRing,
265
0
          "<v:shapetype id=\"_x0000_t29\" coordsize=\"21600,21600\" o:spt=\"29\" "
266
0
          "adj=\"11796480,5400\" "
267
0
          "path=\"al10800,10800,10800,10800@2@14al10800,10800@0@0@2@14e\"><v:formulas><v:f "
268
0
          "eqn=\"val #1\"/><v:f eqn=\"val #0\"/><v:f eqn=\"sum 0 0 #0\"/><v:f eqn=\"sumangle #0 0 "
269
0
          "180\"/><v:f eqn=\"sumangle #0 0 90\"/><v:f eqn=\"prod @4 2 1\"/><v:f eqn=\"sumangle #0 "
270
0
          "90 0\"/><v:f eqn=\"prod @6 2 1\"/><v:f eqn=\"abs #0\"/><v:f eqn=\"sumangle @8 0 "
271
0
          "90\"/><v:f eqn=\"if @9 @7 @5\"/><v:f eqn=\"sumangle @10 0 360\"/><v:f eqn=\"if @10 @11 "
272
0
          "@10\"/><v:f eqn=\"sumangle @12 0 360\"/><v:f eqn=\"if @12 @13 @12\"/><v:f eqn=\"sum 0 0 "
273
0
          "@14\"/><v:f eqn=\"val 10800\"/><v:f eqn=\"sum 10800 0 #1\"/><v:f eqn=\"prod #1 1 "
274
0
          "2\"/><v:f eqn=\"sum @18 5400 0\"/><v:f eqn=\"cos @19 #0\"/><v:f eqn=\"sin @19 "
275
0
          "#0\"/><v:f eqn=\"sum @20 10800 0\"/><v:f eqn=\"sum @21 10800 0\"/><v:f eqn=\"sum 10800 "
276
0
          "0 @20\"/><v:f eqn=\"sum #1 10800 0\"/><v:f eqn=\"if @9 @17 @25\"/><v:f eqn=\"if @9 0 "
277
0
          "21600\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
278
0
          "o:connectlocs=\"10800,@27;@22,@23;10800,@26;@24,@23\"/><v:textpath on=\"t\" "
279
0
          "fitshape=\"t\"/><v:handles><v:h position=\"#1,#0\" polar=\"10800,10800\" "
280
0
          "radiusrange=\"0,10800\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
281
0
          "shapetype=\"t\"/></v:shapetype>" },
282
0
        { mso_sptTextOnCurve,
283
0
          "<v:shapetype id=\"_x0000_t30\" coordsize=\"21600,21600\" o:spt=\"30\" adj=\"3086\" "
284
0
          "path=\"m,qy10800@0,21600,m0@1qy10800,21600,21600@1e\"><v:formulas><v:f eqn=\"val "
285
0
          "#0\"/><v:f eqn=\"sum 21600 0 #0\"/><v:f eqn=\"prod @1 1 2\"/><v:f eqn=\"sum @2 10800 "
286
0
          "0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
287
0
          "o:connectlocs=\"10800,@0;0,@2;10800,21600;21600,@2\" "
288
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
289
0
          "position=\"center,#0\" yrange=\"0,7200\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
290
0
          "shapetype=\"t\"/></v:shapetype>" },
291
0
        { mso_sptTextOnRing,
292
0
          "<v:shapetype id=\"_x0000_t31\" coordsize=\"21600,21600\" o:spt=\"31\" adj=\"11796480\" "
293
0
          "path=\"al10800,10800,10800,10800@2@14e\"><v:formulas><v:f eqn=\"val #1\"/><v:f "
294
0
          "eqn=\"val #0\"/><v:f eqn=\"sum 0 0 #0\"/><v:f eqn=\"sumangle #0 0 180\"/><v:f "
295
0
          "eqn=\"sumangle #0 0 90\"/><v:f eqn=\"prod @4 2 1\"/><v:f eqn=\"sumangle #0 90 0\"/><v:f "
296
0
          "eqn=\"prod @6 2 1\"/><v:f eqn=\"abs #0\"/><v:f eqn=\"sumangle @8 0 90\"/><v:f eqn=\"if "
297
0
          "@9 @7 @5\"/><v:f eqn=\"sumangle @10 0 360\"/><v:f eqn=\"if @10 @11 @10\"/><v:f "
298
0
          "eqn=\"sumangle @12 0 360\"/><v:f eqn=\"if @12 @13 @12\"/><v:f eqn=\"sum 0 0 @14\"/><v:f "
299
0
          "eqn=\"val 10800\"/><v:f eqn=\"cos 10800 #0\"/><v:f eqn=\"sin 10800 #0\"/><v:f eqn=\"sum "
300
0
          "@17 10800 0\"/><v:f eqn=\"sum @18 10800 0\"/><v:f eqn=\"sum 10800 0 @17\"/><v:f "
301
0
          "eqn=\"if @9 0 21600\"/><v:f eqn=\"sum 10800 0 @18\"/></v:formulas><v:path "
302
0
          "textpathok=\"t\" o:connecttype=\"custom\" "
303
0
          "o:connectlocs=\"10800,@22;@19,@20;@21,@20\"/><v:textpath on=\"t\" "
304
0
          "style=\"v-text-kern:t\" fitpath=\"t\"/><v:handles><v:h position=\"@16,#0\" "
305
0
          "polar=\"10800,10800\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
306
0
          "shapetype=\"t\"/></v:shapetype>" },
307
0
        { mso_sptTextPlainText,
308
0
          "<v:shapetype id=\"_x0000_t136\" coordsize=\"21600,21600\" o:spt=\"136\" adj=\"10800\" "
309
0
          "path=\"m@7,l@8,m@5,21600l@6,21600e\"><v:formulas><v:f eqn=\"sum #0 0 10800\"/><v:f "
310
0
          "eqn=\"prod #0 2 1\"/><v:f eqn=\"sum 21600 0 @1\"/><v:f eqn=\"sum 0 0 @2\"/><v:f "
311
0
          "eqn=\"sum 21600 0 @3\"/><v:f eqn=\"if @0 @3 0\"/><v:f eqn=\"if @0 21600 @1\"/><v:f "
312
0
          "eqn=\"if @0 0 @2\"/><v:f eqn=\"if @0 @4 21600\"/><v:f eqn=\"mid @5 @6\"/><v:f eqn=\"mid "
313
0
          "@8 @5\"/><v:f eqn=\"mid @7 @8\"/><v:f eqn=\"mid @6 @7\"/><v:f eqn=\"sum @6 0 "
314
0
          "@5\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
315
0
          "o:connectlocs=\"@9,0;@10,10800;@11,21600;@12,10800\" "
316
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
317
0
          "position=\"#0,bottomRight\" xrange=\"6629,14971\"/></v:handles><o:lock v:ext=\"edit\" "
318
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
319
0
        { mso_sptTextStop,
320
0
          "<v:shapetype id=\"_x0000_t137\" coordsize=\"21600,21600\" o:spt=\"137\" adj=\"4800\" "
321
0
          "path=\"m0@0l7200,r7200,l21600@0m0@1l7200,21600r7200,l21600@1e\"><v:formulas><v:f "
322
0
          "eqn=\"val #0\"/><v:f eqn=\"sum 21600 0 @0\"/></v:formulas><v:path textpathok=\"t\" "
323
0
          "o:connecttype=\"rect\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
324
0
          "position=\"topLeft,#0\" yrange=\"3086,10800\"/></v:handles><o:lock v:ext=\"edit\" "
325
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
326
0
        { mso_sptTextTriangle,
327
0
          "<v:shapetype id=\"_x0000_t138\" coordsize=\"21600,21600\" o:spt=\"138\" adj=\"10800\" "
328
0
          "path=\"m0@0l10800,,21600@0m,21600r10800,l21600,21600e\"><v:formulas><v:f eqn=\"val "
329
0
          "#0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum @1 10800 0\"/><v:f eqn=\"sum 21600 0 "
330
0
          "@1\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
331
0
          "o:connectlocs=\"10800,0;5400,@1;10800,21600;16200,@1\" "
332
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
333
0
          "position=\"topLeft,#0\" yrange=\"0,21600\"/></v:handles><o:lock v:ext=\"edit\" "
334
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
335
0
        { mso_sptTextTriangleInverted,
336
0
          "<v:shapetype id=\"_x0000_t139\" coordsize=\"21600,21600\" o:spt=\"139\" adj=\"10800\" "
337
0
          "path=\"m,l10800,,21600,m0@0l10800,21600,21600@0e\"><v:formulas><v:f eqn=\"val "
338
0
          "#0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum @1 10800 0\"/><v:f eqn=\"sum 21600 0 "
339
0
          "@1\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
340
0
          "o:connectlocs=\"10800,0;5400,@2;10800,21600;16200,@2\" "
341
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
342
0
          "position=\"topLeft,#0\" yrange=\"0,21600\"/></v:handles><o:lock v:ext=\"edit\" "
343
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
344
0
        { mso_sptTextChevron,
345
0
          "<v:shapetype id=\"_x0000_t140\" coordsize=\"21600,21600\" o:spt=\"140\" adj=\"5400\" "
346
0
          "path=\"m0@0l10800,,21600@0m,21600l10800@1,21600,21600e\"><v:formulas><v:f eqn=\"val "
347
0
          "#0\"/><v:f eqn=\"sum 21600 0 @0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum @2 10800 "
348
0
          "0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
349
0
          "o:connectlocs=\"10800,0;0,@3;10800,@1;21600,@3\" "
350
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
351
0
          "position=\"topLeft,#0\" yrange=\"0,10800\"/></v:handles><o:lock v:ext=\"edit\" "
352
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
353
0
        { mso_sptTextChevronInverted,
354
0
          "<v:shapetype id=\"_x0000_t141\" coordsize=\"21600,21600\" o:spt=\"141\" adj=\"16200\" "
355
0
          "path=\"m,l10800@1,21600,m0@0l10800,21600,21600@0e\"><v:formulas><v:f eqn=\"val "
356
0
          "#0\"/><v:f eqn=\"sum 21600 0 @0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum @2 10800 "
357
0
          "0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
358
0
          "o:connectlocs=\"10800,@1;0,@2;10800,21600;21600,@2\" "
359
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
360
0
          "position=\"topLeft,#0\" yrange=\"10800,21600\"/></v:handles><o:lock v:ext=\"edit\" "
361
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
362
0
        { mso_sptTextRingInside,
363
0
          "<v:shapetype id=\"_x0000_t142\" coordsize=\"21600,21600\" o:spt=\"142\" adj=\"13500\" "
364
0
          "path=\"m0@1qy10800,,21600@1,10800@0,0@1m0@2qy10800@3,21600@2,10800,21600,0@2e\"><v:"
365
0
          "formulas><v:f eqn=\"val #0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum height 0 "
366
0
          "@1\"/><v:f eqn=\"sum height 0 #0\"/><v:f eqn=\"sum @2 0 @1\"/></v:formulas><v:path "
367
0
          "textpathok=\"t\" o:connecttype=\"custom\" "
368
0
          "o:connectlocs=\"10800,0;10800,@0;0,10800;10800,21600;10800,@3;21600,10800\" "
369
0
          "o:connectangles=\"270,270,180,90,90,0\"/><v:textpath on=\"t\" "
370
0
          "fitshape=\"t\"/><v:handles><v:h position=\"center,#0\" "
371
0
          "yrange=\"10800,21600\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
372
0
          "shapetype=\"t\"/></v:shapetype>" },
373
0
        { mso_sptTextRingOutside,
374
0
          "<v:shapetype id=\"_x0000_t143\" coordsize=\"21600,21600\" o:spt=\"143\" adj=\"13500\" "
375
0
          "path=\"m0@1qy10800@0,21600@1,10800,,0@1m0@2qy10800,21600,21600@2,10800@3,0@2e\"><v:"
376
0
          "formulas><v:f eqn=\"val #0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum height 0 "
377
0
          "@1\"/><v:f eqn=\"sum height 0 #0\"/><v:f eqn=\"sum @2 0 @1\"/></v:formulas><v:path "
378
0
          "textpathok=\"t\" o:connecttype=\"custom\" "
379
0
          "o:connectlocs=\"10800,0;10800,@0;0,10800;10800,21600;10800,@3;21600,10800\" "
380
0
          "o:connectangles=\"270,270,180,90,90,0\"/><v:textpath on=\"t\" "
381
0
          "fitshape=\"t\"/><v:handles><v:h position=\"center,#0\" "
382
0
          "yrange=\"10800,21600\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
383
0
          "shapetype=\"t\"/></v:shapetype>" },
384
0
        { mso_sptTextArchUpCurve,
385
0
          "<v:shapetype id=\"_x0000_t144\" coordsize=\"21600,21600\" o:spt=\"144\" "
386
0
          "adj=\"11796480\" path=\"al10800,10800,10800,10800@2@14e\"><v:formulas><v:f eqn=\"val "
387
0
          "#1\"/><v:f eqn=\"val #0\"/><v:f eqn=\"sum 0 0 #0\"/><v:f eqn=\"sumangle #0 0 "
388
0
          "180\"/><v:f eqn=\"sumangle #0 0 90\"/><v:f eqn=\"prod @4 2 1\"/><v:f eqn=\"sumangle #0 "
389
0
          "90 0\"/><v:f eqn=\"prod @6 2 1\"/><v:f eqn=\"abs #0\"/><v:f eqn=\"sumangle @8 0 "
390
0
          "90\"/><v:f eqn=\"if @9 @7 @5\"/><v:f eqn=\"sumangle @10 0 360\"/><v:f eqn=\"if @10 @11 "
391
0
          "@10\"/><v:f eqn=\"sumangle @12 0 360\"/><v:f eqn=\"if @12 @13 @12\"/><v:f eqn=\"sum 0 0 "
392
0
          "@14\"/><v:f eqn=\"val 10800\"/><v:f eqn=\"cos 10800 #0\"/><v:f eqn=\"sin 10800 "
393
0
          "#0\"/><v:f eqn=\"sum @17 10800 0\"/><v:f eqn=\"sum @18 10800 0\"/><v:f eqn=\"sum 10800 "
394
0
          "0 @17\"/><v:f eqn=\"if @9 0 21600\"/><v:f eqn=\"sum 10800 0 @18\"/></v:formulas><v:path "
395
0
          "textpathok=\"t\" o:connecttype=\"custom\" "
396
0
          "o:connectlocs=\"10800,@22;@19,@20;@21,@20\"/><v:textpath on=\"t\" "
397
0
          "style=\"v-text-kern:t\" fitpath=\"t\"/><v:handles><v:h position=\"@16,#0\" "
398
0
          "polar=\"10800,10800\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
399
0
          "shapetype=\"t\"/></v:shapetype>" },
400
0
        { mso_sptTextArchDownCurve,
401
0
          "<v:shapetype id=\"_x0000_t145\" coordsize=\"21600,21600\" o:spt=\"145\" "
402
0
          "path=\"al10800,10800,10800,10800@3@15e\"><v:formulas><v:f eqn=\"val #1\"/><v:f "
403
0
          "eqn=\"val #0\"/><v:f eqn=\"sum 0 0 #0\"/><v:f eqn=\"sumangle #0 0 180\"/><v:f "
404
0
          "eqn=\"sumangle #0 0 90\"/><v:f eqn=\"prod @4 2 1\"/><v:f eqn=\"sumangle #0 90 0\"/><v:f "
405
0
          "eqn=\"prod @6 2 1\"/><v:f eqn=\"abs #0\"/><v:f eqn=\"sumangle @8 0 90\"/><v:f eqn=\"if "
406
0
          "@9 @7 @5\"/><v:f eqn=\"sumangle @10 0 360\"/><v:f eqn=\"if @10 @11 @10\"/><v:f "
407
0
          "eqn=\"sumangle @12 0 360\"/><v:f eqn=\"if @12 @13 @12\"/><v:f eqn=\"sum 0 0 @14\"/><v:f "
408
0
          "eqn=\"val 10800\"/><v:f eqn=\"cos 10800 #0\"/><v:f eqn=\"sin 10800 #0\"/><v:f eqn=\"sum "
409
0
          "@17 10800 0\"/><v:f eqn=\"sum @18 10800 0\"/><v:f eqn=\"sum 10800 0 @17\"/><v:f "
410
0
          "eqn=\"if @9 0 21600\"/><v:f eqn=\"sum 10800 0 @18\"/></v:formulas><v:path "
411
0
          "textpathok=\"t\" o:connecttype=\"custom\" "
412
0
          "o:connectlocs=\"10800,@22;@19,@20;@21,@20\"/><v:textpath on=\"t\" "
413
0
          "style=\"v-text-kern:t\" fitpath=\"t\"/><v:handles><v:h position=\"@16,#0\" "
414
0
          "polar=\"10800,10800\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
415
0
          "shapetype=\"t\"/></v:shapetype>" },
416
0
        { mso_sptTextCircleCurve,
417
0
          "<v:shapetype id=\"_x0000_t146\" coordsize=\"21600,21600\" o:spt=\"146\" "
418
0
          "adj=\"-11730944\" path=\"al10800,10800,10800,10800@2@5e\"><v:formulas><v:f eqn=\"val "
419
0
          "#1\"/><v:f eqn=\"val #0\"/><v:f eqn=\"sum 0 0 #0\"/><v:f eqn=\"prod #0 2 1\"/><v:f "
420
0
          "eqn=\"sumangle @3 0 360\"/><v:f eqn=\"if @3 @4 @3\"/><v:f eqn=\"val 10800\"/><v:f "
421
0
          "eqn=\"cos 10800 #0\"/><v:f eqn=\"sin 10800 #0\"/><v:f eqn=\"sum @7 10800 0\"/><v:f "
422
0
          "eqn=\"sum @8 10800 0\"/><v:f eqn=\"sum 10800 0 @8\"/><v:f eqn=\"if #0 0 "
423
0
          "21600\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
424
0
          "o:connectlocs=\"@12,10800;@9,@10;@9,@11\"/><v:textpath on=\"t\" style=\"v-text-kern:t\" "
425
0
          "fitpath=\"t\"/><v:handles><v:h position=\"@6,#0\" "
426
0
          "polar=\"10800,10800\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
427
0
          "shapetype=\"t\"/></v:shapetype>" },
428
0
        { mso_sptTextButtonCurve,
429
0
          "<v:shapetype id=\"_x0000_t147\" coordsize=\"21600,21600\" o:spt=\"147\" "
430
0
          "adj=\"11796480\" "
431
0
          "path=\"al10800,10800,10800,10800@2@14m,10800r21600,al10800,10800,10800,10800@1@15e\"><v:"
432
0
          "formulas><v:f eqn=\"val #1\"/><v:f eqn=\"val #0\"/><v:f eqn=\"sum 0 0 #0\"/><v:f "
433
0
          "eqn=\"sumangle #0 0 180\"/><v:f eqn=\"sumangle #0 0 90\"/><v:f eqn=\"prod @4 2 "
434
0
          "1\"/><v:f eqn=\"sumangle #0 90 0\"/><v:f eqn=\"prod @6 2 1\"/><v:f eqn=\"abs #0\"/><v:f "
435
0
          "eqn=\"sumangle @8 0 90\"/><v:f eqn=\"if @9 @7 @5\"/><v:f eqn=\"sumangle @10 0 "
436
0
          "360\"/><v:f eqn=\"if @10 @11 @10\"/><v:f eqn=\"sumangle @12 0 360\"/><v:f eqn=\"if @12 "
437
0
          "@13 @12\"/><v:f eqn=\"sum 0 0 @14\"/><v:f eqn=\"val 10800\"/><v:f eqn=\"cos 10800 "
438
0
          "#0\"/><v:f eqn=\"sin 10800 #0\"/><v:f eqn=\"sum @17 10800 0\"/><v:f eqn=\"sum @18 10800 "
439
0
          "0\"/><v:f eqn=\"sum 10800 0 @17\"/><v:f eqn=\"if @9 0 21600\"/><v:f eqn=\"sum 10800 0 "
440
0
          "@18\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
441
0
          "o:connectlocs=\"10800,0;@19,@20;@21,@20;10800,10800;0,10800;21600,10800;10800,21600;@19,"
442
0
          "@23;@21,@23\"/><v:textpath on=\"t\" style=\"v-text-kern:t\" "
443
0
          "fitpath=\"t\"/><v:handles><v:h position=\"@16,#0\" "
444
0
          "polar=\"10800,10800\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
445
0
          "shapetype=\"t\"/></v:shapetype>" },
446
0
        { mso_sptTextArchUpPour,
447
0
          "<v:shapetype id=\"_x0000_t148\" coordsize=\"21600,21600\" o:spt=\"148\" "
448
0
          "adj=\"11796480,5400\" "
449
0
          "path=\"al10800,10800,10800,10800@2@14al10800,10800@0@0@2@14e\"><v:formulas><v:f "
450
0
          "eqn=\"val #1\"/><v:f eqn=\"val #0\"/><v:f eqn=\"sum 0 0 #0\"/><v:f eqn=\"sumangle #0 0 "
451
0
          "180\"/><v:f eqn=\"sumangle #0 0 90\"/><v:f eqn=\"prod @4 2 1\"/><v:f eqn=\"sumangle #0 "
452
0
          "90 0\"/><v:f eqn=\"prod @6 2 1\"/><v:f eqn=\"abs #0\"/><v:f eqn=\"sumangle @8 0 "
453
0
          "90\"/><v:f eqn=\"if @9 @7 @5\"/><v:f eqn=\"sumangle @10 0 360\"/><v:f eqn=\"if @10 @11 "
454
0
          "@10\"/><v:f eqn=\"sumangle @12 0 360\"/><v:f eqn=\"if @12 @13 @12\"/><v:f eqn=\"sum 0 0 "
455
0
          "@14\"/><v:f eqn=\"val 10800\"/><v:f eqn=\"sum 10800 0 #1\"/><v:f eqn=\"prod #1 1 "
456
0
          "2\"/><v:f eqn=\"sum @18 5400 0\"/><v:f eqn=\"cos @19 #0\"/><v:f eqn=\"sin @19 "
457
0
          "#0\"/><v:f eqn=\"sum @20 10800 0\"/><v:f eqn=\"sum @21 10800 0\"/><v:f eqn=\"sum 10800 "
458
0
          "0 @20\"/><v:f eqn=\"sum #1 10800 0\"/><v:f eqn=\"if @9 @17 @25\"/><v:f eqn=\"if @9 0 "
459
0
          "21600\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
460
0
          "o:connectlocs=\"10800,@27;@22,@23;10800,@26;@24,@23\"/><v:textpath on=\"t\" "
461
0
          "fitshape=\"t\"/><v:handles><v:h position=\"#1,#0\" polar=\"10800,10800\" "
462
0
          "radiusrange=\"0,10800\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
463
0
          "shapetype=\"t\"/></v:shapetype>" },
464
0
        { mso_sptTextArchDownPour,
465
0
          "<v:shapetype id=\"_x0000_t149\" coordsize=\"21600,21600\" o:spt=\"149\" adj=\",5400\" "
466
0
          "path=\"al10800,10800@0@0@3@15al10800,10800,10800,10800@3@15e\"><v:formulas><v:f "
467
0
          "eqn=\"val #1\"/><v:f eqn=\"val #0\"/><v:f eqn=\"sum 0 0 #0\"/><v:f eqn=\"sumangle #0 0 "
468
0
          "180\"/><v:f eqn=\"sumangle #0 0 90\"/><v:f eqn=\"prod @4 2 1\"/><v:f eqn=\"sumangle #0 "
469
0
          "90 0\"/><v:f eqn=\"prod @6 2 1\"/><v:f eqn=\"abs #0\"/><v:f eqn=\"sumangle @8 0 "
470
0
          "90\"/><v:f eqn=\"if @9 @7 @5\"/><v:f eqn=\"sumangle @10 0 360\"/><v:f eqn=\"if @10 @11 "
471
0
          "@10\"/><v:f eqn=\"sumangle @12 0 360\"/><v:f eqn=\"if @12 @13 @12\"/><v:f eqn=\"sum 0 0 "
472
0
          "@14\"/><v:f eqn=\"val 10800\"/><v:f eqn=\"sum 10800 0 #1\"/><v:f eqn=\"prod #1 1 "
473
0
          "2\"/><v:f eqn=\"sum @18 5400 0\"/><v:f eqn=\"cos @19 #0\"/><v:f eqn=\"sin @19 "
474
0
          "#0\"/><v:f eqn=\"sum @20 10800 0\"/><v:f eqn=\"sum @21 10800 0\"/><v:f eqn=\"sum 10800 "
475
0
          "0 @20\"/><v:f eqn=\"sum #1 10800 0\"/><v:f eqn=\"if @9 @17 @25\"/><v:f eqn=\"if @9 0 "
476
0
          "21600\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
477
0
          "o:connectlocs=\"10800,@27;@22,@23;10800,@26;@24,@23\"/><v:textpath on=\"t\" "
478
0
          "fitshape=\"t\"/><v:handles><v:h position=\"#1,#0\" polar=\"10800,10800\" "
479
0
          "radiusrange=\"0,10800\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
480
0
          "shapetype=\"t\"/></v:shapetype>" },
481
0
        { mso_sptTextCirclePour,
482
0
          "<v:shapetype id=\"_x0000_t150\" coordsize=\"21600,21600\" o:spt=\"150\" "
483
0
          "adj=\"-11730944,5400\" "
484
0
          "path=\"al10800,10800,10800,10800@2@5al10800,10800@0@0@2@5e\"><v:formulas><v:f eqn=\"val "
485
0
          "#1\"/><v:f eqn=\"val #0\"/><v:f eqn=\"sum 0 0 #0\"/><v:f eqn=\"prod #0 2 1\"/><v:f "
486
0
          "eqn=\"sumangle @3 0 360\"/><v:f eqn=\"if @3 @4 @3\"/><v:f eqn=\"val 10800\"/><v:f "
487
0
          "eqn=\"sum 10800 0 #1\"/><v:f eqn=\"prod #1 1 2\"/><v:f eqn=\"sum @8 5400 0\"/><v:f "
488
0
          "eqn=\"cos @9 #0\"/><v:f eqn=\"sin @9 #0\"/><v:f eqn=\"sum @10 10800 0\"/><v:f eqn=\"sum "
489
0
          "@11 10800 0\"/><v:f eqn=\"sum 10800 0 @11\"/><v:f eqn=\"sum #1 10800 0\"/><v:f eqn=\"if "
490
0
          "#0 @7 @15\"/><v:f eqn=\"if #0 0 21600\"/></v:formulas><v:path textpathok=\"t\" "
491
0
          "o:connecttype=\"custom\" "
492
0
          "o:connectlocs=\"@17,10800;@12,@13;@16,10800;@12,@14\"/><v:textpath on=\"t\" "
493
0
          "fitshape=\"t\"/><v:handles><v:h position=\"#1,#0\" polar=\"10800,10800\" "
494
0
          "radiusrange=\"0,10800\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
495
0
          "shapetype=\"t\"/></v:shapetype>" },
496
0
        { mso_sptTextButtonPour,
497
0
          "<v:shapetype id=\"_x0000_t151\" coordsize=\"21600,21600\" o:spt=\"151\" "
498
0
          "adj=\"11796480,5400\" "
499
0
          "path=\"al10800,10800,10800,10800@2@14al10800,10800@0@0@2@14m@25@17l@26@17m@25@18l@26@"
500
0
          "18al10800,10800@0@0@1@15al10800,10800,10800,10800@1@15e\"><v:formulas><v:f eqn=\"val "
501
0
          "#1\"/><v:f eqn=\"val #0\"/><v:f eqn=\"sum 0 0 #0\"/><v:f eqn=\"sumangle #0 0 "
502
0
          "180\"/><v:f eqn=\"sumangle #0 0 90\"/><v:f eqn=\"prod @4 2 1\"/><v:f eqn=\"sumangle #0 "
503
0
          "90 0\"/><v:f eqn=\"prod @6 2 1\"/><v:f eqn=\"abs #0\"/><v:f eqn=\"sumangle @8 0 "
504
0
          "90\"/><v:f eqn=\"if @9 @7 @5\"/><v:f eqn=\"sumangle @10 0 360\"/><v:f eqn=\"if @10 @11 "
505
0
          "@10\"/><v:f eqn=\"sumangle @12 0 360\"/><v:f eqn=\"if @12 @13 @12\"/><v:f eqn=\"sum 0 0 "
506
0
          "@14\"/><v:f eqn=\"sum #1 10800 0\"/><v:f eqn=\"prod @16 1 2\"/><v:f eqn=\"sum 21600 0 "
507
0
          "@17\"/><v:f eqn=\"sum 10800 0 #1\"/><v:f eqn=\"prod @19 1 2\"/><v:f eqn=\"prod @20 @20 "
508
0
          "1\"/><v:f eqn=\"prod #1 #1 1\"/><v:f eqn=\"sum @22 0 @21\"/><v:f eqn=\"sqrt @23\"/><v:f "
509
0
          "eqn=\"sum 10800 0 @24\"/><v:f eqn=\"sum @24 10800 0\"/><v:f eqn=\"val 10800\"/><v:f "
510
0
          "eqn=\"cos @17 #0\"/><v:f eqn=\"sin @17 #0\"/><v:f eqn=\"sum @28 10800 0\"/><v:f "
511
0
          "eqn=\"sum @29 10800 0\"/><v:f eqn=\"sum 10800 0 @28\"/><v:f eqn=\"sum 10800 0 "
512
0
          "@29\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
513
0
          "o:connectlocs=\"10800,0;@30,@31;10800,@19;@32,@31;10800,@17;@25,10800;10800,@18;@26,"
514
0
          "10800;10800,@16;@30,@33;10800,21600;@32,@33\"/><v:textpath on=\"t\" "
515
0
          "fitshape=\"t\"/><v:handles><v:h position=\"#1,#0\" polar=\"10800,10800\" "
516
0
          "radiusrange=\"4320,10800\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
517
0
          "shapetype=\"t\"/></v:shapetype>" },
518
0
        { mso_sptTextCurveUp,
519
0
          "<v:shapetype id=\"_x0000_t152\" coordsize=\"21600,21600\" o:spt=\"152\" adj=\"9931\" "
520
0
          "path=\"m0@0c7200@2,14400@1,21600,m0@5c7200@6,14400@6,21600@5e\"><v:formulas><v:f "
521
0
          "eqn=\"val #0\"/><v:f eqn=\"prod #0 3 4\"/><v:f eqn=\"prod #0 5 4\"/><v:f eqn=\"prod #0 "
522
0
          "3 8\"/><v:f eqn=\"prod #0 1 8\"/><v:f eqn=\"sum 21600 0 @3\"/><v:f eqn=\"sum @4 21600 "
523
0
          "0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"prod @5 1 2\"/><v:f eqn=\"sum @7 @8 0\"/><v:f "
524
0
          "eqn=\"prod #0 7 8\"/><v:f eqn=\"prod @5 1 3\"/><v:f eqn=\"sum @1 @2 0\"/><v:f eqn=\"sum "
525
0
          "@12 @0 0\"/><v:f eqn=\"prod @13 1 4\"/><v:f eqn=\"sum @11 14400 "
526
0
          "@14\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
527
0
          "o:connectlocs=\"10800,@10;0,@9;10800,21600;21600,@8\" "
528
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\" "
529
0
          "xscale=\"t\"/><v:handles><v:h position=\"topLeft,#0\" "
530
0
          "yrange=\"0,12169\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
531
0
          "shapetype=\"t\"/></v:shapetype>" },
532
0
        { mso_sptTextCurveDown,
533
0
          "<v:shapetype id=\"_x0000_t153\" coordsize=\"21600,21600\" o:spt=\"153\" adj=\"9391\" "
534
0
          "path=\"m,c7200@1,14400@2,21600@0m0@5c7200@6,14400@6,21600@5e\"><v:formulas><v:f "
535
0
          "eqn=\"val #0\"/><v:f eqn=\"prod #0 3 4\"/><v:f eqn=\"prod #0 5 4\"/><v:f eqn=\"prod #0 "
536
0
          "3 8\"/><v:f eqn=\"prod #0 1 8\"/><v:f eqn=\"sum 21600 0 @3\"/><v:f eqn=\"sum @4 21600 "
537
0
          "0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"prod @5 1 2\"/><v:f eqn=\"sum @7 @8 0\"/><v:f "
538
0
          "eqn=\"prod #0 7 8\"/><v:f eqn=\"prod @5 1 3\"/><v:f eqn=\"sum @1 @2 0\"/><v:f eqn=\"sum "
539
0
          "@12 @0 0\"/><v:f eqn=\"prod @13 1 4\"/><v:f eqn=\"sum @11 14400 "
540
0
          "@14\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
541
0
          "o:connectlocs=\"10800,@10;0,@8;10800,21600;21600,@9\" "
542
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\" "
543
0
          "xscale=\"t\"/><v:handles><v:h position=\"bottomRight,#0\" "
544
0
          "yrange=\"0,11368\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
545
0
          "shapetype=\"t\"/></v:shapetype>" },
546
0
        { mso_sptTextCascadeUp,
547
0
          "<v:shapetype id=\"_x0000_t154\" coordsize=\"21600,21600\" o:spt=\"154\" adj=\"9600\" "
548
0
          "path=\"m0@2l21600,m,21600l21600@0e\"><v:formulas><v:f eqn=\"val #0\"/><v:f eqn=\"sum "
549
0
          "21600 0 #0\"/><v:f eqn=\"prod @1 1 4\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"prod @2 1 "
550
0
          "2\"/><v:f eqn=\"sum @3 10800 0\"/><v:f eqn=\"sum @4 10800 0\"/><v:f eqn=\"sum @0 21600 "
551
0
          "@2\"/><v:f eqn=\"prod @7 1 2\"/></v:formulas><v:path textpathok=\"t\" "
552
0
          "o:connecttype=\"custom\" o:connectlocs=\"10800,@4;0,@6;10800,@5;21600,@3\" "
553
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
554
0
          "position=\"bottomRight,#0\" yrange=\"6171,21600\"/></v:handles><o:lock v:ext=\"edit\" "
555
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
556
0
        { mso_sptTextCascadeDown,
557
0
          "<v:shapetype id=\"_x0000_t155\" coordsize=\"21600,21600\" o:spt=\"155\" adj=\"9600\" "
558
0
          "path=\"m,l21600@2m0@0l21600,21600e\"><v:formulas><v:f eqn=\"val #0\"/><v:f eqn=\"sum "
559
0
          "21600 0 #0\"/><v:f eqn=\"prod @1 1 4\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"prod @2 1 "
560
0
          "2\"/><v:f eqn=\"sum @3 10800 0\"/><v:f eqn=\"sum @4 10800 0\"/><v:f eqn=\"sum @0 21600 "
561
0
          "@2\"/><v:f eqn=\"prod @7 1 2\"/></v:formulas><v:path textpathok=\"t\" "
562
0
          "o:connecttype=\"custom\" o:connectlocs=\"10800,@4;0,@3;10800,@5;21600,@6\" "
563
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
564
0
          "position=\"topLeft,#0\" yrange=\"6171,21600\"/></v:handles><o:lock v:ext=\"edit\" "
565
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
566
0
        { mso_sptTextWave1,
567
0
          "<v:shapetype id=\"_x0000_t156\" coordsize=\"21600,21600\" o:spt=\"156\" "
568
0
          "adj=\"2809,10800\" "
569
0
          "path=\"m@25@0c@26@3@27@1@28@0m@21@4c@22@5@23@6@24@4e\"><v:formulas><v:f eqn=\"val "
570
0
          "#0\"/><v:f eqn=\"prod @0 41 9\"/><v:f eqn=\"prod @0 23 9\"/><v:f eqn=\"sum 0 0 "
571
0
          "@2\"/><v:f eqn=\"sum 21600 0 #0\"/><v:f eqn=\"sum 21600 0 @1\"/><v:f eqn=\"sum 21600 0 "
572
0
          "@3\"/><v:f eqn=\"sum #1 0 10800\"/><v:f eqn=\"sum 21600 0 #1\"/><v:f eqn=\"prod @8 2 "
573
0
          "3\"/><v:f eqn=\"prod @8 4 3\"/><v:f eqn=\"prod @8 2 1\"/><v:f eqn=\"sum 21600 0 "
574
0
          "@9\"/><v:f eqn=\"sum 21600 0 @10\"/><v:f eqn=\"sum 21600 0 @11\"/><v:f eqn=\"prod #1 2 "
575
0
          "3\"/><v:f eqn=\"prod #1 4 3\"/><v:f eqn=\"prod #1 2 1\"/><v:f eqn=\"sum 21600 0 "
576
0
          "@15\"/><v:f eqn=\"sum 21600 0 @16\"/><v:f eqn=\"sum 21600 0 @17\"/><v:f eqn=\"if @7 @14 "
577
0
          "0\"/><v:f eqn=\"if @7 @13 @15\"/><v:f eqn=\"if @7 @12 @16\"/><v:f eqn=\"if @7 21600 "
578
0
          "@17\"/><v:f eqn=\"if @7 0 @20\"/><v:f eqn=\"if @7 @9 @19\"/><v:f eqn=\"if @7 @10 "
579
0
          "@18\"/><v:f eqn=\"if @7 @11 21600\"/><v:f eqn=\"sum @24 0 @21\"/><v:f eqn=\"sum @4 0 "
580
0
          "@0\"/><v:f eqn=\"max @21 @25\"/><v:f eqn=\"min @24 @28\"/><v:f eqn=\"prod @0 2 "
581
0
          "1\"/><v:f eqn=\"sum 21600 0 @33\"/><v:f eqn=\"mid @26 @27\"/><v:f eqn=\"mid @24 "
582
0
          "@28\"/><v:f eqn=\"mid @22 @23\"/><v:f eqn=\"mid @21 @25\"/></v:formulas><v:path "
583
0
          "textpathok=\"t\" o:connecttype=\"custom\" "
584
0
          "o:connectlocs=\"@35,@0;@38,10800;@37,@4;@36,10800\" "
585
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\" "
586
0
          "xscale=\"t\"/><v:handles><v:h position=\"topLeft,#0\" yrange=\"0,4459\"/><v:h "
587
0
          "position=\"#1,bottomRight\" xrange=\"8640,12960\"/></v:handles><o:lock v:ext=\"edit\" "
588
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
589
0
        { mso_sptTextWave2,
590
0
          "<v:shapetype id=\"_x0000_t157\" coordsize=\"21600,21600\" o:spt=\"157\" "
591
0
          "adj=\"2809,10800\" "
592
0
          "path=\"m@25@0c@26@1@27@3@28@0m@21@4c@22@6@23@5@24@4e\"><v:formulas><v:f eqn=\"val "
593
0
          "#0\"/><v:f eqn=\"prod @0 41 9\"/><v:f eqn=\"prod @0 23 9\"/><v:f eqn=\"sum 0 0 "
594
0
          "@2\"/><v:f eqn=\"sum 21600 0 #0\"/><v:f eqn=\"sum 21600 0 @1\"/><v:f eqn=\"sum 21600 0 "
595
0
          "@3\"/><v:f eqn=\"sum #1 0 10800\"/><v:f eqn=\"sum 21600 0 #1\"/><v:f eqn=\"prod @8 2 "
596
0
          "3\"/><v:f eqn=\"prod @8 4 3\"/><v:f eqn=\"prod @8 2 1\"/><v:f eqn=\"sum 21600 0 "
597
0
          "@9\"/><v:f eqn=\"sum 21600 0 @10\"/><v:f eqn=\"sum 21600 0 @11\"/><v:f eqn=\"prod #1 2 "
598
0
          "3\"/><v:f eqn=\"prod #1 4 3\"/><v:f eqn=\"prod #1 2 1\"/><v:f eqn=\"sum 21600 0 "
599
0
          "@15\"/><v:f eqn=\"sum 21600 0 @16\"/><v:f eqn=\"sum 21600 0 @17\"/><v:f eqn=\"if @7 @14 "
600
0
          "0\"/><v:f eqn=\"if @7 @13 @15\"/><v:f eqn=\"if @7 @12 @16\"/><v:f eqn=\"if @7 21600 "
601
0
          "@17\"/><v:f eqn=\"if @7 0 @20\"/><v:f eqn=\"if @7 @9 @19\"/><v:f eqn=\"if @7 @10 "
602
0
          "@18\"/><v:f eqn=\"if @7 @11 21600\"/><v:f eqn=\"sum @24 0 @21\"/><v:f eqn=\"sum @4 0 "
603
0
          "@0\"/><v:f eqn=\"max @21 @25\"/><v:f eqn=\"min @24 @28\"/><v:f eqn=\"prod @0 2 "
604
0
          "1\"/><v:f eqn=\"sum 21600 0 @33\"/><v:f eqn=\"mid @26 @27\"/><v:f eqn=\"mid @24 "
605
0
          "@28\"/><v:f eqn=\"mid @22 @23\"/><v:f eqn=\"mid @21 @25\"/></v:formulas><v:path "
606
0
          "textpathok=\"t\" o:connecttype=\"custom\" "
607
0
          "o:connectlocs=\"@35,@0;@38,10800;@37,@4;@36,10800\" "
608
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\" "
609
0
          "xscale=\"t\"/><v:handles><v:h position=\"topLeft,#0\" yrange=\"0,4459\"/><v:h "
610
0
          "position=\"#1,bottomRight\" xrange=\"8640,12960\"/></v:handles><o:lock v:ext=\"edit\" "
611
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
612
0
        { mso_sptTextWave3,
613
0
          "<v:shapetype id=\"_x0000_t158\" coordsize=\"21600,21600\" o:spt=\"158\" "
614
0
          "adj=\"1404,10800\" "
615
0
          "path=\"m@37@0c@38@3@39@1@40@0@41@3@42@1@43@0m@30@4c@31@5@32@6@33@4@34@5@35@6@36@4e\"><v:"
616
0
          "formulas><v:f eqn=\"val #0\"/><v:f eqn=\"prod @0 41 9\"/><v:f eqn=\"prod @0 23 "
617
0
          "9\"/><v:f eqn=\"sum 0 0 @2\"/><v:f eqn=\"sum 21600 0 #0\"/><v:f eqn=\"sum 21600 0 "
618
0
          "@1\"/><v:f eqn=\"sum 21600 0 @3\"/><v:f eqn=\"sum #1 0 10800\"/><v:f eqn=\"sum 21600 0 "
619
0
          "#1\"/><v:f eqn=\"prod @8 1 3\"/><v:f eqn=\"prod @8 2 3\"/><v:f eqn=\"prod @8 4 "
620
0
          "3\"/><v:f eqn=\"prod @8 5 3\"/><v:f eqn=\"prod @8 2 1\"/><v:f eqn=\"sum 21600 0 "
621
0
          "@9\"/><v:f eqn=\"sum 21600 0 @10\"/><v:f eqn=\"sum 21600 0 @8\"/><v:f eqn=\"sum 21600 0 "
622
0
          "@11\"/><v:f eqn=\"sum 21600 0 @12\"/><v:f eqn=\"sum 21600 0 @13\"/><v:f eqn=\"prod #1 1 "
623
0
          "3\"/><v:f eqn=\"prod #1 2 3\"/><v:f eqn=\"prod #1 4 3\"/><v:f eqn=\"prod #1 5 3\"/><v:f "
624
0
          "eqn=\"prod #1 2 1\"/><v:f eqn=\"sum 21600 0 @20\"/><v:f eqn=\"sum 21600 0 @21\"/><v:f "
625
0
          "eqn=\"sum 21600 0 @22\"/><v:f eqn=\"sum 21600 0 @23\"/><v:f eqn=\"sum 21600 0 "
626
0
          "@24\"/><v:f eqn=\"if @7 @19 0\"/><v:f eqn=\"if @7 @18 @20\"/><v:f eqn=\"if @7 @17 "
627
0
          "@21\"/><v:f eqn=\"if @7 @16 #1\"/><v:f eqn=\"if @7 @15 @22\"/><v:f eqn=\"if @7 @14 "
628
0
          "@23\"/><v:f eqn=\"if @7 21600 @24\"/><v:f eqn=\"if @7 0 @29\"/><v:f eqn=\"if @7 @9 "
629
0
          "@28\"/><v:f eqn=\"if @7 @10 @27\"/><v:f eqn=\"if @7 @8 @8\"/><v:f eqn=\"if @7 @11 "
630
0
          "@26\"/><v:f eqn=\"if @7 @12 @25\"/><v:f eqn=\"if @7 @13 21600\"/><v:f eqn=\"sum @36 0 "
631
0
          "@30\"/><v:f eqn=\"sum @4 0 @0\"/><v:f eqn=\"max @30 @37\"/><v:f eqn=\"min @36 "
632
0
          "@43\"/><v:f eqn=\"prod @0 2 1\"/><v:f eqn=\"sum 21600 0 @48\"/><v:f eqn=\"mid @36 "
633
0
          "@43\"/><v:f eqn=\"mid @30 @37\"/></v:formulas><v:path textpathok=\"t\" "
634
0
          "o:connecttype=\"custom\" o:connectlocs=\"@40,@0;@51,10800;@33,@4;@50,10800\" "
635
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\" "
636
0
          "xscale=\"t\"/><v:handles><v:h position=\"topLeft,#0\" yrange=\"0,2229\"/><v:h "
637
0
          "position=\"#1,bottomRight\" xrange=\"8640,12960\"/></v:handles><o:lock v:ext=\"edit\" "
638
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
639
0
        { mso_sptTextWave4,
640
0
          "<v:shapetype id=\"_x0000_t159\" coordsize=\"21600,21600\" o:spt=\"159\" "
641
0
          "adj=\"1404,10800\" "
642
0
          "path=\"m@37@0c@38@1@39@3@40@0@41@1@42@3@43@0m@30@4c@31@6@32@5@33@4@34@6@35@5@36@4e\"><v:"
643
0
          "formulas><v:f eqn=\"val #0\"/><v:f eqn=\"prod @0 41 9\"/><v:f eqn=\"prod @0 23 "
644
0
          "9\"/><v:f eqn=\"sum 0 0 @2\"/><v:f eqn=\"sum 21600 0 #0\"/><v:f eqn=\"sum 21600 0 "
645
0
          "@1\"/><v:f eqn=\"sum 21600 0 @3\"/><v:f eqn=\"sum #1 0 10800\"/><v:f eqn=\"sum 21600 0 "
646
0
          "#1\"/><v:f eqn=\"prod @8 1 3\"/><v:f eqn=\"prod @8 2 3\"/><v:f eqn=\"prod @8 4 "
647
0
          "3\"/><v:f eqn=\"prod @8 5 3\"/><v:f eqn=\"prod @8 2 1\"/><v:f eqn=\"sum 21600 0 "
648
0
          "@9\"/><v:f eqn=\"sum 21600 0 @10\"/><v:f eqn=\"sum 21600 0 @8\"/><v:f eqn=\"sum 21600 0 "
649
0
          "@11\"/><v:f eqn=\"sum 21600 0 @12\"/><v:f eqn=\"sum 21600 0 @13\"/><v:f eqn=\"prod #1 1 "
650
0
          "3\"/><v:f eqn=\"prod #1 2 3\"/><v:f eqn=\"prod #1 4 3\"/><v:f eqn=\"prod #1 5 3\"/><v:f "
651
0
          "eqn=\"prod #1 2 1\"/><v:f eqn=\"sum 21600 0 @20\"/><v:f eqn=\"sum 21600 0 @21\"/><v:f "
652
0
          "eqn=\"sum 21600 0 @22\"/><v:f eqn=\"sum 21600 0 @23\"/><v:f eqn=\"sum 21600 0 "
653
0
          "@24\"/><v:f eqn=\"if @7 @19 0\"/><v:f eqn=\"if @7 @18 @20\"/><v:f eqn=\"if @7 @17 "
654
0
          "@21\"/><v:f eqn=\"if @7 @16 #1\"/><v:f eqn=\"if @7 @15 @22\"/><v:f eqn=\"if @7 @14 "
655
0
          "@23\"/><v:f eqn=\"if @7 21600 @24\"/><v:f eqn=\"if @7 0 @29\"/><v:f eqn=\"if @7 @9 "
656
0
          "@28\"/><v:f eqn=\"if @7 @10 @27\"/><v:f eqn=\"if @7 @8 @8\"/><v:f eqn=\"if @7 @11 "
657
0
          "@26\"/><v:f eqn=\"if @7 @12 @25\"/><v:f eqn=\"if @7 @13 21600\"/><v:f eqn=\"sum @36 0 "
658
0
          "@30\"/><v:f eqn=\"sum @4 0 @0\"/><v:f eqn=\"max @30 @37\"/><v:f eqn=\"min @36 "
659
0
          "@43\"/><v:f eqn=\"prod @0 2 1\"/><v:f eqn=\"sum 21600 0 @48\"/><v:f eqn=\"mid @36 "
660
0
          "@43\"/><v:f eqn=\"mid @30 @37\"/></v:formulas><v:path textpathok=\"t\" "
661
0
          "o:connecttype=\"custom\" o:connectlocs=\"@40,@0;@51,10800;@33,@4;@50,10800\" "
662
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\" "
663
0
          "xscale=\"t\"/><v:handles><v:h position=\"topLeft,#0\" yrange=\"0,2229\"/><v:h "
664
0
          "position=\"#1,bottomRight\" xrange=\"8640,12960\"/></v:handles><o:lock v:ext=\"edit\" "
665
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
666
0
        { mso_sptTextInflate,
667
0
          "<v:shapetype id=\"_x0000_t160\" coordsize=\"21600,21600\" o:spt=\"160\" adj=\"2945\" "
668
0
          "path=\"m0@0c7200@2,14400@2,21600@0m0@3c7200@4,14400@4,21600@3e\"><v:formulas><v:f "
669
0
          "eqn=\"val #0\"/><v:f eqn=\"prod #0 1 3\"/><v:f eqn=\"sum 0 0 @1\"/><v:f eqn=\"sum 21600 "
670
0
          "0 #0\"/><v:f eqn=\"sum 21600 0 @2\"/><v:f eqn=\"prod #0 2 3\"/><v:f eqn=\"sum 21600 0 "
671
0
          "@5\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"rect\"/><v:textpath "
672
0
          "on=\"t\" fitshape=\"t\" xscale=\"t\"/><v:handles><v:h position=\"topLeft,#0\" "
673
0
          "yrange=\"0,4629\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
674
0
          "shapetype=\"t\"/></v:shapetype>" },
675
0
        { mso_sptTextDeflate,
676
0
          "<v:shapetype id=\"_x0000_t161\" coordsize=\"21600,21600\" o:spt=\"161\" adj=\"4050\" "
677
0
          "path=\"m,c7200@0,14400@0,21600,m,21600c7200@1,14400@1,21600,21600e\"><v:formulas><v:f "
678
0
          "eqn=\"prod #0 4 3\"/><v:f eqn=\"sum 21600 0 @0\"/><v:f eqn=\"val #0\"/><v:f eqn=\"sum "
679
0
          "21600 0 #0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
680
0
          "o:connectlocs=\"10800,@2;0,10800;10800,@3;21600,10800\" "
681
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\" "
682
0
          "xscale=\"t\"/><v:handles><v:h position=\"center,#0\" "
683
0
          "yrange=\"0,8100\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
684
0
          "shapetype=\"t\"/></v:shapetype>" },
685
0
        { mso_sptTextInflateBottom,
686
0
          "<v:shapetype id=\"_x0000_t162\" coordsize=\"21600,21600\" o:spt=\"162\" adj=\"14706\" "
687
0
          "path=\"m,l21600,m0@0c7200@2,14400@2,21600@0e\"><v:formulas><v:f eqn=\"val #0\"/><v:f "
688
0
          "eqn=\"prod #0 1 3\"/><v:f eqn=\"sum 28800 0 @1\"/><v:f eqn=\"prod #0 1 2\"/><v:f "
689
0
          "eqn=\"sum @1 7200 0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
690
0
          "o:connectlocs=\"10800,0;0,@3;10800,21600;21600,@3\" "
691
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\" "
692
0
          "xscale=\"t\"/><v:handles><v:h position=\"topLeft,#0\" "
693
0
          "yrange=\"11148,21600\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
694
0
          "shapetype=\"t\"/></v:shapetype>" },
695
0
        { mso_sptTextDeflateBottom,
696
0
          "<v:shapetype id=\"_x0000_t163\" coordsize=\"21600,21600\" o:spt=\"163\" adj=\"11475\" "
697
0
          "path=\"m,l21600,m,21600c7200@1,14400@1,21600,21600e\"><v:formulas><v:f eqn=\"prod #0 4 "
698
0
          "3\"/><v:f eqn=\"sum @0 0 7200\"/><v:f eqn=\"val #0\"/><v:f eqn=\"prod #0 2 3\"/><v:f "
699
0
          "eqn=\"sum @3 7200 0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
700
0
          "o:connectlocs=\"10800,0;0,10800;10800,@2;21600,10800\" "
701
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\" "
702
0
          "xscale=\"t\"/><v:handles><v:h position=\"center,#0\" "
703
0
          "yrange=\"1350,21600\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
704
0
          "shapetype=\"t\"/></v:shapetype>" },
705
0
        { mso_sptTextInflateTop,
706
0
          "<v:shapetype id=\"_x0000_t164\" coordsize=\"21600,21600\" o:spt=\"164\" adj=\"6894\" "
707
0
          "path=\"m0@0c7200@2,14400@2,21600@0m,21600r21600,e\"><v:formulas><v:f eqn=\"val "
708
0
          "#0\"/><v:f eqn=\"prod #0 1 3\"/><v:f eqn=\"sum 0 0 @1\"/><v:f eqn=\"prod #0 1 2\"/><v:f "
709
0
          "eqn=\"sum @3 10800 0\"/><v:f eqn=\"sum 21600 0 @1\"/></v:formulas><v:path "
710
0
          "textpathok=\"t\" o:connecttype=\"custom\" "
711
0
          "o:connectlocs=\"10800,0;0,@4;10800,21600;21600,@4\" "
712
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\" "
713
0
          "xscale=\"t\"/><v:handles><v:h position=\"topLeft,#0\" "
714
0
          "yrange=\"0,10452\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
715
0
          "shapetype=\"t\"/></v:shapetype>" },
716
0
        { mso_sptTextDeflateTop,
717
0
          "<v:shapetype id=\"_x0000_t165\" coordsize=\"21600,21600\" o:spt=\"165\" adj=\"10125\" "
718
0
          "path=\"m,c7200@0,14400@0,21600,m,21600r21600,e\"><v:formulas><v:f eqn=\"prod #0 4 "
719
0
          "3\"/><v:f eqn=\"val #0\"/><v:f eqn=\"prod #0 2 3\"/><v:f eqn=\"sum 21600 0 "
720
0
          "@2\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
721
0
          "o:connectlocs=\"10800,@1;0,10800;10800,21600;21600,10800\" "
722
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\" "
723
0
          "xscale=\"t\"/><v:handles><v:h position=\"center,#0\" "
724
0
          "yrange=\"0,20250\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
725
0
          "shapetype=\"t\"/></v:shapetype>" },
726
0
        { mso_sptTextDeflateInflate,
727
0
          "<v:shapetype id=\"_x0000_t166\" coordsize=\"21600,21600\" o:spt=\"166\" adj=\"6054\" "
728
0
          "path=\"m,l21600,m,10125c7200@1,14400@1,21600,10125m,11475c7200@2,14400@2,21600,11475m,"
729
0
          "21600r21600,e\"><v:formulas><v:f eqn=\"prod #0 4 3\"/><v:f eqn=\"sum @0 0 4275\"/><v:f "
730
0
          "eqn=\"sum @0 0 2925\"/></v:formulas><v:path textpathok=\"t\" "
731
0
          "o:connecttype=\"rect\"/><v:textpath on=\"t\" fitshape=\"t\" "
732
0
          "xscale=\"t\"/><v:handles><v:h position=\"center,#0\" "
733
0
          "yrange=\"1308,20292\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
734
0
          "shapetype=\"t\"/></v:shapetype>" },
735
0
        { mso_sptTextDeflateInflateDeflate,
736
0
          "<v:shapetype id=\"_x0000_t167\" coordsize=\"21600,21600\" o:spt=\"167\" adj=\"6054\" "
737
0
          "path=\"m,l21600,m,6609c7200@1,14400@1,21600,6609m,7491c7200@2,14400@2,21600,7491m,"
738
0
          "14109c7200@4,14400@4,21600,14109m,14991c7200@3,14400@3,21600,14991m,21600r21600,e\"><v:"
739
0
          "formulas><v:f eqn=\"prod #0 4 3\"/><v:f eqn=\"sum @0 0 2791\"/><v:f eqn=\"sum @0 0 "
740
0
          "1909\"/><v:f eqn=\"sum 21600 0 @1\"/><v:f eqn=\"sum 21600 0 @2\"/></v:formulas><v:path "
741
0
          "textpathok=\"t\" o:connecttype=\"rect\"/><v:textpath on=\"t\" fitshape=\"t\" "
742
0
          "xscale=\"t\"/><v:handles><v:h position=\"center,#0\" "
743
0
          "yrange=\"854,9525\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
744
0
          "shapetype=\"t\"/></v:shapetype>" },
745
0
        { mso_sptTextFadeRight,
746
0
          "<v:shapetype id=\"_x0000_t168\" coordsize=\"21600,21600\" o:spt=\"168\" adj=\"7200\" "
747
0
          "path=\"m,l21600@0m,21600l21600@1e\"><v:formulas><v:f eqn=\"val #0\"/><v:f eqn=\"sum "
748
0
          "21600 0 @0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum 21600 0 @2\"/><v:f eqn=\"sum @1 "
749
0
          "21600 @0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
750
0
          "o:connectlocs=\"10800,@2;0,10800;10800,@3;21600,10800\" "
751
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
752
0
          "position=\"bottomRight,#0\" yrange=\"0,10800\"/></v:handles><o:lock v:ext=\"edit\" "
753
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
754
0
        { mso_sptTextFadeLeft,
755
0
          "<v:shapetype id=\"_x0000_t169\" coordsize=\"21600,21600\" o:spt=\"169\" adj=\"7200\" "
756
0
          "path=\"m0@0l21600,m0@1l21600,21600e\"><v:formulas><v:f eqn=\"val #0\"/><v:f eqn=\"sum "
757
0
          "21600 0 @0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum 21600 0 @2\"/><v:f eqn=\"sum @1 "
758
0
          "21600 @0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
759
0
          "o:connectlocs=\"10800,@2;0,10800;10800,@3;21600,10800\" "
760
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
761
0
          "position=\"topLeft,#0\" yrange=\"0,10800\"/></v:handles><o:lock v:ext=\"edit\" "
762
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
763
0
        { mso_sptTextFadeUp,
764
0
          "<v:shapetype id=\"_x0000_t170\" coordsize=\"21600,21600\" o:spt=\"170\" adj=\"7200\" "
765
0
          "path=\"m@0,l@1,m,21600r21600,e\"><v:formulas><v:f eqn=\"val #0\"/><v:f eqn=\"sum 21600 "
766
0
          "0 @0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum 21600 0 @2\"/><v:f eqn=\"sum @1 21600 "
767
0
          "@0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
768
0
          "o:connectlocs=\"10800,0;@2,10800;10800,21600;@3,10800\" "
769
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
770
0
          "position=\"#0,topLeft\" xrange=\"0,10792\"/></v:handles><o:lock v:ext=\"edit\" "
771
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
772
0
        { mso_sptTextFadeDown,
773
0
          "<v:shapetype id=\"_x0000_t171\" coordsize=\"21600,21600\" o:spt=\"171\" adj=\"7200\" "
774
0
          "path=\"m,l21600,m@0,21600l@1,21600e\"><v:formulas><v:f eqn=\"val #0\"/><v:f eqn=\"sum "
775
0
          "21600 0 @0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum 21600 0 @2\"/><v:f eqn=\"sum @1 "
776
0
          "21600 @0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
777
0
          "o:connectlocs=\"10800,0;@2,10800;10800,21600;@3,10800\" "
778
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
779
0
          "position=\"#0,bottomRight\" xrange=\"0,10792\"/></v:handles><o:lock v:ext=\"edit\" "
780
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
781
0
        { mso_sptTextSlantUp,
782
0
          "<v:shapetype id=\"_x0000_t172\" coordsize=\"21600,21600\" o:spt=\"172\" adj=\"12000\" "
783
0
          "path=\"m0@0l21600,m,21600l21600@1e\"><v:formulas><v:f eqn=\"val #0\"/><v:f eqn=\"sum "
784
0
          "21600 0 @0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum @2 10800 0\"/><v:f eqn=\"prod @1 "
785
0
          "1 2\"/><v:f eqn=\"sum @4 10800 0\"/></v:formulas><v:path textpathok=\"t\" "
786
0
          "o:connecttype=\"custom\" o:connectlocs=\"10800,@2;0,@3;10800,@5;21600,@4\" "
787
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
788
0
          "position=\"topLeft,#0\" yrange=\"0,15429\"/></v:handles><o:lock v:ext=\"edit\" "
789
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
790
0
        { mso_sptTextSlantDown,
791
0
          "<v:shapetype id=\"_x0000_t173\" coordsize=\"21600,21600\" o:spt=\"173\" adj=\"9600\" "
792
0
          "path=\"m,l21600@1m0@0l21600,21600e\"><v:formulas><v:f eqn=\"val #0\"/><v:f eqn=\"sum "
793
0
          "21600 0 @0\"/><v:f eqn=\"prod #0 1 2\"/><v:f eqn=\"sum @2 10800 0\"/><v:f eqn=\"prod @1 "
794
0
          "1 2\"/><v:f eqn=\"sum @4 10800 0\"/></v:formulas><v:path textpathok=\"t\" "
795
0
          "o:connecttype=\"custom\" o:connectlocs=\"10800,@4;0,@2;10800,@3;21600,@5\" "
796
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
797
0
          "position=\"topLeft,#0\" yrange=\"6171,21600\"/></v:handles><o:lock v:ext=\"edit\" "
798
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
799
0
        { mso_sptTextCanUp,
800
0
          "<v:shapetype id=\"_x0000_t174\" coordsize=\"21600,21600\" o:spt=\"174\" adj=\"18514\" "
801
0
          "path=\"m0@1qy10800,,21600@1m,21600qy10800@0,21600,21600e\"><v:formulas><v:f eqn=\"val "
802
0
          "#0\"/><v:f eqn=\"sum 21600 0 #0\"/><v:f eqn=\"prod @1 1 2\"/><v:f eqn=\"sum @2 10800 "
803
0
          "0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
804
0
          "o:connectlocs=\"10800,0;0,@3;10800,@0;21600,@3\" "
805
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
806
0
          "position=\"center,#0\" yrange=\"14400,21600\"/></v:handles><o:lock v:ext=\"edit\" "
807
0
          "text=\"t\" shapetype=\"t\"/></v:shapetype>" },
808
0
        { mso_sptTextCanDown,
809
0
          "<v:shapetype id=\"_x0000_t175\" coordsize=\"21600,21600\" o:spt=\"175\" adj=\"3086\" "
810
0
          "path=\"m,qy10800@0,21600,m0@1qy10800,21600,21600@1e\"><v:formulas><v:f eqn=\"val "
811
0
          "#0\"/><v:f eqn=\"sum 21600 0 #0\"/><v:f eqn=\"prod @1 1 2\"/><v:f eqn=\"sum @2 10800 "
812
0
          "0\"/></v:formulas><v:path textpathok=\"t\" o:connecttype=\"custom\" "
813
0
          "o:connectlocs=\"10800,@0;0,@2;10800,21600;21600,@2\" "
814
0
          "o:connectangles=\"270,180,90,0\"/><v:textpath on=\"t\" fitshape=\"t\"/><v:handles><v:h "
815
0
          "position=\"center,#0\" yrange=\"0,7200\"/></v:handles><o:lock v:ext=\"edit\" text=\"t\" "
816
0
          "shapetype=\"t\"/></v:shapetype>" }
817
0
    };
818
819
0
    auto i(aTypeToMarkupMap.find(eShapeType));
820
0
    return i == aTypeToMarkupMap.end() ? OString() : i->second;
821
0
}
822
823
void FontworkHelpers::collectCharColorProps(const uno::Reference<text::XText>& rXText,
824
                                            std::vector<beans::PropertyValue>& rCharPropVec)
825
0
{
826
0
    if (!rXText.is())
827
0
        return;
828
0
    uno::Reference<text::XTextCursor> rXTextCursor = rXText->createTextCursor();
829
0
    rXTextCursor->gotoStart(false);
830
0
    rXTextCursor->gotoEnd(true);
831
0
    uno::Reference<container::XEnumerationAccess> paraEnumAccess(rXText, uno::UNO_QUERY);
832
0
    if (!paraEnumAccess.is())
833
0
        return;
834
0
    uno::Reference<container::XEnumeration> paraEnum(paraEnumAccess->createEnumeration());
835
0
    while (paraEnum->hasMoreElements())
836
0
    {
837
0
        uno::Reference<text::XTextRange> xParagraph(paraEnum->nextElement(), uno::UNO_QUERY);
838
0
        uno::Reference<container::XEnumerationAccess> runEnumAccess(xParagraph, uno::UNO_QUERY);
839
0
        if (!runEnumAccess.is())
840
0
            continue;
841
0
        uno::Reference<container::XEnumeration> runEnum = runEnumAccess->createEnumeration();
842
0
        while (runEnum->hasMoreElements())
843
0
        {
844
0
            uno::Reference<text::XTextRange> xRun(runEnum->nextElement(), uno::UNO_QUERY);
845
0
            if (xRun->getString().isEmpty())
846
0
                continue;
847
0
            uno::Reference<beans::XPropertySet> xRunPropSet(xRun, uno::UNO_QUERY);
848
0
            if (!xRunPropSet.is())
849
0
                continue;
850
0
            auto xRunPropSetInfo = xRunPropSet->getPropertySetInfo();
851
0
            if (!xRunPropSetInfo.is())
852
0
                continue;
853
854
            // We have found a non-empty run. Collect its simple color properties.
855
0
            static constexpr auto aNamesArray = std::to_array<std::u16string_view>(
856
0
                { u"CharColor", u"CharLumMod", u"CharLumOff", u"CharColorTheme",
857
0
                  u"CharComplexColor", u"CharTransparence" });
858
0
            for (const auto& sName : aNamesArray)
859
0
            {
860
0
                OUString aPropertyName(sName);
861
0
                if (xRunPropSetInfo->hasPropertyByName(aPropertyName))
862
0
                    rCharPropVec.push_back(comphelper::makePropertyValue(
863
0
                        aPropertyName, xRunPropSet->getPropertyValue(aPropertyName)));
864
0
            }
865
0
            return;
866
0
        }
867
0
    }
868
0
}
869
870
void FontworkHelpers::applyPropsToRuns(const std::vector<beans::PropertyValue>& rTextPropVec,
871
                                       const uno::Reference<text::XText>& rXText)
872
0
{
873
0
    if (!rXText.is())
874
0
        return;
875
0
    uno::Reference<text::XTextCursor> xTextCursor = rXText->createTextCursor();
876
0
    xTextCursor->gotoStart(false);
877
0
    xTextCursor->gotoEnd(true);
878
0
    uno::Reference<container::XEnumerationAccess> paraEnumAccess(rXText, uno::UNO_QUERY);
879
0
    if (!paraEnumAccess.is())
880
0
        return;
881
0
    uno::Reference<container::XEnumeration> paraEnum(paraEnumAccess->createEnumeration());
882
0
    while (paraEnum->hasMoreElements())
883
0
    {
884
0
        uno::Reference<text::XTextRange> xParagraph(paraEnum->nextElement(), uno::UNO_QUERY);
885
0
        uno::Reference<container::XEnumerationAccess> runEnumAccess(xParagraph, uno::UNO_QUERY);
886
0
        if (!runEnumAccess.is())
887
0
            continue;
888
0
        uno::Reference<container::XEnumeration> runEnum = runEnumAccess->createEnumeration();
889
0
        while (runEnum->hasMoreElements())
890
0
        {
891
0
            uno::Reference<text::XTextRange> xRun(runEnum->nextElement(), uno::UNO_QUERY);
892
0
            uno::Reference<beans::XPropertySet> xRunPropSet(xRun, uno::UNO_QUERY);
893
0
            if (!xRunPropSet.is())
894
0
                continue;
895
0
            auto xRunPropSetInfo = xRunPropSet->getPropertySetInfo();
896
0
            if (!xRunPropSetInfo.is())
897
0
                continue;
898
899
0
            for (const beans::PropertyValue& rProp : rTextPropVec)
900
0
            {
901
0
                if (xRunPropSetInfo->hasPropertyByName(rProp.Name)
902
0
                    && !(xRunPropSetInfo->getPropertyByName(rProp.Name).Attributes
903
0
                         & beans::PropertyAttribute::READONLY)
904
0
                    && rProp.Name != u"CharInteropGrabBag")
905
0
                {
906
0
                    xRunPropSet->setPropertyValue(rProp.Name, rProp.Value);
907
0
                }
908
0
            }
909
0
        }
910
0
    }
911
0
}
912
namespace
913
{
914
constexpr auto aCharPropNames = std::to_array<std::u16string_view>(
915
    { u"CharColorLumMod", u"CharColorLumOff", u"CharColorTheme", u"CharComplexColor",
916
      u"CharTransparence" });
917
918
constexpr auto aShapePropNames = std::to_array<std::u16string_view>(
919
    { u"FillColorLumMod", u"FillColorLumOff", u"FillColorTheme", u"FillComplexColor",
920
      u"FillTransparence" });
921
}
922
923
void FontworkHelpers::createCharFillPropsFromShape(
924
    const uno::Reference<beans::XPropertySet>& rXPropSet,
925
    std::vector<beans::PropertyValue>& rCharPropVec)
926
0
{
927
0
    auto xPropSetInfo = rXPropSet->getPropertySetInfo();
928
0
    if (!xPropSetInfo.is())
929
0
        return;
930
    // CharColor contains the color including all color transformations
931
    // FillColor contains darken and lighten but not transparency
932
0
    sal_Int32 nColorRGB = 0;
933
0
    if (xPropSetInfo->hasPropertyByName(u"FillColor"_ustr)
934
0
        && (rXPropSet->getPropertyValue(u"FillColor"_ustr) >>= nColorRGB))
935
0
    {
936
0
        ::Color aColor(ColorTransparency, nColorRGB);
937
0
        sal_Int16 nTransPercent = 0;
938
0
        if (xPropSetInfo->hasPropertyByName(u"FillTransparence"_ustr)
939
0
            && (rXPropSet->getPropertyValue(u"FillTransparence"_ustr) >>= nTransPercent))
940
0
        {
941
0
            sal_uInt8 nAlpha = 255 - sal_uInt8(std::round(double(nTransPercent) * 2.55));
942
0
            aColor.SetAlpha(nAlpha);
943
0
        }
944
0
        rCharPropVec.push_back(comphelper::makePropertyValue(u"CharColor"_ustr, sal_Int32(aColor)));
945
0
    }
946
947
0
    for (size_t i = 0; i < 5; i++)
948
0
    {
949
0
        OUString aPropertyName(aShapePropNames[i]);
950
0
        if (xPropSetInfo->hasPropertyByName(aPropertyName))
951
0
            rCharPropVec.push_back(comphelper::makePropertyValue(
952
0
                OUString(aCharPropNames[i]), rXPropSet->getPropertyValue(aPropertyName)));
953
0
    }
954
0
}
955
956
bool FontworkHelpers::createPrstDashFromLineDash(const drawing::LineDash& rLineDash,
957
                                                 const drawing::LineCap& rLineCap,
958
                                                 OUString& rsPrstDash)
959
0
{
960
0
    bool bIsConverted = false;
961
962
0
    bool bIsRelative(rLineDash.Style == drawing::DashStyle_RECTRELATIVE
963
0
                     || rLineDash.Style == drawing::DashStyle_ROUNDRELATIVE);
964
0
    if (bIsRelative && rLineDash.Dots == 1)
965
0
    { // The length were tweaked on import in case of prstDash. Revert it here.
966
0
        sal_uInt32 nDotLen = rLineDash.DotLen;
967
0
        sal_uInt32 nDashLen = rLineDash.DashLen;
968
0
        sal_uInt32 nDistance = rLineDash.Distance;
969
0
        if (rLineCap != drawing::LineCap_BUTT && nDistance >= 99)
970
0
        {
971
0
            nDistance -= 99;
972
0
            nDotLen += 99;
973
0
            if (nDashLen > 0)
974
0
                nDashLen += 99;
975
0
        }
976
977
        // LO uses length 0 for 100%, if the attribute is missing in ODF.
978
        // Other applications might write 100%. Make is unique for the conditions.
979
0
        if (nDotLen == 0)
980
0
            nDotLen = 100;
981
0
        if (nDashLen == 0 && rLineDash.Dashes > 0)
982
0
            nDashLen = 100;
983
984
0
        bIsConverted = true;
985
0
        if (nDotLen == 100 && rLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300)
986
0
            rsPrstDash = u"dot"_ustr;
987
0
        else if (nDotLen == 400 && rLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300)
988
0
            rsPrstDash = u"dash"_ustr;
989
0
        else if (nDotLen == 400 && rLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 300)
990
0
            rsPrstDash = u"dashDot"_ustr;
991
0
        else if (nDotLen == 800 && rLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300)
992
0
            rsPrstDash = u"lgDash"_ustr;
993
0
        else if (nDotLen == 800 && rLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 300)
994
0
            rsPrstDash = u"lgDashDot"_ustr;
995
0
        else if (nDotLen == 800 && rLineDash.Dashes == 2 && nDashLen == 100 && nDistance == 300)
996
0
            rsPrstDash = u"lgDashDotDot"_ustr;
997
0
        else if (nDotLen == 100 && rLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 100)
998
0
            rsPrstDash = u"sysDot"_ustr;
999
0
        else if (nDotLen == 300 && rLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 100)
1000
0
            rsPrstDash = u"sysDash"_ustr;
1001
0
        else if (nDotLen == 300 && rLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 100)
1002
0
            rsPrstDash = u"sysDashDot"_ustr;
1003
0
        else if (nDotLen == 300 && rLineDash.Dashes == 2 && nDashLen == 100 && nDistance == 100)
1004
0
            rsPrstDash = "sysDashDotDot";
1005
0
        else
1006
0
            bIsConverted = false;
1007
0
    }
1008
0
    return bIsConverted;
1009
0
}
1010
1011
bool FontworkHelpers::getThemeColorFromShape(
1012
    OUString const& rPropertyName, const uno::Reference<beans::XPropertySet>& xPropertySet,
1013
    model::ComplexColor& rComplexColor)
1014
0
{
1015
0
    auto xPropSetInfo = xPropertySet->getPropertySetInfo();
1016
0
    if (!xPropSetInfo.is())
1017
0
        return false;
1018
0
    uno::Reference<util::XComplexColor> xComplexColor;
1019
0
    if (xPropSetInfo->hasPropertyByName(rPropertyName)
1020
0
        && (xPropertySet->getPropertyValue(rPropertyName) >>= xComplexColor) && xComplexColor.is())
1021
0
    {
1022
0
        rComplexColor = model::color::getFromXComplexColor(xComplexColor);
1023
0
        return rComplexColor.isValidThemeType();
1024
0
    }
1025
0
    return false;
1026
0
}
1027
1028
namespace
1029
{
1030
// Contains information about one gradient stop. Each gradient has at least 2 of these.
1031
struct GradientStopColor
1032
{
1033
    // RGBColor contains no transformations. In case TTColor has other type than
1034
    // ThemeColorType::Unknown, it has precedence. The color transformations in TTColor are used
1035
    // for RGBColor as well.
1036
    model::ComplexColor TTColor; // ThemeColorType and color transformations
1037
    ::Color RGBColor;
1038
};
1039
}
1040
1041
// 'first' contains the position in the range 0 (=0%) to 100000 (=100%) in the gradient as needed for
1042
// the 'pos' attribute in <w14:gs> element in oox, 'second' contains color and color transformations
1043
// at this position. The map contains all information needed for a <w14:gsLst> element in oox.
1044
typedef std::multimap<sal_Int32, GradientStopColor> ColorMapType;
1045
1046
namespace
1047
{
1048
constexpr auto W14ColorNames = std::to_array<std::u16string_view>(
1049
    { u"tx1", u"bg1", u"tx2", u"bg2", u"accent1", u"accent2", u"accent3", u"accent4", u"accent5",
1050
      u"accent6", u"hlink", u"folHlink" });
1051
1052
constexpr auto WColorNames = std::to_array<std::u16string_view>(
1053
    { u"text1", u"background1", u"text2", u"background2", u"accent1", u"accent2", u"accent3",
1054
      u"accent4", u"accent5", u"accent6", u"hyperlink", u"followedHyperlink" });
1055
1056
// Returns the string to be used in w14:schemeClr in case of w14:textOutline or w14:textFill
1057
OUString lcl_getW14MarkupStringForThemeColor(const model::ComplexColor& rComplexColor)
1058
0
{
1059
0
    const sal_uInt8 nClrNameIndex = std::clamp<sal_uInt8>(
1060
0
        sal_Int32(rComplexColor.getThemeColorType()), sal_Int32(model::ThemeColorType::Dark1),
1061
0
        sal_Int32(model::ThemeColorType::FollowedHyperlink));
1062
0
    return OUString(W14ColorNames[nClrNameIndex]);
1063
0
}
1064
1065
// Returns the string to be used in w:themeColor. It is exported via CharThemeColor.
1066
OUString lcl_getWMarkupStringForThemeColor(const model::ComplexColor& rComplexColor)
1067
0
{
1068
0
    const sal_uInt8 nClrNameIndex = std::clamp<sal_uInt8>(
1069
0
        sal_Int32(rComplexColor.getThemeColorType()), sal_Int32(model::ThemeColorType::Dark1),
1070
0
        sal_Int32(model::ThemeColorType::FollowedHyperlink));
1071
0
    return OUString(WColorNames[nClrNameIndex]);
1072
0
}
1073
1074
// Puts the value of the first occurrence of rType in rComplexColor into rValue and returns true.
1075
// If such does not exist, rValue is unchanged and the method returns false.
1076
bool lcl_getThemeColorTransformationValue(const model::ComplexColor& rComplexColor,
1077
                                          const model::TransformationType& rType, sal_Int16& rValue)
1078
0
{
1079
0
    const std::vector<model::Transformation>& aTransVec(rComplexColor.getTransformations());
1080
0
    auto bItemFound
1081
0
        = [rType](const model::Transformation& rTrans) { return rType == rTrans.meType; };
1082
0
    auto pIt = std::find_if(aTransVec.begin(), aTransVec.end(), bItemFound);
1083
0
    if (pIt == aTransVec.end())
1084
0
        return false;
1085
0
    rValue = (*pIt).mnValue;
1086
0
    return true;
1087
0
}
1088
1089
// Adds the child elements 'lumMod' and 'lumOff' to 'schemeClr' maCurrentElement of pGrabStack,
1090
// if such exist in rComplexColor. 'alpha' is contained in the maTransformations of rComplexColor
1091
// in case of gradient fill.
1092
void lcl_addColorTransformationToGrabBagStack(
1093
    const model::ComplexColor& rComplexColor,
1094
    const std::unique_ptr<oox::GrabBagStack>& pGrabBagStack)
1095
0
{
1096
0
    if (pGrabBagStack == nullptr)
1097
0
        return;
1098
0
    for (auto const& rColorTransform : rComplexColor.getTransformations())
1099
0
    {
1100
0
        switch (rColorTransform.meType)
1101
0
        {
1102
0
            case model::TransformationType::LumMod:
1103
0
                pGrabBagStack->push(u"lumMod"_ustr);
1104
0
                pGrabBagStack->push(u"attributes"_ustr);
1105
0
                pGrabBagStack->addInt32(u"val"_ustr, rColorTransform.mnValue * 10);
1106
0
                pGrabBagStack->pop();
1107
0
                pGrabBagStack->pop();
1108
0
                break;
1109
0
            case model::TransformationType::LumOff:
1110
0
                pGrabBagStack->push(u"lumOff"_ustr);
1111
0
                pGrabBagStack->push(u"attributes"_ustr);
1112
0
                pGrabBagStack->addInt32(u"val"_ustr, rColorTransform.mnValue * 10);
1113
0
                pGrabBagStack->pop();
1114
0
                pGrabBagStack->pop();
1115
0
                break;
1116
0
            case model::TransformationType::Alpha:
1117
0
                pGrabBagStack->push(u"alpha"_ustr);
1118
0
                pGrabBagStack->push(u"attributes"_ustr);
1119
                // model::TransformationType::Alpha is designed to be used with a:alpha, which has
1120
                // opacity. But w14:alpha uses transparency. So convert it here.
1121
0
                pGrabBagStack->addInt32(u"val"_ustr,
1122
0
                                        oox::drawingml::MAX_PERCENT - rColorTransform.mnValue * 10);
1123
0
                pGrabBagStack->pop();
1124
0
                pGrabBagStack->pop();
1125
0
                break;
1126
0
            default: // other child elements can be added later if needed for Fontwork
1127
0
                break;
1128
0
        }
1129
0
    }
1130
0
}
1131
1132
void lcl_getGradientsFromShape(const uno::Reference<beans::XPropertySet>& rXPropSet,
1133
                               const uno::Reference<beans::XPropertySetInfo>& rXPropSetInfo,
1134
                               awt::Gradient2& rColorGradient, bool& rbHasColorGradient,
1135
                               awt::Gradient2& rTransparenceGradient,
1136
                               bool& rbHasTransparenceGradient)
1137
0
{
1138
0
    OUString sColorGradientName;
1139
0
    rbHasColorGradient
1140
0
        = rXPropSetInfo->hasPropertyByName(u"FillGradientName"_ustr)
1141
0
          && (rXPropSet->getPropertyValue(u"FillGradientName"_ustr) >>= sColorGradientName)
1142
0
          && !sColorGradientName.isEmpty() && rXPropSetInfo->hasPropertyByName(u"FillGradient"_ustr)
1143
0
          && (rXPropSet->getPropertyValue(u"FillGradient"_ustr) >>= rColorGradient);
1144
1145
0
    OUString sTransparenceGradientName;
1146
0
    rbHasTransparenceGradient
1147
0
        = rXPropSetInfo->hasPropertyByName(u"FillTransparenceGradientName"_ustr)
1148
0
          && (rXPropSet->getPropertyValue(u"FillTransparenceGradientName"_ustr)
1149
0
              >>= sTransparenceGradientName)
1150
0
          && !sTransparenceGradientName.isEmpty()
1151
0
          && rXPropSetInfo->hasPropertyByName(u"FillTransparenceGradient"_ustr)
1152
0
          && (rXPropSet->getPropertyValue(u"FillTransparenceGradient"_ustr)
1153
0
              >>= rTransparenceGradient);
1154
0
}
1155
1156
ColorMapType lcl_createColorMapFromShapeProps(
1157
    const uno::Reference<beans::XPropertySet>& rXPropSet,
1158
    const uno::Reference<beans::XPropertySetInfo>& rXPropSetInfo,
1159
    const awt::Gradient2& rColorGradient, const bool& rbHasColorGradient,
1160
    const awt::Gradient2& rTransparenceGradient, const bool& rbHasTransparenceGradient)
1161
0
{
1162
    // LibreOffice can use color gradients and transparency gradients with different geometries.
1163
    // That is not possible in OOXML, so a fill might look different in Word. But a round-trip
1164
    // with gradients imported from Word, should work well.
1165
1166
    // Word has transparency not as separate gradient but as color transformation in a color
1167
    // gradient. Thus we synchronize the gradients. Then they have same offsets and count.
1168
0
    basegfx::BColor aSingleColor;
1169
0
    basegfx::BGradient aColorBGradient;
1170
0
    basegfx::BColorStops aColorStops;
1171
0
    if (rbHasColorGradient)
1172
0
    {
1173
0
        aColorBGradient = model::gradient::getFromUnoGradient2(rColorGradient);
1174
0
        aColorBGradient.tryToApplyStartEndIntensity();
1175
0
        aColorBGradient.tryToApplyBorder();
1176
0
        aColorBGradient.tryToApplyAxial();
1177
0
        basegfx::utils::prepareColorStops(aColorBGradient, aColorStops, aSingleColor);
1178
        // All gradient styles but LINEAR and AXIAL (which is already converted to LINEAR) need the
1179
        // stops sequence reverse.
1180
0
        if (awt::GradientStyle_LINEAR != aColorBGradient.GetGradientStyle())
1181
0
            aColorStops.reverseColorStops();
1182
0
    }
1183
0
    else
1184
0
    {
1185
0
        sal_Int32 nFillColor(0);
1186
0
        if (rXPropSetInfo->hasPropertyByName(u"FillColor"_ustr))
1187
0
            rXPropSet->getPropertyValue(u"FillColor"_ustr) >>= nFillColor;
1188
0
        aSingleColor = ::Color(ColorTransparency, nFillColor).getBColor().clamp();
1189
0
    }
1190
1191
0
    basegfx::BColor aSingleTrans;
1192
0
    basegfx::BGradient aTransBGradient;
1193
0
    basegfx::BColorStops aTransStops;
1194
0
    if (rbHasTransparenceGradient)
1195
0
    {
1196
0
        aTransBGradient = model::gradient::getFromUnoGradient2(rTransparenceGradient);
1197
0
        aTransBGradient.tryToApplyStartEndIntensity(); // usually 100%, but might be set by macro
1198
0
        aTransBGradient.tryToApplyBorder();
1199
0
        aTransBGradient.tryToApplyAxial();
1200
0
        basegfx::utils::prepareColorStops(aTransBGradient, aTransStops, aSingleTrans);
1201
        // All gradient styles but LINEAR and AXIAL (which is already converted to LINEAR) need the
1202
        // stops sequence reverse.
1203
0
        if (awt::GradientStyle_LINEAR != aTransBGradient.GetGradientStyle())
1204
0
            aTransStops.reverseColorStops();
1205
0
    }
1206
0
    else
1207
0
    {
1208
0
        sal_Int16 nAPITrans(0);
1209
0
        if (rXPropSetInfo->hasPropertyByName(u"FillTransparence"_ustr))
1210
0
            rXPropSet->getPropertyValue(u"FillTransparence"_ustr) >>= nAPITrans;
1211
        // API transparency is in range 0..100, BColor in range [0.0, 1.0].
1212
0
        aSingleTrans = basegfx::BColor(nAPITrans * 0.01).clamp();
1213
0
    }
1214
1215
0
    basegfx::utils::synchronizeColorStops(aColorStops, aTransStops, aSingleColor, aSingleTrans);
1216
1217
0
    ColorMapType aColorMap;
1218
1219
    // If we have no color gradient, the fix fill color might be a theme color. In that case we use
1220
    // it instead of the color from the color stop.
1221
0
    GradientStopColor aFixColor;
1222
0
    bool bUseThemeColor(!rbHasColorGradient
1223
0
                        && FontworkHelpers::getThemeColorFromShape(u"FillComplexColor"_ustr,
1224
0
                                                                   rXPropSet, aFixColor.TTColor));
1225
1226
0
    for (auto itC = aColorStops.begin(), itT = aTransStops.begin();
1227
0
         itC != aColorStops.end() && itT != aTransStops.end(); ++itC, ++itT)
1228
0
    {
1229
0
        GradientStopColor aNextStopColor = aFixColor;
1230
0
        if (!bUseThemeColor)
1231
0
        {
1232
0
            aNextStopColor.TTColor = model::ComplexColor();
1233
0
            aNextStopColor.RGBColor = ::Color((*itC).getStopColor());
1234
0
        }
1235
        // model::TransformationType::Alpha is opacity in range 0..10000,
1236
        // BColor is transparency in range [0.0, 1.0]
1237
0
        sal_Int16 nAlpha = std::clamp<sal_Int16>(
1238
0
            10000 - std::lround((*itT).getStopColor().luminance() * 10000.0), 0, 10000);
1239
0
        if (nAlpha < 10000)
1240
0
            aNextStopColor.TTColor.addTransformation({ model::TransformationType::Alpha, nAlpha });
1241
0
        sal_Int32 nPosition
1242
0
            = static_cast<sal_Int32>(std::lround((*itC).getStopOffset() * 100000.0));
1243
0
        aColorMap.insert(std::pair{ nPosition, aNextStopColor });
1244
0
    }
1245
1246
    // If a gradient has only two stops, MS Office renders it with a non-linear method which looks
1247
    // different than gradient in LibreOffice (see tdf#128795). For more than two stops rendering is
1248
    // the same as in LibreOffice, even if two stops are identical.
1249
0
    if (aColorMap.size() == 2)
1250
0
    {
1251
0
        auto it = aColorMap.begin();
1252
0
        aColorMap.insert(std::pair{ 0, (*it).second });
1253
0
    }
1254
0
    return aColorMap;
1255
0
}
1256
} // end namespace
1257
1258
void FontworkHelpers::createCharInteropGrabBagUpdatesFromShapeProps(
1259
    const uno::Reference<beans::XPropertySet>& rXPropSet,
1260
    std::vector<beans::PropertyValue>& rUpdatePropVec)
1261
0
{
1262
0
    auto xPropSetInfo = rXPropSet->getPropertySetInfo();
1263
0
    if (!xPropSetInfo.is())
1264
0
        return;
1265
1266
    // GrabBagStack is a special tool for handling the hierarchy in a GrabBag
1267
0
    std::unique_ptr<oox::GrabBagStack> pGrabBagStack;
1268
1269
    // CharTextFillTextEffect
1270
0
    pGrabBagStack.reset(new oox::GrabBagStack(u"textFill"_ustr));
1271
0
    drawing::FillStyle eFillStyle = drawing::FillStyle_SOLID;
1272
0
    if (xPropSetInfo->hasPropertyByName(u"FillStyle"_ustr))
1273
0
        rXPropSet->getPropertyValue(u"FillStyle"_ustr) >>= eFillStyle;
1274
1275
    // We might have a solid fill but a transparency gradient. That needs to be exported as gradFill
1276
    // too, because Word has transparency not separated but in the color stops in a color gradient.
1277
    // A gradient exists, if the GradientName is not empty.
1278
0
    OUString sTransparenceGradientName;
1279
0
    if (eFillStyle == drawing::FillStyle_SOLID
1280
0
        && xPropSetInfo->hasPropertyByName(u"FillTransparenceGradientName"_ustr)
1281
0
        && (rXPropSet->getPropertyValue(u"FillTransparenceGradientName"_ustr)
1282
0
            >>= sTransparenceGradientName)
1283
0
        && !sTransparenceGradientName.isEmpty())
1284
0
        eFillStyle = drawing::FillStyle_GRADIENT;
1285
1286
0
    switch (eFillStyle)
1287
0
    {
1288
0
        case drawing::FillStyle_NONE:
1289
0
        {
1290
0
            pGrabBagStack->appendElement(u"noFill"_ustr, uno::Any());
1291
0
            break;
1292
0
        }
1293
0
        case drawing::FillStyle_GRADIENT:
1294
0
        {
1295
0
            awt::Gradient2 aColorGradient;
1296
0
            bool bHasColorGradient(false);
1297
0
            awt::Gradient2 aTransparenceGradient;
1298
0
            bool bHasTransparenceGradient(false);
1299
0
            lcl_getGradientsFromShape(rXPropSet, xPropSetInfo, aColorGradient, bHasColorGradient,
1300
0
                                      aTransparenceGradient, bHasTransparenceGradient);
1301
            // aColorMap contains the color stops suitable to generate gsLst
1302
0
            ColorMapType aColorMap = lcl_createColorMapFromShapeProps(
1303
0
                rXPropSet, xPropSetInfo, aColorGradient, bHasColorGradient, aTransparenceGradient,
1304
0
                bHasTransparenceGradient);
1305
0
            pGrabBagStack->push(u"gradFill"_ustr);
1306
0
            pGrabBagStack->push(u"gsLst"_ustr);
1307
0
            for (const auto & [ nPos, rColors ] : aColorMap)
1308
0
            {
1309
0
                pGrabBagStack->push(u"gs"_ustr);
1310
0
                pGrabBagStack->push(u"attributes"_ustr);
1311
0
                pGrabBagStack->addInt32(u"pos"_ustr, nPos);
1312
0
                pGrabBagStack->pop();
1313
0
                if (rColors.TTColor.getThemeColorType() == model::ThemeColorType::Unknown)
1314
0
                {
1315
0
                    pGrabBagStack->push(u"srgbClr"_ustr);
1316
0
                    pGrabBagStack->push(u"attributes"_ustr);
1317
0
                    pGrabBagStack->addString(u"val"_ustr, rColors.RGBColor.AsRGBHexString());
1318
0
                    pGrabBagStack->pop(); // maCurrentElement:'srgbClr', maPropertyList:'attributes'
1319
0
                }
1320
0
                else
1321
0
                {
1322
0
                    pGrabBagStack->push(u"schemeClr"_ustr);
1323
0
                    pGrabBagStack->push(u"attributes"_ustr);
1324
0
                    pGrabBagStack->addString(u"val"_ustr,
1325
0
                                             lcl_getW14MarkupStringForThemeColor(rColors.TTColor));
1326
0
                    pGrabBagStack->pop();
1327
                    // maCurrentElement:'schemeClr', maPropertyList:'attributes'
1328
0
                }
1329
1330
0
                lcl_addColorTransformationToGrabBagStack(rColors.TTColor, pGrabBagStack);
1331
0
                pGrabBagStack->pop();
1332
                // maCurrentElement:'gs', maPropertyList:'attributes', 'srgbClr' or 'schemeClr'
1333
0
                pGrabBagStack->pop(); // maCurrentElement:'gsLst', maPropertyList: at least two 'gs'
1334
0
            }
1335
0
            pGrabBagStack->pop(); // maCurrentElement:'gradFill', maPropertyList: gsLst
1336
1337
            // Kind of gradient
1338
0
            awt::GradientStyle eGradientStyle = awt::GradientStyle_LINEAR;
1339
0
            if (bHasColorGradient)
1340
0
                eGradientStyle = aColorGradient.Style;
1341
0
            else if (bHasTransparenceGradient)
1342
0
                eGradientStyle = aTransparenceGradient.Style;
1343
            // write 'lin' or 'path'. LibreOffice has nothing which corresponds to 'shape'.
1344
0
            if (eGradientStyle == awt::GradientStyle_LINEAR
1345
0
                || eGradientStyle == awt::GradientStyle_AXIAL)
1346
0
            {
1347
                // API angle is in 1/10th deg and describes counter-clockwise rotation of line of
1348
                // equal color. OOX angle is in 1/60000th deg and describes clockwise rotation of
1349
                // color transition direction.
1350
0
                sal_Int32 nAngleOOX = 0;
1351
0
                if (bHasColorGradient)
1352
0
                    nAngleOOX = ((3600 - aColorGradient.Angle + 900) % 3600) * 6000;
1353
0
                else if (bHasTransparenceGradient)
1354
0
                    nAngleOOX = ((3600 - aTransparenceGradient.Angle + 900) % 3600) * 6000;
1355
0
                pGrabBagStack->push(u"lin"_ustr);
1356
0
                pGrabBagStack->push(u"attributes"_ustr);
1357
0
                pGrabBagStack->addInt32(u"ang"_ustr, nAngleOOX);
1358
                // LibreOffice cannot scale a gradient to the shape size.
1359
0
                pGrabBagStack->addString(u"scaled"_ustr, u"0"_ustr);
1360
0
            }
1361
0
            else
1362
0
            {
1363
                // Same rendering as in LibreOffice is not possible:
1364
                // (1) The gradient type 'path' in Word has no rotation.
1365
                // (2) To get the same size of gradient area, the element 'tileRect' is needed, but
1366
                // that is not available for <w14:textFill> element.
1367
                // So we can only set a reasonably suitable focus point.
1368
0
                pGrabBagStack->push(u"path"_ustr);
1369
0
                pGrabBagStack->push(u"attributes"_ustr);
1370
0
                if (eGradientStyle == awt::GradientStyle_RADIAL
1371
0
                    || eGradientStyle == awt::GradientStyle_ELLIPTICAL)
1372
0
                    pGrabBagStack->addString(u"path"_ustr, u"circle"_ustr);
1373
0
                else
1374
0
                    pGrabBagStack->addString(u"path"_ustr, u"rect"_ustr);
1375
0
                pGrabBagStack->pop();
1376
0
                pGrabBagStack->push(u"fillToRect"_ustr);
1377
0
                pGrabBagStack->push(u"attributes"_ustr);
1378
0
                sal_Int32 nLeftPercent
1379
0
                    = bHasColorGradient ? aColorGradient.XOffset : aTransparenceGradient.XOffset;
1380
0
                sal_Int32 nTopPercent
1381
0
                    = bHasColorGradient ? aColorGradient.YOffset : aTransparenceGradient.YOffset;
1382
0
                pGrabBagStack->addInt32(u"l"_ustr, nLeftPercent * 1000);
1383
0
                pGrabBagStack->addInt32(u"t"_ustr, nTopPercent * 1000);
1384
0
                pGrabBagStack->addInt32(u"r"_ustr, (100 - nLeftPercent) * 1000);
1385
0
                pGrabBagStack->addInt32(u"b"_ustr, (100 - nTopPercent) * 1000);
1386
0
            }
1387
            // all remaining pop() calls are in the final getRootProperty() method
1388
0
            break;
1389
0
        }
1390
0
        case drawing::FillStyle_SOLID:
1391
0
        {
1392
0
            pGrabBagStack->push(u"solidFill"_ustr);
1393
0
            model::ComplexColor aComplexColor;
1394
            // It is either "schemeClr" or "srgbClr".
1395
0
            if (FontworkHelpers::getThemeColorFromShape(u"FillComplexColor"_ustr, rXPropSet,
1396
0
                                                        aComplexColor))
1397
0
            {
1398
0
                pGrabBagStack->push(u"schemeClr"_ustr);
1399
0
                pGrabBagStack->push(u"attributes"_ustr);
1400
0
                pGrabBagStack->addString(u"val"_ustr,
1401
0
                                         lcl_getW14MarkupStringForThemeColor(aComplexColor));
1402
0
                pGrabBagStack->pop(); // maCurrentElement:'schemeClr', maPropertyList:'attributes'
1403
0
                lcl_addColorTransformationToGrabBagStack(aComplexColor, pGrabBagStack);
1404
                // maCurrentElement:'schemeClr', maPropertyList:'attributes', maybe 'lumMod' and
1405
                // maybe 'lumOff'
1406
0
            }
1407
0
            else
1408
0
            {
1409
0
                pGrabBagStack->push(u"srgbClr"_ustr);
1410
0
                sal_Int32 nFillColor(0);
1411
0
                if (xPropSetInfo->hasPropertyByName(u"FillColor"_ustr))
1412
0
                    rXPropSet->getPropertyValue(u"FillColor"_ustr) >>= nFillColor;
1413
0
                pGrabBagStack->push(u"attributes"_ustr);
1414
0
                ::Color aColor(ColorTransparency, nFillColor);
1415
0
                pGrabBagStack->addString(u"val"_ustr, aColor.AsRGBHexString());
1416
0
                pGrabBagStack->pop();
1417
                // maCurrentElement:'srgbClr', maPropertyList:'attributes'
1418
0
            }
1419
1420
0
            sal_Int16 nFillTransparence(0);
1421
0
            if (xPropSetInfo->hasPropertyByName(u"FillTransparence"_ustr))
1422
0
                rXPropSet->getPropertyValue(u"FillTransparence"_ustr) >>= nFillTransparence;
1423
0
            if (nFillTransparence != 0)
1424
0
            {
1425
0
                pGrabBagStack->push(u"alpha"_ustr);
1426
0
                pGrabBagStack->push(u"attributes"_ustr);
1427
0
                pGrabBagStack->addInt32(u"val"_ustr, nFillTransparence * 1000);
1428
0
            }
1429
            // all remaining pop() calls are in the final getRootProperty() method
1430
0
            break;
1431
0
        }
1432
0
        default: // BITMAP is VML only export and does not arrive here. HATCH has to be VML only
1433
                 // export too, but is not yet implemented.
1434
0
            break;
1435
0
    }
1436
    // resolve the stack and put resulting PropertyValue into the update vector
1437
0
    beans::PropertyValue aCharTextFillTextEffect;
1438
0
    aCharTextFillTextEffect.Name = "CharTextFillTextEffect";
1439
0
    aCharTextFillTextEffect.Value <<= pGrabBagStack->getRootProperty();
1440
0
    rUpdatePropVec.push_back(aCharTextFillTextEffect);
1441
1442
    // CharTextOutlineTextEffect
1443
0
    pGrabBagStack.reset(new oox::GrabBagStack(u"textOutline"_ustr));
1444
1445
    // attributes
1446
0
    pGrabBagStack->push(u"attributes"_ustr);
1447
    // line width
1448
0
    sal_Int32 nLineWidth(0);
1449
0
    if (xPropSetInfo->hasPropertyByName(u"LineWidth"_ustr))
1450
0
        rXPropSet->getPropertyValue(u"LineWidth"_ustr) >>= nLineWidth;
1451
0
    pGrabBagStack->addInt32(u"w"_ustr, nLineWidth * 360);
1452
    // cap for dashes
1453
0
    drawing::LineCap eLineCap = drawing::LineCap_BUTT;
1454
0
    if (xPropSetInfo->hasPropertyByName(u"LineCap"_ustr))
1455
0
        rXPropSet->getPropertyValue(u"LineCap"_ustr) >>= eLineCap;
1456
0
    OUString sCap = u"flat"_ustr;
1457
0
    if (eLineCap == drawing::LineCap_ROUND)
1458
0
        sCap = u"rnd"_ustr;
1459
0
    else if (eLineCap == drawing::LineCap_SQUARE)
1460
0
        sCap = u"sq"_ustr;
1461
0
    pGrabBagStack->addString(u"cap"_ustr, sCap);
1462
    // LO has no compound lines and always centers the lines
1463
0
    pGrabBagStack->addString(u"cmpd"_ustr, u"sng"_ustr);
1464
0
    pGrabBagStack->addString(u"alng"_ustr, u"ctr"_ustr);
1465
0
    pGrabBagStack->pop();
1466
    // maCurrentElement:'textOutline', maPropertyList:'attributes'
1467
1468
    // style
1469
0
    drawing::LineStyle eLineStyle = drawing::LineStyle_NONE;
1470
0
    if (xPropSetInfo->hasPropertyByName(u"LineStyle"_ustr))
1471
0
        rXPropSet->getPropertyValue(u"LineStyle"_ustr) >>= eLineStyle;
1472
    // 'dashed' is not a separate style in Word. Word has a style 'gradFill', but that is not yet
1473
    // implemented in LO. So only 'noFill' and 'solidFill'.
1474
0
    if (eLineStyle == drawing::LineStyle_NONE)
1475
0
    {
1476
0
        pGrabBagStack->appendElement(u"noFill"_ustr, uno::Any());
1477
0
    }
1478
0
    else
1479
0
    {
1480
0
        pGrabBagStack->push(u"solidFill"_ustr);
1481
        // It is either "schemeClr" or "srgbClr".
1482
0
        model::ComplexColor aComplexColor;
1483
0
        if (FontworkHelpers::getThemeColorFromShape(u"LineComplexColor"_ustr, rXPropSet,
1484
0
                                                    aComplexColor))
1485
0
        {
1486
0
            pGrabBagStack->push(u"schemeClr"_ustr);
1487
0
            pGrabBagStack->push(u"attributes"_ustr);
1488
0
            pGrabBagStack->addString(u"val"_ustr,
1489
0
                                     lcl_getW14MarkupStringForThemeColor(aComplexColor));
1490
0
            pGrabBagStack->pop();
1491
0
            lcl_addColorTransformationToGrabBagStack(aComplexColor, pGrabBagStack);
1492
            // maCurrentElement:'schemeClr', maPropertylist:'attributes'
1493
0
        }
1494
0
        else // not a theme color
1495
0
        {
1496
0
            pGrabBagStack->push(u"srgbClr"_ustr);
1497
0
            pGrabBagStack->push(u"attributes"_ustr);
1498
0
            sal_Int32 nLineColor(0);
1499
0
            if (xPropSetInfo->hasPropertyByName(u"LineColor"_ustr))
1500
0
                rXPropSet->getPropertyValue(u"LineColor"_ustr) >>= nLineColor;
1501
0
            ::Color aColor(ColorTransparency, nLineColor);
1502
0
            pGrabBagStack->addString(u"val"_ustr, aColor.AsRGBHexString());
1503
0
            pGrabBagStack->pop();
1504
            // maCurrentElement:'srgbClr', maPropertylist:'attributes'
1505
0
        }
1506
1507
0
        sal_Int16 nLineTransparence(0);
1508
0
        if (xPropSetInfo->hasPropertyByName(u"LineTransparence"_ustr))
1509
0
            rXPropSet->getPropertyValue(u"LineTransparence"_ustr) >>= nLineTransparence;
1510
0
        if (nLineTransparence != 0)
1511
0
        {
1512
0
            pGrabBagStack->push(u"alpha"_ustr);
1513
0
            pGrabBagStack->push(u"attributes"_ustr);
1514
0
            pGrabBagStack->addInt32(u"val"_ustr, nLineTransparence * 1000);
1515
0
            pGrabBagStack->pop(); // maCurrentElement: 'alpha'
1516
0
            pGrabBagStack->pop(); // maCurrentElement: 'srgbClr' or 'schemeClr'
1517
0
        }
1518
0
        pGrabBagStack->pop();
1519
        // maCurrentElement:'solidFill', maPropertyList:either 'srgbClr' or 'schemeClr
1520
0
        pGrabBagStack->pop();
1521
0
    }
1522
    // maCurrentElement:'textOutline', maPropertyList:'attributes' and either 'noFill' or 'solidFill'
1523
1524
    // prstDash
1525
0
    if (eLineStyle == drawing::LineStyle_DASH)
1526
0
    {
1527
0
        pGrabBagStack->push(u"prstDash"_ustr);
1528
0
        OUString sPrstDash = u"sysDot"_ustr;
1529
0
        drawing::LineDash aLineDash;
1530
0
        if (xPropSetInfo->hasPropertyByName(u"LineDash"_ustr)
1531
0
            && (rXPropSet->getPropertyValue(u"LineDash"_ustr) >>= aLineDash))
1532
0
        {
1533
            // The outline of abc-transform in Word is not able to use custDash. But we know the line
1534
            // is dashed. We keep "sysDot" as fallback in case no prstDash is detected.
1535
0
            FontworkHelpers::createPrstDashFromLineDash(aLineDash, eLineCap, sPrstDash);
1536
0
        }
1537
0
        else
1538
0
        {
1539
            // ToDo: There may be a named dash style, but that is unlikely for Fontwork shapes. So
1540
            // I skip it for now and use the "sysDot" fallback.
1541
0
        }
1542
0
        pGrabBagStack->push(u"attributes"_ustr);
1543
0
        pGrabBagStack->addString(u"val"_ustr, sPrstDash);
1544
0
        pGrabBagStack->pop(); // maCurrentElement:'prstDash'
1545
0
        pGrabBagStack->pop(); // maCurrentElement:'textOutline'
1546
0
    }
1547
    // maCurrentElement:'textOutline', maPropertyList:'attributes', either 'noFill' or 'solidFill',
1548
    // and  maybe 'prstDash'.
1549
1550
    // LineJoint, can be 'round', 'bevel' or 'miter' in Word
1551
0
    drawing::LineJoint eLineJoint = drawing::LineJoint_NONE;
1552
0
    if (xPropSetInfo->hasPropertyByName(u"LineJoint"_ustr))
1553
0
        rXPropSet->getPropertyValue(u"LineJoint"_ustr) >>= eLineJoint;
1554
0
    if (eLineJoint == drawing::LineJoint_NONE || eLineJoint == drawing::LineJoint_BEVEL)
1555
0
        pGrabBagStack->appendElement(u"bevel"_ustr, uno::Any());
1556
0
    else if (eLineJoint == drawing::LineJoint_ROUND)
1557
0
        pGrabBagStack->appendElement(u"round"_ustr, uno::Any());
1558
0
    else // MITER or deprecated MIDDLE
1559
0
    {
1560
0
        pGrabBagStack->push(u"miter"_ustr);
1561
0
        pGrabBagStack->push(u"attributes"_ustr);
1562
0
        pGrabBagStack->addInt32(u"lim"_ustr, 0); // As of Feb. 2023 LO cannot render other values.
1563
0
        pGrabBagStack->pop(); // maCurrentElement:'attributes'
1564
0
        pGrabBagStack->pop(); // maCurrentElement:'miter'
1565
0
    }
1566
    // maCurrentElement:'textOutline', maPropertyList:'attributes', either 'noFill' or
1567
    // 'solidFill', maybe 'prstDash', and either 'bevel', 'round' or 'miter'.
1568
1569
    // resolve the stack and put resulting PropertyValue into the update vector
1570
0
    beans::PropertyValue aCharTextOutlineTextEffect;
1571
0
    aCharTextOutlineTextEffect.Name = "CharTextOutlineTextEffect";
1572
0
    aCharTextOutlineTextEffect.Value <<= pGrabBagStack->getRootProperty();
1573
0
    rUpdatePropVec.push_back(aCharTextOutlineTextEffect);
1574
1575
    // CharThemeOriginalColor, CharThemeColor, and CharThemeColorShade or CharThemeColorTint will be
1576
    // used for <w:color> element. That is evaluated by applications, which do not understand w14
1577
    // namespace, or if w14:textFill is omitted.
1578
0
    model::ComplexColor aComplexColor;
1579
0
    if (FontworkHelpers::getThemeColorFromShape(u"FillComplexColor"_ustr, rXPropSet, aComplexColor))
1580
0
    {
1581
        // CharThemeColor
1582
0
        beans::PropertyValue aCharThemeColor;
1583
0
        aCharThemeColor.Name = u"CharThemeColor"_ustr;
1584
0
        aCharThemeColor.Value <<= lcl_getWMarkupStringForThemeColor(aComplexColor);
1585
0
        rUpdatePropVec.push_back(aCharThemeColor);
1586
1587
        // CharThemeColorShade or CharThemeColorTint
1588
        // MS Office uses themeTint and themeShade on the luminance in a HSL color space, see 2.1.72
1589
        // in [MS-OI29500]. That is different from OOXML specification.
1590
        // We made two assumption here: (1) If LumOff exists and is not zero, it is a 'tint'.
1591
        // (2) LumMod + LumOff == 10000;
1592
0
        sal_Int16 nLumMod;
1593
0
        if (lcl_getThemeColorTransformationValue(aComplexColor, model::TransformationType::LumMod,
1594
0
                                                 nLumMod))
1595
0
        {
1596
0
            sal_Int16 nLumOff;
1597
0
            bool bIsTint = lcl_getThemeColorTransformationValue(
1598
0
                               aComplexColor, model::TransformationType::LumOff, nLumOff)
1599
0
                           && nLumOff != 0;
1600
0
            sal_uInt8 nValue
1601
0
                = std::clamp<sal_uInt8>(lround(double(nLumMod) * 255.0 / 10000.0), 0, 255);
1602
0
            OUString sValue = OUString::number(nValue, 16);
1603
1604
0
            beans::PropertyValue aCharThemeTintOrShade;
1605
0
            aCharThemeTintOrShade.Name = bIsTint ? u"CharThemeColorTint" : u"CharThemeColorShade";
1606
0
            aCharThemeTintOrShade.Value <<= sValue;
1607
0
            rUpdatePropVec.push_back(aCharThemeTintOrShade);
1608
0
        }
1609
0
    }
1610
    // ToDo: Are FillColorLumMod, FillColorLumOff and FillColorTheme possible without
1611
    // FillComplexColor? If yes, we need an 'else' part here.
1612
1613
    // CharThemeOriginalColor.
1614
0
    beans::PropertyValue aCharThemeOriginalColor;
1615
0
    sal_Int32 nFillColor(0);
1616
0
    if (xPropSetInfo->hasPropertyByName(u"FillColor"_ustr))
1617
0
        rXPropSet->getPropertyValue(u"FillColor"_ustr) >>= nFillColor;
1618
0
    aCharThemeOriginalColor.Name = u"CharThemeOriginalColor"_ustr;
1619
0
    ::Color aColor(ColorTransparency, nFillColor);
1620
0
    aCharThemeOriginalColor.Value <<= aColor.AsRGBHEXString();
1621
0
    rUpdatePropVec.push_back(aCharThemeOriginalColor);
1622
0
}
1623
1624
void FontworkHelpers::applyUpdatesToCharInteropGrabBag(
1625
    const std::vector<beans::PropertyValue>& rUpdatePropVec,
1626
    const uno::Reference<text::XText>& rXText)
1627
0
{
1628
0
    if (!rXText.is())
1629
0
        return;
1630
0
    uno::Reference<text::XTextCursor> rXTextCursor = rXText->createTextCursor();
1631
0
    rXTextCursor->gotoStart(false);
1632
0
    rXTextCursor->gotoEnd(true);
1633
0
    uno::Reference<container::XEnumerationAccess> paraEnumAccess(rXText, uno::UNO_QUERY);
1634
0
    if (!paraEnumAccess.is())
1635
0
        return;
1636
0
    uno::Reference<container::XEnumeration> paraEnum(paraEnumAccess->createEnumeration());
1637
0
    while (paraEnum->hasMoreElements())
1638
0
    {
1639
0
        uno::Reference<text::XTextRange> xParagraph(paraEnum->nextElement(), uno::UNO_QUERY);
1640
0
        uno::Reference<container::XEnumerationAccess> runEnumAccess(xParagraph, uno::UNO_QUERY);
1641
0
        if (!runEnumAccess.is())
1642
0
            continue;
1643
0
        uno::Reference<container::XEnumeration> runEnum = runEnumAccess->createEnumeration();
1644
0
        while (runEnum->hasMoreElements())
1645
0
        {
1646
0
            uno::Reference<text::XTextRange> xRun(runEnum->nextElement(), uno::UNO_QUERY);
1647
0
            if (xRun->getString().isEmpty())
1648
0
                continue;
1649
0
            uno::Reference<beans::XPropertySet> xRunPropSet(xRun, uno::UNO_QUERY);
1650
0
            if (!xRunPropSet.is())
1651
0
                continue;
1652
0
            auto xRunPropSetInfo = xRunPropSet->getPropertySetInfo();
1653
0
            if (!xRunPropSetInfo.is())
1654
0
                continue;
1655
1656
            // Now apply the updates to the CharInteropGrabBag of this run
1657
0
            uno::Sequence<beans::PropertyValue> aCharInteropGrabBagSeq;
1658
0
            if (xRunPropSetInfo->hasPropertyByName(u"CharInteropGrabBag"_ustr))
1659
0
                xRunPropSet->getPropertyValue(u"CharInteropGrabBag"_ustr)
1660
0
                    >>= aCharInteropGrabBagSeq;
1661
0
            comphelper::SequenceAsHashMap aGrabBagMap(aCharInteropGrabBagSeq);
1662
0
            for (const auto& rProp : rUpdatePropVec)
1663
0
            {
1664
0
                aGrabBagMap[rProp.Name] = rProp.Value; // [] inserts if not exists
1665
0
            }
1666
0
            xRunPropSet->setPropertyValue(u"CharInteropGrabBag"_ustr,
1667
0
                                          uno::Any(aGrabBagMap.getAsConstPropertyValueList()));
1668
0
        }
1669
0
    }
1670
0
}
1671
1672
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */