Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/oox/source/drawingml/lineproperties.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <sal/config.h>
21
22
#include <comphelper/sequence.hxx>
23
#include <drawingml/lineproperties.hxx>
24
#include <rtl/ustrbuf.hxx>
25
#include <osl/diagnose.h>
26
#include <com/sun/star/beans/NamedValue.hpp>
27
#include <com/sun/star/drawing/LineCap.hpp>
28
#include <com/sun/star/drawing/LineDash.hpp>
29
#include <com/sun/star/drawing/LineJoint.hpp>
30
#include <com/sun/star/drawing/LineStyle.hpp>
31
#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
32
#include <oox/drawingml/drawingmltypes.hxx>
33
#include <oox/drawingml/shapepropertymap.hxx>
34
#include <oox/helper/graphichelper.hxx>
35
#include <oox/token/tokens.hxx>
36
#include <oox/token/properties.hxx>
37
#include <docmodel/uno/UnoComplexColor.hxx>
38
39
using namespace ::com::sun::star;
40
using namespace ::com::sun::star::beans;
41
using namespace ::com::sun::star::drawing;
42
43
44
namespace oox::drawingml {
45
46
namespace {
47
48
void lclSetDashData( LineDash& orLineDash, sal_Int16 nDots, sal_Int32 nDotLen,
49
        sal_Int16 nDashes, sal_Int32 nDashLen, sal_Int32 nDistance )
50
88
{
51
88
    orLineDash.Dots = nDots;
52
88
    orLineDash.DotLen = nDotLen;
53
88
    orLineDash.Dashes = nDashes;
54
88
    orLineDash.DashLen = nDashLen;
55
88
    orLineDash.Distance = nDistance;
56
88
}
57
58
/** Converts the specified preset dash to API dash.
59
 */
60
void lclConvertPresetDash(LineDash& orLineDash, sal_Int32 nPresetDash)
61
88
{
62
88
    switch( nPresetDash )
63
88
    {
64
0
        case XML_dot:           lclSetDashData( orLineDash, 1, 1, 0, 0, 3 );    break;
65
14
        case XML_dash:          lclSetDashData( orLineDash, 1, 4, 0, 0, 3 );    break;
66
6
        case XML_dashDot:       lclSetDashData( orLineDash, 1, 4, 1, 1, 3 );    break;
67
68
22
        case XML_lgDash:        lclSetDashData( orLineDash, 1, 8, 0, 0, 3 );    break;
69
6
        case XML_lgDashDot:     lclSetDashData( orLineDash, 1, 8, 1, 1, 3 );    break;
70
6
        case XML_lgDashDotDot:  lclSetDashData( orLineDash, 1, 8, 2, 1, 3 );    break;
71
72
6
        case XML_sysDot:        lclSetDashData( orLineDash, 1, 1, 0, 0, 1 );    break;
73
28
        case XML_sysDash:       lclSetDashData( orLineDash, 1, 3, 0, 0, 1 );    break;
74
0
        case XML_sysDashDot:    lclSetDashData( orLineDash, 1, 3, 1, 1, 1 );    break;
75
0
        case XML_sysDashDotDot: lclSetDashData( orLineDash, 1, 3, 2, 1, 1 );    break;
76
77
0
        default:
78
0
            OSL_FAIL( "lclConvertPresetDash - unsupported preset dash" );
79
0
            lclSetDashData( orLineDash, 1, 4, 0, 0, 3 );
80
88
    }
81
88
    orLineDash.DotLen *= 100;
82
88
    orLineDash.DashLen *= 100;
83
88
    orLineDash.Distance *= 100;
84
88
}
85
86
/** Converts the passed custom dash to API dash. rCustomDash should not be empty.
87
 * We assume, that there exist only two length values and the distance is the same
88
 * for all dashes. Other kind of dash stop sequences cannot be represented, neither
89
 * in model nor in ODF.
90
 */
91
void lclConvertCustomDash(LineDash& orLineDash, const LineProperties::DashStopVector& rCustomDash)
92
0
{
93
0
    OSL_ASSERT(!rCustomDash.empty());
94
    // Assume all dash stops have the same sp values.
95
0
    orLineDash.Distance = rCustomDash[0].second;
96
    // First kind of dashes go to "Dots"
97
0
    orLineDash.DotLen = rCustomDash[0].first;
98
0
    orLineDash.Dots = 0;
99
0
    for(const auto& rIt : rCustomDash)
100
0
    {
101
0
        if (rIt.first != orLineDash.DotLen)
102
0
            break;
103
0
        ++orLineDash.Dots;
104
0
    }
105
    // All others go to "Dashes", we cannot handle more than two kinds.
106
0
    orLineDash.Dashes = rCustomDash.size() - orLineDash.Dots;
107
0
    if (orLineDash.Dashes > 0)
108
0
        orLineDash.DashLen = rCustomDash[orLineDash.Dots].first;
109
0
    else
110
0
        orLineDash.DashLen = 0;
111
112
    // convert to API, e.g. 123% is 123000 in MS Office and 123 in our API
113
0
    orLineDash.DotLen = orLineDash.DotLen / 1000;
114
0
    orLineDash.DashLen = orLineDash.DashLen / 1000;
115
0
    orLineDash.Distance = orLineDash.Distance / 1000;
116
0
}
117
118
/** LibreOffice uses value 0, if a length attribute is missing in the
119
 * style definition, but treats it as 100%.
120
 * LibreOffice uses absolute values in some style definitions. Try to
121
 * reconstruct them from the imported relative values.
122
 */
123
void lclRecoverStandardDashStyles(LineDash& orLineDash, sal_Int32 nLineWidth)
124
0
{
125
0
    sal_uInt16 nDots = orLineDash.Dots;
126
0
    sal_uInt16 nDashes = orLineDash.Dashes;
127
0
    sal_uInt32 nDotLen = orLineDash.DotLen;
128
0
    sal_uInt32 nDashLen = orLineDash.DashLen;
129
0
    sal_uInt32 nDistance = orLineDash.Distance;
130
    // Use same ersatz for hairline as in export.
131
0
    double fWidthHelp = nLineWidth == 0 ? 26.95/100.0 : nLineWidth / 100.0;
132
    // start with (var) cases, because they have no rounding problems
133
    // "Fine Dashed", "Sparse Dash" and "Dashed (var)" need no recover
134
0
    if (nDots == 3 && nDotLen == 197 &&nDashes == 3 && nDashLen == 100 && nDistance == 100)
135
0
    {   // "3 Dashes 3 Dots (var)"
136
0
        orLineDash.DashLen = 0;
137
0
    }
138
0
    else if (nDots == 1 && nDotLen == 100 && nDashes == 0 && nDistance == 50)
139
0
    {   // "Ultrafine Dotted (var)"
140
0
        orLineDash.DotLen = 0;
141
0
    }
142
0
    else if (nDots == 2 && nDashes == 0 && nDotLen == nDistance
143
0
        && std::abs(nDistance * fWidthHelp - 51.0) < fWidthHelp)
144
0
    {   // "Ultrafine Dashed"
145
0
        orLineDash.Dots = 1;
146
0
        orLineDash.DotLen = 51;
147
0
        orLineDash.Dashes = 1;
148
0
        orLineDash.DashLen = 51;
149
0
        orLineDash.Distance = 51;
150
0
        orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT;
151
0
    }
152
0
    else if (nDots == 2 && nDashes == 3 && std::abs(nDotLen * fWidthHelp - 51.0) < fWidthHelp
153
0
        && std::abs(nDashLen * fWidthHelp - 254.0) < fWidthHelp
154
0
        && std::abs(nDistance * fWidthHelp - 127.0) < fWidthHelp)
155
0
    {   // "Ultrafine 2 Dots 3 Dashes"
156
0
        orLineDash.DotLen = 51;
157
0
        orLineDash.DashLen = 254;
158
0
        orLineDash.Distance = 127;
159
0
        orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT;
160
0
    }
161
0
    else if (nDots == 1 && nDotLen == 100 && nDashes == 0
162
0
        && std::abs(nDistance * fWidthHelp - 457.0) < fWidthHelp)
163
0
    {    // "Fine Dotted"
164
0
        orLineDash.DotLen = 0;
165
0
        orLineDash.Distance = 457;
166
0
        orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT;
167
0
    }
168
0
    else if (nDots == 1 && nDashes == 10 && nDashLen == 100
169
0
        && std::abs(nDistance * fWidthHelp - 152.0) < fWidthHelp)
170
0
    {   // "Line with Fine Dots"
171
0
        orLineDash.DotLen = 2007;
172
0
        orLineDash.DashLen = 0;
173
0
        orLineDash.Distance = 152;
174
0
        orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT;
175
0
    }
176
0
    else if (nDots == 2 && nDotLen == 100 && nDashes == 1 && nDashLen == nDistance
177
0
        && std::abs(nDistance * fWidthHelp - 203.0) < fWidthHelp)
178
0
    {   // "2 Dots 1 Dash"
179
0
        orLineDash.DotLen = 0;
180
0
        orLineDash.DashLen = 203;
181
0
        orLineDash.Distance = 203;
182
0
        orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT;
183
0
    }
184
0
}
185
186
DashStyle lclGetDashStyle( sal_Int32 nToken )
187
88
{
188
88
    OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
189
    // MS Office dashing is always relative to line width
190
88
    switch( nToken )
191
88
    {
192
0
        case XML_rnd:   return DashStyle_ROUNDRELATIVE;
193
0
        case XML_sq:    return DashStyle_RECTRELATIVE; // default in OOXML
194
88
        case XML_flat:  return DashStyle_RECTRELATIVE; // default in MS Office
195
88
    }
196
0
    return DashStyle_RECTRELATIVE;
197
88
}
198
199
LineCap lclGetLineCap( sal_Int32 nToken )
200
8.29k
{
201
8.29k
    OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
202
8.29k
    switch( nToken )
203
8.29k
    {
204
0
        case XML_rnd:   return LineCap_ROUND;
205
0
        case XML_sq:    return LineCap_SQUARE; // default in OOXML
206
8.29k
        case XML_flat:  return LineCap_BUTT; // default in MS Office
207
8.29k
    }
208
0
    return LineCap_BUTT;
209
8.29k
}
210
211
LineJoint lclGetLineJoint( sal_Int32 nToken )
212
11.7k
{
213
11.7k
    OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
214
11.7k
    switch( nToken )
215
11.7k
    {
216
7.36k
        case XML_round: return LineJoint_ROUND;
217
3
        case XML_bevel: return LineJoint_BEVEL;
218
4.38k
        case XML_miter: return LineJoint_MITER;
219
11.7k
    }
220
0
    return LineJoint_ROUND;
221
11.7k
}
222
223
const sal_Int32 OOX_ARROWSIZE_SMALL     = 0;
224
const sal_Int32 OOX_ARROWSIZE_MEDIUM    = 1;
225
const sal_Int32 OOX_ARROWSIZE_LARGE     = 2;
226
227
sal_Int32 lclGetArrowSize( sal_Int32 nToken )
228
1.40k
{
229
1.40k
    OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
230
1.40k
    switch( nToken )
231
1.40k
    {
232
96
        case XML_sm:    return OOX_ARROWSIZE_SMALL;
233
1.31k
        case XML_med:   return OOX_ARROWSIZE_MEDIUM;
234
0
        case XML_lg:    return OOX_ARROWSIZE_LARGE;
235
1.40k
    }
236
0
    return OOX_ARROWSIZE_MEDIUM;
237
1.40k
}
238
239
void lclPushMarkerProperties( ShapePropertyMap& rPropMap,
240
        const LineArrowProperties& rArrowProps, sal_Int32 nLineWidth, bool bLineEnd )
241
127k
{
242
    /*  Store the marker polygon and the marker name in a single value, to be
243
        able to pass both to the ShapePropertyMap::setProperty() function. */
244
127k
    NamedValue aNamedMarker;
245
246
127k
    OUStringBuffer aBuffer;
247
127k
    sal_Int32 nMarkerWidth = 0;
248
127k
    bool bMarkerCenter = false;
249
127k
    sal_Int32 nArrowType = rArrowProps.moArrowType.value_or( XML_none );
250
127k
    OSL_ASSERT((nArrowType & sal_Int32(0xFFFF0000))==0);
251
127k
    switch( nArrowType )
252
127k
    {
253
271
        case XML_triangle:
254
271
            aBuffer.append( "msArrowEnd" );
255
271
        break;
256
429
        case XML_arrow:
257
429
            aBuffer.append( "msArrowOpenEnd" );
258
429
        break;
259
3
        case XML_stealth:
260
3
            aBuffer.append( "msArrowStealthEnd" );
261
3
        break;
262
0
        case XML_diamond:
263
0
            aBuffer.append( "msArrowDiamondEnd" );
264
0
            bMarkerCenter = true;
265
0
        break;
266
0
        case XML_oval:
267
0
            aBuffer.append( "msArrowOvalEnd" );
268
0
            bMarkerCenter = true;
269
0
        break;
270
127k
    }
271
272
127k
    if( !aBuffer.isEmpty() )
273
703
    {
274
703
        bool bIsArrow = nArrowType == XML_arrow;
275
703
        sal_Int32 nLength = lclGetArrowSize( rArrowProps.moArrowLength.value_or( XML_med ) );
276
703
        sal_Int32 nWidth  = lclGetArrowSize( rArrowProps.moArrowWidth.value_or( XML_med ) );
277
278
703
        sal_Int32 nNameIndex = nWidth * 3 + nLength + 1;
279
703
        aBuffer.append( " " + OUString::number( nNameIndex ));
280
703
        if (bIsArrow)
281
429
        {
282
            // Arrow marker form depends also on line width
283
429
            aBuffer.append(" " + OUString::number(nLineWidth));
284
429
        }
285
703
        OUString aMarkerName = aBuffer.makeStringAndClear();
286
287
703
        double fArrowLength = 1.0;
288
703
        switch( nLength )
289
703
        {
290
48
            case OOX_ARROWSIZE_SMALL:   fArrowLength = (bIsArrow ? 2.5 : 2.0); break;
291
655
            case OOX_ARROWSIZE_MEDIUM:  fArrowLength = (bIsArrow ? 3.5 : 3.0); break;
292
0
            case OOX_ARROWSIZE_LARGE:   fArrowLength = (bIsArrow ? 5.5 : 5.0); break;
293
703
        }
294
703
        double fArrowWidth = 1.0;
295
703
        switch( nWidth )
296
703
        {
297
48
            case OOX_ARROWSIZE_SMALL:   fArrowWidth = (bIsArrow ? 2.5 : 2.0);  break;
298
655
            case OOX_ARROWSIZE_MEDIUM:  fArrowWidth = (bIsArrow ? 3.5 : 3.0);  break;
299
0
            case OOX_ARROWSIZE_LARGE:   fArrowWidth = (bIsArrow ? 5.5 : 5.0);  break;
300
703
        }
301
        // set arrow width relative to line width
302
703
        sal_Int32 nBaseLineWidth = ::std::max< sal_Int32 >( nLineWidth, 70 );
303
703
        nMarkerWidth = static_cast<sal_Int32>( fArrowWidth * nBaseLineWidth );
304
305
        /*  Test if the marker already exists in the marker table, do not
306
            create it again in this case. If markers are inserted explicitly
307
            instead by their name, the polygon will be created always.
308
            TODO: this can be optimized by using a map. */
309
703
        if( !rPropMap.hasNamedLineMarkerInTable( aMarkerName ) )
310
203
        {
311
            // pass X and Y as percentage to OOX_ARROW_POINT
312
1.36k
            auto OOX_ARROW_POINT = [fArrowLength, fArrowWidth]( double x, double y ) { return awt::Point( static_cast< sal_Int32 >( fArrowWidth * x ), static_cast< sal_Int32 >( fArrowLength * y ) ); };
313
            // tdf#100491 Arrow line marker, unlike other markers, depends on line width.
314
            // So calculate width of half line (more convenient during drawing) taking into account
315
            // further conversions/scaling done in OOX_ARROW_POINT and scaling to nMarkerWidth.
316
203
            const double fArrowLineHalfWidth = ::std::max< double >( 100.0 * 0.5 * nLineWidth / nMarkerWidth, 1 );
317
318
203
            ::std::vector< awt::Point > aPoints;
319
203
            OSL_ASSERT((rArrowProps.moArrowType.value() & sal_Int32(0xFFFF0000))==0);
320
203
            switch( rArrowProps.moArrowType.value() )
321
203
            {
322
108
                case XML_triangle:
323
108
                    aPoints.push_back( OOX_ARROW_POINT(  50,   0 ) );
324
108
                    aPoints.push_back( OOX_ARROW_POINT( 100, 100 ) );
325
108
                    aPoints.push_back( OOX_ARROW_POINT(   0, 100 ) );
326
108
                    aPoints.push_back( OOX_ARROW_POINT(  50,   0 ) );
327
108
                break;
328
92
                case XML_arrow:
329
92
                    aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) );
330
92
                    aPoints.push_back( OOX_ARROW_POINT( 100, 100 - fArrowLineHalfWidth * 1.5) );
331
92
                    aPoints.push_back( OOX_ARROW_POINT( 100 - fArrowLineHalfWidth * 1.5, 100 ) );
332
92
                    aPoints.push_back( OOX_ARROW_POINT( 50.0 + fArrowLineHalfWidth, 5.5 * fArrowLineHalfWidth) );
333
92
                    aPoints.push_back( OOX_ARROW_POINT( 50.0 + fArrowLineHalfWidth, 100 ) );
334
92
                    aPoints.push_back( OOX_ARROW_POINT( 50.0 - fArrowLineHalfWidth, 100 ) );
335
92
                    aPoints.push_back( OOX_ARROW_POINT( 50.0 - fArrowLineHalfWidth, 5.5 * fArrowLineHalfWidth) );
336
92
                    aPoints.push_back( OOX_ARROW_POINT( fArrowLineHalfWidth * 1.5, 100 ) );
337
92
                    aPoints.push_back( OOX_ARROW_POINT( 0, 100 - fArrowLineHalfWidth * 1.5) );
338
92
                    aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) );
339
92
                break;
340
3
                case XML_stealth:
341
3
                    aPoints.push_back( OOX_ARROW_POINT(  50,   0 ) );
342
3
                    aPoints.push_back( OOX_ARROW_POINT( 100, 100 ) );
343
3
                    aPoints.push_back( OOX_ARROW_POINT(  50,  60 ) );
344
3
                    aPoints.push_back( OOX_ARROW_POINT(   0, 100 ) );
345
3
                    aPoints.push_back( OOX_ARROW_POINT(  50,   0 ) );
346
3
                break;
347
0
                case XML_diamond:
348
0
                    aPoints.push_back( OOX_ARROW_POINT(  50,   0 ) );
349
0
                    aPoints.push_back( OOX_ARROW_POINT( 100,  50 ) );
350
0
                    aPoints.push_back( OOX_ARROW_POINT(  50, 100 ) );
351
0
                    aPoints.push_back( OOX_ARROW_POINT(   0,  50 ) );
352
0
                    aPoints.push_back( OOX_ARROW_POINT(  50,   0 ) );
353
0
                break;
354
0
                case XML_oval:
355
0
                    aPoints.push_back( OOX_ARROW_POINT(  50,   0 ) );
356
0
                    aPoints.push_back( OOX_ARROW_POINT(  75,   7 ) );
357
0
                    aPoints.push_back( OOX_ARROW_POINT(  93,  25 ) );
358
0
                    aPoints.push_back( OOX_ARROW_POINT( 100,  50 ) );
359
0
                    aPoints.push_back( OOX_ARROW_POINT(  93,  75 ) );
360
0
                    aPoints.push_back( OOX_ARROW_POINT(  75,  93 ) );
361
0
                    aPoints.push_back( OOX_ARROW_POINT(  50, 100 ) );
362
0
                    aPoints.push_back( OOX_ARROW_POINT(  25,  93 ) );
363
0
                    aPoints.push_back( OOX_ARROW_POINT(   7,  75 ) );
364
0
                    aPoints.push_back( OOX_ARROW_POINT(   0,  50 ) );
365
0
                    aPoints.push_back( OOX_ARROW_POINT(   7,  25 ) );
366
0
                    aPoints.push_back( OOX_ARROW_POINT(  25,   7 ) );
367
0
                    aPoints.push_back( OOX_ARROW_POINT(  50,   0 ) );
368
0
                break;
369
203
            }
370
371
203
            OSL_ENSURE( !aPoints.empty(), "lclPushMarkerProperties - missing arrow coordinates" );
372
203
            if( !aPoints.empty() )
373
203
            {
374
203
                PolyPolygonBezierCoords aMarkerCoords;
375
203
                aMarkerCoords.Coordinates = { comphelper::containerToSequence( aPoints ) };
376
377
203
                ::std::vector< PolygonFlags > aFlags( aPoints.size(), PolygonFlags_NORMAL );
378
203
                aMarkerCoords.Flags = { comphelper::containerToSequence( aFlags ) };
379
380
203
                aNamedMarker.Name = aMarkerName;
381
203
                aNamedMarker.Value <<= aMarkerCoords;
382
203
            }
383
203
        }
384
500
        else
385
500
        {
386
            /*  Named marker object exists already in the marker table, pass
387
                its name only. This will set the name as property value, but
388
                does not create a new object in the marker table. */
389
500
            aNamedMarker.Name = aMarkerName;
390
500
        }
391
703
    }
392
393
    // push the properties (filled aNamedMarker.Name indicates valid marker)
394
127k
    if( aNamedMarker.Name.isEmpty() )
395
126k
        return;
396
397
703
    if( bLineEnd )
398
540
    {
399
540
        rPropMap.setProperty( ShapeProperty::LineEnd, aNamedMarker );
400
540
        rPropMap.setProperty( ShapeProperty::LineEndWidth, nMarkerWidth );
401
540
        rPropMap.setProperty( ShapeProperty::LineEndCenter, bMarkerCenter );
402
540
    }
403
163
    else
404
163
    {
405
163
        rPropMap.setProperty( ShapeProperty::LineStart, aNamedMarker );
406
163
        rPropMap.setProperty( ShapeProperty::LineStartWidth, nMarkerWidth );
407
163
        rPropMap.setProperty( ShapeProperty::LineStartCenter, bMarkerCenter );
408
163
    }
409
703
}
410
411
} // namespace
412
413
void LineArrowProperties::assignUsed( const LineArrowProperties& rSourceProps )
414
376k
{
415
376k
    assignIfUsed( moArrowType, rSourceProps.moArrowType );
416
376k
    assignIfUsed( moArrowWidth, rSourceProps.moArrowWidth );
417
376k
    assignIfUsed( moArrowLength, rSourceProps.moArrowLength );
418
376k
}
419
420
void LineProperties::assignUsed( const LineProperties& rSourceProps )
421
188k
{
422
188k
    maStartArrow.assignUsed( rSourceProps.maStartArrow );
423
188k
    maEndArrow.assignUsed( rSourceProps.maEndArrow );
424
188k
    maLineFill.assignUsed( rSourceProps.maLineFill );
425
188k
    if( !rSourceProps.maCustomDash.empty() )
426
0
        maCustomDash = rSourceProps.maCustomDash;
427
188k
    assignIfUsed( moLineWidth, rSourceProps.moLineWidth );
428
188k
    assignIfUsed( moPresetDash, rSourceProps.moPresetDash );
429
188k
    assignIfUsed( moLineCompound, rSourceProps.moLineCompound );
430
188k
    assignIfUsed( moLineCap, rSourceProps.moLineCap );
431
188k
    assignIfUsed( moLineJoint, rSourceProps.moLineJoint );
432
188k
}
433
434
void LineProperties::pushToPropMap( ShapePropertyMap& rPropMap,
435
        const GraphicHelper& rGraphicHelper, ::Color nPhClr, sal_Int16 nPhClrTheme) const
436
63.6k
{
437
    // line fill type must exist, otherwise ignore other properties
438
63.6k
    if( !maLineFill.moFillType.has_value() )
439
0
        return;
440
441
    // line style (our core only supports none and solid)
442
63.6k
    drawing::LineStyle eLineStyle = (maLineFill.moFillType.value() == XML_noFill) ? drawing::LineStyle_NONE : drawing::LineStyle_SOLID;
443
444
    // line width in 1/100mm
445
63.6k
    sal_Int32 nLineWidth = getLineWidth(); // includes conversion from EMUs to 1/100mm
446
63.6k
    rPropMap.setProperty( ShapeProperty::LineWidth, nLineWidth );
447
448
    // line cap type
449
63.6k
    LineCap eLineCap = moLineCap.has_value() ? lclGetLineCap( moLineCap.value() ) : LineCap_BUTT;
450
63.6k
    if( moLineCap.has_value() )
451
7.29k
        rPropMap.setProperty( ShapeProperty::LineCap, eLineCap );
452
453
    // create line dash from preset dash token or dash stop vector (not for invisible line)
454
63.6k
    if( (eLineStyle != drawing::LineStyle_NONE) &&
455
12.0k
        ((moPresetDash.has_value() && moPresetDash.value() != XML_solid) || !maCustomDash.empty()) )
456
88
    {
457
88
        LineDash aLineDash;
458
88
        aLineDash.Style = lclGetDashStyle( moLineCap.value_or( XML_flat ) );
459
460
88
        if(moPresetDash.has_value() && moPresetDash.value() != XML_solid)
461
88
            lclConvertPresetDash(aLineDash, moPresetDash.value_or(XML_dash));
462
0
        else // !maCustomDash.empty()
463
0
        {
464
0
            lclConvertCustomDash(aLineDash, maCustomDash);
465
0
            lclRecoverStandardDashStyles(aLineDash, nLineWidth);
466
0
        }
467
468
        // In MS Office (2020) for preset dash style line caps round and square are included in dash length.
469
        // For custom dash style round line cap is included, square line cap is added. In ODF line caps are
470
        // always added to dash length. Tweak the length accordingly.
471
88
        if (eLineCap == LineCap_ROUND || (eLineCap == LineCap_SQUARE && maCustomDash.empty()))
472
0
        {
473
            // Cannot use -100 because that results in 0 length in some cases and
474
            // LibreOffice interprets 0 length as 100%.
475
0
            if (aLineDash.DotLen >= 100 || aLineDash.DashLen >= 100)
476
0
                aLineDash.Distance += 99;
477
0
            if (aLineDash.DotLen >= 100)
478
0
                aLineDash.DotLen -= 99;
479
0
            if (aLineDash.DashLen >= 100)
480
0
                aLineDash.DashLen -= 99;
481
0
        }
482
483
88
        if( rPropMap.setProperty( ShapeProperty::LineDash, aLineDash ) )
484
88
            eLineStyle = drawing::LineStyle_DASH;
485
88
    }
486
487
    // set final line style property
488
63.6k
    rPropMap.setProperty( ShapeProperty::LineStyle, eLineStyle );
489
490
    // line joint type
491
63.6k
    if( moLineJoint.has_value() )
492
11.6k
        rPropMap.setProperty( ShapeProperty::LineJoint, lclGetLineJoint( moLineJoint.value() ) );
493
494
    // line color and transparence
495
63.6k
    Color aLineColor = maLineFill.getBestSolidColor();
496
63.6k
    if (aLineColor.isUsed())
497
12.0k
    {
498
12.0k
        ::Color aColor = aLineColor.getColor(rGraphicHelper, nPhClr);
499
12.0k
        rPropMap.setProperty(ShapeProperty::LineColor, aColor);
500
12.0k
        if( aLineColor.hasTransparency() )
501
0
            rPropMap.setProperty( ShapeProperty::LineTransparency, aLineColor.getTransparency() );
502
503
12.0k
        model::ComplexColor aComplexColor;
504
505
12.0k
        if (aColor == nPhClr)
506
639
        {
507
639
            aComplexColor.setThemeColor(model::convertToThemeColorType(nPhClrTheme));
508
639
            rPropMap.setProperty(PROP_LineComplexColor, model::color::createXComplexColor(aComplexColor));
509
639
        }
510
11.4k
        else
511
11.4k
        {
512
11.4k
            aComplexColor.setThemeColor(model::convertToThemeColorType(aLineColor.getSchemeColorIndex()));
513
11.4k
            if (aLineColor.getLumMod() != 10000)
514
133
                aComplexColor.addTransformation({model::TransformationType::LumMod, aLineColor.getLumMod()});
515
11.4k
            if (aLineColor.getLumOff() != 0)
516
130
                aComplexColor.addTransformation({model::TransformationType::LumOff, aLineColor.getLumOff()});
517
11.4k
            if (aLineColor.getTintOrShade() > 0)
518
20
                aComplexColor.addTransformation({model::TransformationType::Tint, aLineColor.getTintOrShade()});
519
11.4k
            if (aLineColor.getTintOrShade() < 0)
520
175
            {
521
175
                sal_Int16 nShade = o3tl::narrowing<sal_Int16>(-aLineColor.getTintOrShade());
522
175
                aComplexColor.addTransformation({model::TransformationType::Shade, nShade});
523
175
            }
524
11.4k
            rPropMap.setProperty(PROP_LineComplexColor, model::color::createXComplexColor(aComplexColor));
525
11.4k
        }
526
12.0k
    }
527
528
    // line markers
529
63.6k
    lclPushMarkerProperties( rPropMap, maStartArrow, nLineWidth, false );
530
63.6k
    lclPushMarkerProperties( rPropMap, maEndArrow,   nLineWidth, true );
531
63.6k
}
532
533
drawing::LineStyle LineProperties::getLineStyle() const
534
3.43k
{
535
    // rules to calculate the line style inferred from the code in LineProperties::pushToPropMap
536
3.43k
    if (maLineFill.moFillType.value() == XML_noFill)
537
2.43k
        return drawing::LineStyle_NONE;
538
1.00k
    if ((moPresetDash.has_value() && moPresetDash.value() != XML_solid) ||
539
1.00k
        (!moPresetDash && !maCustomDash.empty()))
540
0
       return drawing::LineStyle_DASH;
541
1.00k
    return drawing::LineStyle_SOLID;
542
1.00k
}
543
544
drawing::LineCap LineProperties::getLineCap() const
545
3.43k
{
546
3.43k
    if( moLineCap.has_value() )
547
1.00k
        return lclGetLineCap( moLineCap.value() );
548
549
2.43k
    return drawing::LineCap_BUTT;
550
3.43k
}
551
552
drawing::LineJoint LineProperties::getLineJoint() const
553
3.43k
{
554
3.43k
    if( moLineJoint.has_value() )
555
109
        return lclGetLineJoint( moLineJoint.value() );
556
557
3.32k
    return drawing::LineJoint_NONE;
558
3.43k
}
559
560
sal_Int32 LineProperties::getLineWidth() const
561
67.0k
{
562
67.0k
    return convertEmuToHmm( moLineWidth.value_or( 0 ) );
563
67.0k
}
564
565
} // namespace oox
566
567
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */