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 <sax/tools/converter.hxx>
22 #include <com/sun/star/i18n/UnicodeType.hpp>
23 #include <com/sun/star/util/DateTime.hpp>
24 #include <com/sun/star/util/Date.hpp>
25 #include <com/sun/star/util/Duration.hpp>
26 #include <com/sun/star/util/Time.hpp>
29 #include <rtl/ustrbuf.hxx>
30 #include <rtl/math.hxx>
31 #include <rtl/character.hxx>
32 #include <sal/log.hxx>
33 #include <o3tl/string_view.hxx>
34 #include <o3tl/typed_flags_set.hxx>
35 #include <o3tl/unit_conversion.hxx>
36 #include <osl/diagnose.h>
37 #include <tools/long.hxx>
38 #include <tools/time.hxx>
42 #include <string_view>
44 using namespace com::sun::star
;
45 using namespace com::sun::star::uno
;
46 using namespace com::sun::star::util
;
51 const std::string_view gpsMM
= "mm";
52 const std::string_view gpsCM
= "cm";
53 const std::string_view gpsPT
= "pt";
54 const std::string_view gpsINCH
= "in";
55 const std::string_view gpsPC
= "pc";
56 const std::string_view gpsPX
= "px";
57 const std::string_view gpsPERCENT
= "%";
58 const std::string_view gpsFONT_EM
= "em";
59 const std::string_view gpsFONT_IC
= "ic";
61 const sal_Int8 XML_MAXDIGITSCOUNT_TIME
= 14;
63 static sal_Int64
toInt64_WithLength(const sal_Unicode
* str
, sal_Int16 radix
, sal_Int32 nStrLength
)
65 return rtl_ustr_toInt64_WithLength(str
, radix
, nStrLength
);
67 static sal_Int64
toInt64_WithLength(const char * str
, sal_Int16 radix
, sal_Int32 nStrLength
)
69 return rtl_str_toInt64_WithLength(str
, radix
, nStrLength
);
74 const std::map
<sal_Int16
, std::string_view
> stConvertMeasureUnitStrMap
{
75 { MeasureUnit::MM
, gpsMM
}, { MeasureUnit::CM
, gpsCM
},
76 { MeasureUnit::INCH
, gpsINCH
}, { MeasureUnit::POINT
, gpsPT
},
77 { MeasureUnit::PICA
, gpsPC
}, { MeasureUnit::PERCENT
, gpsPERCENT
},
78 { MeasureUnit::PIXEL
, gpsPX
}, { MeasureUnit::FONT_EM
, gpsFONT_EM
},
79 { MeasureUnit::FONT_CJK_ADVANCE
, gpsFONT_IC
}
82 o3tl::Length
Measure2O3tlUnit(sal_Int16 nUnit
)
86 case MeasureUnit::TWIP
:
87 return o3tl::Length::twip
;
88 case MeasureUnit::POINT
:
89 return o3tl::Length::pt
;
90 case MeasureUnit::MM_10TH
:
91 return o3tl::Length::mm10
;
92 case MeasureUnit::MM_100TH
:
93 return o3tl::Length::mm100
;
95 return o3tl::Length::mm
;
97 return o3tl::Length::cm
;
99 SAL_WARN("sax", "unit not supported for length");
101 case MeasureUnit::INCH
:
102 return o3tl::Length::in
;
106 std::string_view
Measure2UnitString(sal_Int16 nUnit
)
110 case MeasureUnit::TWIP
:
112 case MeasureUnit::POINT
:
114 case MeasureUnit::MM_10TH
:
115 case MeasureUnit::MM_100TH
:
117 case MeasureUnit::MM
:
119 case MeasureUnit::CM
:
121 case MeasureUnit::INCH
:
127 template <typename V
> bool wordEndsWith(V string
, std::string_view expected
)
129 V substr
= string
.substr(0, expected
.size());
130 return std::equal(substr
.begin(), substr
.end(), expected
.begin(), expected
.end(),
131 [](sal_uInt32 c1
, sal_uInt32 c2
) { return rtl::toAsciiLowerCase(c1
) == c2
; })
132 && (string
.size() == expected
.size() || string
[expected
.size()] == ' ');
137 /** parse unit substring into measure unit*/
138 template <class V
> static std::optional
<sal_Int16
> lcl_parseMeasureUnit(const V
& rString
)
145 switch (rtl::toAsciiLowerCase
<sal_uInt32
>(rString
[0]))
148 return MeasureUnit::PERCENT
;
151 if (wordEndsWith(rString
.substr(1), "m"))
152 return MeasureUnit::CM
;
156 if (wordEndsWith(rString
.substr(1), "m"))
157 return MeasureUnit::FONT_EM
;
161 if (wordEndsWith(rString
.substr(1), "c"))
162 return MeasureUnit::FONT_CJK_ADVANCE
;
163 if (wordEndsWith(rString
.substr(1), "n"))
164 return MeasureUnit::INCH
;
168 if (wordEndsWith(rString
.substr(1), "m"))
169 return MeasureUnit::MM
;
173 if (wordEndsWith(rString
.substr(1), "c"))
174 return MeasureUnit::PICA
;
175 if (wordEndsWith(rString
.substr(1), "t"))
176 return MeasureUnit::POINT
;
177 if (wordEndsWith(rString
.substr(1), "x"))
178 return MeasureUnit::PIXEL
;
185 /** parse measure string into double and measure unit*/
187 static bool lcl_parseMeasure(double& rValue
, std::optional
<sal_Int16
>& rSourceUnit
, bool& rNeg
, const V
& rString
)
197 sal_Int32
const nLen
= rString
.size();
200 while( (nPos
< nLen
) && (rString
[nPos
] <= ' ') )
203 if( nPos
< nLen
&& '-' == rString
[nPos
] )
210 while( nPos
< nLen
&&
211 '0' <= rString
[nPos
] &&
212 '9' >= rString
[nPos
] )
214 // TODO: check overflow!
216 nVal
+= (rString
[nPos
] - '0');
219 if( nPos
< nLen
&& '.' == rString
[nPos
] )
224 while( nPos
< nLen
&&
225 '0' <= rString
[nPos
] &&
226 '9' >= rString
[nPos
] )
228 // TODO: check overflow!
230 nVal
+= ( static_cast<double>(rString
[nPos
] - '0') / nDiv
);
236 while( (nPos
< nLen
) && (rString
[nPos
] <= ' ') )
241 // Parse unit from the tail
242 auto nUnit
= lcl_parseMeasureUnit(rString
.substr(nPos
));
243 if (!nUnit
.has_value())
248 rSourceUnit
= nUnit
.value();
257 /** convert string to measure using optional min and max values*/
259 static bool lcl_convertMeasure(sal_Int32
& rValue
, const V
& rString
,
260 sal_Int16 nTargetUnit
/* = MeasureUnit::MM_100TH */,
261 sal_Int32 nMin
/* = SAL_MIN_INT32 */,
262 sal_Int32 nMax
/* = SAL_MAX_INT32 */)
265 std::optional
<sal_Int16
> nSourceUnit
;
268 if (!lcl_parseMeasure(nVal
, nSourceUnit
, bNeg
, rString
))
273 if (nSourceUnit
.has_value())
275 if( MeasureUnit::PERCENT
== nTargetUnit
)
277 if (MeasureUnit::PERCENT
!= nSourceUnit
)
280 else if( MeasureUnit::PIXEL
== nTargetUnit
)
282 if (MeasureUnit::PIXEL
!= nSourceUnit
)
287 OSL_ENSURE( MeasureUnit::TWIP
== nTargetUnit
|| MeasureUnit::POINT
== nTargetUnit
||
288 MeasureUnit::MM_100TH
== nTargetUnit
|| MeasureUnit::MM_10TH
== nTargetUnit
||
289 MeasureUnit::PIXEL
== nTargetUnit
, "unit is not supported");
291 o3tl::Length eFrom
= o3tl::Length::invalid
;
293 if( MeasureUnit::TWIP
== nTargetUnit
)
295 switch (nSourceUnit
.value())
297 case MeasureUnit::CM
:
298 eFrom
= o3tl::Length::cm
;
300 case MeasureUnit::INCH
:
301 eFrom
= o3tl::Length::in
;
303 case MeasureUnit::MM
:
304 eFrom
= o3tl::Length::mm
;
306 case MeasureUnit::POINT
:
307 eFrom
= o3tl::Length::pt
;
309 case MeasureUnit::PICA
:
310 eFrom
= o3tl::Length::pc
;
314 else if( MeasureUnit::MM_100TH
== nTargetUnit
|| MeasureUnit::MM_10TH
== nTargetUnit
)
316 switch (nSourceUnit
.value())
318 case MeasureUnit::CM
:
319 eFrom
= o3tl::Length::cm
;
321 case MeasureUnit::INCH
:
322 eFrom
= o3tl::Length::in
;
324 case MeasureUnit::MM
:
325 eFrom
= o3tl::Length::mm
;
327 case MeasureUnit::POINT
:
328 eFrom
= o3tl::Length::pt
;
330 case MeasureUnit::PICA
:
331 eFrom
= o3tl::Length::pc
;
333 case MeasureUnit::PIXEL
:
334 eFrom
= o3tl::Length::px
;
338 else if( MeasureUnit::POINT
== nTargetUnit
)
340 if (MeasureUnit::POINT
== nSourceUnit
)
341 eFrom
= o3tl::Length::pt
;
344 if (eFrom
== o3tl::Length::invalid
)
347 // TODO: check overflow
348 nVal
= o3tl::convert(nVal
, eFrom
, Measure2O3tlUnit(nTargetUnit
));
356 if( nVal
<= static_cast<double>(nMin
) )
358 else if( nVal
>= static_cast<double>(nMax
) )
361 rValue
= static_cast<sal_Int32
>(nVal
);
366 /** convert string to measure using optional min and max values*/
367 bool Converter::convertMeasure( sal_Int32
& rValue
,
368 std::u16string_view rString
,
369 sal_Int16 nTargetUnit
/* = MeasureUnit::MM_100TH */,
370 sal_Int32 nMin
/* = SAL_MIN_INT32 */,
371 sal_Int32 nMax
/* = SAL_MAX_INT32 */ )
373 return lcl_convertMeasure(rValue
, rString
, nTargetUnit
, nMin
, nMax
);
376 /** convert string to measure using optional min and max values*/
377 bool Converter::convertMeasure( sal_Int32
& rValue
,
378 std::string_view rString
,
379 sal_Int16 nTargetUnit
/* = MeasureUnit::MM_100TH */,
380 sal_Int32 nMin
/* = SAL_MIN_INT32 */,
381 sal_Int32 nMax
/* = SAL_MAX_INT32 */ )
383 return lcl_convertMeasure(rValue
, rString
, nTargetUnit
, nMin
, nMax
);
387 /** convert measure in given unit to string with given unit */
388 void Converter::convertMeasure( OUStringBuffer
& rBuffer
,
390 sal_Int16 nSourceUnit
/* = MeasureUnit::MM_100TH */,
391 sal_Int16 nTargetUnit
/* = MeasureUnit::INCH */ )
393 if( nSourceUnit
== MeasureUnit::PERCENT
)
395 OSL_ENSURE( nTargetUnit
== MeasureUnit::PERCENT
,
396 "MeasureUnit::PERCENT only maps to MeasureUnit::PERCENT!" );
398 rBuffer
.append( nMeasure
);
399 rBuffer
.append( '%' );
403 sal_Int64
nValue(nMeasure
); // extend to 64-bit first to avoid overflow
404 // the sign is processed separately
408 rBuffer
.append( '-' );
411 o3tl::Length eFrom
= o3tl::Length::in
, eTo
= o3tl::Length::in
;
412 int nFac
= 100; // used to get specific number of decimals (2 by default)
413 std::string_view psUnit
;
414 switch( nSourceUnit
)
416 case MeasureUnit::TWIP
:
417 eFrom
= o3tl::Length::twip
;
418 switch( nTargetUnit
)
420 case MeasureUnit::MM_100TH
:
421 case MeasureUnit::MM_10TH
:
422 OSL_ENSURE( MeasureUnit::INCH
== nTargetUnit
,"output unit not supported for twip values" );
424 case MeasureUnit::MM
:
425 eTo
= o3tl::Length::mm
;
430 case MeasureUnit::CM
:
431 eTo
= o3tl::Length::cm
;
436 case MeasureUnit::POINT
:
437 eTo
= o3tl::Length::pt
;
442 case MeasureUnit::INCH
:
444 OSL_ENSURE( MeasureUnit::INCH
== nTargetUnit
,
445 "output unit not supported for twip values" );
452 case MeasureUnit::POINT
:
453 // 1pt = 1pt (exactly)
454 OSL_ENSURE( MeasureUnit::POINT
== nTargetUnit
,
455 "output unit not supported for pt values" );
456 eFrom
= eTo
= o3tl::Length::pt
;
460 case MeasureUnit::MM_10TH
:
461 case MeasureUnit::MM_100TH
:
463 int nFac2
= (MeasureUnit::MM_100TH
== nSourceUnit
) ? 100 : 10;
464 eFrom
= Measure2O3tlUnit(nSourceUnit
);
465 switch( nTargetUnit
)
467 case MeasureUnit::MM_100TH
:
468 case MeasureUnit::MM_10TH
:
469 OSL_ENSURE( MeasureUnit::INCH
== nTargetUnit
,
470 "output unit not supported for 1/100mm values" );
472 case MeasureUnit::MM
:
473 eTo
= o3tl::Length::mm
;
478 case MeasureUnit::CM
:
479 eTo
= o3tl::Length::cm
;
484 case MeasureUnit::POINT
:
485 eTo
= o3tl::Length::pt
;
490 case MeasureUnit::INCH
:
492 OSL_ENSURE( MeasureUnit::INCH
== nTargetUnit
,
493 "output unit not supported for 1/100mm values" );
501 OSL_ENSURE(false, "sax::Converter::convertMeasure(): "
502 "source unit not supported");
506 nValue
= o3tl::convert(nValue
* nFac
, eFrom
, eTo
);
508 rBuffer
.append( static_cast<sal_Int64
>(nValue
/ nFac
) );
509 if (nFac
> 1 && (nValue
% nFac
) != 0)
511 rBuffer
.append( '.' );
512 while (nFac
> 1 && (nValue
% nFac
) != 0)
515 rBuffer
.append( static_cast<sal_Int32
>((nValue
/ nFac
) % 10) );
519 if (psUnit
.length() > 0)
520 rBuffer
.appendAscii(psUnit
.data(), psUnit
.length());
523 /** convert string to measure with unit*/
524 bool Converter::convertMeasureUnit(double& rValue
, std::optional
<sal_Int16
>& rValueUnit
,
525 std::u16string_view rString
)
528 bool bResult
= lcl_parseMeasure(rValue
, rValueUnit
, bNeg
, rString
);
538 /** convert string to measure with unit*/
539 bool Converter::convertMeasureUnit(double& rValue
, std::optional
<sal_Int16
>& rValueUnit
,
540 std::string_view rString
)
543 bool bResult
= lcl_parseMeasure(rValue
, rValueUnit
, bNeg
, rString
);
553 /** convert measure with given unit to string with given unit*/
554 void Converter::convertMeasureUnit(OUStringBuffer
& rBuffer
, double dValue
,
555 std::optional
<sal_Int16
> nValueUnit
)
557 ::rtl::math::doubleToUStringBuffer(rBuffer
, dValue
, rtl_math_StringFormat_Automatic
,
558 rtl_math_DecimalPlaces_Max
, '.', true);
560 if (nValueUnit
.has_value())
562 if (auto it
= stConvertMeasureUnitStrMap
.find(*nValueUnit
);
563 it
!= stConvertMeasureUnitStrMap
.end())
565 rBuffer
.appendAscii(it
->second
.data(), it
->second
.length());
570 /** convert string to boolean */
571 bool Converter::convertBool( bool& rBool
, std::u16string_view rString
)
573 rBool
= rString
== u
"true";
575 return rBool
|| (rString
== u
"false");
578 /** convert string to boolean */
579 bool Converter::convertBool( bool& rBool
, std::string_view rString
)
581 rBool
= rString
== "true";
583 return rBool
|| (rString
== "false");
586 /** convert boolean to string */
587 void Converter::convertBool( OUStringBuffer
& rBuffer
, bool bValue
)
589 rBuffer
.append( bValue
);
592 /** convert string to percent */
593 bool Converter::convertPercent( sal_Int32
& rPercent
, std::u16string_view rString
)
595 return convertMeasure( rPercent
, rString
, MeasureUnit::PERCENT
);
598 /** convert string to percent */
599 bool Converter::convertPercent( sal_Int32
& rPercent
, std::string_view rString
)
601 return convertMeasure( rPercent
, rString
, MeasureUnit::PERCENT
);
604 /** convert percent to string */
605 void Converter::convertPercent( OUStringBuffer
& rBuffer
, sal_Int32 nValue
)
607 rBuffer
.append( nValue
);
608 rBuffer
.append( '%' );
611 /** convert string to pixel measure */
612 bool Converter::convertMeasurePx( sal_Int32
& rPixel
, std::u16string_view rString
)
614 return convertMeasure( rPixel
, rString
, MeasureUnit::PIXEL
);
617 /** convert string to pixel measure */
618 bool Converter::convertMeasurePx( sal_Int32
& rPixel
, std::string_view rString
)
620 return convertMeasure( rPixel
, rString
, MeasureUnit::PIXEL
);
623 /** convert pixel measure to string */
624 void Converter::convertMeasurePx( OUStringBuffer
& rBuffer
, sal_Int32 nValue
)
626 rBuffer
.append( nValue
);
627 rBuffer
.append( 'p' );
628 rBuffer
.append( 'x' );
631 static int lcl_gethex( int nChar
)
633 if( nChar
>= '0' && nChar
<= '9' )
635 else if( nChar
>= 'a' && nChar
<= 'f' )
636 return nChar
- 'a' + 10;
637 else if( nChar
>= 'A' && nChar
<= 'F' )
638 return nChar
- 'A' + 10;
643 /** convert string to rgb color */
645 static bool lcl_convertColor( sal_Int32
& rColor
, V rValue
)
647 if( rValue
.size() != 7 || rValue
[0] != '#' )
650 rColor
= lcl_gethex( rValue
[1] ) * 16 + lcl_gethex( rValue
[2] );
653 rColor
|= lcl_gethex( rValue
[3] ) * 16 + lcl_gethex( rValue
[4] );
656 rColor
|= lcl_gethex( rValue
[5] ) * 16 + lcl_gethex( rValue
[6] );
661 /** convert string to rgb color */
662 bool Converter::convertColor( sal_Int32
& rColor
, std::u16string_view rValue
)
664 return lcl_convertColor(rColor
, rValue
);
667 /** convert string to rgb color */
668 bool Converter::convertColor( sal_Int32
& rColor
, std::string_view rValue
)
670 return lcl_convertColor(rColor
, rValue
);
673 const char aHexTab
[] = "0123456789abcdef";
675 /** convert color to string */
676 void Converter::convertColor( OUStringBuffer
& rBuffer
, sal_Int32 nColor
)
678 rBuffer
.append( '#' );
680 sal_uInt8 nCol
= static_cast<sal_uInt8
>(nColor
>> 16);
681 rBuffer
.append( sal_Unicode( aHexTab
[ nCol
>> 4 ] ) );
682 rBuffer
.append( sal_Unicode( aHexTab
[ nCol
& 0xf ] ) );
684 nCol
= static_cast<sal_uInt8
>(nColor
>> 8);
685 rBuffer
.append( sal_Unicode( aHexTab
[ nCol
>> 4 ] ) );
686 rBuffer
.append( sal_Unicode( aHexTab
[ nCol
& 0xf ] ) );
688 nCol
= static_cast<sal_uInt8
>(nColor
);
689 rBuffer
.append( sal_Unicode( aHexTab
[ nCol
>> 4 ] ) );
690 rBuffer
.append( sal_Unicode( aHexTab
[ nCol
& 0xf ] ) );
693 /** convert string to number with optional min and max values */
694 bool Converter::convertNumber( sal_Int32
& rValue
,
695 std::u16string_view aString
,
696 sal_Int32 nMin
, sal_Int32 nMax
)
699 sal_Int64 nNumber
= 0;
700 bool bRet
= convertNumber64(nNumber
,aString
,nMin
,nMax
);
702 rValue
= static_cast<sal_Int32
>(nNumber
);
706 /** convert string to number with optional min and max values */
707 bool Converter::convertNumber( sal_Int32
& rValue
,
708 std::string_view aString
,
709 sal_Int32 nMin
, sal_Int32 nMax
)
712 sal_Int64 nNumber
= 0;
713 bool bRet
= convertNumber64(nNumber
,aString
,nMin
,nMax
);
715 rValue
= static_cast<sal_Int32
>(nNumber
);
719 /** convert string to 64-bit number with optional min and max values */
721 static bool lcl_convertNumber64( sal_Int64
& rValue
,
723 sal_Int64 nMin
, sal_Int64 nMax
)
726 sal_Int32
const nLen
= aString
.size();
729 while( (nPos
< nLen
) && (aString
[nPos
] <= ' ') )
732 sal_Int32 nNumberStartPos
= nPos
;
734 if( nPos
< nLen
&& '-' == aString
[nPos
] )
740 while( nPos
< nLen
&&
741 '0' <= aString
[nPos
] &&
742 '9' >= aString
[nPos
] )
747 rValue
= toInt64_WithLength(aString
.data() + nNumberStartPos
, 10, nPos
- nNumberStartPos
);
751 else if( rValue
> nMax
)
754 return ( nPos
== nLen
&& rValue
>= nMin
&& rValue
<= nMax
);
757 /** convert string to 64-bit number with optional min and max values */
758 bool Converter::convertNumber64( sal_Int64
& rValue
,
759 std::u16string_view aString
,
760 sal_Int64 nMin
, sal_Int64 nMax
)
762 return lcl_convertNumber64(rValue
, aString
, nMin
, nMax
);
765 /** convert string to 64-bit number with optional min and max values */
766 bool Converter::convertNumber64( sal_Int64
& rValue
,
767 std::string_view aString
,
768 sal_Int64 nMin
, sal_Int64 nMax
)
770 return lcl_convertNumber64(rValue
, aString
, nMin
, nMax
);
774 /** convert double number to string (using ::rtl::math) */
775 void Converter::convertDouble( OUStringBuffer
& rBuffer
,
778 sal_Int16 nSourceUnit
,
779 sal_Int16 nTargetUnit
)
781 if(MeasureUnit::PERCENT
== nSourceUnit
)
783 OSL_ENSURE( nTargetUnit
== MeasureUnit::PERCENT
, "MeasureUnit::PERCENT only maps to MeasureUnit::PERCENT!" );
784 ::rtl::math::doubleToUStringBuffer( rBuffer
, fNumber
, rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
, '.', true);
790 OUStringBuffer sUnit
;
791 double fFactor
= GetConversionFactor(sUnit
, nSourceUnit
, nTargetUnit
);
794 ::rtl::math::doubleToUStringBuffer( rBuffer
, fNumber
, rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
, '.', true);
796 rBuffer
.append(sUnit
);
800 /** convert double number to string (using ::rtl::math) */
801 void Converter::convertDouble( OUStringBuffer
& rBuffer
, double fNumber
)
803 ::rtl::math::doubleToUStringBuffer( rBuffer
, fNumber
, rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
, '.', true);
806 /** convert string to double number (using ::rtl::math) */
807 bool Converter::convertDouble(double& rValue
,
808 std::u16string_view rString
, sal_Int16 nSourceUnit
, sal_Int16 nTargetUnit
)
810 if (!convertDouble(rValue
, rString
))
813 OUStringBuffer sUnit
;
814 // fdo#48969: switch source and target because factor is used to divide!
815 double const fFactor
=
816 GetConversionFactor(sUnit
, nTargetUnit
, nSourceUnit
);
817 if(fFactor
!= 1.0 && fFactor
!= 0.0)
822 /** convert string to double number (using ::rtl::math) */
823 bool Converter::convertDouble(double& rValue
,
824 std::string_view rString
, sal_Int16 nSourceUnit
, sal_Int16 nTargetUnit
)
826 if (!convertDouble(rValue
, rString
))
830 // fdo#48969: switch source and target because factor is used to divide!
831 double const fFactor
=
832 GetConversionFactor(sUnit
, nTargetUnit
, nSourceUnit
);
833 if(fFactor
!= 1.0 && fFactor
!= 0.0)
838 /** convert string to double number (using ::rtl::math) */
839 bool Converter::convertDouble(double& rValue
, std::u16string_view rString
, std::u16string_view
* pRest
)
841 rtl_math_ConversionStatus eStatus
;
842 const sal_Unicode
* pEnd
;
843 rValue
= rtl_math_uStringToDouble(rString
.data(),
844 rString
.data() + rString
.size(),
845 /*cDecSeparator*/'.', /*cGroupSeparator*/',',
848 *pRest
= rString
.substr(pEnd
- rString
.data());
849 return ( eStatus
== rtl_math_ConversionStatus_Ok
);
852 /** convert string to double number (using ::rtl::math) */
853 bool Converter::convertDouble(double& rValue
, std::string_view rString
, std::string_view
* pRest
)
855 rtl_math_ConversionStatus eStatus
;
857 rValue
= rtl_math_stringToDouble(rString
.data(),
858 rString
.data() + rString
.size(),
859 /*cDecSeparator*/'.', /*cGroupSeparator*/',',
862 *pRest
= rString
.substr(pEnd
- rString
.data());
863 return ( eStatus
== rtl_math_ConversionStatus_Ok
);
866 /** convert number, 10th of degrees with range [0..3600] to SVG angle */
867 void Converter::convert10thDegAngle(OUStringBuffer
& rBuffer
, sal_Int16
const nAngle
,
868 const bool isWrongOOo10thDegAngle
)
870 if (isWrongOOo10thDegAngle
)
872 rBuffer
.append(static_cast<sal_Int32
>(nAngle
));
876 double fAngle(double(nAngle
) / 10.0);
877 ::sax::Converter::convertDouble(rBuffer
, fAngle
);
878 rBuffer
.append("deg");
882 /** convert SVG angle to number in 10th of degrees */
883 bool Converter::convert10thDegAngle(sal_Int16
& rAngle
, std::u16string_view rString
,
884 bool const isWrongOOo10thDegAngle
)
886 // ODF 1.1 leaves it undefined what the number means, but ODF 1.2 says it's
887 // degrees, while OOo has historically used 10th of degrees :(
888 // So import degrees when we see the "deg" suffix but continue with 10th of
889 // degrees for now for the sake of existing OOo/LO documents, until the
890 // new versions that can read "deg" suffix are widely deployed and we can
891 // start to write the "deg" suffix.
893 std::u16string_view aRest
;
894 bool bRet
= ::sax::Converter::convertDouble(fAngle
, rString
, &aRest
);
899 else if (aRest
== u
"grad")
900 fAngle
*= 9.0; // 360deg = 400grad
901 else if (aRest
== u
"rad")
902 fAngle
= basegfx::rad2deg
<10>(fAngle
);
903 else // no explicit unit
904 { // isWrongOOo10thDegAngle = true: nothing to do here. Wrong, but backward compatible.
907 // Wrong unit. Don't change rAngle, rely on callers checking boolean return
910 if (!isWrongOOo10thDegAngle
)
911 fAngle
*= 10.0; // conform to ODF 1.2 and newer
913 fAngle
= std::clamp
<double>(basegfx::fround(fAngle
), SHRT_MIN
, SHRT_MAX
);
914 rAngle
= static_cast<sal_Int16
>(fAngle
);
919 /** convert SVG angle to number, 10th of degrees with range [0..3600] */
920 bool Converter::convert10thDegAngle(sal_Int16
& rAngle
, std::string_view rString
,
921 bool const isWrongOOo10thDegAngle
)
923 // ODF 1.1 leaves it undefined what the number means, but ODF 1.2 says it's
924 // degrees, while OOo has historically used 10th of degrees :(
925 // So import degrees when we see the "deg" suffix but continue with 10th of
926 // degrees for now for the sake of existing OOo/LO documents, until the
927 // new versions that can read "deg" suffix are widely deployed and we can
928 // start to write the "deg" suffix.
930 std::string_view aRest
;
931 bool bRet
= ::sax::Converter::convertDouble(fAngle
, rString
, &aRest
);
936 else if (aRest
== "grad")
937 fAngle
*= 9.0; // 360deg = 400grad
938 else if (aRest
== "rad")
939 fAngle
= basegfx::rad2deg
<10>(fAngle
);
940 else // no explicit unit
941 { // isWrongOOo10thDegAngle = true: nothing to do here. Wrong, but backward compatible.
944 // Wrong unit. Don't change rAngle, rely on callers checking boolean return
947 if (!isWrongOOo10thDegAngle
)
948 fAngle
*= 10.0; // conform to ODF 1.2 and newer
950 fAngle
= std::clamp
<double>(basegfx::fround(fAngle
), SHRT_MIN
, SHRT_MAX
);
951 rAngle
= static_cast<sal_Int16
>(fAngle
);
956 /** convert SVG angle to number, in degrees, range [0..360] */
957 bool Converter::convertAngle(double& rAngle
, std::u16string_view rString
)
959 // ODF uses in several places angles in data type 'angle' (18.3.1, ODF 1.3). That is a double
960 // followed by unit identifier deg, grad or rad or a unitless value in degrees.
961 // This method converts ODF 'angle' to double degrees and normalizes it to range
962 // [0..360]. Further type converting and range restriction are done by the caller.
963 std::u16string_view aRest
;
964 bool bRet
= ::sax::Converter::convertDouble(rAngle
, rString
, &aRest
);
968 if (aRest
== u
"grad")
969 rAngle
*= 0.9; // 360deg = 400grad
970 else if (aRest
== u
"rad")
971 rAngle
= basegfx::rad2deg(rAngle
);
972 else if (aRest
!= u
"deg" && !aRest
.empty())
978 // degrees in range [0..360]
979 rAngle
= basegfx::snapToZeroRange(rAngle
, 360.0);
984 /** convert SVG angle to number, in degrees, range [0..360] */
985 bool Converter::convertAngle(double& rAngle
, std::string_view rString
)
987 // ODF uses in several places angles in data type 'angle' (18.3.1, ODF 1.3). That is a double
988 // followed by unit identifier deg, grad or rad or a unitless value in degrees.
989 // This method converts ODF 'angle' to double degrees and normalizes it to range
990 // [0..360]. Further type converting and range restriction are done by the caller.
991 std::string_view aRest
;
992 bool bRet
= ::sax::Converter::convertDouble(rAngle
, rString
, &aRest
);
997 rAngle
*= 0.9; // 360deg = 400grad
998 else if (aRest
== "rad")
999 rAngle
= basegfx::rad2deg(rAngle
);
1000 else if (aRest
!= "deg" && !aRest
.empty())
1006 // degrees in range [0..360]
1007 rAngle
= basegfx::snapToZeroRange(rAngle
, 360.0);
1012 /** convert double to ISO "duration" string; negative durations allowed */
1013 void Converter::convertDuration(OUStringBuffer
& rBuffer
,
1016 double fValue
= fTime
;
1018 // take care of negative durations as specified in:
1019 // XML Schema, W3C Working Draft 07 April 2000, section 3.2.6.1
1022 rBuffer
.append('-');
1026 rBuffer
.append( "PT" );
1028 double fHoursValue
= ::rtl::math::approxFloor (fValue
);
1029 fValue
-= fHoursValue
;
1031 double fMinsValue
= ::rtl::math::approxFloor (fValue
);
1032 fValue
-= fMinsValue
;
1034 double fSecsValue
= ::rtl::math::approxFloor (fValue
);
1035 fValue
-= fSecsValue
;
1036 double fNanoSecsValue
;
1037 if (fValue
> 0.00000000001)
1038 fNanoSecsValue
= ::rtl::math::round( fValue
, XML_MAXDIGITSCOUNT_TIME
- 5);
1040 fNanoSecsValue
= 0.0;
1042 if (fNanoSecsValue
== 1.0)
1044 fNanoSecsValue
= 0.0;
1047 if (fSecsValue
>= 60.0)
1052 if (fMinsValue
>= 60.0)
1058 if (fHoursValue
< 10)
1059 rBuffer
.append( '0');
1060 rBuffer
.append( sal_Int32( fHoursValue
));
1061 rBuffer
.append( 'H');
1062 if (fMinsValue
< 10)
1063 rBuffer
.append( '0');
1064 rBuffer
.append( sal_Int32( fMinsValue
));
1065 rBuffer
.append( 'M');
1066 if (fSecsValue
< 10)
1067 rBuffer
.append( '0');
1068 rBuffer
.append( sal_Int32( fSecsValue
));
1069 if (fNanoSecsValue
> 0.0)
1071 OUString
aNS( ::rtl::math::doubleToUString( fValue
,
1072 rtl_math_StringFormat_F
, XML_MAXDIGITSCOUNT_TIME
- 5, '.',
1074 if ( aNS
.getLength() > 2 )
1076 rBuffer
.append( '.');
1077 rBuffer
.append( aNS
.subView(2) ); // strip "0."
1080 rBuffer
.append( 'S');
1083 /** helper function of Converter::convertDuration */
1084 template<typename V
>
1085 static bool convertDurationHelper(double& rfTime
, V pStr
)
1087 // negative time duration?
1088 bool bIsNegativeDuration
= false;
1089 if ( '-' == (*pStr
) )
1091 bIsNegativeDuration
= true;
1095 if ( *pStr
!= 'P' && *pStr
!= 'p' ) // duration must start with "P"
1099 OUStringBuffer sDoubleStr
;
1100 bool bSuccess
= true;
1102 bool bTimePart
= false;
1103 bool bIsFraction
= false;
1104 sal_Int32 nDays
= 0;
1105 sal_Int32 nHours
= 0;
1106 sal_Int32 nMins
= 0;
1107 sal_Int32 nSecs
= 0;
1108 sal_Int32 nTemp
= 0;
1110 while ( bSuccess
&& !bDone
)
1112 sal_Unicode c
= *(pStr
++);
1115 else if ( '0' <= c
&& '9' >= c
)
1117 if ( nTemp
>= SAL_MAX_INT32
/ 10 )
1124 nTemp
+= (c
- u
'0');
1128 sDoubleStr
.append(c
);
1132 else if ( bTimePart
)
1134 if ( c
== 'H' || c
== 'h' )
1139 else if ( c
== 'M' || c
== 'm')
1144 else if ( (c
== ',') || (c
== '.') )
1151 else if ( c
== 'S' || c
== 's' )
1161 bSuccess
= false; // invalid character
1165 if ( c
== 'T' || c
== 't' ) // "T" starts time part
1167 else if ( c
== 'D' || c
== 'd')
1172 else if ( c
== 'Y' || c
== 'y' || c
== 'M' || c
== 'm' )
1174 //! how many days is a year or month?
1176 OSL_FAIL( "years or months in duration: not implemented");
1180 bSuccess
= false; // invalid character
1186 // Calculate similar to ImpSvNumberInputScan::GetTimeRef: first, sum whole seconds, add
1187 // second fraction, and finally, divide. Produces less rounding errors than calculating
1188 // fractions of a day from seconds, minutes, hours separately, and then adding together.
1189 double seconds
= nDays
* tools::Time::secondPerDay
+ nHours
* tools::Time::secondPerHour
1190 + nMins
* tools::Time::secondPerMinute
+ nSecs
1191 + o3tl::toDouble(sDoubleStr
);
1192 double fTempTime
= seconds
/ tools::Time::secondPerDay
;
1194 // negative duration?
1195 if ( bIsNegativeDuration
)
1197 fTempTime
= -fTempTime
;
1205 /** convert ISO "duration" string to double; negative durations allowed */
1206 bool Converter::convertDuration(double& rfTime
,
1207 std::string_view rString
)
1209 std::string_view aTrimmed
= o3tl::trim(rString
);
1210 const char* pStr
= aTrimmed
.data();
1212 return convertDurationHelper(rfTime
, pStr
);
1215 /** convert util::Duration to ISO8601 "duration" string */
1216 void Converter::convertDuration(OUStringBuffer
& rBuffer
,
1217 const ::util::Duration
& rDuration
)
1219 if (rDuration
.Negative
)
1221 rBuffer
.append('-');
1223 rBuffer
.append('P');
1224 const bool bHaveDate(rDuration
.Years
!= 0 ||
1225 rDuration
.Months
!= 0 ||
1226 rDuration
.Days
!= 0);
1227 if (rDuration
.Years
)
1229 rBuffer
.append(static_cast<sal_Int32
>(rDuration
.Years
));
1230 rBuffer
.append('Y');
1232 if (rDuration
.Months
)
1234 rBuffer
.append(static_cast<sal_Int32
>(rDuration
.Months
));
1235 rBuffer
.append('M');
1239 rBuffer
.append(static_cast<sal_Int32
>(rDuration
.Days
));
1240 rBuffer
.append('D');
1242 if ( rDuration
.Hours
!= 0
1243 || rDuration
.Minutes
!= 0
1244 || rDuration
.Seconds
!= 0
1245 || rDuration
.NanoSeconds
!= 0 )
1247 rBuffer
.append('T'); // time separator
1248 if (rDuration
.Hours
)
1250 rBuffer
.append(static_cast<sal_Int32
>(rDuration
.Hours
));
1251 rBuffer
.append('H');
1253 if (rDuration
.Minutes
)
1255 rBuffer
.append(static_cast<sal_Int32
>(rDuration
.Minutes
));
1256 rBuffer
.append('M');
1258 if (rDuration
.Seconds
!= 0 || rDuration
.NanoSeconds
!= 0)
1260 // seconds must not be omitted (i.e. ".42S" is not valid)
1261 rBuffer
.append(static_cast<sal_Int32
>(rDuration
.Seconds
));
1262 if (rDuration
.NanoSeconds
)
1264 OSL_ENSURE(rDuration
.NanoSeconds
< 1000000000,"NanoSeconds cannot be more than 999 999 999");
1265 rBuffer
.append('.');
1266 std::ostringstream ostr
;
1269 ostr
<< rDuration
.NanoSeconds
;
1270 rBuffer
.appendAscii(ostr
.str().c_str());
1272 rBuffer
.append('S');
1275 else if (!bHaveDate
)
1277 // zero duration: XMLSchema-2 says there must be at least one component
1278 rBuffer
.append('0');
1279 rBuffer
.append('D');
1285 enum Result
{ R_NOTHING
, R_OVERFLOW
, R_SUCCESS
};
1289 template <typename V
>
1291 readUnsignedNumber(V rString
,
1292 size_t & io_rnPos
, sal_Int32
& o_rNumber
)
1294 size_t nPos(io_rnPos
);
1296 while (nPos
< rString
.size())
1298 const typename
V::value_type c
= rString
[nPos
];
1299 if (('0' > c
) || (c
> '9'))
1304 if (io_rnPos
== nPos
) // read something?
1310 const sal_Int64 nTemp
= toInt64_WithLength(rString
.data() + io_rnPos
, 10, nPos
- io_rnPos
);
1312 const bool bOverflow
= (nTemp
>= SAL_MAX_INT32
);
1316 return bOverflow
? R_OVERFLOW
: R_SUCCESS
;
1319 template<typename V
>
1321 readUnsignedNumberMaxDigits(int maxDigits
,
1322 V rString
, size_t & io_rnPos
,
1323 sal_Int32
& o_rNumber
)
1325 bool bOverflow(false);
1327 size_t nPos(io_rnPos
);
1328 OSL_ENSURE(maxDigits
>= 0, "negative amount of digits makes no sense");
1330 while (nPos
< rString
.size())
1332 const sal_Unicode c
= rString
[nPos
];
1333 if (('0' <= c
) && (c
<= '9'))
1338 nTemp
+= (c
- u
'0');
1339 if (nTemp
>= SAL_MAX_INT32
)
1353 if (io_rnPos
== nPos
) // read something?
1361 return bOverflow
? R_OVERFLOW
: R_SUCCESS
;
1364 template<typename V
>
1366 readDurationT(V rString
, size_t & io_rnPos
)
1368 if ((io_rnPos
< rString
.size()) &&
1369 (rString
[io_rnPos
] == 'T' || rString
[io_rnPos
] == 't'))
1377 template<typename V
>
1379 readDurationComponent(V rString
,
1380 size_t & io_rnPos
, sal_Int32
& io_rnTemp
, bool & io_rbTimePart
,
1381 sal_Int32
& o_rnTarget
, const sal_Unicode cLower
, const sal_Unicode cUpper
)
1383 if (io_rnPos
< rString
.size())
1385 if (cLower
== rString
[io_rnPos
] || cUpper
== rString
[io_rnPos
])
1388 if (-1 != io_rnTemp
)
1390 o_rnTarget
= io_rnTemp
;
1394 io_rbTimePart
= readDurationT(rString
, io_rnPos
);
1396 return (R_OVERFLOW
!=
1397 readUnsignedNumber(rString
, io_rnPos
, io_rnTemp
));
1408 template <typename V
>
1409 static bool convertDurationHelper(util::Duration
& rDuration
, V string
)
1413 bool bIsNegativeDuration(false);
1414 if (!string
.empty() && ('-' == string
[0]))
1416 bIsNegativeDuration
= true;
1420 if (nPos
< string
.size()
1421 && string
[nPos
] != 'P' && string
[nPos
] != 'p') // duration must start with "P"
1428 /// last read number; -1 == no valid number! always reset after using!
1429 sal_Int32
nTemp(-1);
1430 bool bTimePart(false); // have we read 'T'?
1431 bool bSuccess(false);
1432 sal_Int32
nYears(0);
1433 sal_Int32
nMonths(0);
1435 sal_Int32
nHours(0);
1436 sal_Int32
nMinutes(0);
1437 sal_Int32
nSeconds(0);
1438 sal_Int32
nNanoSeconds(0);
1440 bTimePart
= readDurationT(string
, nPos
);
1441 bSuccess
= (R_SUCCESS
== readUnsignedNumber(string
, nPos
, nTemp
));
1443 if (!bTimePart
&& bSuccess
)
1445 bSuccess
= readDurationComponent(string
, nPos
, nTemp
, bTimePart
,
1449 if (!bTimePart
&& bSuccess
)
1451 bSuccess
= readDurationComponent(string
, nPos
, nTemp
, bTimePart
,
1455 if (!bTimePart
&& bSuccess
)
1457 bSuccess
= readDurationComponent(string
, nPos
, nTemp
, bTimePart
,
1463 if (-1 == nTemp
) // a 'T' must be followed by a component
1470 bSuccess
= readDurationComponent(string
, nPos
, nTemp
, bTimePart
,
1476 bSuccess
= readDurationComponent(string
, nPos
, nTemp
, bTimePart
,
1477 nMinutes
, 'm', 'M');
1480 // eeek! seconds are icky.
1481 if ((nPos
< string
.size()) && bSuccess
)
1483 if (string
[nPos
] == '.' ||
1484 string
[nPos
] == ',')
1491 const sal_Int32
nStart(nPos
);
1492 bSuccess
= readUnsignedNumberMaxDigits(9, string
, nPos
, nTemp
) == R_SUCCESS
;
1493 if ((nPos
< string
.size()) && bSuccess
)
1497 nNanoSeconds
= nTemp
;
1498 sal_Int32 nDigits
= nPos
- nStart
;
1499 assert(nDigits
>= 0);
1500 for (; nDigits
< 9; ++nDigits
)
1505 if ('S' == string
[nPos
] || 's' == string
[nPos
])
1525 else if ('S' == string
[nPos
] || 's' == string
[nPos
])
1541 if (nPos
!= string
.size()) // string not processed completely?
1546 if (nTemp
!= -1) // unprocessed number?
1553 rDuration
.Negative
= bIsNegativeDuration
;
1554 rDuration
.Years
= static_cast<sal_Int16
>(nYears
);
1555 rDuration
.Months
= static_cast<sal_Int16
>(nMonths
);
1556 rDuration
.Days
= static_cast<sal_Int16
>(nDays
);
1557 rDuration
.Hours
= static_cast<sal_Int16
>(nHours
);
1558 rDuration
.Minutes
= static_cast<sal_Int16
>(nMinutes
);
1559 rDuration
.Seconds
= static_cast<sal_Int16
>(nSeconds
);
1560 rDuration
.NanoSeconds
= nNanoSeconds
;
1566 /** convert ISO8601 "duration" string to util::Duration */
1567 bool Converter::convertDuration(util::Duration
& rDuration
,
1568 std::u16string_view rString
)
1570 return convertDurationHelper(rDuration
, o3tl::trim(rString
));
1573 /** convert ISO8601 "duration" string to util::Duration */
1574 bool Converter::convertDuration(util::Duration
& rDuration
,
1575 std::string_view rString
)
1577 return convertDurationHelper(rDuration
, o3tl::trim(rString
));
1581 lcl_AppendTimezone(OUStringBuffer
& i_rBuffer
, int const nOffset
)
1585 i_rBuffer
.append('Z');
1591 i_rBuffer
.append('+');
1595 i_rBuffer
.append('-');
1597 const sal_Int32
nHours (abs(nOffset
) / 60);
1598 const sal_Int32
nMinutes(abs(nOffset
) % 60);
1599 SAL_WARN_IF(nHours
> 14 || (nHours
== 14 && nMinutes
> 0),
1600 "sax", "convertDateTime: timezone overflow");
1603 i_rBuffer
.append('0');
1605 i_rBuffer
.append(nHours
);
1606 i_rBuffer
.append(':');
1609 i_rBuffer
.append('0');
1611 i_rBuffer
.append(nMinutes
);
1615 /** convert util::Date to ISO "date" string */
1616 void Converter::convertDate(
1617 OUStringBuffer
& i_rBuffer
,
1618 const util::Date
& i_rDate
,
1619 sal_Int16
const*const pTimeZoneOffset
)
1621 const util::DateTime
dt(0, 0, 0, 0,
1622 i_rDate
.Day
, i_rDate
.Month
, i_rDate
.Year
, false);
1623 convertDateTime(i_rBuffer
, dt
, pTimeZoneOffset
);
1626 static void convertTime(
1627 OUStringBuffer
& i_rBuffer
,
1628 const css::util::DateTime
& i_rDateTime
)
1630 if (i_rDateTime
.Hours
< 10) {
1631 i_rBuffer
.append('0');
1633 i_rBuffer
.append( OUString::number(static_cast<sal_Int32
>(i_rDateTime
.Hours
)) + ":");
1634 if (i_rDateTime
.Minutes
< 10) {
1635 i_rBuffer
.append('0');
1637 i_rBuffer
.append( OUString::number(static_cast<sal_Int32
>(i_rDateTime
.Minutes
) ) + ":");
1638 if (i_rDateTime
.Seconds
< 10) {
1639 i_rBuffer
.append('0');
1641 i_rBuffer
.append( static_cast<sal_Int32
>(i_rDateTime
.Seconds
) );
1642 if (i_rDateTime
.NanoSeconds
> 0) {
1643 OSL_ENSURE(i_rDateTime
.NanoSeconds
< 1000000000,"NanoSeconds cannot be more than 999 999 999");
1644 i_rBuffer
.append('.');
1645 std::ostringstream ostr
;
1648 ostr
<< i_rDateTime
.NanoSeconds
;
1649 i_rBuffer
.appendAscii(ostr
.str().c_str());
1653 static void convertTimeZone(
1654 OUStringBuffer
& i_rBuffer
,
1655 const css::util::DateTime
& i_rDateTime
,
1656 sal_Int16
const* pTimeZoneOffset
)
1658 if (pTimeZoneOffset
)
1660 lcl_AppendTimezone(i_rBuffer
, *pTimeZoneOffset
);
1662 else if (i_rDateTime
.IsUTC
)
1664 lcl_AppendTimezone(i_rBuffer
, 0);
1668 /** convert util::DateTime to ISO "time" or "dateTime" string */
1669 void Converter::convertTimeOrDateTime(
1670 OUStringBuffer
& i_rBuffer
,
1671 const css::util::DateTime
& i_rDateTime
)
1673 if (i_rDateTime
.Year
== 0 ||
1674 i_rDateTime
.Month
< 1 || i_rDateTime
.Month
> 12 ||
1675 i_rDateTime
.Day
< 1 || i_rDateTime
.Day
> 31)
1677 convertTime(i_rBuffer
, i_rDateTime
);
1678 convertTimeZone(i_rBuffer
, i_rDateTime
, nullptr);
1682 convertDateTime(i_rBuffer
, i_rDateTime
, nullptr, true);
1686 /** convert util::DateTime to ISO "date" or "dateTime" string */
1687 void Converter::convertDateTime(
1688 OUStringBuffer
& i_rBuffer
,
1689 const css::util::DateTime
& i_rDateTime
,
1690 sal_Int16
const*const pTimeZoneOffset
,
1691 bool i_bAddTimeIf0AM
)
1693 const sal_Unicode
dash('-');
1694 const sal_Unicode
zero('0');
1696 sal_Int32
const nYear(abs(i_rDateTime
.Year
));
1697 if (i_rDateTime
.Year
< 0) {
1698 i_rBuffer
.append(dash
); // negative
1701 i_rBuffer
.append(zero
);
1704 i_rBuffer
.append(zero
);
1707 i_rBuffer
.append(zero
);
1709 i_rBuffer
.append( OUString::number(nYear
) + OUStringChar(dash
) );
1710 if( i_rDateTime
.Month
< 10 ) {
1711 i_rBuffer
.append(zero
);
1713 i_rBuffer
.append( OUString::number(i_rDateTime
.Month
) + OUStringChar(dash
) );
1714 if( i_rDateTime
.Day
< 10 ) {
1715 i_rBuffer
.append(zero
);
1717 i_rBuffer
.append( static_cast<sal_Int32
>(i_rDateTime
.Day
) );
1719 if( i_rDateTime
.Seconds
!= 0 ||
1720 i_rDateTime
.Minutes
!= 0 ||
1721 i_rDateTime
.Hours
!= 0 ||
1724 i_rBuffer
.append('T');
1725 convertTime(i_rBuffer
, i_rDateTime
);
1728 convertTimeZone(i_rBuffer
, i_rDateTime
, pTimeZoneOffset
);
1731 /** convert ISO "date" or "dateTime" string to util::DateTime */
1732 bool Converter::parseDateTime( util::DateTime
& rDateTime
,
1733 std::u16string_view rString
)
1736 return parseDateOrDateTime(nullptr, rDateTime
, isDateTime
, nullptr,
1740 /** convert ISO "date" or "dateTime" string to util::DateTime */
1741 bool Converter::parseDateTime( util::DateTime
& rDateTime
,
1742 std::string_view rString
)
1745 return parseDateOrDateTime(nullptr, rDateTime
, isDateTime
, nullptr,
1749 static bool lcl_isLeapYear(const sal_uInt32 nYear
)
1751 return ((nYear
% 4) == 0)
1752 && (((nYear
% 100) != 0) || ((nYear
% 400) == 0));
1756 lcl_MaxDaysPerMonth(const sal_Int32 nMonth
, const sal_Int32 nYear
)
1758 static const sal_uInt16 s_MaxDaysPerMonth
[12] =
1759 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
1760 assert(0 < nMonth
&& nMonth
<= 12);
1761 if ((2 == nMonth
) && lcl_isLeapYear(nYear
))
1765 return s_MaxDaysPerMonth
[nMonth
- 1];
1768 static void lcl_ConvertToUTC(
1769 sal_Int16
& o_rYear
, sal_uInt16
& o_rMonth
, sal_uInt16
& o_rDay
,
1770 sal_uInt16
& o_rHours
, sal_uInt16
& o_rMinutes
,
1771 int const nSourceOffset
)
1773 sal_Int16
nOffsetHours(abs(nSourceOffset
) / 60);
1774 sal_Int16
const nOffsetMinutes(abs(nSourceOffset
) % 60);
1775 o_rMinutes
+= nOffsetMinutes
;
1776 if (nSourceOffset
< 0)
1778 o_rMinutes
+= nOffsetMinutes
;
1779 if (60 <= o_rMinutes
)
1784 o_rHours
+= nOffsetHours
;
1789 sal_Int16
nDayAdd(0);
1790 while (24 <= o_rHours
)
1797 return; // handle time without date - don't adjust what isn't there
1800 sal_Int16
const nDaysInMonth(lcl_MaxDaysPerMonth(o_rMonth
, o_rYear
));
1801 if (o_rDay
<= nDaysInMonth
)
1805 o_rDay
-= nDaysInMonth
;
1812 ++o_rYear
; // works for negative year too
1814 else if (0 < nSourceOffset
)
1816 // argh everything is unsigned
1817 if (o_rMinutes
< nOffsetMinutes
)
1822 o_rMinutes
-= nOffsetMinutes
;
1823 sal_Int16
nDaySubtract(0);
1824 while (o_rHours
< nOffsetHours
)
1829 o_rHours
-= nOffsetHours
;
1832 return; // handle time without date - don't adjust what isn't there
1834 if (nDaySubtract
< o_rDay
)
1836 o_rDay
-= nDaySubtract
;
1839 sal_Int16
const nPrevMonth((o_rMonth
== 1) ? 12 : o_rMonth
- 1);
1840 sal_Int16
const nDaysInMonth(lcl_MaxDaysPerMonth(nPrevMonth
, o_rYear
));
1841 o_rDay
+= nDaysInMonth
;
1846 --o_rYear
; // works for negative year too
1848 o_rDay
-= nDaySubtract
;
1852 template <typename V
>
1854 readDateTimeComponent(V rString
,
1855 size_t & io_rnPos
, sal_Int32
& o_rnTarget
,
1856 const sal_Int32 nMinLength
, const bool bExactLength
)
1858 const size_t nOldPos(io_rnPos
);
1860 if (R_SUCCESS
!= readUnsignedNumber
<V
>(rString
, io_rnPos
, nTemp
))
1864 const sal_Int32
nTokenLength(io_rnPos
- nOldPos
);
1865 if ((nTokenLength
< nMinLength
) ||
1866 (bExactLength
&& (nTokenLength
> nMinLength
)))
1868 return false; // bad length
1874 /** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
1875 template<typename V
>
1876 static bool lcl_parseDate(
1878 sal_Int32
& nYear
, sal_Int32
& nMonth
, sal_Int32
& nDay
,
1882 bool const bIgnoreInvalidOrMissingDate
)
1884 bool bSuccess
= true;
1886 if (string
.size() > nPos
)
1888 if ('-' == string
[nPos
])
1896 // While W3C XMLSchema specifies years with a minimum of 4 digits, be
1897 // lenient in what we accept for years < 1000. One digit is acceptable
1898 // if the remainders match.
1899 bSuccess
= readDateTimeComponent
<V
>(string
, nPos
, nYear
, 1, false);
1900 if (!bIgnoreInvalidOrMissingDate
)
1902 bSuccess
&= (0 < nYear
);
1904 bSuccess
&= (nPos
< string
.size()); // not last token
1906 if (bSuccess
&& ('-' != string
[nPos
])) // separator
1914 bSuccess
= readDateTimeComponent
<V
>(string
, nPos
, nMonth
, 2, true);
1915 if (!bIgnoreInvalidOrMissingDate
)
1917 bSuccess
&= (0 < nMonth
);
1919 bSuccess
&= (nMonth
<= 12);
1920 bSuccess
&= (nPos
< string
.size()); // not last token
1922 if (bSuccess
&& ('-' != string
[nPos
])) // separator
1930 bSuccess
= readDateTimeComponent(string
, nPos
, nDay
, 2, true);
1931 if (!bIgnoreInvalidOrMissingDate
)
1933 bSuccess
&= (0 < nDay
);
1935 if (nMonth
> 0) // not possible to check if month was missing
1937 bSuccess
&= (nDay
<= lcl_MaxDaysPerMonth(nMonth
, nYear
));
1939 else assert(bIgnoreInvalidOrMissingDate
);
1942 if (bSuccess
&& (nPos
< string
.size()))
1944 if ('T' == string
[nPos
] || 't' == string
[nPos
]) // time separator
1954 /** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
1955 template <typename V
>
1956 static bool lcl_parseDateTime(
1957 util::Date
*const pDate
, util::DateTime
& rDateTime
,
1959 std::optional
<sal_Int16
> *const pTimeZoneOffset
,
1961 bool const bIgnoreInvalidOrMissingDate
)
1963 bool bSuccess
= true;
1965 string
= o3tl::trim(string
);
1967 bool isNegative(false);
1969 sal_Int32
nMonth(0);
1972 bool bHaveTime(false);
1974 if ( !bIgnoreInvalidOrMissingDate
1975 || string
.find(':') == V::npos
// no time?
1976 || (string
.find('-') != V::npos
1977 && string
.find('-') < string
.find(':')))
1979 bSuccess
&= lcl_parseDate
<V
>(isNegative
, nYear
, nMonth
, nDay
,
1980 bHaveTime
, nPos
, string
, bIgnoreInvalidOrMissingDate
);
1987 sal_Int32
nHours(0);
1988 sal_Int32
nMinutes(0);
1989 sal_Int32
nSeconds(0);
1990 sal_Int32
nNanoSeconds(0);
1991 if (bSuccess
&& bHaveTime
)
1994 bSuccess
= readDateTimeComponent(string
, nPos
, nHours
, 2, true);
1995 bSuccess
&= (0 <= nHours
) && (nHours
<= 24);
1996 bSuccess
&= (nPos
< string
.size()); // not last token
1998 if (bSuccess
&& (':' != string
[nPos
])) // separator
2006 bSuccess
= readDateTimeComponent(string
, nPos
, nMinutes
, 2, true);
2007 bSuccess
&= (0 <= nMinutes
) && (nMinutes
< 60);
2008 bSuccess
&= (nPos
< string
.size()); // not last token
2010 if (bSuccess
&& (':' != string
[nPos
])) // separator
2018 bSuccess
= readDateTimeComponent(string
, nPos
, nSeconds
, 2, true);
2019 bSuccess
&= (0 <= nSeconds
) && (nSeconds
< 60);
2021 if (bSuccess
&& (nPos
< string
.size()) &&
2022 ('.' == string
[nPos
] || ',' == string
[nPos
])) // fraction separator
2025 const sal_Int32
nStart(nPos
);
2027 if (R_NOTHING
== readUnsignedNumberMaxDigits
<V
>(9, string
, nPos
, nTemp
))
2033 sal_Int32 nDigits
= std::min
<sal_Int32
>(nPos
- nStart
, 9);
2034 assert(nDigits
> 0);
2035 for (; nDigits
< 9; ++nDigits
)
2039 nNanoSeconds
= nTemp
;
2043 if (bSuccess
&& (nHours
== 24))
2045 if (!((0 == nMinutes
) && (0 == nSeconds
) && (0 == nNanoSeconds
)))
2047 bSuccess
= false; // only 24:00:00 is valid
2052 bool bHaveTimezone(false);
2053 bool bHaveTimezonePlus(false);
2054 bool bHaveTimezoneMinus(false);
2055 if (bSuccess
&& (nPos
< string
.size()))
2057 const sal_Unicode
c(string
[nPos
]);
2060 bHaveTimezone
= true;
2061 bHaveTimezonePlus
= true;
2066 bHaveTimezone
= true;
2067 bHaveTimezoneMinus
= true;
2070 else if ('Z' == c
|| 'z' == c
)
2072 bHaveTimezone
= true;
2080 sal_Int32
nTimezoneHours(0);
2081 sal_Int32
nTimezoneMinutes(0);
2082 if (bSuccess
&& (bHaveTimezonePlus
|| bHaveTimezoneMinus
))
2084 bSuccess
= readDateTimeComponent
<V
>(
2085 string
, nPos
, nTimezoneHours
, 2, true);
2086 bSuccess
&= (0 <= nTimezoneHours
) && (nTimezoneHours
<= 14);
2087 bSuccess
&= (nPos
< string
.size()); // not last token
2088 if (bSuccess
&& (':' != string
[nPos
])) // separator
2096 bSuccess
= readDateTimeComponent
<V
>(
2097 string
, nPos
, nTimezoneMinutes
, 2, true);
2098 bSuccess
&= (0 <= nTimezoneMinutes
) && (nTimezoneMinutes
< 60);
2100 if (bSuccess
&& (nTimezoneHours
== 14))
2102 if (0 != nTimezoneMinutes
)
2104 bSuccess
= false; // only +-14:00 is valid
2109 bSuccess
&= (nPos
== string
.size()); // trailing junk?
2113 sal_Int16
const nTimezoneOffset
= (bHaveTimezoneMinus
? -1 : +1)
2114 * ((nTimezoneHours
* 60) + nTimezoneMinutes
);
2115 if (!pDate
|| bHaveTime
) // time is optional
2118 (isNegative
? -1 : +1) * static_cast<sal_Int16
>(nYear
);
2119 rDateTime
.Month
= static_cast<sal_uInt16
>(nMonth
);
2120 rDateTime
.Day
= static_cast<sal_uInt16
>(nDay
);
2121 rDateTime
.Hours
= static_cast<sal_uInt16
>(nHours
);
2122 rDateTime
.Minutes
= static_cast<sal_uInt16
>(nMinutes
);
2123 rDateTime
.Seconds
= static_cast<sal_uInt16
>(nSeconds
);
2124 rDateTime
.NanoSeconds
= static_cast<sal_uInt32
>(nNanoSeconds
);
2127 if (pTimeZoneOffset
)
2129 *pTimeZoneOffset
= nTimezoneOffset
;
2130 rDateTime
.IsUTC
= (0 == nTimezoneOffset
);
2134 lcl_ConvertToUTC(rDateTime
.Year
, rDateTime
.Month
,
2135 rDateTime
.Day
, rDateTime
.Hours
, rDateTime
.Minutes
,
2137 rDateTime
.IsUTC
= true;
2142 if (pTimeZoneOffset
)
2144 pTimeZoneOffset
->reset();
2146 rDateTime
.IsUTC
= false;
2148 rbDateTime
= bHaveTime
;
2153 (isNegative
? -1 : +1) * static_cast<sal_Int16
>(nYear
);
2154 pDate
->Month
= static_cast<sal_uInt16
>(nMonth
);
2155 pDate
->Day
= static_cast<sal_uInt16
>(nDay
);
2158 if (pTimeZoneOffset
)
2160 *pTimeZoneOffset
= nTimezoneOffset
;
2164 // a Date cannot be adjusted
2165 SAL_INFO("sax", "dropping timezone");
2170 if (pTimeZoneOffset
)
2172 pTimeZoneOffset
->reset();
2181 /** convert ISO "time" or "dateTime" string to util::DateTime */
2182 bool Converter::parseTimeOrDateTime(
2183 util::DateTime
& rDateTime
,
2184 std::u16string_view rString
)
2187 return lcl_parseDateTime(
2188 nullptr, rDateTime
, dummy
, nullptr, rString
, true);
2191 /** convert ISO "time" or "dateTime" string to util::DateTime */
2192 bool Converter::parseTimeOrDateTime(
2193 util::DateTime
& rDateTime
,
2194 std::string_view rString
)
2197 return lcl_parseDateTime(
2198 nullptr, rDateTime
, dummy
, nullptr, rString
, true);
2201 /** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
2202 bool Converter::parseDateOrDateTime(
2203 util::Date
*const pDate
, util::DateTime
& rDateTime
,
2205 std::optional
<sal_Int16
> *const pTimeZoneOffset
,
2206 std::u16string_view rString
)
2208 return lcl_parseDateTime(
2209 pDate
, rDateTime
, rbDateTime
, pTimeZoneOffset
, rString
, false);
2212 /** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
2213 bool Converter::parseDateOrDateTime(
2214 util::Date
*const pDate
, util::DateTime
& rDateTime
,
2216 std::optional
<sal_Int16
> *const pTimeZoneOffset
,
2217 std::string_view rString
)
2219 return lcl_parseDateTime(
2220 pDate
, rDateTime
, rbDateTime
, pTimeZoneOffset
, rString
, false);
2223 /** gets the position of the first comma after npos in the string
2224 rStr. Commas inside '"' pairs are not matched */
2225 sal_Int32
Converter::indexOfComma( std::u16string_view rStr
,
2228 sal_Unicode cQuote
= 0;
2229 sal_Int32 nLen
= rStr
.size();
2230 for( ; nPos
< nLen
; nPos
++ )
2232 sal_Unicode c
= rStr
[nPos
];
2238 else if( '\'' == cQuote
)
2245 else if( '\"' == cQuote
)
2259 double Converter::GetConversionFactor(OUStringBuffer
& rUnit
, sal_Int16 nSourceUnit
, sal_Int16 nTargetUnit
)
2261 double fRetval(1.0);
2265 if(nSourceUnit
!= nTargetUnit
)
2267 const o3tl::Length eFrom
= Measure2O3tlUnit(nSourceUnit
);
2268 const o3tl::Length eTo
= Measure2O3tlUnit(nTargetUnit
);
2269 fRetval
= o3tl::convert(1.0, eFrom
, eTo
);
2271 if (const auto sUnit
= Measure2UnitString(nTargetUnit
); sUnit
.size() > 0)
2272 rUnit
.appendAscii(sUnit
.data(), sUnit
.size());
2278 double Converter::GetConversionFactor(OStringBuffer
& rUnit
, sal_Int16 nSourceUnit
, sal_Int16 nTargetUnit
)
2280 double fRetval(1.0);
2284 if(nSourceUnit
!= nTargetUnit
)
2286 const o3tl::Length eFrom
= Measure2O3tlUnit(nSourceUnit
);
2287 const o3tl::Length eTo
= Measure2O3tlUnit(nTargetUnit
);
2288 fRetval
= o3tl::convert(1.0, eFrom
, eTo
);
2290 if (const auto sUnit
= Measure2UnitString(nTargetUnit
); sUnit
.size() > 0)
2291 rUnit
.append(sUnit
.data(), sUnit
.size());
2297 template<typename V
>
2298 static sal_Int16
lcl_GetUnitFromString(V rString
, sal_Int16 nDefaultUnit
)
2301 sal_Int32 nLen
= rString
.size();
2302 sal_Int16 nRetUnit
= nDefaultUnit
;
2305 while( nPos
< nLen
&& ' ' == rString
[nPos
] )
2309 if( nPos
< nLen
&& '-' == rString
[nPos
] )
2313 while( nPos
< nLen
&& '0' <= rString
[nPos
] && '9' >= rString
[nPos
] )
2316 if( nPos
< nLen
&& '.' == rString
[nPos
] )
2319 while( nPos
< nLen
&& '0' <= rString
[nPos
] && '9' >= rString
[nPos
] )
2324 while( nPos
< nLen
&& ' ' == rString
[nPos
] )
2329 switch(rString
[nPos
])
2333 nRetUnit
= MeasureUnit::PERCENT
;
2339 if(nPos
+1 < nLen
&& (rString
[nPos
+1] == 'm'
2340 || rString
[nPos
+1] == 'M'))
2341 nRetUnit
= MeasureUnit::CM
;
2347 // CSS1_EMS or CSS1_EMX later
2353 if(nPos
+1 < nLen
&& (rString
[nPos
+1] == 'n'
2354 || rString
[nPos
+1] == 'N'))
2355 nRetUnit
= MeasureUnit::INCH
;
2361 if(nPos
+1 < nLen
&& (rString
[nPos
+1] == 'm'
2362 || rString
[nPos
+1] == 'M'))
2363 nRetUnit
= MeasureUnit::MM
;
2369 if(nPos
+1 < nLen
&& (rString
[nPos
+1] == 't'
2370 || rString
[nPos
+1] == 'T'))
2371 nRetUnit
= MeasureUnit::POINT
;
2372 if(nPos
+1 < nLen
&& (rString
[nPos
+1] == 'c'
2373 || rString
[nPos
+1] == 'C'))
2374 nRetUnit
= MeasureUnit::TWIP
;
2383 sal_Int16
Converter::GetUnitFromString(std::u16string_view rString
, sal_Int16 nDefaultUnit
)
2385 return lcl_GetUnitFromString(rString
, nDefaultUnit
);
2387 sal_Int16
Converter::GetUnitFromString(std::string_view rString
, sal_Int16 nDefaultUnit
)
2389 return lcl_GetUnitFromString(rString
, nDefaultUnit
);
2392 bool Converter::convertAny(OUStringBuffer
& rsValue
,
2393 OUStringBuffer
& rsType
,
2394 const css::uno::Any
& rValue
)
2396 bool bConverted
= false;
2398 rsValue
.setLength(0);
2399 rsType
.setLength (0);
2401 switch (rValue
.getValueTypeClass())
2403 case css::uno::TypeClass_BYTE
:
2404 case css::uno::TypeClass_SHORT
:
2405 case css::uno::TypeClass_UNSIGNED_SHORT
:
2406 case css::uno::TypeClass_LONG
:
2407 case css::uno::TypeClass_UNSIGNED_LONG
:
2409 sal_Int32 nTempValue
= 0;
2410 if (rValue
>>= nTempValue
)
2412 rsType
.append("integer");
2414 rsValue
.append(nTempValue
);
2419 case css::uno::TypeClass_BOOLEAN
:
2421 bool bTempValue
= false;
2422 if (rValue
>>= bTempValue
)
2424 rsType
.append("boolean");
2426 ::sax::Converter::convertBool(rsValue
, bTempValue
);
2431 case css::uno::TypeClass_FLOAT
:
2432 case css::uno::TypeClass_DOUBLE
:
2434 double fTempValue
= 0.0;
2435 if (rValue
>>= fTempValue
)
2437 rsType
.append("float");
2439 ::sax::Converter::convertDouble(rsValue
, fTempValue
);
2444 case css::uno::TypeClass_STRING
:
2446 OUString sTempValue
;
2447 if (rValue
>>= sTempValue
)
2449 rsType
.append("string");
2451 rsValue
.append(sTempValue
);
2456 case css::uno::TypeClass_STRUCT
:
2458 css::util::Date aDate
;
2459 css::util::Time aTime
;
2460 css::util::DateTime aDateTime
;
2462 if (rValue
>>= aDate
)
2464 rsType
.append("date");
2466 css::util::DateTime aTempValue
;
2467 aTempValue
.Day
= aDate
.Day
;
2468 aTempValue
.Month
= aDate
.Month
;
2469 aTempValue
.Year
= aDate
.Year
;
2470 aTempValue
.NanoSeconds
= 0;
2471 aTempValue
.Seconds
= 0;
2472 aTempValue
.Minutes
= 0;
2473 aTempValue
.Hours
= 0;
2474 ::sax::Converter::convertDateTime(rsValue
, aTempValue
, nullptr);
2477 if (rValue
>>= aTime
)
2479 rsType
.append("time");
2481 css::util::Duration aTempValue
;
2482 aTempValue
.Days
= 0;
2483 aTempValue
.Months
= 0;
2484 aTempValue
.Years
= 0;
2485 aTempValue
.NanoSeconds
= aTime
.NanoSeconds
;
2486 aTempValue
.Seconds
= aTime
.Seconds
;
2487 aTempValue
.Minutes
= aTime
.Minutes
;
2488 aTempValue
.Hours
= aTime
.Hours
;
2489 ::sax::Converter::convertDuration(rsValue
, aTempValue
);
2492 if (rValue
>>= aDateTime
)
2494 rsType
.append("date");
2496 ::sax::Converter::convertDateTime(rsValue
, aDateTime
, nullptr);
2507 void Converter::convertBytesToHexBinary(OUStringBuffer
& rBuffer
, const void* pBytes
,
2510 rBuffer
.setLength(0);
2511 rBuffer
.ensureCapacity(nBytes
* 2);
2512 auto pChars
= static_cast<const unsigned char*>(pBytes
);
2513 for (sal_Int32 i
= 0; i
< nBytes
; ++i
)
2515 sal_Int32 c
= *pChars
++;
2517 rBuffer
.append('0');
2518 rBuffer
.append(c
, 16);
2524 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */