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