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 "drawingml/lineproperties.hxx"
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/LineDash.hpp>
27 #include <com/sun/star/drawing/LineJoint.hpp>
28 #include <com/sun/star/drawing/LineStyle.hpp>
29 #include <com/sun/star/drawing/PointSequence.hpp>
30 #include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
31 #include "oox/drawingml/drawingmltypes.hxx"
32 #include "oox/drawingml/shapepropertymap.hxx"
33 #include "oox/helper/containerhelper.hxx"
34 #include "oox/helper/graphichelper.hxx"
35 #include "oox/token/tokens.hxx"
37 using namespace ::com::sun::star
;
38 using namespace ::com::sun::star::beans
;
39 using namespace ::com::sun::star::drawing
;
47 void lclSetDashData( LineDash
& orLineDash
, sal_Int16 nDots
, sal_Int32 nDotLen
,
48 sal_Int16 nDashes
, sal_Int32 nDashLen
, sal_Int32 nDistance
)
50 orLineDash
.Dots
= nDots
;
51 orLineDash
.DotLen
= nDotLen
;
52 orLineDash
.Dashes
= nDashes
;
53 orLineDash
.DashLen
= nDashLen
;
54 orLineDash
.Distance
= nDistance
;
57 /** Converts the specified preset dash to API dash.
59 Line length and dot length are set relative to line width and have to be
60 multiplied by the actual line width after this function.
62 void lclConvertPresetDash( LineDash
& orLineDash
, sal_Int32 nPresetDash
)
66 case XML_dot
: lclSetDashData( orLineDash
, 1, 1, 0, 0, 3 ); break;
67 case XML_dash
: lclSetDashData( orLineDash
, 0, 0, 1, 4, 3 ); break;
68 case XML_dashDot
: lclSetDashData( orLineDash
, 1, 1, 1, 4, 3 ); break;
70 case XML_lgDash
: lclSetDashData( orLineDash
, 0, 0, 1, 8, 3 ); break;
71 case XML_lgDashDot
: lclSetDashData( orLineDash
, 1, 1, 1, 8, 3 ); break;
72 case XML_lgDashDotDot
: lclSetDashData( orLineDash
, 2, 1, 1, 8, 3 ); break;
74 case XML_sysDot
: lclSetDashData( orLineDash
, 1, 1, 0, 0, 1 ); break;
75 case XML_sysDash
: lclSetDashData( orLineDash
, 0, 0, 1, 3, 1 ); break;
76 case XML_sysDashDot
: lclSetDashData( orLineDash
, 1, 1, 1, 3, 1 ); break;
77 case XML_sysDashDotDot
: lclSetDashData( orLineDash
, 2, 1, 1, 3, 1 ); break;
80 OSL_FAIL( "lclConvertPresetDash - unsupported preset dash" );
81 lclSetDashData( orLineDash
, 0, 0, 1, 4, 3 );
85 /** Converts the passed custom dash to API dash.
87 Line length and dot length are set relative to line width and have to be
88 multiplied by the actual line width after this function.
90 void lclConvertCustomDash( LineDash
& orLineDash
, const LineProperties::DashStopVector
& rCustomDash
)
92 if( rCustomDash
.empty() )
94 OSL_FAIL( "lclConvertCustomDash - unexpected empty custom dash" );
95 lclSetDashData( orLineDash
, 0, 0, 1, 4, 3 );
99 // count dashes and dots (stops equal or less than 2 are assumed to be dots)
101 sal_Int32 nDotLen
= 0;
102 sal_Int16 nDashes
= 0;
103 sal_Int32 nDashLen
= 0;
104 sal_Int32 nDistance
= 0;
105 sal_Int32 nConvertedLen
= 0;
106 sal_Int32 nConvertedDistance
= 0;
107 for( LineProperties::DashStopVector::const_iterator aIt
= rCustomDash
.begin(), aEnd
= rCustomDash
.end(); aIt
!= aEnd
; ++aIt
)
109 // Get from "1000th of percent" ==> percent ==> multiplier
110 nConvertedLen
= aIt
->first
/ 1000 / 100;
111 nConvertedDistance
= aIt
->second
/ 1000 / 100;
113 // Check if it is a dot (100% = dot)
114 if( nConvertedLen
== 1 )
117 nDotLen
+= nConvertedLen
;
122 nDashLen
+= nConvertedLen
;
124 nDistance
+= nConvertedDistance
;
126 orLineDash
.DotLen
= (nDots
> 0) ? ::std::max
< sal_Int32
>( nDotLen
/ nDots
, 1 ) : 0;
127 orLineDash
.Dots
= nDots
;
128 orLineDash
.DashLen
= (nDashes
> 0) ? ::std::max
< sal_Int32
>( nDashLen
/ nDashes
, 1 ) : 0;
129 orLineDash
.Dashes
= nDashes
;
130 orLineDash
.Distance
= ::std::max
< sal_Int32
>( nDistance
/ rCustomDash
.size(), 1 );
133 DashStyle
lclGetDashStyle( sal_Int32 nToken
)
135 OSL_ASSERT((nToken
& sal_Int32(0xFFFF0000))==0);
138 case XML_rnd
: return DashStyle_ROUNDRELATIVE
;
139 case XML_sq
: return DashStyle_RECTRELATIVE
;
140 case XML_flat
: return DashStyle_RECT
;
142 return DashStyle_ROUNDRELATIVE
;
145 LineJoint
lclGetLineJoint( sal_Int32 nToken
)
147 OSL_ASSERT((nToken
& sal_Int32(0xFFFF0000))==0);
150 case XML_round
: return LineJoint_ROUND
;
151 case XML_bevel
: return LineJoint_BEVEL
;
152 case XML_miter
: return LineJoint_MITER
;
154 return LineJoint_ROUND
;
157 const sal_Int32 OOX_ARROWSIZE_SMALL
= 0;
158 const sal_Int32 OOX_ARROWSIZE_MEDIUM
= 1;
159 const sal_Int32 OOX_ARROWSIZE_LARGE
= 2;
161 sal_Int32
lclGetArrowSize( sal_Int32 nToken
)
163 OSL_ASSERT((nToken
& sal_Int32(0xFFFF0000))==0);
166 case XML_sm
: return OOX_ARROWSIZE_SMALL
;
167 case XML_med
: return OOX_ARROWSIZE_MEDIUM
;
168 case XML_lg
: return OOX_ARROWSIZE_LARGE
;
170 return OOX_ARROWSIZE_MEDIUM
;
173 void lclPushMarkerProperties( ShapePropertyMap
& rPropMap
,
174 const LineArrowProperties
& rArrowProps
, sal_Int32 nLineWidth
, bool bLineEnd
)
176 /* Store the marker polygon and the marker name in a single value, to be
177 able to pass both to the ShapePropertyMap::setProperty() function. */
178 NamedValue aNamedMarker
;
180 OUStringBuffer aBuffer
;
181 sal_Int32 nMarkerWidth
= 0;
182 bool bMarkerCenter
= false;
183 sal_Int32 nArrowType
= rArrowProps
.moArrowType
.get( XML_none
);
184 OSL_ASSERT((nArrowType
& sal_Int32(0xFFFF0000))==0);
188 aBuffer
.append( "msArrowEnd" );
191 aBuffer
.append( "msArrowOpenEnd" );
194 aBuffer
.append( "msArrowStealthEnd" );
197 aBuffer
.append( "msArrowDiamondEnd" );
198 bMarkerCenter
= true;
201 aBuffer
.append( "msArrowOvalEnd" );
202 bMarkerCenter
= true;
206 if( !aBuffer
.isEmpty() )
208 bool bIsArrow
= nArrowType
== XML_arrow
;
209 sal_Int32 nLength
= lclGetArrowSize( rArrowProps
.moArrowLength
.get( XML_med
) );
210 sal_Int32 nWidth
= lclGetArrowSize( rArrowProps
.moArrowWidth
.get( XML_med
) );
212 sal_Int32 nNameIndex
= nWidth
* 3 + nLength
+ 1;
213 aBuffer
.append( ' ' ).append( nNameIndex
);
216 // Arrow marker form depends also on line width
217 aBuffer
.append(' ').append(nLineWidth
);
219 OUString aMarkerName
= aBuffer
.makeStringAndClear();
221 double fArrowLength
= 1.0;
224 case OOX_ARROWSIZE_SMALL
: fArrowLength
= (bIsArrow
? 3.5 : 2.0); break;
225 case OOX_ARROWSIZE_MEDIUM
: fArrowLength
= (bIsArrow
? 4.5 : 3.0); break;
226 case OOX_ARROWSIZE_LARGE
: fArrowLength
= (bIsArrow
? 6.0 : 5.0); break;
228 double fArrowWidth
= 1.0;
231 case OOX_ARROWSIZE_SMALL
: fArrowWidth
= (bIsArrow
? 3.5 : 2.0); break;
232 case OOX_ARROWSIZE_MEDIUM
: fArrowWidth
= (bIsArrow
? 4.5 : 3.0); break;
233 case OOX_ARROWSIZE_LARGE
: fArrowWidth
= (bIsArrow
? 6.0 : 5.0); break;
235 // set arrow width relative to line width
236 sal_Int32 nBaseLineWidth
= ::std::max
< sal_Int32
>( nLineWidth
, 70 );
237 nMarkerWidth
= static_cast<sal_Int32
>( fArrowWidth
* nBaseLineWidth
);
239 /* Test if the marker already exists in the marker table, do not
240 create it again in this case. If markers are inserted explicitly
241 instead by their name, the polygon will be created always.
242 TODO: this can be optimized by using a map. */
243 if( !rPropMap
.hasNamedLineMarkerInTable( aMarkerName
) )
245 // pass X and Y as percentage to OOX_ARROW_POINT
246 #define OOX_ARROW_POINT( x, y ) awt::Point( static_cast< sal_Int32 >( fArrowWidth * ( x ) ), static_cast< sal_Int32 >( fArrowLength * ( y ) ) )
247 // tdf#100491 Arrow line marker, unlike other markers, depends on line width.
248 // So calculate width of half line (more convinient during drawing) taking into account
249 // further conversions/scaling done in OOX_ARROW_POINT macro and scaling to nMarkerWidth.
250 const double fArrowLineHalfWidth
= ::std::max
< double >( 100.0 * 0.5 * nLineWidth
/ nMarkerWidth
, 1 );
252 ::std::vector
< awt::Point
> aPoints
;
253 OSL_ASSERT((rArrowProps
.moArrowType
.get() & sal_Int32(0xFFFF0000))==0);
254 switch( rArrowProps
.moArrowType
.get() )
257 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
258 aPoints
.push_back( OOX_ARROW_POINT( 100, 100 ) );
259 aPoints
.push_back( OOX_ARROW_POINT( 0, 100 ) );
260 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
263 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
264 aPoints
.push_back( OOX_ARROW_POINT( 100, 100 - fArrowLineHalfWidth
* 1.5) );
265 aPoints
.push_back( OOX_ARROW_POINT( 100 - fArrowLineHalfWidth
* 1.5, 100 ) );
266 aPoints
.push_back( OOX_ARROW_POINT( 50.0 + fArrowLineHalfWidth
, 5.5 * fArrowLineHalfWidth
) );
267 aPoints
.push_back( OOX_ARROW_POINT( 50.0 + fArrowLineHalfWidth
, 100 ) );
268 aPoints
.push_back( OOX_ARROW_POINT( 50.0 - fArrowLineHalfWidth
, 100 ) );
269 aPoints
.push_back( OOX_ARROW_POINT( 50.0 - fArrowLineHalfWidth
, 5.5 * fArrowLineHalfWidth
) );
270 aPoints
.push_back( OOX_ARROW_POINT( fArrowLineHalfWidth
* 1.5, 100 ) );
271 aPoints
.push_back( OOX_ARROW_POINT( 0, 100 - fArrowLineHalfWidth
* 1.5) );
272 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
275 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
276 aPoints
.push_back( OOX_ARROW_POINT( 100, 100 ) );
277 aPoints
.push_back( OOX_ARROW_POINT( 50, 60 ) );
278 aPoints
.push_back( OOX_ARROW_POINT( 0, 100 ) );
279 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
282 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
283 aPoints
.push_back( OOX_ARROW_POINT( 100, 50 ) );
284 aPoints
.push_back( OOX_ARROW_POINT( 50, 100 ) );
285 aPoints
.push_back( OOX_ARROW_POINT( 0, 50 ) );
286 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
289 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
290 aPoints
.push_back( OOX_ARROW_POINT( 75, 7 ) );
291 aPoints
.push_back( OOX_ARROW_POINT( 93, 25 ) );
292 aPoints
.push_back( OOX_ARROW_POINT( 100, 50 ) );
293 aPoints
.push_back( OOX_ARROW_POINT( 93, 75 ) );
294 aPoints
.push_back( OOX_ARROW_POINT( 75, 93 ) );
295 aPoints
.push_back( OOX_ARROW_POINT( 50, 100 ) );
296 aPoints
.push_back( OOX_ARROW_POINT( 25, 93 ) );
297 aPoints
.push_back( OOX_ARROW_POINT( 7, 75 ) );
298 aPoints
.push_back( OOX_ARROW_POINT( 0, 50 ) );
299 aPoints
.push_back( OOX_ARROW_POINT( 7, 25 ) );
300 aPoints
.push_back( OOX_ARROW_POINT( 25, 7 ) );
301 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
304 #undef OOX_ARROW_POINT
306 OSL_ENSURE( !aPoints
.empty(), "lclPushMarkerProperties - missing arrow coordinates" );
307 if( !aPoints
.empty() )
309 PolyPolygonBezierCoords aMarkerCoords
;
310 aMarkerCoords
.Coordinates
.realloc( 1 );
311 aMarkerCoords
.Coordinates
[ 0 ] = ContainerHelper::vectorToSequence( aPoints
);
313 ::std::vector
< PolygonFlags
> aFlags( aPoints
.size(), PolygonFlags_NORMAL
);
314 aMarkerCoords
.Flags
.realloc( 1 );
315 aMarkerCoords
.Flags
[ 0 ] = ContainerHelper::vectorToSequence( aFlags
);
317 aNamedMarker
.Name
= aMarkerName
;
318 aNamedMarker
.Value
<<= aMarkerCoords
;
323 /* Named marker object exists already in the marker table, pass
324 its name only. This will set the name as property value, but
325 does not create a new object in the marker table. */
326 aNamedMarker
.Name
= aMarkerName
;
330 // push the properties (filled aNamedMarker.Name indicates valid marker)
331 if( !aNamedMarker
.Name
.isEmpty() )
335 rPropMap
.setProperty( ShapeProperty::LineEnd
, aNamedMarker
);
336 rPropMap
.setProperty( ShapeProperty::LineEndWidth
, nMarkerWidth
);
337 rPropMap
.setProperty( ShapeProperty::LineEndCenter
, bMarkerCenter
);
341 rPropMap
.setProperty( ShapeProperty::LineStart
, aNamedMarker
);
342 rPropMap
.setProperty( ShapeProperty::LineStartWidth
, nMarkerWidth
);
343 rPropMap
.setProperty( ShapeProperty::LineStartCenter
, bMarkerCenter
);
350 void LineArrowProperties::assignUsed( const LineArrowProperties
& rSourceProps
)
352 moArrowType
.assignIfUsed( rSourceProps
.moArrowType
);
353 moArrowWidth
.assignIfUsed( rSourceProps
.moArrowWidth
);
354 moArrowLength
.assignIfUsed( rSourceProps
.moArrowLength
);
357 void LineProperties::assignUsed( const LineProperties
& rSourceProps
)
359 maStartArrow
.assignUsed( rSourceProps
.maStartArrow
);
360 maEndArrow
.assignUsed( rSourceProps
.maEndArrow
);
361 maLineFill
.assignUsed( rSourceProps
.maLineFill
);
362 if( !rSourceProps
.maCustomDash
.empty() )
363 maCustomDash
= rSourceProps
.maCustomDash
;
364 moLineWidth
.assignIfUsed( rSourceProps
.moLineWidth
);
365 moPresetDash
.assignIfUsed( rSourceProps
.moPresetDash
);
366 moLineCompound
.assignIfUsed( rSourceProps
.moLineCompound
);
367 moLineCap
.assignIfUsed( rSourceProps
.moLineCap
);
368 moLineJoint
.assignIfUsed( rSourceProps
.moLineJoint
);
371 void LineProperties::pushToPropMap( ShapePropertyMap
& rPropMap
,
372 const GraphicHelper
& rGraphicHelper
, sal_Int32 nPhClr
) const
374 // line fill type must exist, otherwise ignore other properties
375 if( maLineFill
.moFillType
.has() )
377 // line style (our core only supports none and solid)
378 drawing::LineStyle eLineStyle
= (maLineFill
.moFillType
.get() == XML_noFill
) ? drawing::LineStyle_NONE
: drawing::LineStyle_SOLID
;
380 // convert line width from EMUs to 1/100mm
381 sal_Int32 nLineWidth
= getLineWidth();
383 // create line dash from preset dash token (not for invisible line)
384 if( (eLineStyle
!= drawing::LineStyle_NONE
) && (moPresetDash
.differsFrom( XML_solid
) || !maCustomDash
.empty()) )
387 aLineDash
.Style
= lclGetDashStyle( moLineCap
.get( XML_rnd
) );
389 // convert preset dash or custom dash
390 if( moPresetDash
.differsFrom( XML_solid
) )
391 lclConvertPresetDash( aLineDash
, moPresetDash
.get() );
393 lclConvertCustomDash( aLineDash
, maCustomDash
);
395 // convert relative dash/dot length to absolute length
396 sal_Int32 nBaseLineWidth
= ::std::max
< sal_Int32
>( nLineWidth
, 35 );
397 aLineDash
.DotLen
*= nBaseLineWidth
;
398 aLineDash
.DashLen
*= nBaseLineWidth
;
399 aLineDash
.Distance
*= nBaseLineWidth
;
401 if( rPropMap
.setProperty( ShapeProperty::LineDash
, aLineDash
) )
402 eLineStyle
= drawing::LineStyle_DASH
;
405 // set final line style property
406 rPropMap
.setProperty( ShapeProperty::LineStyle
, eLineStyle
);
409 if( moLineJoint
.has() )
410 rPropMap
.setProperty( ShapeProperty::LineJoint
, lclGetLineJoint( moLineJoint
.get() ) );
412 // line width in 1/100mm
413 rPropMap
.setProperty( ShapeProperty::LineWidth
, nLineWidth
);
415 // line color and transparence
416 Color aLineColor
= maLineFill
.getBestSolidColor();
417 if( aLineColor
.isUsed() )
419 rPropMap
.setProperty( ShapeProperty::LineColor
, aLineColor
.getColor( rGraphicHelper
, nPhClr
) );
420 if( aLineColor
.hasTransparency() )
421 rPropMap
.setProperty( ShapeProperty::LineTransparency
, aLineColor
.getTransparency() );
425 lclPushMarkerProperties( rPropMap
, maStartArrow
, nLineWidth
, false );
426 lclPushMarkerProperties( rPropMap
, maEndArrow
, nLineWidth
, true );
430 drawing::LineStyle
LineProperties::getLineStyle() const
432 // rules to calculate the line style inferred from the code in LineProperties::pushToPropMap
433 return (maLineFill
.moFillType
.get() == XML_noFill
) ?
434 drawing::LineStyle_NONE
:
435 (moPresetDash
.differsFrom( XML_solid
) || (!moPresetDash
&& !maCustomDash
.empty())) ?
436 drawing::LineStyle_DASH
:
437 drawing::LineStyle_SOLID
;
440 drawing::LineJoint
LineProperties::getLineJoint() const
442 if( moLineJoint
.has() )
443 return lclGetLineJoint( moLineJoint
.get() );
445 return drawing::LineJoint_NONE
;
448 sal_Int32
LineProperties::getLineWidth() const
450 return convertEmuToHmm( moLineWidth
.get( 0 ) );
453 } // namespace drawingml
456 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */