LibreOffice Module oox (master)  1
lineproperties.cxx
Go to the documentation of this file.
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 
21 #include <rtl/ustrbuf.hxx>
22 #include <osl/diagnose.h>
23 #include <com/sun/star/beans/NamedValue.hpp>
24 #include <com/sun/star/drawing/LineCap.hpp>
25 #include <com/sun/star/drawing/LineDash.hpp>
26 #include <com/sun/star/drawing/LineJoint.hpp>
27 #include <com/sun/star/drawing/LineStyle.hpp>
28 #include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
33 #include <oox/token/tokens.hxx>
34 
35 using namespace ::com::sun::star;
36 using namespace ::com::sun::star::beans;
37 using namespace ::com::sun::star::drawing;
38 
39 
40 namespace oox::drawingml {
41 
42 namespace {
43 
44 void lclSetDashData( LineDash& orLineDash, sal_Int16 nDots, sal_Int32 nDotLen,
45  sal_Int16 nDashes, sal_Int32 nDashLen, sal_Int32 nDistance )
46 {
47  orLineDash.Dots = nDots;
48  orLineDash.DotLen = nDotLen;
49  orLineDash.Dashes = nDashes;
50  orLineDash.DashLen = nDashLen;
51  orLineDash.Distance = nDistance;
52 }
53 
56 void lclConvertPresetDash(LineDash& orLineDash, sal_Int32 nPresetDash)
57 {
58  switch( nPresetDash )
59  {
60  case XML_dot: lclSetDashData( orLineDash, 1, 1, 0, 0, 3 ); break;
61  case XML_dash: lclSetDashData( orLineDash, 1, 4, 0, 0, 3 ); break;
62  case XML_dashDot: lclSetDashData( orLineDash, 1, 4, 1, 1, 3 ); break;
63 
64  case XML_lgDash: lclSetDashData( orLineDash, 1, 8, 0, 0, 3 ); break;
65  case XML_lgDashDot: lclSetDashData( orLineDash, 1, 8, 1, 1, 3 ); break;
66  case XML_lgDashDotDot: lclSetDashData( orLineDash, 1, 8, 2, 1, 3 ); break;
67 
68  case XML_sysDot: lclSetDashData( orLineDash, 1, 1, 0, 0, 1 ); break;
69  case XML_sysDash: lclSetDashData( orLineDash, 1, 3, 0, 0, 1 ); break;
70  case XML_sysDashDot: lclSetDashData( orLineDash, 1, 3, 1, 1, 1 ); break;
71  case XML_sysDashDotDot: lclSetDashData( orLineDash, 1, 3, 2, 1, 1 ); break;
72 
73  default:
74  OSL_FAIL( "lclConvertPresetDash - unsupported preset dash" );
75  lclSetDashData( orLineDash, 1, 4, 0, 0, 3 );
76  }
77  orLineDash.DotLen *= 100;
78  orLineDash.DashLen *= 100;
79  orLineDash.Distance *= 100;
80 }
81 
87 void lclConvertCustomDash(LineDash& orLineDash, const LineProperties::DashStopVector& rCustomDash)
88 {
89  OSL_ASSERT(!rCustomDash.empty());
90  // Assume all dash stops have the same sp values.
91  orLineDash.Distance = rCustomDash[0].second;
92  // First kind of dashes go to "Dots"
93  orLineDash.DotLen = rCustomDash[0].first;
94  orLineDash.Dots = 0;
95  for(const auto& rIt : rCustomDash)
96  {
97  if (rIt.first != orLineDash.DotLen)
98  break;
99  ++orLineDash.Dots;
100  }
101  // All others go to "Dashes", we cannot handle more than two kinds.
102  orLineDash.Dashes = rCustomDash.size() - orLineDash.Dots;
103  if (orLineDash.Dashes > 0)
104  orLineDash.DashLen = rCustomDash[orLineDash.Dots].first;
105  else
106  orLineDash.DashLen = 0;
107 
108  // convert to API, e.g. 123% is 123000 in MS Office and 123 in our API
109  orLineDash.DotLen = orLineDash.DotLen / 1000;
110  orLineDash.DashLen = orLineDash.DashLen / 1000;
111  orLineDash.Distance = orLineDash.Distance / 1000;
112 }
113 
119 void lclRecoverStandardDashStyles(LineDash& orLineDash, sal_Int32 nLineWidth)
120 {
121  sal_uInt16 nDots = orLineDash.Dots;
122  sal_uInt16 nDashes = orLineDash.Dashes;
123  sal_uInt32 nDotLen = orLineDash.DotLen;
124  sal_uInt32 nDashLen = orLineDash.DashLen;
125  sal_uInt32 nDistance = orLineDash.Distance;
126  // Use same ersatz for hairline as in export.
127  double fWidthHelp = nLineWidth == 0 ? 26.95/100.0 : nLineWidth / 100.0;
128  // start with (var) cases, because they have no rounding problems
129  // "Fine Dashed", "Line Style 9" and "Dashed (var)" need no recover
130  if (nDots == 3 && nDotLen == 197 &&nDashes == 3 && nDashLen == 100 && nDistance == 100)
131  { // "3 Dashes 3 Dots (var)"
132  orLineDash.DashLen = 0;
133  }
134  else if (nDots == 1 && nDotLen == 100 && nDashes == 0 && nDistance == 50)
135  { // "Ultrafine Dotted (var)"
136  orLineDash.DotLen = 0;
137  }
138  else if (nDots == 2 && nDashes == 0 && nDotLen == nDistance
139  && std::abs(nDistance * fWidthHelp - 51.0) < fWidthHelp)
140  { // "Ultrafine Dashed"
141  orLineDash.Dots = 1;
142  orLineDash.DotLen = 51;
143  orLineDash.Dashes = 1;
144  orLineDash.DashLen = 51;
145  orLineDash.Distance = 51;
146  orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT;
147  }
148  else if (nDots == 2 && nDashes == 3 && std::abs(nDotLen * fWidthHelp - 51.0) < fWidthHelp
149  && std::abs(nDashLen * fWidthHelp - 254.0) < fWidthHelp
150  && std::abs(nDistance * fWidthHelp - 127.0) < fWidthHelp)
151  { // "Ultrafine 2 Dots 3 Dashes"
152  orLineDash.DotLen = 51;
153  orLineDash.DashLen = 254;
154  orLineDash.Distance = 127;
155  orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT;
156  }
157  else if (nDots == 1 && nDotLen == 100 && nDashes == 0
158  && std::abs(nDistance * fWidthHelp - 457.0) < fWidthHelp)
159  { // "Fine Dotted"
160  orLineDash.DotLen = 0;
161  orLineDash.Distance = 457;
162  orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT;
163  }
164  else if (nDots == 1 && nDashes == 10 && nDashLen == 100
165  && std::abs(nDistance * fWidthHelp - 152.0) < fWidthHelp)
166  { // "Line with Fine Dots"
167  orLineDash.DotLen = 2007;
168  orLineDash.DashLen = 0;
169  orLineDash.Distance = 152;
170  orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT;
171  }
172  else if (nDots == 2 && nDotLen == 100 && nDashes == 1 && nDashLen == nDistance
173  && std::abs(nDistance * fWidthHelp - 203.0) < fWidthHelp)
174  { // "2 Dots 1 Dash"
175  orLineDash.DotLen = 0;
176  orLineDash.DashLen = 203;
177  orLineDash.Distance = 203;
178  orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT;
179  }
180 }
181 
182 DashStyle lclGetDashStyle( sal_Int32 nToken )
183 {
184  OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
185  // MS Office dashing is always relative to line width
186  switch( nToken )
187  {
188  case XML_rnd: return DashStyle_ROUNDRELATIVE;
189  case XML_sq: return DashStyle_RECTRELATIVE; // default in OOXML
190  case XML_flat: return DashStyle_RECTRELATIVE; // default in MS Office
191  }
192  return DashStyle_RECTRELATIVE;
193 }
194 
195 LineCap lclGetLineCap( sal_Int32 nToken )
196 {
197  OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
198  switch( nToken )
199  {
200  case XML_rnd: return LineCap_ROUND;
201  case XML_sq: return LineCap_SQUARE; // default in OOXML
202  case XML_flat: return LineCap_BUTT; // default in MS Office
203  }
204  return LineCap_BUTT;
205 }
206 
207 LineJoint lclGetLineJoint( sal_Int32 nToken )
208 {
209  OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
210  switch( nToken )
211  {
212  case XML_round: return LineJoint_ROUND;
213  case XML_bevel: return LineJoint_BEVEL;
214  case XML_miter: return LineJoint_MITER;
215  }
216  return LineJoint_ROUND;
217 }
218 
219 const sal_Int32 OOX_ARROWSIZE_SMALL = 0;
220 const sal_Int32 OOX_ARROWSIZE_MEDIUM = 1;
221 const sal_Int32 OOX_ARROWSIZE_LARGE = 2;
222 
223 sal_Int32 lclGetArrowSize( sal_Int32 nToken )
224 {
225  OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
226  switch( nToken )
227  {
228  case XML_sm: return OOX_ARROWSIZE_SMALL;
229  case XML_med: return OOX_ARROWSIZE_MEDIUM;
230  case XML_lg: return OOX_ARROWSIZE_LARGE;
231  }
232  return OOX_ARROWSIZE_MEDIUM;
233 }
234 
235 void lclPushMarkerProperties( ShapePropertyMap& rPropMap,
236  const LineArrowProperties& rArrowProps, sal_Int32 nLineWidth, bool bLineEnd )
237 {
238  /* Store the marker polygon and the marker name in a single value, to be
239  able to pass both to the ShapePropertyMap::setProperty() function. */
240  NamedValue aNamedMarker;
241 
242  OUStringBuffer aBuffer;
243  sal_Int32 nMarkerWidth = 0;
244  bool bMarkerCenter = false;
245  sal_Int32 nArrowType = rArrowProps.moArrowType.get( XML_none );
246  OSL_ASSERT((nArrowType & sal_Int32(0xFFFF0000))==0);
247  switch( nArrowType )
248  {
249  case XML_triangle:
250  aBuffer.append( "msArrowEnd" );
251  break;
252  case XML_arrow:
253  aBuffer.append( "msArrowOpenEnd" );
254  break;
255  case XML_stealth:
256  aBuffer.append( "msArrowStealthEnd" );
257  break;
258  case XML_diamond:
259  aBuffer.append( "msArrowDiamondEnd" );
260  bMarkerCenter = true;
261  break;
262  case XML_oval:
263  aBuffer.append( "msArrowOvalEnd" );
264  bMarkerCenter = true;
265  break;
266  }
267 
268  if( !aBuffer.isEmpty() )
269  {
270  bool bIsArrow = nArrowType == XML_arrow;
271  sal_Int32 nLength = lclGetArrowSize( rArrowProps.moArrowLength.get( XML_med ) );
272  sal_Int32 nWidth = lclGetArrowSize( rArrowProps.moArrowWidth.get( XML_med ) );
273 
274  sal_Int32 nNameIndex = nWidth * 3 + nLength + 1;
275  aBuffer.append( ' ' ).append( nNameIndex );
276  if (bIsArrow)
277  {
278  // Arrow marker form depends also on line width
279  aBuffer.append(' ').append(nLineWidth);
280  }
281  OUString aMarkerName = aBuffer.makeStringAndClear();
282 
283  double fArrowLength = 1.0;
284  switch( nLength )
285  {
286  case OOX_ARROWSIZE_SMALL: fArrowLength = (bIsArrow ? 2.5 : 2.0); break;
287  case OOX_ARROWSIZE_MEDIUM: fArrowLength = (bIsArrow ? 3.5 : 3.0); break;
288  case OOX_ARROWSIZE_LARGE: fArrowLength = (bIsArrow ? 5.5 : 5.0); break;
289  }
290  double fArrowWidth = 1.0;
291  switch( nWidth )
292  {
293  case OOX_ARROWSIZE_SMALL: fArrowWidth = (bIsArrow ? 2.5 : 2.0); break;
294  case OOX_ARROWSIZE_MEDIUM: fArrowWidth = (bIsArrow ? 3.5 : 3.0); break;
295  case OOX_ARROWSIZE_LARGE: fArrowWidth = (bIsArrow ? 5.5 : 5.0); break;
296  }
297  // set arrow width relative to line width
298  sal_Int32 nBaseLineWidth = ::std::max< sal_Int32 >( nLineWidth, 70 );
299  nMarkerWidth = static_cast<sal_Int32>( fArrowWidth * nBaseLineWidth );
300 
301  /* Test if the marker already exists in the marker table, do not
302  create it again in this case. If markers are inserted explicitly
303  instead by their name, the polygon will be created always.
304  TODO: this can be optimized by using a map. */
305  if( !rPropMap.hasNamedLineMarkerInTable( aMarkerName ) )
306  {
307  // pass X and Y as percentage to OOX_ARROW_POINT
308  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 ) ); };
309  // tdf#100491 Arrow line marker, unlike other markers, depends on line width.
310  // So calculate width of half line (more convenient during drawing) taking into account
311  // further conversions/scaling done in OOX_ARROW_POINT and scaling to nMarkerWidth.
312  const double fArrowLineHalfWidth = ::std::max< double >( 100.0 * 0.5 * nLineWidth / nMarkerWidth, 1 );
313 
314  ::std::vector< awt::Point > aPoints;
315  OSL_ASSERT((rArrowProps.moArrowType.get() & sal_Int32(0xFFFF0000))==0);
316  switch( rArrowProps.moArrowType.get() )
317  {
318  case XML_triangle:
319  aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) );
320  aPoints.push_back( OOX_ARROW_POINT( 100, 100 ) );
321  aPoints.push_back( OOX_ARROW_POINT( 0, 100 ) );
322  aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) );
323  break;
324  case XML_arrow:
325  aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) );
326  aPoints.push_back( OOX_ARROW_POINT( 100, 100 - fArrowLineHalfWidth * 1.5) );
327  aPoints.push_back( OOX_ARROW_POINT( 100 - fArrowLineHalfWidth * 1.5, 100 ) );
328  aPoints.push_back( OOX_ARROW_POINT( 50.0 + fArrowLineHalfWidth, 5.5 * fArrowLineHalfWidth) );
329  aPoints.push_back( OOX_ARROW_POINT( 50.0 + fArrowLineHalfWidth, 100 ) );
330  aPoints.push_back( OOX_ARROW_POINT( 50.0 - fArrowLineHalfWidth, 100 ) );
331  aPoints.push_back( OOX_ARROW_POINT( 50.0 - fArrowLineHalfWidth, 5.5 * fArrowLineHalfWidth) );
332  aPoints.push_back( OOX_ARROW_POINT( fArrowLineHalfWidth * 1.5, 100 ) );
333  aPoints.push_back( OOX_ARROW_POINT( 0, 100 - fArrowLineHalfWidth * 1.5) );
334  aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) );
335  break;
336  case XML_stealth:
337  aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) );
338  aPoints.push_back( OOX_ARROW_POINT( 100, 100 ) );
339  aPoints.push_back( OOX_ARROW_POINT( 50, 60 ) );
340  aPoints.push_back( OOX_ARROW_POINT( 0, 100 ) );
341  aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) );
342  break;
343  case XML_diamond:
344  aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) );
345  aPoints.push_back( OOX_ARROW_POINT( 100, 50 ) );
346  aPoints.push_back( OOX_ARROW_POINT( 50, 100 ) );
347  aPoints.push_back( OOX_ARROW_POINT( 0, 50 ) );
348  aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) );
349  break;
350  case XML_oval:
351  aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) );
352  aPoints.push_back( OOX_ARROW_POINT( 75, 7 ) );
353  aPoints.push_back( OOX_ARROW_POINT( 93, 25 ) );
354  aPoints.push_back( OOX_ARROW_POINT( 100, 50 ) );
355  aPoints.push_back( OOX_ARROW_POINT( 93, 75 ) );
356  aPoints.push_back( OOX_ARROW_POINT( 75, 93 ) );
357  aPoints.push_back( OOX_ARROW_POINT( 50, 100 ) );
358  aPoints.push_back( OOX_ARROW_POINT( 25, 93 ) );
359  aPoints.push_back( OOX_ARROW_POINT( 7, 75 ) );
360  aPoints.push_back( OOX_ARROW_POINT( 0, 50 ) );
361  aPoints.push_back( OOX_ARROW_POINT( 7, 25 ) );
362  aPoints.push_back( OOX_ARROW_POINT( 25, 7 ) );
363  aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) );
364  break;
365  }
366 
367  OSL_ENSURE( !aPoints.empty(), "lclPushMarkerProperties - missing arrow coordinates" );
368  if( !aPoints.empty() )
369  {
370  PolyPolygonBezierCoords aMarkerCoords;
371  aMarkerCoords.Coordinates.realloc( 1 );
372  aMarkerCoords.Coordinates[ 0 ] = ContainerHelper::vectorToSequence( aPoints );
373 
374  ::std::vector< PolygonFlags > aFlags( aPoints.size(), PolygonFlags_NORMAL );
375  aMarkerCoords.Flags.realloc( 1 );
376  aMarkerCoords.Flags[ 0 ] = ContainerHelper::vectorToSequence( aFlags );
377 
378  aNamedMarker.Name = aMarkerName;
379  aNamedMarker.Value <<= aMarkerCoords;
380  }
381  }
382  else
383  {
384  /* Named marker object exists already in the marker table, pass
385  its name only. This will set the name as property value, but
386  does not create a new object in the marker table. */
387  aNamedMarker.Name = aMarkerName;
388  }
389  }
390 
391  // push the properties (filled aNamedMarker.Name indicates valid marker)
392  if( aNamedMarker.Name.isEmpty() )
393  return;
394 
395  if( bLineEnd )
396  {
397  rPropMap.setProperty( ShapeProperty::LineEnd, aNamedMarker );
398  rPropMap.setProperty( ShapeProperty::LineEndWidth, nMarkerWidth );
399  rPropMap.setProperty( ShapeProperty::LineEndCenter, bMarkerCenter );
400  }
401  else
402  {
403  rPropMap.setProperty( ShapeProperty::LineStart, aNamedMarker );
404  rPropMap.setProperty( ShapeProperty::LineStartWidth, nMarkerWidth );
405  rPropMap.setProperty( ShapeProperty::LineStartCenter, bMarkerCenter );
406  }
407 }
408 
409 } // namespace
410 
412 {
413  moArrowType.assignIfUsed( rSourceProps.moArrowType );
414  moArrowWidth.assignIfUsed( rSourceProps.moArrowWidth );
415  moArrowLength.assignIfUsed( rSourceProps.moArrowLength );
416 }
417 
418 void LineProperties::assignUsed( const LineProperties& rSourceProps )
419 {
420  maStartArrow.assignUsed( rSourceProps.maStartArrow );
421  maEndArrow.assignUsed( rSourceProps.maEndArrow );
422  maLineFill.assignUsed( rSourceProps.maLineFill );
423  if( !rSourceProps.maCustomDash.empty() )
424  maCustomDash = rSourceProps.maCustomDash;
425  moLineWidth.assignIfUsed( rSourceProps.moLineWidth );
426  moPresetDash.assignIfUsed( rSourceProps.moPresetDash );
428  moLineCap.assignIfUsed( rSourceProps.moLineCap );
429  moLineJoint.assignIfUsed( rSourceProps.moLineJoint );
430 }
431 
433  const GraphicHelper& rGraphicHelper, ::Color nPhClr ) const
434 {
435  // line fill type must exist, otherwise ignore other properties
436  if( !maLineFill.moFillType.has() )
437  return;
438 
439  // line style (our core only supports none and solid)
440  drawing::LineStyle eLineStyle = (maLineFill.moFillType.get() == XML_noFill) ? drawing::LineStyle_NONE : drawing::LineStyle_SOLID;
441 
442  // line width in 1/100mm
443  sal_Int32 nLineWidth = getLineWidth(); // includes conversion from EMUs to 1/100mm
444  rPropMap.setProperty( ShapeProperty::LineWidth, nLineWidth );
445 
446  // line cap type
447  LineCap eLineCap = moLineCap.has() ? lclGetLineCap( moLineCap.get() ) : LineCap_BUTT;
448  if( moLineCap.has() )
449  rPropMap.setProperty( ShapeProperty::LineCap, eLineCap );
450 
451  // create line dash from preset dash token or dash stop vector (not for invisible line)
452  if( (eLineStyle != drawing::LineStyle_NONE) && (moPresetDash.differsFrom( XML_solid ) || !maCustomDash.empty()) )
453  {
454  LineDash aLineDash;
455  aLineDash.Style = lclGetDashStyle( moLineCap.get( XML_flat ) );
456 
457  if(moPresetDash.differsFrom(XML_solid))
458  lclConvertPresetDash(aLineDash, moPresetDash.get(XML_dash));
459  else // !maCustomDash.empty()
460  {
461  lclConvertCustomDash(aLineDash, maCustomDash);
462  lclRecoverStandardDashStyles(aLineDash, nLineWidth);
463  }
464 
465  // In MS Office (2020) for preset dash style line caps round and square are included in dash length.
466  // For custom dash style round line cap is included, square line cap is added. In ODF line caps are
467  // always added to dash length. Tweak the length accordingly.
468  if (eLineCap == LineCap_ROUND || (eLineCap == LineCap_SQUARE && maCustomDash.empty()))
469  {
470  // Cannot use -100 because that results in 0 length in some cases and
471  // LibreOffice interprets 0 length as 100%.
472  if (aLineDash.DotLen >= 100 || aLineDash.DashLen >= 100)
473  aLineDash.Distance += 99;
474  if (aLineDash.DotLen >= 100)
475  aLineDash.DotLen -= 99;
476  if (aLineDash.DashLen >= 100)
477  aLineDash.DashLen -= 99;
478  }
479 
480  if( rPropMap.setProperty( ShapeProperty::LineDash, aLineDash ) )
481  eLineStyle = drawing::LineStyle_DASH;
482  }
483 
484  // set final line style property
485  rPropMap.setProperty( ShapeProperty::LineStyle, eLineStyle );
486 
487  // line joint type
488  if( moLineJoint.has() )
489  rPropMap.setProperty( ShapeProperty::LineJoint, lclGetLineJoint( moLineJoint.get() ) );
490 
491  // line color and transparence
492  Color aLineColor = maLineFill.getBestSolidColor();
493  if( aLineColor.isUsed() )
494  {
495  rPropMap.setProperty( ShapeProperty::LineColor, aLineColor.getColor( rGraphicHelper, nPhClr ) );
496  if( aLineColor.hasTransparency() )
498  }
499 
500  // line markers
501  lclPushMarkerProperties( rPropMap, maStartArrow, nLineWidth, false );
502  lclPushMarkerProperties( rPropMap, maEndArrow, nLineWidth, true );
503 }
504 
506 {
507  // rules to calculate the line style inferred from the code in LineProperties::pushToPropMap
508  return (maLineFill.moFillType.get() == XML_noFill) ?
509  drawing::LineStyle_NONE :
510  (moPresetDash.differsFrom( XML_solid ) || (!moPresetDash && !maCustomDash.empty())) ?
511  drawing::LineStyle_DASH :
512  drawing::LineStyle_SOLID;
513 }
514 
516 {
517  if( moLineCap.has() )
518  return lclGetLineCap( moLineCap.get() );
519 
520  return drawing::LineCap_BUTT;
521 }
522 
524 {
525  if( moLineJoint.has() )
526  return lclGetLineJoint( moLineJoint.get() );
527 
528  return drawing::LineJoint_NONE;
529 }
530 
532 {
533  return convertEmuToHmm( moLineWidth.get( 0 ) );
534 }
535 
536 } // namespace oox
537 
538 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Provides helper functions for colors, device measurement conversion, graphics, and graphic objects ha...
bool hasTransparency() const
Returns true, if the color is transparent.
Definition: color.cxx:653
sal_Int32 convertEmuToHmm(sal_Int64 nValue)
Converts the passed 64-bit integer value from EMUs to 1/100 mm.
bool setProperty(ShapeProperty ePropId, const Type &rValue)
Sets the specified shape property to the passed value.
OptValue< sal_Int32 > moFillType
OptValue< sal_Int32 > moLineCompound
Preset dash (OOXML token).
Color getBestSolidColor() const
Tries to resolve current settings to a solid color, e.g.
sal_Int32 getLineWidth() const
Calculates the line width attribute from the internal state of the object.
OptValue< sal_Int32 > moPresetDash
Line width (EMUs).
float x
css::drawing::LineJoint getLineJoint() const
Calculates the line joint attribute from the internal state of the object.
OptValue< sal_Int32 > moLineCap
Line compound type (OOXML token).
void assignUsed(const LineArrowProperties &rSourceProps)
Overwrites all members that are explicitly set in rSourceProps.
LineArrowProperties maStartArrow
LineArrowProperties maEndArrow
Start line arrow style.
const Type & get() const
Definition: helper.hxx:185
OptValue< sal_Int32 > moLineJoint
Line cap (OOXML token).
css::drawing::LineStyle getLineStyle() const
Calculates the line style attribute from the internal state of the object.
float y
void pushToPropMap(ShapePropertyMap &rPropMap, const GraphicHelper &rGraphicHelper,::Color nPhClr=API_RGB_TRANSPARENT) const
Writes the properties to the passed property map.
::Color getColor(const GraphicHelper &rGraphicHelper,::Color nPhClr=API_RGB_TRANSPARENT) const
Returns the final RGB color value.
Definition: color.cxx:481
bool isUsed() const
Returns true, if the color is initialized.
Definition: color.hxx:87
void assignIfUsed(const OptValue &rValue)
Definition: helper.hxx:196
::std::vector< DashStop > DashStopVector
std::unique_ptr< char[]> aBuffer
FillProperties maLineFill
End line arrow style.
css::drawing::LineCap getLineCap() const
Calculates the line cap attribute from the internal state of the object.
sal_Int16 getTransparency() const
Returns the transparency of the color (0 = opaque, 100 = full transparent).
Definition: color.cxx:658
static css::uno::Sequence< typename VectorType::value_type > vectorToSequence(const VectorType &rVector)
Creates a UNO sequence from a std::vector with copies of all elements.
void assignUsed(const FillProperties &rSourceProps)
Properties for bitmap fills.
Explicit line dash or name of a line dash stored in a global container.
Explicit line start marker or name of a line marker stored in a global container. ...
sal_Int32 nLength
void assignUsed(const LineProperties &rSourceProps)
Line joint type (OOXML token).
OptValue< sal_Int32 > moLineWidth
User-defined line dash style.
DashStopVector maCustomDash
Line fill (solid, gradient, ...).
bool has() const
Definition: helper.hxx:181
Explicit line end marker or name of a line marker stored in a global container.
bool differsFrom(const Type &rValue) const
Definition: helper.hxx:183