/src/libreoffice/oox/source/drawingml/lineproperties.cxx
Line | Count | Source (jump to first uncovered line) |
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 <sal/config.h> |
21 | | |
22 | | #include <comphelper/sequence.hxx> |
23 | | #include <drawingml/lineproperties.hxx> |
24 | | #include <rtl/ustrbuf.hxx> |
25 | | #include <osl/diagnose.h> |
26 | | #include <com/sun/star/beans/NamedValue.hpp> |
27 | | #include <com/sun/star/drawing/LineCap.hpp> |
28 | | #include <com/sun/star/drawing/LineDash.hpp> |
29 | | #include <com/sun/star/drawing/LineJoint.hpp> |
30 | | #include <com/sun/star/drawing/LineStyle.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/graphichelper.hxx> |
35 | | #include <oox/token/tokens.hxx> |
36 | | #include <oox/token/properties.hxx> |
37 | | #include <docmodel/uno/UnoComplexColor.hxx> |
38 | | |
39 | | using namespace ::com::sun::star; |
40 | | using namespace ::com::sun::star::beans; |
41 | | using namespace ::com::sun::star::drawing; |
42 | | |
43 | | |
44 | | namespace oox::drawingml { |
45 | | |
46 | | namespace { |
47 | | |
48 | | void lclSetDashData( LineDash& orLineDash, sal_Int16 nDots, sal_Int32 nDotLen, |
49 | | sal_Int16 nDashes, sal_Int32 nDashLen, sal_Int32 nDistance ) |
50 | 124 | { |
51 | 124 | orLineDash.Dots = nDots; |
52 | 124 | orLineDash.DotLen = nDotLen; |
53 | 124 | orLineDash.Dashes = nDashes; |
54 | 124 | orLineDash.DashLen = nDashLen; |
55 | 124 | orLineDash.Distance = nDistance; |
56 | 124 | } |
57 | | |
58 | | /** Converts the specified preset dash to API dash. |
59 | | */ |
60 | | void lclConvertPresetDash(LineDash& orLineDash, sal_Int32 nPresetDash) |
61 | 124 | { |
62 | 124 | switch( nPresetDash ) |
63 | 124 | { |
64 | 0 | case XML_dot: lclSetDashData( orLineDash, 1, 1, 0, 0, 3 ); break; |
65 | 19 | case XML_dash: lclSetDashData( orLineDash, 1, 4, 0, 0, 3 ); break; |
66 | 10 | case XML_dashDot: lclSetDashData( orLineDash, 1, 4, 1, 1, 3 ); break; |
67 | | |
68 | 32 | case XML_lgDash: lclSetDashData( orLineDash, 1, 8, 0, 0, 3 ); break; |
69 | 10 | case XML_lgDashDot: lclSetDashData( orLineDash, 1, 8, 1, 1, 3 ); break; |
70 | 10 | case XML_lgDashDotDot: lclSetDashData( orLineDash, 1, 8, 2, 1, 3 ); break; |
71 | | |
72 | 10 | case XML_sysDot: lclSetDashData( orLineDash, 1, 1, 0, 0, 1 ); break; |
73 | 33 | case XML_sysDash: lclSetDashData( orLineDash, 1, 3, 0, 0, 1 ); break; |
74 | 0 | case XML_sysDashDot: lclSetDashData( orLineDash, 1, 3, 1, 1, 1 ); break; |
75 | 0 | case XML_sysDashDotDot: lclSetDashData( orLineDash, 1, 3, 2, 1, 1 ); break; |
76 | | |
77 | 0 | default: |
78 | 0 | OSL_FAIL( "lclConvertPresetDash - unsupported preset dash" ); |
79 | 0 | lclSetDashData( orLineDash, 1, 4, 0, 0, 3 ); |
80 | 124 | } |
81 | 124 | orLineDash.DotLen *= 100; |
82 | 124 | orLineDash.DashLen *= 100; |
83 | 124 | orLineDash.Distance *= 100; |
84 | 124 | } |
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 | | */ |
91 | | void lclConvertCustomDash(LineDash& orLineDash, const LineProperties::DashStopVector& rCustomDash) |
92 | 0 | { |
93 | 0 | OSL_ASSERT(!rCustomDash.empty()); |
94 | | // Assume all dash stops have the same sp values. |
95 | 0 | orLineDash.Distance = rCustomDash[0].second; |
96 | | // First kind of dashes go to "Dots" |
97 | 0 | orLineDash.DotLen = rCustomDash[0].first; |
98 | 0 | orLineDash.Dots = 0; |
99 | 0 | for(const auto& rIt : rCustomDash) |
100 | 0 | { |
101 | 0 | if (rIt.first != orLineDash.DotLen) |
102 | 0 | break; |
103 | 0 | ++orLineDash.Dots; |
104 | 0 | } |
105 | | // All others go to "Dashes", we cannot handle more than two kinds. |
106 | 0 | orLineDash.Dashes = rCustomDash.size() - orLineDash.Dots; |
107 | 0 | if (orLineDash.Dashes > 0) |
108 | 0 | orLineDash.DashLen = rCustomDash[orLineDash.Dots].first; |
109 | 0 | else |
110 | 0 | orLineDash.DashLen = 0; |
111 | | |
112 | | // convert to API, e.g. 123% is 123000 in MS Office and 123 in our API |
113 | 0 | orLineDash.DotLen = orLineDash.DotLen / 1000; |
114 | 0 | orLineDash.DashLen = orLineDash.DashLen / 1000; |
115 | 0 | orLineDash.Distance = orLineDash.Distance / 1000; |
116 | 0 | } |
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 | | */ |
123 | | void lclRecoverStandardDashStyles(LineDash& orLineDash, sal_Int32 nLineWidth) |
124 | 0 | { |
125 | 0 | sal_uInt16 nDots = orLineDash.Dots; |
126 | 0 | sal_uInt16 nDashes = orLineDash.Dashes; |
127 | 0 | sal_uInt32 nDotLen = orLineDash.DotLen; |
128 | 0 | sal_uInt32 nDashLen = orLineDash.DashLen; |
129 | 0 | sal_uInt32 nDistance = orLineDash.Distance; |
130 | | // Use same ersatz for hairline as in export. |
131 | 0 | 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", "Sparse Dash" and "Dashed (var)" need no recover |
134 | 0 | if (nDots == 3 && nDotLen == 197 &&nDashes == 3 && nDashLen == 100 && nDistance == 100) |
135 | 0 | { // "3 Dashes 3 Dots (var)" |
136 | 0 | orLineDash.DashLen = 0; |
137 | 0 | } |
138 | 0 | else if (nDots == 1 && nDotLen == 100 && nDashes == 0 && nDistance == 50) |
139 | 0 | { // "Ultrafine Dotted (var)" |
140 | 0 | orLineDash.DotLen = 0; |
141 | 0 | } |
142 | 0 | else if (nDots == 2 && nDashes == 0 && nDotLen == nDistance |
143 | 0 | && std::abs(nDistance * fWidthHelp - 51.0) < fWidthHelp) |
144 | 0 | { // "Ultrafine Dashed" |
145 | 0 | orLineDash.Dots = 1; |
146 | 0 | orLineDash.DotLen = 51; |
147 | 0 | orLineDash.Dashes = 1; |
148 | 0 | orLineDash.DashLen = 51; |
149 | 0 | orLineDash.Distance = 51; |
150 | 0 | orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT; |
151 | 0 | } |
152 | 0 | else if (nDots == 2 && nDashes == 3 && std::abs(nDotLen * fWidthHelp - 51.0) < fWidthHelp |
153 | 0 | && std::abs(nDashLen * fWidthHelp - 254.0) < fWidthHelp |
154 | 0 | && std::abs(nDistance * fWidthHelp - 127.0) < fWidthHelp) |
155 | 0 | { // "Ultrafine 2 Dots 3 Dashes" |
156 | 0 | orLineDash.DotLen = 51; |
157 | 0 | orLineDash.DashLen = 254; |
158 | 0 | orLineDash.Distance = 127; |
159 | 0 | orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT; |
160 | 0 | } |
161 | 0 | else if (nDots == 1 && nDotLen == 100 && nDashes == 0 |
162 | 0 | && std::abs(nDistance * fWidthHelp - 457.0) < fWidthHelp) |
163 | 0 | { // "Fine Dotted" |
164 | 0 | orLineDash.DotLen = 0; |
165 | 0 | orLineDash.Distance = 457; |
166 | 0 | orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT; |
167 | 0 | } |
168 | 0 | else if (nDots == 1 && nDashes == 10 && nDashLen == 100 |
169 | 0 | && std::abs(nDistance * fWidthHelp - 152.0) < fWidthHelp) |
170 | 0 | { // "Line with Fine Dots" |
171 | 0 | orLineDash.DotLen = 2007; |
172 | 0 | orLineDash.DashLen = 0; |
173 | 0 | orLineDash.Distance = 152; |
174 | 0 | orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT; |
175 | 0 | } |
176 | 0 | else if (nDots == 2 && nDotLen == 100 && nDashes == 1 && nDashLen == nDistance |
177 | 0 | && std::abs(nDistance * fWidthHelp - 203.0) < fWidthHelp) |
178 | 0 | { // "2 Dots 1 Dash" |
179 | 0 | orLineDash.DotLen = 0; |
180 | 0 | orLineDash.DashLen = 203; |
181 | 0 | orLineDash.Distance = 203; |
182 | 0 | orLineDash.Style = orLineDash.Style == DashStyle_ROUNDRELATIVE ? DashStyle_ROUND : DashStyle_RECT; |
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | | DashStyle lclGetDashStyle( sal_Int32 nToken ) |
187 | 124 | { |
188 | 124 | OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0); |
189 | | // MS Office dashing is always relative to line width |
190 | 124 | switch( nToken ) |
191 | 124 | { |
192 | 0 | case XML_rnd: return DashStyle_ROUNDRELATIVE; |
193 | 0 | case XML_sq: return DashStyle_RECTRELATIVE; // default in OOXML |
194 | 124 | case XML_flat: return DashStyle_RECTRELATIVE; // default in MS Office |
195 | 124 | } |
196 | 0 | return DashStyle_RECTRELATIVE; |
197 | 124 | } |
198 | | |
199 | | LineCap lclGetLineCap( sal_Int32 nToken ) |
200 | 11.2k | { |
201 | 11.2k | OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0); |
202 | 11.2k | switch( nToken ) |
203 | 11.2k | { |
204 | 0 | case XML_rnd: return LineCap_ROUND; |
205 | 0 | case XML_sq: return LineCap_SQUARE; // default in OOXML |
206 | 11.2k | case XML_flat: return LineCap_BUTT; // default in MS Office |
207 | 11.2k | } |
208 | 0 | return LineCap_BUTT; |
209 | 11.2k | } |
210 | | |
211 | | LineJoint lclGetLineJoint( sal_Int32 nToken ) |
212 | 15.1k | { |
213 | 15.1k | OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0); |
214 | 15.1k | switch( nToken ) |
215 | 15.1k | { |
216 | 9.93k | case XML_round: return LineJoint_ROUND; |
217 | 5 | case XML_bevel: return LineJoint_BEVEL; |
218 | 5.22k | case XML_miter: return LineJoint_MITER; |
219 | 15.1k | } |
220 | 0 | return LineJoint_ROUND; |
221 | 15.1k | } |
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 | | |
227 | | sal_Int32 lclGetArrowSize( sal_Int32 nToken ) |
228 | 1.94k | { |
229 | 1.94k | OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0); |
230 | 1.94k | switch( nToken ) |
231 | 1.94k | { |
232 | 128 | case XML_sm: return OOX_ARROWSIZE_SMALL; |
233 | 1.81k | case XML_med: return OOX_ARROWSIZE_MEDIUM; |
234 | 0 | case XML_lg: return OOX_ARROWSIZE_LARGE; |
235 | 1.94k | } |
236 | 0 | return OOX_ARROWSIZE_MEDIUM; |
237 | 1.94k | } |
238 | | |
239 | | void lclPushMarkerProperties( ShapePropertyMap& rPropMap, |
240 | | const LineArrowProperties& rArrowProps, sal_Int32 nLineWidth, bool bLineEnd ) |
241 | 179k | { |
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 | 179k | NamedValue aNamedMarker; |
245 | | |
246 | 179k | OUStringBuffer aBuffer; |
247 | 179k | sal_Int32 nMarkerWidth = 0; |
248 | 179k | bool bMarkerCenter = false; |
249 | 179k | sal_Int32 nArrowType = rArrowProps.moArrowType.value_or( XML_none ); |
250 | 179k | OSL_ASSERT((nArrowType & sal_Int32(0xFFFF0000))==0); |
251 | 179k | switch( nArrowType ) |
252 | 179k | { |
253 | 358 | case XML_triangle: |
254 | 358 | aBuffer.append( "msArrowEnd" ); |
255 | 358 | break; |
256 | 610 | case XML_arrow: |
257 | 610 | aBuffer.append( "msArrowOpenEnd" ); |
258 | 610 | break; |
259 | 3 | case XML_stealth: |
260 | 3 | aBuffer.append( "msArrowStealthEnd" ); |
261 | 3 | break; |
262 | 0 | case XML_diamond: |
263 | 0 | aBuffer.append( "msArrowDiamondEnd" ); |
264 | 0 | bMarkerCenter = true; |
265 | 0 | break; |
266 | 0 | case XML_oval: |
267 | 0 | aBuffer.append( "msArrowOvalEnd" ); |
268 | 0 | bMarkerCenter = true; |
269 | 0 | break; |
270 | 179k | } |
271 | | |
272 | 179k | if( !aBuffer.isEmpty() ) |
273 | 971 | { |
274 | 971 | bool bIsArrow = nArrowType == XML_arrow; |
275 | 971 | sal_Int32 nLength = lclGetArrowSize( rArrowProps.moArrowLength.value_or( XML_med ) ); |
276 | 971 | sal_Int32 nWidth = lclGetArrowSize( rArrowProps.moArrowWidth.value_or( XML_med ) ); |
277 | | |
278 | 971 | sal_Int32 nNameIndex = nWidth * 3 + nLength + 1; |
279 | 971 | aBuffer.append( " " + OUString::number( nNameIndex )); |
280 | 971 | if (bIsArrow) |
281 | 610 | { |
282 | | // Arrow marker form depends also on line width |
283 | 610 | aBuffer.append(" " + OUString::number(nLineWidth)); |
284 | 610 | } |
285 | 971 | OUString aMarkerName = aBuffer.makeStringAndClear(); |
286 | | |
287 | 971 | double fArrowLength = 1.0; |
288 | 971 | switch( nLength ) |
289 | 971 | { |
290 | 64 | case OOX_ARROWSIZE_SMALL: fArrowLength = (bIsArrow ? 2.5 : 2.0); break; |
291 | 907 | case OOX_ARROWSIZE_MEDIUM: fArrowLength = (bIsArrow ? 3.5 : 3.0); break; |
292 | 0 | case OOX_ARROWSIZE_LARGE: fArrowLength = (bIsArrow ? 5.5 : 5.0); break; |
293 | 971 | } |
294 | 971 | double fArrowWidth = 1.0; |
295 | 971 | switch( nWidth ) |
296 | 971 | { |
297 | 64 | case OOX_ARROWSIZE_SMALL: fArrowWidth = (bIsArrow ? 2.5 : 2.0); break; |
298 | 907 | case OOX_ARROWSIZE_MEDIUM: fArrowWidth = (bIsArrow ? 3.5 : 3.0); break; |
299 | 0 | case OOX_ARROWSIZE_LARGE: fArrowWidth = (bIsArrow ? 5.5 : 5.0); break; |
300 | 971 | } |
301 | | // set arrow width relative to line width |
302 | 971 | sal_Int32 nBaseLineWidth = ::std::max< sal_Int32 >( nLineWidth, 70 ); |
303 | 971 | 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 | 971 | if( !rPropMap.hasNamedLineMarkerInTable( aMarkerName ) ) |
310 | 270 | { |
311 | | // pass X and Y as percentage to OOX_ARROW_POINT |
312 | 1.82k | 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 | 270 | const double fArrowLineHalfWidth = ::std::max< double >( 100.0 * 0.5 * nLineWidth / nMarkerWidth, 1 ); |
317 | | |
318 | 270 | ::std::vector< awt::Point > aPoints; |
319 | 270 | OSL_ASSERT((rArrowProps.moArrowType.value() & sal_Int32(0xFFFF0000))==0); |
320 | 270 | switch( rArrowProps.moArrowType.value() ) |
321 | 270 | { |
322 | 143 | case XML_triangle: |
323 | 143 | aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); |
324 | 143 | aPoints.push_back( OOX_ARROW_POINT( 100, 100 ) ); |
325 | 143 | aPoints.push_back( OOX_ARROW_POINT( 0, 100 ) ); |
326 | 143 | aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); |
327 | 143 | break; |
328 | 124 | case XML_arrow: |
329 | 124 | aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); |
330 | 124 | aPoints.push_back( OOX_ARROW_POINT( 100, 100 - fArrowLineHalfWidth * 1.5) ); |
331 | 124 | aPoints.push_back( OOX_ARROW_POINT( 100 - fArrowLineHalfWidth * 1.5, 100 ) ); |
332 | 124 | aPoints.push_back( OOX_ARROW_POINT( 50.0 + fArrowLineHalfWidth, 5.5 * fArrowLineHalfWidth) ); |
333 | 124 | aPoints.push_back( OOX_ARROW_POINT( 50.0 + fArrowLineHalfWidth, 100 ) ); |
334 | 124 | aPoints.push_back( OOX_ARROW_POINT( 50.0 - fArrowLineHalfWidth, 100 ) ); |
335 | 124 | aPoints.push_back( OOX_ARROW_POINT( 50.0 - fArrowLineHalfWidth, 5.5 * fArrowLineHalfWidth) ); |
336 | 124 | aPoints.push_back( OOX_ARROW_POINT( fArrowLineHalfWidth * 1.5, 100 ) ); |
337 | 124 | aPoints.push_back( OOX_ARROW_POINT( 0, 100 - fArrowLineHalfWidth * 1.5) ); |
338 | 124 | aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); |
339 | 124 | break; |
340 | 3 | case XML_stealth: |
341 | 3 | aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); |
342 | 3 | aPoints.push_back( OOX_ARROW_POINT( 100, 100 ) ); |
343 | 3 | aPoints.push_back( OOX_ARROW_POINT( 50, 60 ) ); |
344 | 3 | aPoints.push_back( OOX_ARROW_POINT( 0, 100 ) ); |
345 | 3 | aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); |
346 | 3 | break; |
347 | 0 | case XML_diamond: |
348 | 0 | aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); |
349 | 0 | aPoints.push_back( OOX_ARROW_POINT( 100, 50 ) ); |
350 | 0 | aPoints.push_back( OOX_ARROW_POINT( 50, 100 ) ); |
351 | 0 | aPoints.push_back( OOX_ARROW_POINT( 0, 50 ) ); |
352 | 0 | aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); |
353 | 0 | break; |
354 | 0 | case XML_oval: |
355 | 0 | aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); |
356 | 0 | aPoints.push_back( OOX_ARROW_POINT( 75, 7 ) ); |
357 | 0 | aPoints.push_back( OOX_ARROW_POINT( 93, 25 ) ); |
358 | 0 | aPoints.push_back( OOX_ARROW_POINT( 100, 50 ) ); |
359 | 0 | aPoints.push_back( OOX_ARROW_POINT( 93, 75 ) ); |
360 | 0 | aPoints.push_back( OOX_ARROW_POINT( 75, 93 ) ); |
361 | 0 | aPoints.push_back( OOX_ARROW_POINT( 50, 100 ) ); |
362 | 0 | aPoints.push_back( OOX_ARROW_POINT( 25, 93 ) ); |
363 | 0 | aPoints.push_back( OOX_ARROW_POINT( 7, 75 ) ); |
364 | 0 | aPoints.push_back( OOX_ARROW_POINT( 0, 50 ) ); |
365 | 0 | aPoints.push_back( OOX_ARROW_POINT( 7, 25 ) ); |
366 | 0 | aPoints.push_back( OOX_ARROW_POINT( 25, 7 ) ); |
367 | 0 | aPoints.push_back( OOX_ARROW_POINT( 50, 0 ) ); |
368 | 0 | break; |
369 | 270 | } |
370 | | |
371 | 270 | OSL_ENSURE( !aPoints.empty(), "lclPushMarkerProperties - missing arrow coordinates" ); |
372 | 270 | if( !aPoints.empty() ) |
373 | 270 | { |
374 | 270 | PolyPolygonBezierCoords aMarkerCoords; |
375 | 270 | aMarkerCoords.Coordinates = { comphelper::containerToSequence( aPoints ) }; |
376 | | |
377 | 270 | ::std::vector< PolygonFlags > aFlags( aPoints.size(), PolygonFlags_NORMAL ); |
378 | 270 | aMarkerCoords.Flags = { comphelper::containerToSequence( aFlags ) }; |
379 | | |
380 | 270 | aNamedMarker.Name = aMarkerName; |
381 | 270 | aNamedMarker.Value <<= aMarkerCoords; |
382 | 270 | } |
383 | 270 | } |
384 | 701 | else |
385 | 701 | { |
386 | | /* Named marker object exists already in the marker table, pass |
387 | | its name only. This will set the name as property value, but |
388 | | does not create a new object in the marker table. */ |
389 | 701 | aNamedMarker.Name = aMarkerName; |
390 | 701 | } |
391 | 971 | } |
392 | | |
393 | | // push the properties (filled aNamedMarker.Name indicates valid marker) |
394 | 179k | if( aNamedMarker.Name.isEmpty() ) |
395 | 178k | return; |
396 | | |
397 | 971 | if( bLineEnd ) |
398 | 746 | { |
399 | 746 | rPropMap.setProperty( ShapeProperty::LineEnd, aNamedMarker ); |
400 | 746 | rPropMap.setProperty( ShapeProperty::LineEndWidth, nMarkerWidth ); |
401 | 746 | rPropMap.setProperty( ShapeProperty::LineEndCenter, bMarkerCenter ); |
402 | 746 | } |
403 | 225 | else |
404 | 225 | { |
405 | 225 | rPropMap.setProperty( ShapeProperty::LineStart, aNamedMarker ); |
406 | 225 | rPropMap.setProperty( ShapeProperty::LineStartWidth, nMarkerWidth ); |
407 | 225 | rPropMap.setProperty( ShapeProperty::LineStartCenter, bMarkerCenter ); |
408 | 225 | } |
409 | 971 | } |
410 | | |
411 | | } // namespace |
412 | | |
413 | | void LineArrowProperties::assignUsed( const LineArrowProperties& rSourceProps ) |
414 | 544k | { |
415 | 544k | assignIfUsed( moArrowType, rSourceProps.moArrowType ); |
416 | 544k | assignIfUsed( moArrowWidth, rSourceProps.moArrowWidth ); |
417 | 544k | assignIfUsed( moArrowLength, rSourceProps.moArrowLength ); |
418 | 544k | } |
419 | | |
420 | | void LineProperties::assignUsed( const LineProperties& rSourceProps ) |
421 | 272k | { |
422 | 272k | maStartArrow.assignUsed( rSourceProps.maStartArrow ); |
423 | 272k | maEndArrow.assignUsed( rSourceProps.maEndArrow ); |
424 | 272k | maLineFill.assignUsed( rSourceProps.maLineFill ); |
425 | 272k | if( !rSourceProps.maCustomDash.empty() ) |
426 | 0 | maCustomDash = rSourceProps.maCustomDash; |
427 | 272k | assignIfUsed( moLineWidth, rSourceProps.moLineWidth ); |
428 | 272k | assignIfUsed( moPresetDash, rSourceProps.moPresetDash ); |
429 | 272k | assignIfUsed( moLineCompound, rSourceProps.moLineCompound ); |
430 | 272k | assignIfUsed( moLineCap, rSourceProps.moLineCap ); |
431 | 272k | assignIfUsed( moLineJoint, rSourceProps.moLineJoint ); |
432 | 272k | } |
433 | | |
434 | | void LineProperties::pushToPropMap( ShapePropertyMap& rPropMap, |
435 | | const GraphicHelper& rGraphicHelper, ::Color nPhClr, sal_Int16 nPhClrTheme) const |
436 | 89.8k | { |
437 | | // line fill type must exist, otherwise ignore other properties |
438 | 89.8k | if( !maLineFill.moFillType.has_value() ) |
439 | 0 | return; |
440 | | |
441 | | // line style (our core only supports none and solid) |
442 | 89.8k | drawing::LineStyle eLineStyle = (maLineFill.moFillType.value() == XML_noFill) ? drawing::LineStyle_NONE : drawing::LineStyle_SOLID; |
443 | | |
444 | | // line width in 1/100mm |
445 | 89.8k | sal_Int32 nLineWidth = getLineWidth(); // includes conversion from EMUs to 1/100mm |
446 | 89.8k | rPropMap.setProperty( ShapeProperty::LineWidth, nLineWidth ); |
447 | | |
448 | | // line cap type |
449 | 89.8k | LineCap eLineCap = moLineCap.has_value() ? lclGetLineCap( moLineCap.value() ) : LineCap_BUTT; |
450 | 89.8k | if( moLineCap.has_value() ) |
451 | 9.82k | rPropMap.setProperty( ShapeProperty::LineCap, eLineCap ); |
452 | | |
453 | | // create line dash from preset dash token or dash stop vector (not for invisible line) |
454 | 89.8k | if( (eLineStyle != drawing::LineStyle_NONE) && |
455 | 89.8k | ((moPresetDash.has_value() && moPresetDash.value() != XML_solid) || !maCustomDash.empty()) ) |
456 | 124 | { |
457 | 124 | LineDash aLineDash; |
458 | 124 | aLineDash.Style = lclGetDashStyle( moLineCap.value_or( XML_flat ) ); |
459 | | |
460 | 124 | if(moPresetDash.has_value() && moPresetDash.value() != XML_solid) |
461 | 124 | lclConvertPresetDash(aLineDash, moPresetDash.value_or(XML_dash)); |
462 | 0 | else // !maCustomDash.empty() |
463 | 0 | { |
464 | 0 | lclConvertCustomDash(aLineDash, maCustomDash); |
465 | 0 | lclRecoverStandardDashStyles(aLineDash, nLineWidth); |
466 | 0 | } |
467 | | |
468 | | // In MS Office (2020) for preset dash style line caps round and square are included in dash length. |
469 | | // For custom dash style round line cap is included, square line cap is added. In ODF line caps are |
470 | | // always added to dash length. Tweak the length accordingly. |
471 | 124 | if (eLineCap == LineCap_ROUND || (eLineCap == LineCap_SQUARE && maCustomDash.empty())) |
472 | 0 | { |
473 | | // Cannot use -100 because that results in 0 length in some cases and |
474 | | // LibreOffice interprets 0 length as 100%. |
475 | 0 | if (aLineDash.DotLen >= 100 || aLineDash.DashLen >= 100) |
476 | 0 | aLineDash.Distance += 99; |
477 | 0 | if (aLineDash.DotLen >= 100) |
478 | 0 | aLineDash.DotLen -= 99; |
479 | 0 | if (aLineDash.DashLen >= 100) |
480 | 0 | aLineDash.DashLen -= 99; |
481 | 0 | } |
482 | | |
483 | 124 | if( rPropMap.setProperty( ShapeProperty::LineDash, aLineDash ) ) |
484 | 124 | eLineStyle = drawing::LineStyle_DASH; |
485 | 124 | } |
486 | | |
487 | | // set final line style property |
488 | 89.8k | rPropMap.setProperty( ShapeProperty::LineStyle, eLineStyle ); |
489 | | |
490 | | // line joint type |
491 | 89.8k | if( moLineJoint.has_value() ) |
492 | 14.9k | rPropMap.setProperty( ShapeProperty::LineJoint, lclGetLineJoint( moLineJoint.value() ) ); |
493 | | |
494 | | // line color and transparence |
495 | 89.8k | Color aLineColor = maLineFill.getBestSolidColor(); |
496 | 89.8k | if (aLineColor.isUsed()) |
497 | 15.6k | { |
498 | 15.6k | ::Color aColor = aLineColor.getColor(rGraphicHelper, nPhClr); |
499 | 15.6k | rPropMap.setProperty(ShapeProperty::LineColor, aColor); |
500 | 15.6k | if( aLineColor.hasTransparency() ) |
501 | 0 | rPropMap.setProperty( ShapeProperty::LineTransparency, aLineColor.getTransparency() ); |
502 | | |
503 | 15.6k | model::ComplexColor aComplexColor; |
504 | | |
505 | 15.6k | if (aColor == nPhClr) |
506 | 955 | { |
507 | 955 | aComplexColor.setThemeColor(model::convertToThemeColorType(nPhClrTheme)); |
508 | 955 | rPropMap.setProperty(PROP_LineComplexColor, model::color::createXComplexColor(aComplexColor)); |
509 | 955 | } |
510 | 14.6k | else |
511 | 14.6k | { |
512 | 14.6k | aComplexColor.setThemeColor(model::convertToThemeColorType(aLineColor.getSchemeColorIndex())); |
513 | 14.6k | if (aLineColor.getLumMod() != 10000) |
514 | 130 | aComplexColor.addTransformation({model::TransformationType::LumMod, aLineColor.getLumMod()}); |
515 | 14.6k | if (aLineColor.getLumOff() != 0) |
516 | 127 | aComplexColor.addTransformation({model::TransformationType::LumOff, aLineColor.getLumOff()}); |
517 | 14.6k | if (aLineColor.getTintOrShade() > 0) |
518 | 20 | aComplexColor.addTransformation({model::TransformationType::Tint, aLineColor.getTintOrShade()}); |
519 | 14.6k | if (aLineColor.getTintOrShade() < 0) |
520 | 299 | { |
521 | 299 | sal_Int16 nShade = o3tl::narrowing<sal_Int16>(-aLineColor.getTintOrShade()); |
522 | 299 | aComplexColor.addTransformation({model::TransformationType::Shade, nShade}); |
523 | 299 | } |
524 | 14.6k | rPropMap.setProperty(PROP_LineComplexColor, model::color::createXComplexColor(aComplexColor)); |
525 | 14.6k | } |
526 | 15.6k | } |
527 | | |
528 | | // line markers |
529 | 89.8k | lclPushMarkerProperties( rPropMap, maStartArrow, nLineWidth, false ); |
530 | 89.8k | lclPushMarkerProperties( rPropMap, maEndArrow, nLineWidth, true ); |
531 | 89.8k | } |
532 | | |
533 | | drawing::LineStyle LineProperties::getLineStyle() const |
534 | 3.91k | { |
535 | | // rules to calculate the line style inferred from the code in LineProperties::pushToPropMap |
536 | 3.91k | if (maLineFill.moFillType.value() == XML_noFill) |
537 | 2.45k | return drawing::LineStyle_NONE; |
538 | 1.45k | if ((moPresetDash.has_value() && moPresetDash.value() != XML_solid) || |
539 | 1.45k | (!moPresetDash && !maCustomDash.empty())) |
540 | 0 | return drawing::LineStyle_DASH; |
541 | 1.45k | return drawing::LineStyle_SOLID; |
542 | 1.45k | } |
543 | | |
544 | | drawing::LineCap LineProperties::getLineCap() const |
545 | 3.91k | { |
546 | 3.91k | if( moLineCap.has_value() ) |
547 | 1.45k | return lclGetLineCap( moLineCap.value() ); |
548 | | |
549 | 2.45k | return drawing::LineCap_BUTT; |
550 | 3.91k | } |
551 | | |
552 | | drawing::LineJoint LineProperties::getLineJoint() const |
553 | 3.91k | { |
554 | 3.91k | if( moLineJoint.has_value() ) |
555 | 205 | return lclGetLineJoint( moLineJoint.value() ); |
556 | | |
557 | 3.71k | return drawing::LineJoint_NONE; |
558 | 3.91k | } |
559 | | |
560 | | sal_Int32 LineProperties::getLineWidth() const |
561 | 93.7k | { |
562 | 93.7k | return convertEmuToHmm( moLineWidth.value_or( 0 ) ); |
563 | 93.7k | } |
564 | | |
565 | | } // namespace oox |
566 | | |
567 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |