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 <oox/drawingml/drawingmltypes.hxx>
21 #include <com/sun/star/awt/FontUnderline.hpp>
22 #include <com/sun/star/awt/FontStrikeout.hpp>
23 #include <com/sun/star/drawing/Hatch.hpp>
24 #include <com/sun/star/style/CaseMap.hpp>
25 #include <com/sun/star/xml/sax/XFastAttributeList.hpp>
26
27 #include <o3tl/string_view.hxx>
28 #include <optional>
29 #include <osl/diagnose.h>
30 #include <sax/tools/converter.hxx>
31 #include <oox/token/tokens.hxx>
32
33 using ::com::sun::star::uno::Reference;
34 using ::com::sun::star::xml::sax::XFastAttributeList;
35 using namespace ::com::sun::star;
36 using namespace ::com::sun::star::drawing;
37 using namespace ::com::sun::star::geometry;
38 using namespace ::com::sun::star::style;
39
40 namespace oox::drawingml {
41
42 /** converts EMUs into 1/100th mmm */
GetCoordinate(sal_Int32 nValue)43 sal_Int32 GetCoordinate( sal_Int32 nValue )
44 {
45 return o3tl::convert(nValue, o3tl::Length::emu, o3tl::Length::mm100);
46 }
47
48 /** converts an emu string into 1/100th mmm */
GetCoordinate(std::u16string_view sValue)49 sal_Int32 GetCoordinate( std::u16string_view sValue )
50 {
51 sal_Int32 nRet = 0;
52 if( !::sax::Converter::convertNumber( nRet, sValue ) )
53 nRet = 0;
54 return GetCoordinate( nRet );
55 }
56
57 /** converts 1/100mm to EMU */
GetPointFromCoordinate(sal_Int32 nValue)58 sal_Int32 GetPointFromCoordinate( sal_Int32 nValue )
59 {
60 return o3tl::convert(nValue, o3tl::Length::mm100, o3tl::Length::emu);
61 }
62
63 /** converts a ST_Percentage % string into 1/1000th of % */
GetPercent(std::u16string_view sValue)64 sal_Int32 GetPercent( std::u16string_view sValue )
65 {
66 sal_Int32 nRet = 0;
67 if( !::sax::Converter::convertNumber( nRet, sValue ) )
68 nRet = 0;
69
70 return nRet;
71 }
72
GetPositiveFixedPercentage(const OUString & sValue)73 double GetPositiveFixedPercentage( const OUString& sValue )
74 {
75 double fPercent = sValue.toFloat() / 100000.;
76 return fPercent;
77 }
78
79 /** converts the attributes from a CT_TLPoint into an awt Point with 1/1000% */
GetPointPercent(const Reference<XFastAttributeList> & xAttribs)80 awt::Point GetPointPercent( const Reference< XFastAttributeList >& xAttribs )
81 {
82 return awt::Point(GetPercent(xAttribs->getOptionalValue(XML_x)), GetPercent(xAttribs->getOptionalValue(XML_y)));
83 }
84
85 /** converts the ST_TextFontSize to point */
GetTextSize(std::u16string_view sValue)86 float GetTextSize( std::u16string_view sValue )
87 {
88 float fRet = 0;
89 sal_Int32 nRet;
90 if( ::sax::Converter::convertNumber( nRet, sValue ) )
91 fRet = static_cast< float >( static_cast< double >( nRet ) / 100.0 );
92 return fRet;
93 }
94
95 /** converts the ST_TextSpacingPoint (1/100pt) to 1/100mm */
GetTextSpacingPoint(std::u16string_view sValue)96 sal_Int32 GetTextSpacingPoint( std::u16string_view sValue )
97 {
98 sal_Int32 nRet;
99 if( ::sax::Converter::convertNumber( nRet, sValue ) )
100 nRet = GetTextSpacingPoint( nRet );
101 return nRet;
102 }
103
GetTextSpacingPoint(sal_Int32 nValue)104 sal_Int32 GetTextSpacingPoint(sal_Int32 nValue)
105 {
106 constexpr auto mdFromPt = o3tl::getConversionMulDiv(o3tl::Length::pt, o3tl::Length::mm100);
107 constexpr o3tl::detail::m_and_d md(mdFromPt.first, mdFromPt.second * 100);
108 return o3tl::convertNarrowing<sal_Int32, md.m, md.d>(nValue);
109 }
110
GetFontHeight(sal_Int32 nHeight)111 float GetFontHeight( sal_Int32 nHeight )
112 {
113 // convert 1/100 points to points
114 return static_cast< float >( nHeight / 100.0 );
115 }
116
GetFontUnderline(sal_Int32 nToken)117 sal_Int16 GetFontUnderline( sal_Int32 nToken )
118 {
119 OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
120 switch( nToken )
121 {
122 case XML_none: return awt::FontUnderline::NONE;
123 case XML_dash: return awt::FontUnderline::DASH;
124 case XML_dashHeavy: return awt::FontUnderline::BOLDDASH;
125 case XML_dashLong: return awt::FontUnderline::LONGDASH;
126 case XML_dashLongHeavy: return awt::FontUnderline::BOLDLONGDASH;
127 case XML_dbl: return awt::FontUnderline::DOUBLE;
128 case XML_dotDash: return awt::FontUnderline::DASHDOT;
129 case XML_dotDashHeavy: return awt::FontUnderline::BOLDDASHDOT;
130 case XML_dotDotDash: return awt::FontUnderline::DASHDOTDOT;
131 case XML_dotDotDashHeavy: return awt::FontUnderline::BOLDDASHDOTDOT;
132 case XML_dotted: return awt::FontUnderline::DOTTED;
133 case XML_dottedHeavy: return awt::FontUnderline::BOLDDOTTED;
134 case XML_heavy: return awt::FontUnderline::BOLD;
135 case XML_sng: return awt::FontUnderline::SINGLE;
136 case XML_wavy: return awt::FontUnderline::WAVE;
137 case XML_wavyDbl: return awt::FontUnderline::DOUBLEWAVE;
138 case XML_wavyHeavy: return awt::FontUnderline::BOLDWAVE;
139 // case XML_words: // TODO
140 }
141 return awt::FontUnderline::DONTKNOW;
142 }
143
GetFontStrikeout(sal_Int32 nToken)144 sal_Int16 GetFontStrikeout( sal_Int32 nToken )
145 {
146 OSL_ASSERT((nToken & sal_Int32(0xFFFF0000))==0);
147 switch( nToken )
148 {
149 case XML_dblStrike: return awt::FontStrikeout::DOUBLE;
150 case XML_noStrike: return awt::FontStrikeout::NONE;
151 case XML_sngStrike: return awt::FontStrikeout::SINGLE;
152 }
153 return awt::FontStrikeout::DONTKNOW;
154 }
155
GetCaseMap(sal_Int32 nToken)156 sal_Int16 GetCaseMap( sal_Int32 nToken )
157 {
158 switch( nToken )
159 {
160 case XML_all: return CaseMap::UPPERCASE;
161 case XML_small: return CaseMap::SMALLCAPS;
162 }
163 return CaseMap::NONE;
164 }
165
166 /** converts a paragraph align to a ParaAdjust */
GetParaAdjust(sal_Int32 nAlign)167 ParagraphAdjust GetParaAdjust( sal_Int32 nAlign )
168 {
169 OSL_ASSERT((nAlign & sal_Int32(0xFFFF0000))==0);
170 ParagraphAdjust nEnum;
171 switch( nAlign )
172 {
173 case XML_ctr:
174 nEnum = ParagraphAdjust_CENTER;
175 break;
176 case XML_just:
177 case XML_justLow:
178 nEnum = ParagraphAdjust_BLOCK;
179 break;
180 case XML_r:
181 nEnum = ParagraphAdjust_RIGHT;
182 break;
183 case XML_thaiDist:
184 case XML_dist:
185 nEnum = ParagraphAdjust_STRETCH;
186 break;
187 case XML_l:
188 default:
189 nEnum = ParagraphAdjust_LEFT;
190 break;
191 }
192 return nEnum;
193 }
194
GetTextVerticalAdjust(sal_Int32 nToken)195 TextVerticalAdjust GetTextVerticalAdjust( sal_Int32 nToken )
196 {
197 TextVerticalAdjust aVertAdjust;
198 switch( nToken )
199 {
200 case XML_b:
201 aVertAdjust = TextVerticalAdjust_BOTTOM;
202 break;
203 case XML_dist:
204 case XML_just:
205 case XML_ctr:
206 aVertAdjust = TextVerticalAdjust_CENTER;
207 break;
208 case XML_t:
209 default:
210 aVertAdjust = TextVerticalAdjust_TOP;
211 break;
212 }
213 return aVertAdjust;
214 }
215
GetTextVerticalAdjust(TextVerticalAdjust eAdjust)216 const char* GetTextVerticalAdjust( TextVerticalAdjust eAdjust )
217 {
218 const char* sVerticalAdjust = nullptr;
219 switch( eAdjust )
220 {
221 case TextVerticalAdjust_BOTTOM:
222 sVerticalAdjust = "b";
223 break;
224 case TextVerticalAdjust_CENTER:
225 sVerticalAdjust = "ctr";
226 break;
227 case TextVerticalAdjust_TOP:
228 default:
229 sVerticalAdjust = "t";
230 break;
231 }
232 return sVerticalAdjust;
233 }
234
GetTabAlign(sal_Int32 aToken)235 TabAlign GetTabAlign( sal_Int32 aToken )
236 {
237 OSL_ASSERT((aToken & sal_Int32(0xFFFF0000))==0);
238 TabAlign nEnum;
239 switch( aToken )
240 {
241 case XML_ctr:
242 nEnum = TabAlign_CENTER;
243 break;
244 case XML_dec:
245 nEnum = TabAlign_DECIMAL;
246 break;
247 case XML_l:
248 nEnum = TabAlign_LEFT;
249 break;
250 case XML_r:
251 nEnum = TabAlign_RIGHT;
252 break;
253 default:
254 nEnum = TabAlign_DEFAULT;
255 break;
256 }
257 return nEnum;
258 }
259
GetHatchPattern(const drawing::Hatch & rHatch)260 const char* GetHatchPattern( const drawing::Hatch& rHatch )
261 {
262 const char* sPattern = nullptr;
263 const sal_Int32 nAngle = rHatch.Angle > 1800 ? rHatch.Angle - 1800 : rHatch.Angle;
264 // Angle ~ 0° (horizontal)
265 if( (nAngle >= 0 && nAngle < 225) || nAngle >= 1575 )
266 {
267 switch( rHatch.Style )
268 {
269 case drawing::HatchStyle_SINGLE:
270 {
271 if( rHatch.Distance < 75 )
272 sPattern = "ltHorz";
273 else
274 sPattern = "horz";
275
276 break;
277 }
278 case drawing::HatchStyle_DOUBLE:
279 case drawing::HatchStyle_TRIPLE:
280 {
281 if( rHatch.Distance < 75 )
282 sPattern = "smGrid";
283 else
284 sPattern = "lgGrid";
285
286 break;
287 }
288 default: break;
289 }
290 }
291 // Angle ~ 45° (upward diagonal)
292 else if( nAngle < 675 )
293 {
294 switch( rHatch.Style )
295 {
296 case drawing::HatchStyle_SINGLE:
297 {
298 if( rHatch.Distance < 75 )
299 sPattern = "ltUpDiag";
300 else
301 sPattern = "wdUpDiag";
302
303 break;
304 }
305 case drawing::HatchStyle_DOUBLE:
306 case drawing::HatchStyle_TRIPLE:
307 {
308 if( rHatch.Distance < 75 )
309 sPattern = "smCheck";
310 else
311 sPattern = "openDmnd";
312
313 break;
314 }
315 default: break;
316 }
317 }
318 // Angle ~ 90° (vertical)
319 else if( nAngle < 1125 )
320 {
321 switch( rHatch.Style )
322 {
323 case drawing::HatchStyle_SINGLE:
324 {
325 // dkVert is imported as Distance = 25, ltVert as Distance = 50, export them accordingly.
326 if( rHatch.Distance < 50 )
327 sPattern = "dkVert";
328 else if( rHatch.Distance < 75 )
329 sPattern = "ltVert";
330 else
331 sPattern = "vert";
332
333 break;
334 }
335 case drawing::HatchStyle_DOUBLE:
336 case drawing::HatchStyle_TRIPLE:
337 {
338 if( rHatch.Distance < 75 )
339 sPattern = "smGrid";
340 else
341 sPattern = "lgGrid";
342
343 break;
344 }
345 default: break;
346 }
347 }
348 // Angle ~ 135° (downward diagonal)
349 else if( nAngle < 1575 )
350 {
351 switch( rHatch.Style )
352 {
353 case drawing::HatchStyle_SINGLE:
354 {
355 if( rHatch.Distance < 75 )
356 sPattern = "ltDnDiag";
357 else
358 sPattern = "wdDnDiag";
359
360 break;
361 }
362 case drawing::HatchStyle_DOUBLE:
363 case drawing::HatchStyle_TRIPLE:
364 {
365 if( rHatch.Distance < 75 )
366 sPattern = "smCheck";
367 else
368 sPattern = "openDmnd";
369
370 break;
371 }
372 default: break;
373 }
374 }
375 return sPattern;
376 }
377
GetTextVerticalType(sal_Int32 nRotateAngle)378 std::optional<OString> GetTextVerticalType(sal_Int32 nRotateAngle)
379 {
380 switch (nRotateAngle)
381 {
382 case 9000:
383 return "vert270";
384 case 27000:
385 return "vert";
386 default:
387 return {};
388 }
389 }
390
391 namespace
392 {
393 // ISO/IEC-29500 Part 1 ST_Percentage, and [MS-OI29500] 2.1.1324
GetST_Percentage(std::u16string_view s)394 sal_Int32 GetST_Percentage(std::u16string_view s)
395 {
396 if (o3tl::ends_with(s, u"%"))
397 return std::round(o3tl::toDouble(s) * 1000);
398 return o3tl::toInt32(s);
399 }
400 }
401
402 /** converts the attributes from a CT_RelativeRect to an IntegerRectangle2D */
GetRelativeRect(const Reference<XFastAttributeList> & xAttribs)403 IntegerRectangle2D GetRelativeRect( const Reference< XFastAttributeList >& xAttribs )
404 {
405 IntegerRectangle2D r;
406
407 r.X1 = GetST_Percentage(xAttribs->getOptionalValue( XML_l ));
408 r.Y1 = GetST_Percentage(xAttribs->getOptionalValue( XML_t ));
409 r.X2 = GetST_Percentage(xAttribs->getOptionalValue( XML_r ));
410 r.Y2 = GetST_Percentage(xAttribs->getOptionalValue( XML_b ));
411
412 return r;
413 }
414
fillRelativeRectangle(model::RelativeRectangle & rRelativeRectangle,const Reference<XFastAttributeList> & xAttribs)415 void fillRelativeRectangle(model::RelativeRectangle& rRelativeRectangle, const Reference<XFastAttributeList>& xAttribs)
416 {
417 rRelativeRectangle.mnLeft = GetST_Percentage(xAttribs->getOptionalValue(XML_l));
418 rRelativeRectangle.mnTop = GetST_Percentage(xAttribs->getOptionalValue(XML_t));
419 rRelativeRectangle.mnRight = GetST_Percentage(xAttribs->getOptionalValue(XML_r));
420 rRelativeRectangle.mnBottom = GetST_Percentage(xAttribs->getOptionalValue(XML_b));
421 }
422
423 /** converts the attributes from a CT_Size2D into an awt Size with 1/100thmm */
GetSize2D(const Reference<XFastAttributeList> & xAttribs)424 awt::Size GetSize2D( const Reference< XFastAttributeList >& xAttribs )
425 {
426 return awt::Size( GetCoordinate( xAttribs->getOptionalValue( XML_cx ) ), GetCoordinate( xAttribs->getOptionalValue( XML_cy ) ) );
427 }
428
GetIndexRange(const Reference<XFastAttributeList> & xAttributes)429 IndexRange GetIndexRange( const Reference< XFastAttributeList >& xAttributes )
430 {
431 IndexRange range;
432 range.start = xAttributes->getOptionalValue( XML_st ).toInt32();
433 range.end = xAttributes->getOptionalValue( XML_end ).toInt32();
434 return range;
435 }
436
437
convertToRectangleAlignment(sal_Int32 nToken)438 model::RectangleAlignment convertToRectangleAlignment(sal_Int32 nToken)
439 {
440 switch (nToken)
441 {
442 case XML_tl: return model::RectangleAlignment::TopLeft;
443 case XML_t: return model::RectangleAlignment::Top;
444 case XML_tr: return model::RectangleAlignment::TopRight;
445 case XML_l: return model::RectangleAlignment::Left;
446 case XML_ctr: return model::RectangleAlignment::Center;
447 case XML_r: return model::RectangleAlignment::Right;
448 case XML_bl: return model::RectangleAlignment::BottomLeft;
449 case XML_b: return model::RectangleAlignment::Bottom;
450 case XML_br: return model::RectangleAlignment::BottomRight;
451 default:
452 break;
453 }
454 return model::RectangleAlignment::Unset;
455 }
456
457 } // namespace oox::drawingml
458
459 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
460