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 <drawingml/lineproperties.hxx>
21 #include <vector>
22 #include <rtl/ustrbuf.hxx>
23 #include <osl/diagnose.h>
24 #include <com/sun/star/beans/NamedValue.hpp>
25 #include <com/sun/star/drawing/FlagSequence.hpp>
26 #include <com/sun/star/drawing/LineCap.hpp>
27 #include <com/sun/star/drawing/LineDash.hpp>
28 #include <com/sun/star/drawing/LineJoint.hpp>
29 #include <com/sun/star/drawing/LineStyle.hpp>
30 #include <com/sun/star/drawing/PointSequence.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/containerhelper.hxx>
35 #include <oox/helper/graphichelper.hxx>
36 #include <oox/token/tokens.hxx>
37
38 using namespace ::com::sun::star;
39 using namespace ::com::sun::star::beans;
40 using namespace ::com::sun::star::drawing;
41
42
43 namespace oox {
44 namespace drawingml {
45
46 namespace {
47
lclSetDashData(LineDash & orLineDash,sal_Int16 nDots,sal_Int32 nDotLen,sal_Int16 nDashes,sal_Int32 nDashLen,sal_Int32 nDistance)48 void lclSetDashData( LineDash& orLineDash, sal_Int16 nDots, sal_Int32 nDotLen,
49 sal_Int16 nDashes, sal_Int32 nDashLen, sal_Int32 nDistance )
50 {
51 orLineDash.Dots = nDots;
52 orLineDash.DotLen = nDotLen;
53 orLineDash.Dashes = nDashes;
54 orLineDash.DashLen = nDashLen;
55 orLineDash.Distance = nDistance;
56 }
57
58 /** Converts the specified preset dash to API dash.
59 */
lclConvertPresetDash(LineDash & orLineDash,sal_Int32 nPresetDash)60 void lclConvertPresetDash(LineDash& orLineDash, sal_Int32 nPresetDash)
61 {
62 switch( nPresetDash )
63 {
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;
67
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;
71
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;
76
77 default:
78 OSL_FAIL( "lclConvertPresetDash - unsupported preset dash" );
79 lclSetDashData( orLineDash, 1, 4, 0, 0, 3 );
80 }
81 orLineDash.DotLen *= 100;
82 orLineDash.DashLen *= 100;
83 orLineDash.Distance *= 100;
84 }
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 */
lclConvertCustomDash(LineDash & orLineDash,const LineProperties::DashStopVector & rCustomDash)91 void lclConvertCustomDash(LineDash& orLineDash, const LineProperties::DashStopVector& rCustomDash)
92 {
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;
98 orLineDash.Dots = 0;
99 for(const auto& rIt : rCustomDash)
100 {
101 if (rIt.first != orLineDash.DotLen)
102 break;
103 ++orLineDash.Dots;
104 }
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;
109 else
110 orLineDash.DashLen = 0;
111
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;
116 }
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 */
lclRecoverStandardDashStyles(LineDash & orLineDash,sal_Int32 nLineWidth)123 void lclRecoverStandardDashStyles(LineDash& orLineDash, sal_Int32 nLineWidth)
124 {
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;
137 }
138 else if (nDots == 1 && nDotLen == 100 && nDashes == 0 && nDistance == 50)
139 { // "Ultrafine Dotted (var)"
140 orLineDash.DotLen = 0;
141 }
142 else if (nDots == 2 && nDashes == 0 && nDotLen == nDistance
143 && std::abs(nDistance * fWidthHelp - 51.0) < fWidthHelp)
144 { // "Ultrafine Dashed"
145 orLineDash.Dots = 1;
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;
151 }
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;
160 }
161 else if (nDots == 1 && nDotLen == 100 && nDashes == 0
162 && std::abs(nDistance * fWidthHelp - 457.0) < fWidthHelp)
163 { // "Fine Dotted"
164 orLineDash.DotLen = 0;
165 orLineDash.Distance = 457;
166 orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT;
167 }
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;
175 }
176 else if (nDots == 2 && nDotLen == 100 && nDashes == 1 && nDashLen == nDistance
177 && std::abs(nDistance * fWidthHelp - 203.0) < fWidthHelp)
178 { // "2 Dots 1 Dash"
179 orLineDash.DotLen = 0;
180 orLineDash.DashLen = 203;
181 orLineDash.Distance = 203;
182 orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT;
183 }
184 }
185
lclGetDashStyle(sal_Int32 nToken)186 DashStyle lclGetDashStyle( sal_Int32 nToken )
187 {
188 OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
189 // MS Office dashing is always relative to line width
190 switch( nToken )
191 {
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
195 }
196 return DashStyle_RECTRELATIVE;
197 }
198
lclGetLineCap(sal_Int32 nToken)199 LineCap lclGetLineCap( sal_Int32 nToken )
200 {
201 OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
202 switch( nToken )
203 {
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
207 }
208 return LineCap_BUTT;
209 }
210
lclGetLineJoint(sal_Int32 nToken)211 LineJoint lclGetLineJoint( sal_Int32 nToken )
212 {
213 OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
214 switch( nToken )
215 {
216 case XML_round: return LineJoint_ROUND;
217 case XML_bevel: return LineJoint_BEVEL;
218 case XML_miter: return LineJoint_MITER;
219 }
220 return LineJoint_ROUND;
221 }
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
lclGetArrowSize(sal_Int32 nToken)227 sal_Int32 lclGetArrowSize( sal_Int32 nToken )
228 {
229 OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
230 switch( nToken )
231 {
232 case XML_sm: return OOX_ARROWSIZE_SMALL;
233 case XML_med: return OOX_ARROWSIZE_MEDIUM;
234 case XML_lg: return OOX_ARROWSIZE_LARGE;
235 }
236 return OOX_ARROWSIZE_MEDIUM;
237 }
238
lclPushMarkerProperties(ShapePropertyMap & rPropMap,const LineArrowProperties & rArrowProps,sal_Int32 nLineWidth,bool bLineEnd)239 void lclPushMarkerProperties( ShapePropertyMap& rPropMap,
240 const LineArrowProperties& rArrowProps, sal_Int32 nLineWidth, bool bLineEnd )
241 {
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;
245
246 OUStringBuffer aBuffer;
247 sal_Int32 nMarkerWidth = 0;
248 bool bMarkerCenter = false;
249 sal_Int32 nArrowType = rArrowProps.moArrowType.get( XML_none );
250 OSL_ASSERT((nArrowType & sal_Int32(0xFFFF0000))==0);
251 switch( nArrowType )
252 {
253 case XML_triangle:
254 aBuffer.append( "msArrowEnd" );
255 break;
256 case XML_arrow:
257 aBuffer.append( "msArrowOpenEnd" );
258 break;
259 case XML_stealth:
260 aBuffer.append( "msArrowStealthEnd" );
261 break;
262 case XML_diamond:
263 aBuffer.append( "msArrowDiamondEnd" );
264 bMarkerCenter = true;
265 break;
266 case XML_oval:
267 aBuffer.append( "msArrowOvalEnd" );
268 bMarkerCenter = true;
269 break;
270 }
271
272 if( !aBuffer.isEmpty() )
273 {
274 bool bIsArrow = nArrowType == XML_arrow;
275 sal_Int32 nLength = lclGetArrowSize( rArrowProps.moArrowLength.get( XML_med ) );
276 sal_Int32 nWidth = lclGetArrowSize( rArrowProps.moArrowWidth.get( XML_med ) );
277
278 sal_Int32 nNameIndex = nWidth * 3 + nLength + 1;
279 aBuffer.append( ' ' ).append( nNameIndex );
280 if (bIsArrow)
281 {
282 // Arrow marker form depends also on line width
283 aBuffer.append(' ').append(nLineWidth);
284 }
285 OUString aMarkerName = aBuffer.makeStringAndClear();
286
287 double fArrowLength = 1.0;
288 switch( nLength )
289 {
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;
293 }
294 double fArrowWidth = 1.0;
295 switch( nWidth )
296 {
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;
300 }
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 );
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 if( !rPropMap.hasNamedLineMarkerInTable( aMarkerName ) )
310 {
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 );
317
318 ::std::vector< awt::Point > aPoints;
319 OSL_ASSERT((rArrowProps.moArrowType.get() & sal_Int32(0xFFFF0000))==0);
320 switch( rArrowProps.moArrowType.get() )
321 {
322 case XML_triangle:
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 ) );
327 break;
328 case XML_arrow:
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 ) );
339 break;
340 case XML_stealth:
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 ) );
346 break;
347 case XML_diamond:
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 ) );
353 break;
354 case XML_oval:
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 ) );
368 break;
369 }
370
371 OSL_ENSURE( !aPoints.empty(), "lclPushMarkerProperties - missing arrow coordinates" );
372 if( !aPoints.empty() )
373 {
374 PolyPolygonBezierCoords aMarkerCoords;
375 aMarkerCoords.Coordinates.realloc( 1 );
376 aMarkerCoords.Coordinates[ 0 ] = ContainerHelper::vectorToSequence( aPoints );
377
378 ::std::vector< PolygonFlags > aFlags( aPoints.size(), PolygonFlags_NORMAL );
379 aMarkerCoords.Flags.realloc( 1 );
380 aMarkerCoords.Flags[ 0 ] = ContainerHelper::vectorToSequence( aFlags );
381
382 aNamedMarker.Name = aMarkerName;
383 aNamedMarker.Value <<= aMarkerCoords;
384 }
385 }
386 else
387 {
388 /* Named marker object exists already in the marker table, pass
389 its name only. This will set the name as property value, but
390 does not create a new object in the marker table. */
391 aNamedMarker.Name = aMarkerName;
392 }
393 }
394
395 // push the properties (filled aNamedMarker.Name indicates valid marker)
396 if( !aNamedMarker.Name.isEmpty() )
397 {
398 if( bLineEnd )
399 {
400 rPropMap.setProperty( ShapeProperty::LineEnd, aNamedMarker );
401 rPropMap.setProperty( ShapeProperty::LineEndWidth, nMarkerWidth );
402 rPropMap.setProperty( ShapeProperty::LineEndCenter, bMarkerCenter );
403 }
404 else
405 {
406 rPropMap.setProperty( ShapeProperty::LineStart, aNamedMarker );
407 rPropMap.setProperty( ShapeProperty::LineStartWidth, nMarkerWidth );
408 rPropMap.setProperty( ShapeProperty::LineStartCenter, bMarkerCenter );
409 }
410 }
411 }
412
413 } // namespace
414
assignUsed(const LineArrowProperties & rSourceProps)415 void LineArrowProperties::assignUsed( const LineArrowProperties& rSourceProps )
416 {
417 moArrowType.assignIfUsed( rSourceProps.moArrowType );
418 moArrowWidth.assignIfUsed( rSourceProps.moArrowWidth );
419 moArrowLength.assignIfUsed( rSourceProps.moArrowLength );
420 }
421
assignUsed(const LineProperties & rSourceProps)422 void LineProperties::assignUsed( const LineProperties& rSourceProps )
423 {
424 maStartArrow.assignUsed( rSourceProps.maStartArrow );
425 maEndArrow.assignUsed( rSourceProps.maEndArrow );
426 maLineFill.assignUsed( rSourceProps.maLineFill );
427 if( !rSourceProps.maCustomDash.empty() )
428 maCustomDash = rSourceProps.maCustomDash;
429 moLineWidth.assignIfUsed( rSourceProps.moLineWidth );
430 moPresetDash.assignIfUsed( rSourceProps.moPresetDash );
431 moLineCompound.assignIfUsed( rSourceProps.moLineCompound );
432 moLineCap.assignIfUsed( rSourceProps.moLineCap );
433 moLineJoint.assignIfUsed( rSourceProps.moLineJoint );
434 }
435
pushToPropMap(ShapePropertyMap & rPropMap,const GraphicHelper & rGraphicHelper,::Color nPhClr) const436 void LineProperties::pushToPropMap( ShapePropertyMap& rPropMap,
437 const GraphicHelper& rGraphicHelper, ::Color nPhClr ) const
438 {
439 // line fill type must exist, otherwise ignore other properties
440 if( maLineFill.moFillType.has() )
441 {
442 // line style (our core only supports none and solid)
443 drawing::LineStyle eLineStyle = (maLineFill.moFillType.get() == XML_noFill) ? drawing::LineStyle_NONE : drawing::LineStyle_SOLID;
444
445 // line width in 1/100mm
446 sal_Int32 nLineWidth = getLineWidth(); // includes conversion from EMUs to 1/100mm
447 rPropMap.setProperty( ShapeProperty::LineWidth, nLineWidth );
448
449 // create line dash from preset dash token or dash stop vector (not for invisible line)
450 if( (eLineStyle != drawing::LineStyle_NONE) && (moPresetDash.differsFrom( XML_solid ) || !maCustomDash.empty()) )
451 {
452 LineDash aLineDash;
453 aLineDash.Style = lclGetDashStyle( moLineCap.get( XML_flat ) );
454
455 if(moPresetDash.differsFrom(XML_solid))
456 lclConvertPresetDash(aLineDash, moPresetDash.get(XML_dash));
457 else // !maCustomDash.empty()
458 {
459 lclConvertCustomDash(aLineDash, maCustomDash);
460 lclRecoverStandardDashStyles(aLineDash, nLineWidth);
461 }
462 if( rPropMap.setProperty( ShapeProperty::LineDash, aLineDash ) )
463 eLineStyle = drawing::LineStyle_DASH;
464 }
465 // line cap type
466 if( moLineCap.has() )
467 rPropMap.setProperty( ShapeProperty::LineCap, lclGetLineCap( moLineCap.get() ) );
468
469 // set final line style property
470 rPropMap.setProperty( ShapeProperty::LineStyle, eLineStyle );
471
472 // line joint type
473 if( moLineJoint.has() )
474 rPropMap.setProperty( ShapeProperty::LineJoint, lclGetLineJoint( moLineJoint.get() ) );
475
476 // line color and transparence
477 Color aLineColor = maLineFill.getBestSolidColor();
478 if( aLineColor.isUsed() )
479 {
480 rPropMap.setProperty( ShapeProperty::LineColor, aLineColor.getColor( rGraphicHelper, nPhClr ) );
481 if( aLineColor.hasTransparency() )
482 rPropMap.setProperty( ShapeProperty::LineTransparency, aLineColor.getTransparency() );
483 }
484
485 // line markers
486 lclPushMarkerProperties( rPropMap, maStartArrow, nLineWidth, false );
487 lclPushMarkerProperties( rPropMap, maEndArrow, nLineWidth, true );
488 }
489 }
490
getLineStyle() const491 drawing::LineStyle LineProperties::getLineStyle() const
492 {
493 // rules to calculate the line style inferred from the code in LineProperties::pushToPropMap
494 return (maLineFill.moFillType.get() == XML_noFill) ?
495 drawing::LineStyle_NONE :
496 (moPresetDash.differsFrom( XML_solid ) || (!moPresetDash && !maCustomDash.empty())) ?
497 drawing::LineStyle_DASH :
498 drawing::LineStyle_SOLID;
499 }
500
getLineCap() const501 drawing::LineCap LineProperties::getLineCap() const
502 {
503 if( moLineCap.has() )
504 return lclGetLineCap( moLineCap.get() );
505
506 return drawing::LineCap_BUTT;
507 }
508
getLineJoint() const509 drawing::LineJoint LineProperties::getLineJoint() const
510 {
511 if( moLineJoint.has() )
512 return lclGetLineJoint( moLineJoint.get() );
513
514 return drawing::LineJoint_NONE;
515 }
516
getLineWidth() const517 sal_Int32 LineProperties::getLineWidth() const
518 {
519 return convertEmuToHmm( moLineWidth.get( 0 ) );
520 }
521
522 } // namespace drawingml
523 } // namespace oox
524
525 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
526