1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
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>
39 using namespace ::com::sun::star
;
40 using namespace ::com::sun::star::beans
;
41 using namespace ::com::sun::star::drawing
;
44 namespace oox::drawingml
{
48 void lclSetDashData( LineDash
& orLineDash
, sal_Int16 nDots
, sal_Int32 nDotLen
,
49 sal_Int16 nDashes
, sal_Int32 nDashLen
, sal_Int32 nDistance
)
51 orLineDash
.Dots
= nDots
;
52 orLineDash
.DotLen
= nDotLen
;
53 orLineDash
.Dashes
= nDashes
;
54 orLineDash
.DashLen
= nDashLen
;
55 orLineDash
.Distance
= nDistance
;
58 /** Converts the specified preset dash to API dash.
60 void lclConvertPresetDash(LineDash
& orLineDash
, sal_Int32 nPresetDash
)
64 case XML_dot
: lclSetDashData( orLineDash
, 1, 1, 0, 0, 3 ); break;
65 case XML_dash
: lclSetDashData( orLineDash
, 1, 4, 0, 0, 3 ); break;
66 case XML_dashDot
: lclSetDashData( orLineDash
, 1, 4, 1, 1, 3 ); break;
68 case XML_lgDash
: lclSetDashData( orLineDash
, 1, 8, 0, 0, 3 ); break;
69 case XML_lgDashDot
: lclSetDashData( orLineDash
, 1, 8, 1, 1, 3 ); break;
70 case XML_lgDashDotDot
: lclSetDashData( orLineDash
, 1, 8, 2, 1, 3 ); break;
72 case XML_sysDot
: lclSetDashData( orLineDash
, 1, 1, 0, 0, 1 ); break;
73 case XML_sysDash
: lclSetDashData( orLineDash
, 1, 3, 0, 0, 1 ); break;
74 case XML_sysDashDot
: lclSetDashData( orLineDash
, 1, 3, 1, 1, 1 ); break;
75 case XML_sysDashDotDot
: lclSetDashData( orLineDash
, 1, 3, 2, 1, 1 ); break;
78 OSL_FAIL( "lclConvertPresetDash - unsupported preset dash" );
79 lclSetDashData( orLineDash
, 1, 4, 0, 0, 3 );
81 orLineDash
.DotLen
*= 100;
82 orLineDash
.DashLen
*= 100;
83 orLineDash
.Distance
*= 100;
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.
91 void lclConvertCustomDash(LineDash
& orLineDash
, const LineProperties::DashStopVector
& rCustomDash
)
93 OSL_ASSERT(!rCustomDash
.empty());
94 // Assume all dash stops have the same sp values.
95 orLineDash
.Distance
= rCustomDash
[0].second
;
96 // First kind of dashes go to "Dots"
97 orLineDash
.DotLen
= rCustomDash
[0].first
;
99 for(const auto& rIt
: rCustomDash
)
101 if (rIt
.first
!= orLineDash
.DotLen
)
105 // All others go to "Dashes", we cannot handle more than two kinds.
106 orLineDash
.Dashes
= rCustomDash
.size() - orLineDash
.Dots
;
107 if (orLineDash
.Dashes
> 0)
108 orLineDash
.DashLen
= rCustomDash
[orLineDash
.Dots
].first
;
110 orLineDash
.DashLen
= 0;
112 // convert to API, e.g. 123% is 123000 in MS Office and 123 in our API
113 orLineDash
.DotLen
= orLineDash
.DotLen
/ 1000;
114 orLineDash
.DashLen
= orLineDash
.DashLen
/ 1000;
115 orLineDash
.Distance
= orLineDash
.Distance
/ 1000;
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.
123 void lclRecoverStandardDashStyles(LineDash
& orLineDash
, sal_Int32 nLineWidth
)
125 sal_uInt16 nDots
= orLineDash
.Dots
;
126 sal_uInt16 nDashes
= orLineDash
.Dashes
;
127 sal_uInt32 nDotLen
= orLineDash
.DotLen
;
128 sal_uInt32 nDashLen
= orLineDash
.DashLen
;
129 sal_uInt32 nDistance
= orLineDash
.Distance
;
130 // Use same ersatz for hairline as in export.
131 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", "Line Style 9" and "Dashed (var)" need no recover
134 if (nDots
== 3 && nDotLen
== 197 &&nDashes
== 3 && nDashLen
== 100 && nDistance
== 100)
135 { // "3 Dashes 3 Dots (var)"
136 orLineDash
.DashLen
= 0;
138 else if (nDots
== 1 && nDotLen
== 100 && nDashes
== 0 && nDistance
== 50)
139 { // "Ultrafine Dotted (var)"
140 orLineDash
.DotLen
= 0;
142 else if (nDots
== 2 && nDashes
== 0 && nDotLen
== nDistance
143 && std::abs(nDistance
* fWidthHelp
- 51.0) < fWidthHelp
)
144 { // "Ultrafine Dashed"
146 orLineDash
.DotLen
= 51;
147 orLineDash
.Dashes
= 1;
148 orLineDash
.DashLen
= 51;
149 orLineDash
.Distance
= 51;
150 orLineDash
.Style
= orLineDash
.Style
== DashStyle_ROUNDRELATIVE
? DashStyle_ROUND
: DashStyle_RECT
;
152 else if (nDots
== 2 && nDashes
== 3 && std::abs(nDotLen
* fWidthHelp
- 51.0) < fWidthHelp
153 && std::abs(nDashLen
* fWidthHelp
- 254.0) < fWidthHelp
154 && std::abs(nDistance
* fWidthHelp
- 127.0) < fWidthHelp
)
155 { // "Ultrafine 2 Dots 3 Dashes"
156 orLineDash
.DotLen
= 51;
157 orLineDash
.DashLen
= 254;
158 orLineDash
.Distance
= 127;
159 orLineDash
.Style
= orLineDash
.Style
== DashStyle_ROUNDRELATIVE
? DashStyle_ROUND
: DashStyle_RECT
;
161 else if (nDots
== 1 && nDotLen
== 100 && nDashes
== 0
162 && std::abs(nDistance
* fWidthHelp
- 457.0) < fWidthHelp
)
164 orLineDash
.DotLen
= 0;
165 orLineDash
.Distance
= 457;
166 orLineDash
.Style
= orLineDash
.Style
== DashStyle_ROUNDRELATIVE
? DashStyle_ROUND
: DashStyle_RECT
;
168 else if (nDots
== 1 && nDashes
== 10 && nDashLen
== 100
169 && std::abs(nDistance
* fWidthHelp
- 152.0) < fWidthHelp
)
170 { // "Line with Fine Dots"
171 orLineDash
.DotLen
= 2007;
172 orLineDash
.DashLen
= 0;
173 orLineDash
.Distance
= 152;
174 orLineDash
.Style
= orLineDash
.Style
== DashStyle_ROUNDRELATIVE
? DashStyle_ROUND
: DashStyle_RECT
;
176 else if (nDots
== 2 && nDotLen
== 100 && nDashes
== 1 && nDashLen
== nDistance
177 && std::abs(nDistance
* fWidthHelp
- 203.0) < fWidthHelp
)
179 orLineDash
.DotLen
= 0;
180 orLineDash
.DashLen
= 203;
181 orLineDash
.Distance
= 203;
182 orLineDash
.Style
= orLineDash
.Style
== DashStyle_ROUNDRELATIVE
? DashStyle_ROUND
: DashStyle_RECT
;
186 DashStyle
lclGetDashStyle( sal_Int32 nToken
)
188 OSL_ASSERT((nToken
& sal_Int32(0xFFFF0000))==0);
189 // MS Office dashing is always relative to line width
192 case XML_rnd
: return DashStyle_ROUNDRELATIVE
;
193 case XML_sq
: return DashStyle_RECTRELATIVE
; // default in OOXML
194 case XML_flat
: return DashStyle_RECTRELATIVE
; // default in MS Office
196 return DashStyle_RECTRELATIVE
;
199 LineCap
lclGetLineCap( sal_Int32 nToken
)
201 OSL_ASSERT((nToken
& sal_Int32(0xFFFF0000))==0);
204 case XML_rnd
: return LineCap_ROUND
;
205 case XML_sq
: return LineCap_SQUARE
; // default in OOXML
206 case XML_flat
: return LineCap_BUTT
; // default in MS Office
211 LineJoint
lclGetLineJoint( sal_Int32 nToken
)
213 OSL_ASSERT((nToken
& sal_Int32(0xFFFF0000))==0);
216 case XML_round
: return LineJoint_ROUND
;
217 case XML_bevel
: return LineJoint_BEVEL
;
218 case XML_miter
: return LineJoint_MITER
;
220 return LineJoint_ROUND
;
223 const sal_Int32 OOX_ARROWSIZE_SMALL
= 0;
224 const sal_Int32 OOX_ARROWSIZE_MEDIUM
= 1;
225 const sal_Int32 OOX_ARROWSIZE_LARGE
= 2;
227 sal_Int32
lclGetArrowSize( sal_Int32 nToken
)
229 OSL_ASSERT((nToken
& sal_Int32(0xFFFF0000))==0);
232 case XML_sm
: return OOX_ARROWSIZE_SMALL
;
233 case XML_med
: return OOX_ARROWSIZE_MEDIUM
;
234 case XML_lg
: return OOX_ARROWSIZE_LARGE
;
236 return OOX_ARROWSIZE_MEDIUM
;
239 void lclPushMarkerProperties( ShapePropertyMap
& rPropMap
,
240 const LineArrowProperties
& rArrowProps
, sal_Int32 nLineWidth
, bool bLineEnd
)
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 NamedValue aNamedMarker
;
246 OUStringBuffer aBuffer
;
247 sal_Int32 nMarkerWidth
= 0;
248 bool bMarkerCenter
= false;
249 sal_Int32 nArrowType
= rArrowProps
.moArrowType
.value_or( XML_none
);
250 OSL_ASSERT((nArrowType
& sal_Int32(0xFFFF0000))==0);
254 aBuffer
.append( "msArrowEnd" );
257 aBuffer
.append( "msArrowOpenEnd" );
260 aBuffer
.append( "msArrowStealthEnd" );
263 aBuffer
.append( "msArrowDiamondEnd" );
264 bMarkerCenter
= true;
267 aBuffer
.append( "msArrowOvalEnd" );
268 bMarkerCenter
= true;
272 if( !aBuffer
.isEmpty() )
274 bool bIsArrow
= nArrowType
== XML_arrow
;
275 sal_Int32 nLength
= lclGetArrowSize( rArrowProps
.moArrowLength
.value_or( XML_med
) );
276 sal_Int32 nWidth
= lclGetArrowSize( rArrowProps
.moArrowWidth
.value_or( XML_med
) );
278 sal_Int32 nNameIndex
= nWidth
* 3 + nLength
+ 1;
279 aBuffer
.append( " " + OUString::number( nNameIndex
));
282 // Arrow marker form depends also on line width
283 aBuffer
.append(" " + OUString::number(nLineWidth
));
285 OUString aMarkerName
= aBuffer
.makeStringAndClear();
287 double fArrowLength
= 1.0;
290 case OOX_ARROWSIZE_SMALL
: fArrowLength
= (bIsArrow
? 2.5 : 2.0); break;
291 case OOX_ARROWSIZE_MEDIUM
: fArrowLength
= (bIsArrow
? 3.5 : 3.0); break;
292 case OOX_ARROWSIZE_LARGE
: fArrowLength
= (bIsArrow
? 5.5 : 5.0); break;
294 double fArrowWidth
= 1.0;
297 case OOX_ARROWSIZE_SMALL
: fArrowWidth
= (bIsArrow
? 2.5 : 2.0); break;
298 case OOX_ARROWSIZE_MEDIUM
: fArrowWidth
= (bIsArrow
? 3.5 : 3.0); break;
299 case OOX_ARROWSIZE_LARGE
: fArrowWidth
= (bIsArrow
? 5.5 : 5.0); break;
301 // set arrow width relative to line width
302 sal_Int32 nBaseLineWidth
= ::std::max
< sal_Int32
>( nLineWidth
, 70 );
303 nMarkerWidth
= static_cast<sal_Int32
>( fArrowWidth
* nBaseLineWidth
);
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 if( !rPropMap
.hasNamedLineMarkerInTable( aMarkerName
) )
311 // pass X and Y as percentage to OOX_ARROW_POINT
312 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 const double fArrowLineHalfWidth
= ::std::max
< double >( 100.0 * 0.5 * nLineWidth
/ nMarkerWidth
, 1 );
318 ::std::vector
< awt::Point
> aPoints
;
319 OSL_ASSERT((rArrowProps
.moArrowType
.value() & sal_Int32(0xFFFF0000))==0);
320 switch( rArrowProps
.moArrowType
.value() )
323 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
324 aPoints
.push_back( OOX_ARROW_POINT( 100, 100 ) );
325 aPoints
.push_back( OOX_ARROW_POINT( 0, 100 ) );
326 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
329 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
330 aPoints
.push_back( OOX_ARROW_POINT( 100, 100 - fArrowLineHalfWidth
* 1.5) );
331 aPoints
.push_back( OOX_ARROW_POINT( 100 - fArrowLineHalfWidth
* 1.5, 100 ) );
332 aPoints
.push_back( OOX_ARROW_POINT( 50.0 + fArrowLineHalfWidth
, 5.5 * fArrowLineHalfWidth
) );
333 aPoints
.push_back( OOX_ARROW_POINT( 50.0 + fArrowLineHalfWidth
, 100 ) );
334 aPoints
.push_back( OOX_ARROW_POINT( 50.0 - fArrowLineHalfWidth
, 100 ) );
335 aPoints
.push_back( OOX_ARROW_POINT( 50.0 - fArrowLineHalfWidth
, 5.5 * fArrowLineHalfWidth
) );
336 aPoints
.push_back( OOX_ARROW_POINT( fArrowLineHalfWidth
* 1.5, 100 ) );
337 aPoints
.push_back( OOX_ARROW_POINT( 0, 100 - fArrowLineHalfWidth
* 1.5) );
338 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
341 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
342 aPoints
.push_back( OOX_ARROW_POINT( 100, 100 ) );
343 aPoints
.push_back( OOX_ARROW_POINT( 50, 60 ) );
344 aPoints
.push_back( OOX_ARROW_POINT( 0, 100 ) );
345 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
348 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
349 aPoints
.push_back( OOX_ARROW_POINT( 100, 50 ) );
350 aPoints
.push_back( OOX_ARROW_POINT( 50, 100 ) );
351 aPoints
.push_back( OOX_ARROW_POINT( 0, 50 ) );
352 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
355 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
356 aPoints
.push_back( OOX_ARROW_POINT( 75, 7 ) );
357 aPoints
.push_back( OOX_ARROW_POINT( 93, 25 ) );
358 aPoints
.push_back( OOX_ARROW_POINT( 100, 50 ) );
359 aPoints
.push_back( OOX_ARROW_POINT( 93, 75 ) );
360 aPoints
.push_back( OOX_ARROW_POINT( 75, 93 ) );
361 aPoints
.push_back( OOX_ARROW_POINT( 50, 100 ) );
362 aPoints
.push_back( OOX_ARROW_POINT( 25, 93 ) );
363 aPoints
.push_back( OOX_ARROW_POINT( 7, 75 ) );
364 aPoints
.push_back( OOX_ARROW_POINT( 0, 50 ) );
365 aPoints
.push_back( OOX_ARROW_POINT( 7, 25 ) );
366 aPoints
.push_back( OOX_ARROW_POINT( 25, 7 ) );
367 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
371 OSL_ENSURE( !aPoints
.empty(), "lclPushMarkerProperties - missing arrow coordinates" );
372 if( !aPoints
.empty() )
374 PolyPolygonBezierCoords aMarkerCoords
;
375 aMarkerCoords
.Coordinates
= { comphelper::containerToSequence( aPoints
) };
377 ::std::vector
< PolygonFlags
> aFlags( aPoints
.size(), PolygonFlags_NORMAL
);
378 aMarkerCoords
.Flags
= { comphelper::containerToSequence( aFlags
) };
380 aNamedMarker
.Name
= aMarkerName
;
381 aNamedMarker
.Value
<<= aMarkerCoords
;
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 aNamedMarker
.Name
= aMarkerName
;
393 // push the properties (filled aNamedMarker.Name indicates valid marker)
394 if( aNamedMarker
.Name
.isEmpty() )
399 rPropMap
.setProperty( ShapeProperty::LineEnd
, aNamedMarker
);
400 rPropMap
.setProperty( ShapeProperty::LineEndWidth
, nMarkerWidth
);
401 rPropMap
.setProperty( ShapeProperty::LineEndCenter
, bMarkerCenter
);
405 rPropMap
.setProperty( ShapeProperty::LineStart
, aNamedMarker
);
406 rPropMap
.setProperty( ShapeProperty::LineStartWidth
, nMarkerWidth
);
407 rPropMap
.setProperty( ShapeProperty::LineStartCenter
, bMarkerCenter
);
413 void LineArrowProperties::assignUsed( const LineArrowProperties
& rSourceProps
)
415 assignIfUsed( moArrowType
, rSourceProps
.moArrowType
);
416 assignIfUsed( moArrowWidth
, rSourceProps
.moArrowWidth
);
417 assignIfUsed( moArrowLength
, rSourceProps
.moArrowLength
);
420 void LineProperties::assignUsed( const LineProperties
& rSourceProps
)
422 maStartArrow
.assignUsed( rSourceProps
.maStartArrow
);
423 maEndArrow
.assignUsed( rSourceProps
.maEndArrow
);
424 maLineFill
.assignUsed( rSourceProps
.maLineFill
);
425 if( !rSourceProps
.maCustomDash
.empty() )
426 maCustomDash
= rSourceProps
.maCustomDash
;
427 assignIfUsed( moLineWidth
, rSourceProps
.moLineWidth
);
428 assignIfUsed( moPresetDash
, rSourceProps
.moPresetDash
);
429 assignIfUsed( moLineCompound
, rSourceProps
.moLineCompound
);
430 assignIfUsed( moLineCap
, rSourceProps
.moLineCap
);
431 assignIfUsed( moLineJoint
, rSourceProps
.moLineJoint
);
434 void LineProperties::pushToPropMap( ShapePropertyMap
& rPropMap
,
435 const GraphicHelper
& rGraphicHelper
, ::Color nPhClr
, sal_Int16 nPhClrTheme
) const
437 // line fill type must exist, otherwise ignore other properties
438 if( !maLineFill
.moFillType
.has_value() )
441 // line style (our core only supports none and solid)
442 drawing::LineStyle eLineStyle
= (maLineFill
.moFillType
.value() == XML_noFill
) ? drawing::LineStyle_NONE
: drawing::LineStyle_SOLID
;
444 // line width in 1/100mm
445 sal_Int32 nLineWidth
= getLineWidth(); // includes conversion from EMUs to 1/100mm
446 rPropMap
.setProperty( ShapeProperty::LineWidth
, nLineWidth
);
449 LineCap eLineCap
= moLineCap
.has_value() ? lclGetLineCap( moLineCap
.value() ) : LineCap_BUTT
;
450 if( moLineCap
.has_value() )
451 rPropMap
.setProperty( ShapeProperty::LineCap
, eLineCap
);
453 // create line dash from preset dash token or dash stop vector (not for invisible line)
454 if( (eLineStyle
!= drawing::LineStyle_NONE
) &&
455 ((moPresetDash
.has_value() && moPresetDash
.value() != XML_solid
) || !maCustomDash
.empty()) )
458 aLineDash
.Style
= lclGetDashStyle( moLineCap
.value_or( XML_flat
) );
460 if(moPresetDash
.has_value() && moPresetDash
.value() != XML_solid
)
461 lclConvertPresetDash(aLineDash
, moPresetDash
.value_or(XML_dash
));
462 else // !maCustomDash.empty()
464 lclConvertCustomDash(aLineDash
, maCustomDash
);
465 lclRecoverStandardDashStyles(aLineDash
, nLineWidth
);
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 if (eLineCap
== LineCap_ROUND
|| (eLineCap
== LineCap_SQUARE
&& maCustomDash
.empty()))
473 // Cannot use -100 because that results in 0 length in some cases and
474 // LibreOffice interprets 0 length as 100%.
475 if (aLineDash
.DotLen
>= 100 || aLineDash
.DashLen
>= 100)
476 aLineDash
.Distance
+= 99;
477 if (aLineDash
.DotLen
>= 100)
478 aLineDash
.DotLen
-= 99;
479 if (aLineDash
.DashLen
>= 100)
480 aLineDash
.DashLen
-= 99;
483 if( rPropMap
.setProperty( ShapeProperty::LineDash
, aLineDash
) )
484 eLineStyle
= drawing::LineStyle_DASH
;
487 // set final line style property
488 rPropMap
.setProperty( ShapeProperty::LineStyle
, eLineStyle
);
491 if( moLineJoint
.has_value() )
492 rPropMap
.setProperty( ShapeProperty::LineJoint
, lclGetLineJoint( moLineJoint
.value() ) );
494 // line color and transparence
495 Color aLineColor
= maLineFill
.getBestSolidColor();
496 if (aLineColor
.isUsed())
498 ::Color aColor
= aLineColor
.getColor(rGraphicHelper
, nPhClr
);
499 rPropMap
.setProperty(ShapeProperty::LineColor
, aColor
);
500 if( aLineColor
.hasTransparency() )
501 rPropMap
.setProperty( ShapeProperty::LineTransparency
, aLineColor
.getTransparency() );
503 model::ComplexColor aComplexColor
;
505 if (aColor
== nPhClr
)
507 aComplexColor
.setSchemeColor(model::convertToThemeColorType(nPhClrTheme
));
508 rPropMap
.setProperty(PROP_LineComplexColor
, model::color::createXComplexColor(aComplexColor
));
512 aComplexColor
.setSchemeColor(model::convertToThemeColorType(aLineColor
.getSchemeColorIndex()));
513 if (aLineColor
.getLumMod() != 10000)
514 aComplexColor
.addTransformation({model::TransformationType::LumMod
, aLineColor
.getLumMod()});
515 if (aLineColor
.getLumOff() != 0)
516 aComplexColor
.addTransformation({model::TransformationType::LumOff
, aLineColor
.getLumOff()});
517 if (aLineColor
.getTintOrShade() > 0)
518 aComplexColor
.addTransformation({model::TransformationType::Tint
, aLineColor
.getTintOrShade()});
519 if (aLineColor
.getTintOrShade() < 0)
521 sal_Int16 nShade
= o3tl::narrowing
<sal_Int16
>(-aLineColor
.getTintOrShade());
522 aComplexColor
.addTransformation({model::TransformationType::Shade
, nShade
});
524 rPropMap
.setProperty(PROP_LineComplexColor
, model::color::createXComplexColor(aComplexColor
));
529 lclPushMarkerProperties( rPropMap
, maStartArrow
, nLineWidth
, false );
530 lclPushMarkerProperties( rPropMap
, maEndArrow
, nLineWidth
, true );
533 drawing::LineStyle
LineProperties::getLineStyle() const
535 // rules to calculate the line style inferred from the code in LineProperties::pushToPropMap
536 if (maLineFill
.moFillType
.value() == XML_noFill
)
537 return drawing::LineStyle_NONE
;
538 if ((moPresetDash
.has_value() && moPresetDash
.value() != XML_solid
) ||
539 (!moPresetDash
&& !maCustomDash
.empty()))
540 return drawing::LineStyle_DASH
;
541 return drawing::LineStyle_SOLID
;
544 drawing::LineCap
LineProperties::getLineCap() const
546 if( moLineCap
.has_value() )
547 return lclGetLineCap( moLineCap
.value() );
549 return drawing::LineCap_BUTT
;
552 drawing::LineJoint
LineProperties::getLineJoint() const
554 if( moLineJoint
.has_value() )
555 return lclGetLineJoint( moLineJoint
.value() );
557 return drawing::LineJoint_NONE
;
560 sal_Int32
LineProperties::getLineWidth() const
562 return convertEmuToHmm( moLineWidth
.value_or( 0 ) );
567 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */