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 "oox/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/container/XNameContainer.hpp>
26 #include <com/sun/star/drawing/FlagSequence.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"
38 using namespace ::com::sun::star
;
39 using namespace ::com::sun::star::beans
;
40 using namespace ::com::sun::star::drawing
;
42 using ::com::sun::star::uno::Any
;
43 using ::com::sun::star::uno::Reference
;
44 using ::com::sun::star::container::XNameContainer
;
51 void lclSetDashData( LineDash
& orLineDash
, sal_Int16 nDots
, sal_Int32 nDotLen
,
52 sal_Int16 nDashes
, sal_Int32 nDashLen
, sal_Int32 nDistance
)
54 orLineDash
.Dots
= nDots
;
55 orLineDash
.DotLen
= nDotLen
;
56 orLineDash
.Dashes
= nDashes
;
57 orLineDash
.DashLen
= nDashLen
;
58 orLineDash
.Distance
= nDistance
;
61 /** Converts the specified preset dash to API dash.
63 Line length and dot length are set relative to line width and have to be
64 multiplied by the actual line width after this function.
66 void lclConvertPresetDash( LineDash
& orLineDash
, sal_Int32 nPresetDash
)
70 case XML_dot
: lclSetDashData( orLineDash
, 1, 1, 0, 0, 3 ); break;
71 case XML_dash
: lclSetDashData( orLineDash
, 0, 0, 1, 4, 3 ); break;
72 case XML_dashDot
: lclSetDashData( orLineDash
, 1, 1, 1, 4, 3 ); break;
74 case XML_lgDash
: lclSetDashData( orLineDash
, 0, 0, 1, 8, 3 ); break;
75 case XML_lgDashDot
: lclSetDashData( orLineDash
, 1, 1, 1, 8, 3 ); break;
76 case XML_lgDashDotDot
: lclSetDashData( orLineDash
, 2, 1, 1, 8, 3 ); break;
78 case XML_sysDot
: lclSetDashData( orLineDash
, 1, 1, 0, 0, 1 ); break;
79 case XML_sysDash
: lclSetDashData( orLineDash
, 0, 0, 1, 3, 1 ); break;
80 case XML_sysDashDot
: lclSetDashData( orLineDash
, 1, 1, 1, 3, 1 ); break;
81 case XML_sysDashDotDot
: lclSetDashData( orLineDash
, 2, 1, 1, 3, 1 ); break;
84 OSL_FAIL( "lclConvertPresetDash - unsupported preset dash" );
85 lclSetDashData( orLineDash
, 0, 0, 1, 4, 3 );
89 /** Converts the passed custom dash to API dash.
91 Line length and dot length are set relative to line width and have to be
92 multiplied by the actual line width after this function.
94 void lclConvertCustomDash( LineDash
& orLineDash
, const LineProperties::DashStopVector
& rCustomDash
)
96 if( rCustomDash
.empty() )
98 OSL_FAIL( "lclConvertCustomDash - unexpected empty custom dash" );
99 lclSetDashData( orLineDash
, 0, 0, 1, 4, 3 );
103 // count dashes and dots (stops equal or less than 2 are assumed to be dots)
105 sal_Int32 nDotLen
= 0;
106 sal_Int16 nDashes
= 0;
107 sal_Int32 nDashLen
= 0;
108 sal_Int32 nDistance
= 0;
109 sal_Int32 nConvertedLen
= 0;
110 sal_Int32 nConvertedDistance
= 0;
111 for( LineProperties::DashStopVector::const_iterator aIt
= rCustomDash
.begin(), aEnd
= rCustomDash
.end(); aIt
!= aEnd
; ++aIt
)
113 // Get from "1000th of percent" ==> percent ==> multiplier
114 nConvertedLen
= aIt
->first
/ 1000 / 100;
115 nConvertedDistance
= aIt
->second
/ 1000 / 100;
117 // Check if it is a dot (100% = dot)
118 if( nConvertedLen
== 1 )
121 nDotLen
+= nConvertedLen
;
126 nDashLen
+= nConvertedLen
;
128 nDistance
+= nConvertedDistance
;
130 orLineDash
.DotLen
= (nDots
> 0) ? ::std::max
< sal_Int32
>( nDotLen
/ nDots
, 1 ) : 0;
131 orLineDash
.Dots
= nDots
;
132 orLineDash
.DashLen
= (nDashes
> 0) ? ::std::max
< sal_Int32
>( nDashLen
/ nDashes
, 1 ) : 0;
133 orLineDash
.Dashes
= nDashes
;
134 orLineDash
.Distance
= ::std::max
< sal_Int32
>( nDistance
/ rCustomDash
.size(), 1 );
137 DashStyle
lclGetDashStyle( sal_Int32 nToken
)
139 OSL_ASSERT((nToken
& sal_Int32(0xFFFF0000))==0);
142 case XML_rnd
: return DashStyle_ROUNDRELATIVE
;
143 case XML_sq
: return DashStyle_RECTRELATIVE
;
144 case XML_flat
: return DashStyle_RECT
;
146 return DashStyle_ROUNDRELATIVE
;
149 LineJoint
lclGetLineJoint( sal_Int32 nToken
)
151 OSL_ASSERT((nToken
& sal_Int32(0xFFFF0000))==0);
154 case XML_round
: return LineJoint_ROUND
;
155 case XML_bevel
: return LineJoint_BEVEL
;
156 case XML_miter
: return LineJoint_MITER
;
158 return LineJoint_ROUND
;
161 const sal_Int32 OOX_ARROWSIZE_SMALL
= 0;
162 const sal_Int32 OOX_ARROWSIZE_MEDIUM
= 1;
163 const sal_Int32 OOX_ARROWSIZE_LARGE
= 2;
165 sal_Int32
lclGetArrowSize( sal_Int32 nToken
)
167 OSL_ASSERT((nToken
& sal_Int32(0xFFFF0000))==0);
170 case XML_sm
: return OOX_ARROWSIZE_SMALL
;
171 case XML_med
: return OOX_ARROWSIZE_MEDIUM
;
172 case XML_lg
: return OOX_ARROWSIZE_LARGE
;
174 return OOX_ARROWSIZE_MEDIUM
;
177 void lclPushMarkerProperties( ShapePropertyMap
& rPropMap
,
178 const LineArrowProperties
& rArrowProps
, sal_Int32 nLineWidth
, bool bLineEnd
)
180 /* Store the marker polygon and the marker name in a single value, to be
181 able to pass both to the ShapePropertyMap::setProperty() function. */
182 NamedValue aNamedMarker
;
184 OUStringBuffer aBuffer
;
185 sal_Int32 nMarkerWidth
= 0;
186 bool bMarkerCenter
= false;
187 sal_Int32 nArrowType
= rArrowProps
.moArrowType
.get( XML_none
);
188 OSL_ASSERT((nArrowType
& sal_Int32(0xFFFF0000))==0);
192 aBuffer
.append( "msArrowEnd" );
195 aBuffer
.append( "msArrowOpenEnd" );
198 aBuffer
.append( "msArrowStealthEnd" );
201 aBuffer
.append( "msArrowDiamondEnd" );
202 bMarkerCenter
= true;
205 aBuffer
.append( "msArrowOvalEnd" );
206 bMarkerCenter
= true;
210 if( !aBuffer
.isEmpty() )
212 sal_Int32 nLength
= lclGetArrowSize( rArrowProps
.moArrowLength
.get( XML_med
) );
213 sal_Int32 nWidth
= lclGetArrowSize( rArrowProps
.moArrowWidth
.get( XML_med
) );
215 sal_Int32 nNameIndex
= nWidth
* 3 + nLength
+ 1;
216 aBuffer
.append( ' ' ).append( nNameIndex
);
217 OUString aMarkerName
= aBuffer
.makeStringAndClear();
219 bool bIsArrow
= nArrowType
== XML_arrow
;
220 double fArrowLength
= 1.0;
223 case OOX_ARROWSIZE_SMALL
: fArrowLength
= (bIsArrow
? 3.5 : 2.0); break;
224 case OOX_ARROWSIZE_MEDIUM
: fArrowLength
= (bIsArrow
? 4.5 : 3.0); break;
225 case OOX_ARROWSIZE_LARGE
: fArrowLength
= (bIsArrow
? 6.0 : 5.0); break;
227 double fArrowWidth
= 1.0;
230 case OOX_ARROWSIZE_SMALL
: fArrowWidth
= (bIsArrow
? 3.5 : 2.0); break;
231 case OOX_ARROWSIZE_MEDIUM
: fArrowWidth
= (bIsArrow
? 4.5 : 3.0); break;
232 case OOX_ARROWSIZE_LARGE
: fArrowWidth
= (bIsArrow
? 6.0 : 5.0); break;
234 // set arrow width relative to line width
235 sal_Int32 nBaseLineWidth
= ::std::max
< sal_Int32
>( nLineWidth
, 70 );
236 nMarkerWidth
= static_cast< sal_Int32
>( fArrowWidth
* nBaseLineWidth
);
238 /* Test if the marker already exists in the marker table, do not
239 create it again in this case. If markers are inserted explicitly
240 instead by their name, the polygon will be created always.
241 TODO: this can be optimized by using a map. */
242 if( !rPropMap
.hasNamedLineMarkerInTable( aMarkerName
) )
244 // pass X and Y as percentage to OOX_ARROW_POINT
245 #define OOX_ARROW_POINT( x, y ) awt::Point( static_cast< sal_Int32 >( fArrowWidth * x ), static_cast< sal_Int32 >( fArrowLength * y ) )
247 ::std::vector
< awt::Point
> aPoints
;
248 OSL_ASSERT((rArrowProps
.moArrowType
.get() & sal_Int32(0xFFFF0000))==0);
249 switch( rArrowProps
.moArrowType
.get() )
252 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
253 aPoints
.push_back( OOX_ARROW_POINT( 100, 100 ) );
254 aPoints
.push_back( OOX_ARROW_POINT( 0, 100 ) );
255 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
258 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
259 aPoints
.push_back( OOX_ARROW_POINT( 100, 91 ) );
260 aPoints
.push_back( OOX_ARROW_POINT( 85, 100 ) );
261 aPoints
.push_back( OOX_ARROW_POINT( 50, 36 ) );
262 aPoints
.push_back( OOX_ARROW_POINT( 15, 100 ) );
263 aPoints
.push_back( OOX_ARROW_POINT( 0, 91 ) );
264 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
267 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
268 aPoints
.push_back( OOX_ARROW_POINT( 100, 100 ) );
269 aPoints
.push_back( OOX_ARROW_POINT( 50, 60 ) );
270 aPoints
.push_back( OOX_ARROW_POINT( 0, 100 ) );
271 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
274 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
275 aPoints
.push_back( OOX_ARROW_POINT( 100, 50 ) );
276 aPoints
.push_back( OOX_ARROW_POINT( 50, 100 ) );
277 aPoints
.push_back( OOX_ARROW_POINT( 0, 50 ) );
278 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
281 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
282 aPoints
.push_back( OOX_ARROW_POINT( 75, 7 ) );
283 aPoints
.push_back( OOX_ARROW_POINT( 93, 25 ) );
284 aPoints
.push_back( OOX_ARROW_POINT( 100, 50 ) );
285 aPoints
.push_back( OOX_ARROW_POINT( 93, 75 ) );
286 aPoints
.push_back( OOX_ARROW_POINT( 75, 93 ) );
287 aPoints
.push_back( OOX_ARROW_POINT( 50, 100 ) );
288 aPoints
.push_back( OOX_ARROW_POINT( 25, 93 ) );
289 aPoints
.push_back( OOX_ARROW_POINT( 7, 75 ) );
290 aPoints
.push_back( OOX_ARROW_POINT( 0, 50 ) );
291 aPoints
.push_back( OOX_ARROW_POINT( 7, 25 ) );
292 aPoints
.push_back( OOX_ARROW_POINT( 25, 7 ) );
293 aPoints
.push_back( OOX_ARROW_POINT( 50, 0 ) );
296 #undef OOX_ARROW_POINT
298 OSL_ENSURE( !aPoints
.empty(), "lclPushMarkerProperties - missing arrow coordinates" );
299 if( !aPoints
.empty() )
301 PolyPolygonBezierCoords aMarkerCoords
;
302 aMarkerCoords
.Coordinates
.realloc( 1 );
303 aMarkerCoords
.Coordinates
[ 0 ] = ContainerHelper::vectorToSequence( aPoints
);
305 ::std::vector
< PolygonFlags
> aFlags( aPoints
.size(), PolygonFlags_NORMAL
);
306 aMarkerCoords
.Flags
.realloc( 1 );
307 aMarkerCoords
.Flags
[ 0 ] = ContainerHelper::vectorToSequence( aFlags
);
309 aNamedMarker
.Name
= aMarkerName
;
310 aNamedMarker
.Value
<<= aMarkerCoords
;
315 /* Named marker object exists already in the marker table, pass
316 its name only. This will set the name as property value, but
317 does not create a new object in the marker table. */
318 aNamedMarker
.Name
= aMarkerName
;
322 // push the properties (filled aNamedMarker.Name indicates valid marker)
323 if( !aNamedMarker
.Name
.isEmpty() )
327 rPropMap
.setProperty( SHAPEPROP_LineEnd
, aNamedMarker
);
328 rPropMap
.setProperty( SHAPEPROP_LineEndWidth
, nMarkerWidth
);
329 rPropMap
.setProperty( SHAPEPROP_LineEndCenter
, bMarkerCenter
);
333 rPropMap
.setProperty( SHAPEPROP_LineStart
, aNamedMarker
);
334 rPropMap
.setProperty( SHAPEPROP_LineStartWidth
, nMarkerWidth
);
335 rPropMap
.setProperty( SHAPEPROP_LineStartCenter
, bMarkerCenter
);
342 void LineArrowProperties::assignUsed( const LineArrowProperties
& rSourceProps
)
344 moArrowType
.assignIfUsed( rSourceProps
.moArrowType
);
345 moArrowWidth
.assignIfUsed( rSourceProps
.moArrowWidth
);
346 moArrowLength
.assignIfUsed( rSourceProps
.moArrowLength
);
349 void LineProperties::assignUsed( const LineProperties
& rSourceProps
)
351 maStartArrow
.assignUsed( rSourceProps
.maStartArrow
);
352 maEndArrow
.assignUsed( rSourceProps
.maEndArrow
);
353 maLineFill
.assignUsed( rSourceProps
.maLineFill
);
354 if( !rSourceProps
.maCustomDash
.empty() )
355 maCustomDash
= rSourceProps
.maCustomDash
;
356 moLineWidth
.assignIfUsed( rSourceProps
.moLineWidth
);
357 moPresetDash
.assignIfUsed( rSourceProps
.moPresetDash
);
358 moLineCompound
.assignIfUsed( rSourceProps
.moLineCompound
);
359 moLineCap
.assignIfUsed( rSourceProps
.moLineCap
);
360 moLineJoint
.assignIfUsed( rSourceProps
.moLineJoint
);
363 void LineProperties::pushToPropMap( ShapePropertyMap
& rPropMap
,
364 const GraphicHelper
& rGraphicHelper
, sal_Int32 nPhClr
) const
366 // line fill type must exist, otherwise ignore other properties
367 if( maLineFill
.moFillType
.has() )
369 // line style (our core only supports none and solid)
370 drawing::LineStyle eLineStyle
= (maLineFill
.moFillType
.get() == XML_noFill
) ? drawing::LineStyle_NONE
: drawing::LineStyle_SOLID
;
372 // convert line width from EMUs to 1/100mm
373 sal_Int32 nLineWidth
= getLineWidth();
375 // create line dash from preset dash token (not for invisible line)
376 if( (eLineStyle
!= drawing::LineStyle_NONE
) && (moPresetDash
.differsFrom( XML_solid
) || !maCustomDash
.empty()) )
379 aLineDash
.Style
= lclGetDashStyle( moLineCap
.get( XML_rnd
) );
381 // convert preset dash or custom dash
382 if( moPresetDash
.differsFrom( XML_solid
) )
383 lclConvertPresetDash( aLineDash
, moPresetDash
.get() );
385 lclConvertCustomDash( aLineDash
, maCustomDash
);
387 // convert relative dash/dot length to absolute length
388 sal_Int32 nBaseLineWidth
= ::std::max
< sal_Int32
>( nLineWidth
, 35 );
389 aLineDash
.DotLen
*= nBaseLineWidth
;
390 aLineDash
.DashLen
*= nBaseLineWidth
;
391 aLineDash
.Distance
*= nBaseLineWidth
;
393 if( rPropMap
.setProperty( SHAPEPROP_LineDash
, aLineDash
) )
394 eLineStyle
= drawing::LineStyle_DASH
;
397 // set final line style property
398 rPropMap
.setProperty( SHAPEPROP_LineStyle
, eLineStyle
);
401 if( moLineJoint
.has() )
402 rPropMap
.setProperty( SHAPEPROP_LineJoint
, lclGetLineJoint( moLineJoint
.get() ) );
404 // line width in 1/100mm
405 rPropMap
.setProperty( SHAPEPROP_LineWidth
, nLineWidth
);
407 // line color and transparence
408 Color aLineColor
= maLineFill
.getBestSolidColor();
409 if( aLineColor
.isUsed() )
411 rPropMap
.setProperty( SHAPEPROP_LineColor
, aLineColor
.getColor( rGraphicHelper
, nPhClr
) );
412 if( aLineColor
.hasTransparency() )
413 rPropMap
.setProperty( SHAPEPROP_LineTransparency
, aLineColor
.getTransparency() );
417 lclPushMarkerProperties( rPropMap
, maStartArrow
, nLineWidth
, false );
418 lclPushMarkerProperties( rPropMap
, maEndArrow
, nLineWidth
, true );
422 drawing::LineStyle
LineProperties::getLineStyle() const
424 // rules to calculate the line style inferred from the code in LineProperties::pushToPropMap
425 return (maLineFill
.moFillType
.get() == XML_noFill
) ?
426 drawing::LineStyle_NONE
:
427 (moPresetDash
.differsFrom( XML_solid
) || (!moPresetDash
&& !maCustomDash
.empty())) ?
428 drawing::LineStyle_DASH
:
429 drawing::LineStyle_SOLID
;
432 drawing::LineJoint
LineProperties::getLineJoint() const
434 if( moLineJoint
.has() )
435 return lclGetLineJoint( moLineJoint
.get() );
437 return drawing::LineJoint_NONE
;
440 sal_Int32
LineProperties::getLineWidth() const
442 return convertEmuToHmm( moLineWidth
.get( 0 ) );
445 } // namespace drawingml
448 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */