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>
40 #include <string_view>
42 using namespace com::sun::star
;
43 using namespace com::sun::star::uno
;
44 using namespace com::sun::star::util
;
45 using namespace ::com::sun::star::i18n
;
50 const std::string_view gpsMM
= "mm";
51 const std::string_view gpsCM
= "cm";
52 const std::string_view gpsPT
= "pt";
53 const std::string_view gpsINCH
= "in";
54 const std::string_view gpsPC
= "pc";
56 const sal_Int8 XML_MAXDIGITSCOUNT_TIME
= 14;
58 static sal_Int64
toInt64_WithLength(const sal_Unicode
* str
, sal_Int16 radix
, sal_Int32 nStrLength
)
60 return rtl_ustr_toInt64_WithLength(str
, radix
, nStrLength
);
62 static sal_Int64
toInt64_WithLength(const char * str
, sal_Int16 radix
, sal_Int32 nStrLength
)
64 return rtl_str_toInt64_WithLength(str
, radix
, nStrLength
);
69 o3tl::Length
Measure2O3tlUnit(sal_Int16 nUnit
)
73 case MeasureUnit::TWIP
:
74 return o3tl::Length::twip
;
75 case MeasureUnit::POINT
:
76 return o3tl::Length::pt
;
77 case MeasureUnit::MM_10TH
:
78 return o3tl::Length::mm10
;
79 case MeasureUnit::MM_100TH
:
80 return o3tl::Length::mm100
;
82 return o3tl::Length::mm
;
84 return o3tl::Length::cm
;
86 SAL_WARN("sax", "unit not supported for length");
88 case MeasureUnit::INCH
:
89 return o3tl::Length::in
;
93 std::string_view
Measure2UnitString(sal_Int16 nUnit
)
97 case MeasureUnit::TWIP
:
99 case MeasureUnit::POINT
:
101 case MeasureUnit::MM_10TH
:
102 case MeasureUnit::MM_100TH
:
104 case MeasureUnit::MM
:
106 case MeasureUnit::CM
:
108 case MeasureUnit::INCH
:
114 template <typename V
> bool wordEndsWith(V string
, std::string_view expected
)
116 V substr
= string
.substr(0, expected
.size());
117 return std::equal(substr
.begin(), substr
.end(), expected
.begin(), expected
.end(),
118 [](sal_uInt32 c1
, sal_uInt32 c2
) { return rtl::toAsciiLowerCase(c1
) == c2
; })
119 && (string
.size() == expected
.size() || string
[expected
.size()] == ' ');
124 /** convert string to measure using optional min and max values*/
126 static bool lcl_convertMeasure( sal_Int32
& rValue
,
128 sal_Int16 nTargetUnit
/* = MeasureUnit::MM_100TH */,
129 sal_Int32 nMin
/* = SAL_MIN_INT32 */,
130 sal_Int32 nMax
/* = SAL_MAX_INT32 */ )
136 sal_Int32
const nLen
= rString
.size();
139 while( (nPos
< nLen
) && (rString
[nPos
] <= ' ') )
142 if( nPos
< nLen
&& '-' == rString
[nPos
] )
149 while( nPos
< nLen
&&
150 '0' <= rString
[nPos
] &&
151 '9' >= rString
[nPos
] )
153 // TODO: check overflow!
155 nVal
+= (rString
[nPos
] - '0');
158 if( nPos
< nLen
&& '.' == rString
[nPos
] )
163 while( nPos
< nLen
&&
164 '0' <= rString
[nPos
] &&
165 '9' >= rString
[nPos
] )
167 // TODO: check overflow!
169 nVal
+= ( static_cast<double>(rString
[nPos
] - '0') / nDiv
);
175 while( (nPos
< nLen
) && (rString
[nPos
] <= ' ') )
181 if( MeasureUnit::PERCENT
== nTargetUnit
)
183 if( '%' != rString
[nPos
] )
186 else if( MeasureUnit::PIXEL
== nTargetUnit
)
188 if( nPos
+ 1 >= nLen
||
189 ('p' != rString
[nPos
] &&
190 'P' != rString
[nPos
])||
191 ('x' != rString
[nPos
+1] &&
192 'X' != rString
[nPos
+1]) )
197 OSL_ENSURE( MeasureUnit::TWIP
== nTargetUnit
|| MeasureUnit::POINT
== nTargetUnit
||
198 MeasureUnit::MM_100TH
== nTargetUnit
|| MeasureUnit::MM_10TH
== nTargetUnit
||
199 MeasureUnit::PIXEL
== nTargetUnit
, "unit is not supported");
201 o3tl::Length eFrom
= o3tl::Length::invalid
;
203 if( MeasureUnit::TWIP
== nTargetUnit
)
205 switch (rtl::toAsciiLowerCase
<sal_uInt32
>(rString
[nPos
]))
208 if (wordEndsWith(rString
.substr(nPos
+ 1), "m"))
209 eFrom
= o3tl::Length::cm
;
212 if (wordEndsWith(rString
.substr(nPos
+ 1), "n"))
213 eFrom
= o3tl::Length::in
;
216 if (wordEndsWith(rString
.substr(nPos
+ 1), "m"))
217 eFrom
= o3tl::Length::mm
;
220 if (wordEndsWith(rString
.substr(nPos
+ 1), "t"))
221 eFrom
= o3tl::Length::pt
;
222 else if (wordEndsWith(rString
.substr(nPos
+ 1), "c"))
223 eFrom
= o3tl::Length::pc
;
227 else if( MeasureUnit::MM_100TH
== nTargetUnit
|| MeasureUnit::MM_10TH
== nTargetUnit
)
229 switch (rtl::toAsciiLowerCase
<sal_uInt32
>(rString
[nPos
]))
232 if (wordEndsWith(rString
.substr(nPos
+ 1), "m"))
233 eFrom
= o3tl::Length::cm
;
236 if (wordEndsWith(rString
.substr(nPos
+ 1), "n"))
237 eFrom
= o3tl::Length::in
;
240 if (wordEndsWith(rString
.substr(nPos
+ 1), "m"))
241 eFrom
= o3tl::Length::mm
;
244 if (wordEndsWith(rString
.substr(nPos
+ 1), "t"))
245 eFrom
= o3tl::Length::pt
;
246 else if (wordEndsWith(rString
.substr(nPos
+ 1), "c"))
247 eFrom
= o3tl::Length::pc
;
248 else if (wordEndsWith(rString
.substr(nPos
+ 1), "x"))
249 eFrom
= o3tl::Length::px
;
253 else if( MeasureUnit::POINT
== nTargetUnit
)
255 if (wordEndsWith(rString
.substr(nPos
), "pt"))
256 eFrom
= o3tl::Length::pt
;
259 if (eFrom
== o3tl::Length::invalid
)
262 // TODO: check overflow
263 nVal
= o3tl::convert(nVal
, eFrom
, Measure2O3tlUnit(nTargetUnit
));
271 if( nVal
<= static_cast<double>(nMin
) )
273 else if( nVal
>= static_cast<double>(nMax
) )
276 rValue
= static_cast<sal_Int32
>(nVal
);
281 /** convert string to measure using optional min and max values*/
282 bool Converter::convertMeasure( sal_Int32
& rValue
,
283 std::u16string_view rString
,
284 sal_Int16 nTargetUnit
/* = MeasureUnit::MM_100TH */,
285 sal_Int32 nMin
/* = SAL_MIN_INT32 */,
286 sal_Int32 nMax
/* = SAL_MAX_INT32 */ )
288 return lcl_convertMeasure(rValue
, rString
, nTargetUnit
, nMin
, nMax
);
291 /** convert string to measure using optional min and max values*/
292 bool Converter::convertMeasure( sal_Int32
& rValue
,
293 std::string_view rString
,
294 sal_Int16 nTargetUnit
/* = MeasureUnit::MM_100TH */,
295 sal_Int32 nMin
/* = SAL_MIN_INT32 */,
296 sal_Int32 nMax
/* = SAL_MAX_INT32 */ )
298 return lcl_convertMeasure(rValue
, rString
, nTargetUnit
, nMin
, nMax
);
302 /** convert measure in given unit to string with given unit */
303 void Converter::convertMeasure( OUStringBuffer
& rBuffer
,
305 sal_Int16 nSourceUnit
/* = MeasureUnit::MM_100TH */,
306 sal_Int16 nTargetUnit
/* = MeasureUnit::INCH */ )
308 if( nSourceUnit
== MeasureUnit::PERCENT
)
310 OSL_ENSURE( nTargetUnit
== MeasureUnit::PERCENT
,
311 "MeasureUnit::PERCENT only maps to MeasureUnit::PERCENT!" );
313 rBuffer
.append( nMeasure
);
314 rBuffer
.append( '%' );
318 sal_Int64
nValue(nMeasure
); // extend to 64-bit first to avoid overflow
319 // the sign is processed separately
323 rBuffer
.append( '-' );
326 o3tl::Length eFrom
= o3tl::Length::in
, eTo
= o3tl::Length::in
;
327 int nFac
= 100; // used to get specific number of decimals (2 by default)
328 std::string_view psUnit
;
329 switch( nSourceUnit
)
331 case MeasureUnit::TWIP
:
332 eFrom
= o3tl::Length::twip
;
333 switch( nTargetUnit
)
335 case MeasureUnit::MM_100TH
:
336 case MeasureUnit::MM_10TH
:
337 OSL_ENSURE( MeasureUnit::INCH
== nTargetUnit
,"output unit not supported for twip values" );
339 case MeasureUnit::MM
:
340 eTo
= o3tl::Length::mm
;
345 case MeasureUnit::CM
:
346 eTo
= o3tl::Length::cm
;
351 case MeasureUnit::POINT
:
352 eTo
= o3tl::Length::pt
;
357 case MeasureUnit::INCH
:
359 OSL_ENSURE( MeasureUnit::INCH
== nTargetUnit
,
360 "output unit not supported for twip values" );
367 case MeasureUnit::POINT
:
368 // 1pt = 1pt (exactly)
369 OSL_ENSURE( MeasureUnit::POINT
== nTargetUnit
,
370 "output unit not supported for pt values" );
371 eFrom
= eTo
= o3tl::Length::pt
;
375 case MeasureUnit::MM_10TH
:
376 case MeasureUnit::MM_100TH
:
378 int nFac2
= (MeasureUnit::MM_100TH
== nSourceUnit
) ? 100 : 10;
379 eFrom
= Measure2O3tlUnit(nSourceUnit
);
380 switch( nTargetUnit
)
382 case MeasureUnit::MM_100TH
:
383 case MeasureUnit::MM_10TH
:
384 OSL_ENSURE( MeasureUnit::INCH
== nTargetUnit
,
385 "output unit not supported for 1/100mm values" );
387 case MeasureUnit::MM
:
388 eTo
= o3tl::Length::mm
;
393 case MeasureUnit::CM
:
394 eTo
= o3tl::Length::cm
;
399 case MeasureUnit::POINT
:
400 eTo
= o3tl::Length::pt
;
405 case MeasureUnit::INCH
:
407 OSL_ENSURE( MeasureUnit::INCH
== nTargetUnit
,
408 "output unit not supported for 1/100mm values" );
416 OSL_ENSURE(false, "sax::Converter::convertMeasure(): "
417 "source unit not supported");
421 nValue
= o3tl::convert(nValue
* nFac
, eFrom
, eTo
);
423 rBuffer
.append( static_cast<sal_Int64
>(nValue
/ nFac
) );
424 if (nFac
> 1 && (nValue
% nFac
) != 0)
426 rBuffer
.append( '.' );
427 while (nFac
> 1 && (nValue
% nFac
) != 0)
430 rBuffer
.append( static_cast<sal_Int32
>((nValue
/ nFac
) % 10) );
434 if (psUnit
.length() > 0)
435 rBuffer
.appendAscii(psUnit
.data(), psUnit
.length());
438 /** convert string to boolean */
439 bool Converter::convertBool( bool& rBool
, std::u16string_view rString
)
441 rBool
= rString
== u
"true";
443 return rBool
|| (rString
== u
"false");
446 /** convert string to boolean */
447 bool Converter::convertBool( bool& rBool
, std::string_view rString
)
449 rBool
= rString
== "true";
451 return rBool
|| (rString
== "false");
454 /** convert boolean to string */
455 void Converter::convertBool( OUStringBuffer
& rBuffer
, bool bValue
)
457 rBuffer
.append( bValue
);
460 /** convert string to percent */
461 bool Converter::convertPercent( sal_Int32
& rPercent
, std::u16string_view rString
)
463 return convertMeasure( rPercent
, rString
, MeasureUnit::PERCENT
);
466 /** convert string to percent */
467 bool Converter::convertPercent( sal_Int32
& rPercent
, std::string_view rString
)
469 return convertMeasure( rPercent
, rString
, MeasureUnit::PERCENT
);
472 /** convert percent to string */
473 void Converter::convertPercent( OUStringBuffer
& rBuffer
, sal_Int32 nValue
)
475 rBuffer
.append( nValue
);
476 rBuffer
.append( '%' );
479 /** convert string to pixel measure */
480 bool Converter::convertMeasurePx( sal_Int32
& rPixel
, std::u16string_view rString
)
482 return convertMeasure( rPixel
, rString
, MeasureUnit::PIXEL
);
485 /** convert string to pixel measure */
486 bool Converter::convertMeasurePx( sal_Int32
& rPixel
, std::string_view rString
)
488 return convertMeasure( rPixel
, rString
, MeasureUnit::PIXEL
);
491 /** convert pixel measure to string */
492 void Converter::convertMeasurePx( OUStringBuffer
& rBuffer
, sal_Int32 nValue
)
494 rBuffer
.append( nValue
);
495 rBuffer
.append( 'p' );
496 rBuffer
.append( 'x' );
499 static int lcl_gethex( int nChar
)
501 if( nChar
>= '0' && nChar
<= '9' )
503 else if( nChar
>= 'a' && nChar
<= 'f' )
504 return nChar
- 'a' + 10;
505 else if( nChar
>= 'A' && nChar
<= 'F' )
506 return nChar
- 'A' + 10;
511 /** convert string to rgb color */
513 static bool lcl_convertColor( sal_Int32
& rColor
, V rValue
)
515 if( rValue
.size() != 7 || rValue
[0] != '#' )
518 rColor
= lcl_gethex( rValue
[1] ) * 16 + lcl_gethex( rValue
[2] );
521 rColor
|= lcl_gethex( rValue
[3] ) * 16 + lcl_gethex( rValue
[4] );
524 rColor
|= lcl_gethex( rValue
[5] ) * 16 + lcl_gethex( rValue
[6] );
529 /** convert string to rgb color */
530 bool Converter::convertColor( sal_Int32
& rColor
, std::u16string_view rValue
)
532 return lcl_convertColor(rColor
, rValue
);
535 /** convert string to rgb color */
536 bool Converter::convertColor( sal_Int32
& rColor
, std::string_view rValue
)
538 return lcl_convertColor(rColor
, rValue
);
541 const char aHexTab
[] = "0123456789abcdef";
543 /** convert color to string */
544 void Converter::convertColor( OUStringBuffer
& rBuffer
, sal_Int32 nColor
)
546 rBuffer
.append( '#' );
548 sal_uInt8 nCol
= static_cast<sal_uInt8
>(nColor
>> 16);
549 rBuffer
.append( sal_Unicode( aHexTab
[ nCol
>> 4 ] ) );
550 rBuffer
.append( sal_Unicode( aHexTab
[ nCol
& 0xf ] ) );
552 nCol
= static_cast<sal_uInt8
>(nColor
>> 8);
553 rBuffer
.append( sal_Unicode( aHexTab
[ nCol
>> 4 ] ) );
554 rBuffer
.append( sal_Unicode( aHexTab
[ nCol
& 0xf ] ) );
556 nCol
= static_cast<sal_uInt8
>(nColor
);
557 rBuffer
.append( sal_Unicode( aHexTab
[ nCol
>> 4 ] ) );
558 rBuffer
.append( sal_Unicode( aHexTab
[ nCol
& 0xf ] ) );
561 /** convert string to number with optional min and max values */
562 bool Converter::convertNumber( sal_Int32
& rValue
,
563 std::u16string_view aString
,
564 sal_Int32 nMin
, sal_Int32 nMax
)
567 sal_Int64 nNumber
= 0;
568 bool bRet
= convertNumber64(nNumber
,aString
,nMin
,nMax
);
570 rValue
= static_cast<sal_Int32
>(nNumber
);
574 /** convert string to number with optional min and max values */
575 bool Converter::convertNumber( sal_Int32
& rValue
,
576 std::string_view aString
,
577 sal_Int32 nMin
, sal_Int32 nMax
)
580 sal_Int64 nNumber
= 0;
581 bool bRet
= convertNumber64(nNumber
,aString
,nMin
,nMax
);
583 rValue
= static_cast<sal_Int32
>(nNumber
);
587 /** convert string to 64-bit number with optional min and max values */
589 static bool lcl_convertNumber64( sal_Int64
& rValue
,
591 sal_Int64 nMin
, sal_Int64 nMax
)
594 sal_Int32
const nLen
= aString
.size();
597 while( (nPos
< nLen
) && (aString
[nPos
] <= ' ') )
600 sal_Int32 nNumberStartPos
= nPos
;
602 if( nPos
< nLen
&& '-' == aString
[nPos
] )
608 while( nPos
< nLen
&&
609 '0' <= aString
[nPos
] &&
610 '9' >= aString
[nPos
] )
615 rValue
= toInt64_WithLength(aString
.data() + nNumberStartPos
, 10, nPos
- nNumberStartPos
);
619 else if( rValue
> nMax
)
622 return ( nPos
== nLen
&& rValue
>= nMin
&& rValue
<= nMax
);
625 /** convert string to 64-bit number with optional min and max values */
626 bool Converter::convertNumber64( sal_Int64
& rValue
,
627 std::u16string_view aString
,
628 sal_Int64 nMin
, sal_Int64 nMax
)
630 return lcl_convertNumber64(rValue
, aString
, nMin
, nMax
);
633 /** convert string to 64-bit number with optional min and max values */
634 bool Converter::convertNumber64( sal_Int64
& rValue
,
635 std::string_view aString
,
636 sal_Int64 nMin
, sal_Int64 nMax
)
638 return lcl_convertNumber64(rValue
, aString
, nMin
, nMax
);
642 /** convert double number to string (using ::rtl::math) */
643 void Converter::convertDouble( OUStringBuffer
& rBuffer
,
646 sal_Int16 nSourceUnit
,
647 sal_Int16 nTargetUnit
)
649 if(MeasureUnit::PERCENT
== nSourceUnit
)
651 OSL_ENSURE( nTargetUnit
== MeasureUnit::PERCENT
, "MeasureUnit::PERCENT only maps to MeasureUnit::PERCENT!" );
652 ::rtl::math::doubleToUStringBuffer( rBuffer
, fNumber
, rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
, '.', true);
658 OUStringBuffer sUnit
;
659 double fFactor
= GetConversionFactor(sUnit
, nSourceUnit
, nTargetUnit
);
662 ::rtl::math::doubleToUStringBuffer( rBuffer
, fNumber
, rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
, '.', true);
664 rBuffer
.append(sUnit
);
668 /** convert double number to string (using ::rtl::math) */
669 void Converter::convertDouble( OUStringBuffer
& rBuffer
, double fNumber
)
671 ::rtl::math::doubleToUStringBuffer( rBuffer
, fNumber
, rtl_math_StringFormat_Automatic
, rtl_math_DecimalPlaces_Max
, '.', true);
674 /** convert string to double number (using ::rtl::math) */
675 bool Converter::convertDouble(double& rValue
,
676 std::u16string_view rString
, sal_Int16 nSourceUnit
, sal_Int16 nTargetUnit
)
678 if (!convertDouble(rValue
, rString
))
681 OUStringBuffer sUnit
;
682 // fdo#48969: switch source and target because factor is used to divide!
683 double const fFactor
=
684 GetConversionFactor(sUnit
, nTargetUnit
, nSourceUnit
);
685 if(fFactor
!= 1.0 && fFactor
!= 0.0)
690 /** convert string to double number (using ::rtl::math) */
691 bool Converter::convertDouble(double& rValue
,
692 std::string_view rString
, sal_Int16 nSourceUnit
, sal_Int16 nTargetUnit
)
694 if (!convertDouble(rValue
, rString
))
698 // fdo#48969: switch source and target because factor is used to divide!
699 double const fFactor
=
700 GetConversionFactor(sUnit
, nTargetUnit
, nSourceUnit
);
701 if(fFactor
!= 1.0 && fFactor
!= 0.0)
706 /** convert string to double number (using ::rtl::math) */
707 bool Converter::convertDouble(double& rValue
, std::u16string_view rString
)
709 rtl_math_ConversionStatus eStatus
;
710 rValue
= rtl_math_uStringToDouble(rString
.data(),
711 rString
.data() + rString
.size(),
712 /*cDecSeparator*/'.', /*cGroupSeparator*/',',
714 return ( eStatus
== rtl_math_ConversionStatus_Ok
);
717 /** convert string to double number (using ::rtl::math) */
718 bool Converter::convertDouble(double& rValue
, std::string_view rString
)
720 rtl_math_ConversionStatus eStatus
;
721 rValue
= rtl_math_stringToDouble(rString
.data(),
722 rString
.data() + rString
.size(),
723 /*cDecSeparator*/'.', /*cGroupSeparator*/',',
725 return ( eStatus
== rtl_math_ConversionStatus_Ok
);
728 /** convert number, 10th of degrees with range [0..3600] to SVG angle */
729 void Converter::convertAngle(OUStringBuffer
& rBuffer
, sal_Int16
const nAngle
,
730 SvtSaveOptions::ODFSaneDefaultVersion
const nVersion
)
732 if (nVersion
< SvtSaveOptions::ODFSVER_012
|| nVersion
== SvtSaveOptions::ODFSVER_012_EXT_COMPAT
)
734 // wrong, but backward compatible with OOo/LO < 4.4
735 rBuffer
.append(static_cast<sal_Int32
>(nAngle
));
738 { // OFFICE-3774 tdf#89475 write valid ODF 1.2 angle; needs LO 4.4 to import
739 double fAngle(double(nAngle
) / 10.0);
740 ::sax::Converter::convertDouble(rBuffer
, fAngle
);
741 rBuffer
.append("deg");
745 /** convert SVG angle to number, 10th of degrees with range [0..3600] */
746 bool Converter::convertAngle(sal_Int16
& rAngle
, std::u16string_view rString
,
747 bool const isWrongOOo10thDegAngle
)
749 // ODF 1.1 leaves it undefined what the number means, but ODF 1.2 says it's
750 // degrees, while OOo has historically used 10th of degrees :(
751 // So import degrees when we see the "deg" suffix but continue with 10th of
752 // degrees for now for the sake of existing OOo/LO documents, until the
753 // new versions that can read "deg" suffix are widely deployed and we can
754 // start to write the "deg" suffix.
757 bool bRet
= ::sax::Converter::convertDouble(fValue
, rString
);
758 if (std::u16string_view::npos
!= rString
.find(u
"deg"))
760 nValue
= fValue
* 10.0;
762 else if (std::u16string_view::npos
!= rString
.find(u
"grad"))
764 nValue
= (fValue
* 9.0 / 10.0) * 10.0;
766 else if (std::u16string_view::npos
!= rString
.find(u
"rad"))
768 nValue
= basegfx::rad2deg
<10>(fValue
);
770 else // no explicit unit
772 if (isWrongOOo10thDegAngle
)
774 nValue
= fValue
; // wrong, but backward compatible with OOo/LO < 7.0
778 nValue
= fValue
* 10.0; // ODF 1.2
781 // limit to valid range [0..3600]
782 nValue
= nValue
% 3600;
787 assert(0 <= nValue
&& nValue
<= 3600);
790 rAngle
= sal::static_int_cast
<sal_Int16
>(nValue
);
795 /** convert SVG angle to number, 10th of degrees with range [0..3600] */
796 bool Converter::convertAngle(sal_Int16
& rAngle
, std::string_view rString
,
797 bool const isWrongOOo10thDegAngle
)
799 // ODF 1.1 leaves it undefined what the number means, but ODF 1.2 says it's
800 // degrees, while OOo has historically used 10th of degrees :(
801 // So import degrees when we see the "deg" suffix but continue with 10th of
802 // degrees for now for the sake of existing OOo/LO documents, until the
803 // new versions that can read "deg" suffix are widely deployed and we can
804 // start to write the "deg" suffix.
807 bool bRet
= ::sax::Converter::convertDouble(fValue
, rString
);
808 if (std::string_view::npos
!= rString
.find("deg"))
810 nValue
= fValue
* 10.0;
812 else if (std::string_view::npos
!= rString
.find("grad"))
814 nValue
= (fValue
* 9.0 / 10.0) * 10.0;
816 else if (std::string_view::npos
!= rString
.find("rad"))
818 nValue
= basegfx::rad2deg
<10>(fValue
);
820 else // no explicit unit
822 if (isWrongOOo10thDegAngle
)
824 nValue
= fValue
; // wrong, but backward compatible with OOo/LO < 7.0
828 nValue
= fValue
* 10.0; // ODF 1.2
831 // limit to valid range [0..3600]
832 nValue
= nValue
% 3600;
837 assert(0 <= nValue
&& nValue
<= 3600);
840 rAngle
= sal::static_int_cast
<sal_Int16
>(nValue
);
845 /** convert double to ISO "duration" string; negative durations allowed */
846 void Converter::convertDuration(OUStringBuffer
& rBuffer
,
849 double fValue
= fTime
;
851 // take care of negative durations as specified in:
852 // XML Schema, W3C Working Draft 07 April 2000, section 3.2.6.1
859 rBuffer
.append( "PT" );
861 double fHoursValue
= ::rtl::math::approxFloor (fValue
);
862 fValue
-= fHoursValue
;
864 double fMinsValue
= ::rtl::math::approxFloor (fValue
);
865 fValue
-= fMinsValue
;
867 double fSecsValue
= ::rtl::math::approxFloor (fValue
);
868 fValue
-= fSecsValue
;
869 double fNanoSecsValue
;
870 if (fValue
> 0.00000000001)
871 fNanoSecsValue
= ::rtl::math::round( fValue
, XML_MAXDIGITSCOUNT_TIME
- 5);
873 fNanoSecsValue
= 0.0;
875 if (fNanoSecsValue
== 1.0)
877 fNanoSecsValue
= 0.0;
880 if (fSecsValue
>= 60.0)
885 if (fMinsValue
>= 60.0)
891 if (fHoursValue
< 10)
892 rBuffer
.append( '0');
893 rBuffer
.append( sal_Int32( fHoursValue
));
894 rBuffer
.append( 'H');
896 rBuffer
.append( '0');
897 rBuffer
.append( sal_Int32( fMinsValue
));
898 rBuffer
.append( 'M');
900 rBuffer
.append( '0');
901 rBuffer
.append( sal_Int32( fSecsValue
));
902 if (fNanoSecsValue
> 0.0)
904 OUString
aNS( ::rtl::math::doubleToUString( fValue
,
905 rtl_math_StringFormat_F
, XML_MAXDIGITSCOUNT_TIME
- 5, '.',
907 if ( aNS
.getLength() > 2 )
909 rBuffer
.append( '.');
910 rBuffer
.append( aNS
.subView(2) ); // strip "0."
913 rBuffer
.append( 'S');
916 /** helper function of Converter::convertDuration */
918 static bool convertDurationHelper(double& rfTime
, V pStr
)
920 // negative time duration?
921 bool bIsNegativeDuration
= false;
922 if ( '-' == (*pStr
) )
924 bIsNegativeDuration
= true;
928 if ( *pStr
!= 'P' && *pStr
!= 'p' ) // duration must start with "P"
932 OUStringBuffer sDoubleStr
;
933 bool bSuccess
= true;
935 bool bTimePart
= false;
936 bool bIsFraction
= false;
938 sal_Int32 nHours
= 0;
943 while ( bSuccess
&& !bDone
)
945 sal_Unicode c
= *(pStr
++);
948 else if ( '0' <= c
&& '9' >= c
)
950 if ( nTemp
>= SAL_MAX_INT32
/ 10 )
961 sDoubleStr
.append(c
);
965 else if ( bTimePart
)
967 if ( c
== 'H' || c
== 'h' )
972 else if ( c
== 'M' || c
== 'm')
977 else if ( (c
== ',') || (c
== '.') )
984 else if ( c
== 'S' || c
== 's' )
994 bSuccess
= false; // invalid character
998 if ( c
== 'T' || c
== 't' ) // "T" starts time part
1000 else if ( c
== 'D' || c
== 'd')
1005 else if ( c
== 'Y' || c
== 'y' || c
== 'M' || c
== 'm' )
1007 //! how many days is a year or month?
1009 OSL_FAIL( "years or months in duration: not implemented");
1013 bSuccess
= false; // invalid character
1020 nHours
+= nDays
* 24; // add the days to the hours part
1021 double fHour
= nHours
;
1022 double fMin
= nMins
;
1023 double fSec
= nSecs
;
1024 double fFraction
= o3tl::toDouble(sDoubleStr
);
1025 double fTempTime
= fHour
/ 24;
1026 fTempTime
+= fMin
/ (24 * 60);
1027 fTempTime
+= fSec
/ (24 * 60 * 60);
1028 fTempTime
+= fFraction
/ (24 * 60 * 60);
1030 // negative duration?
1031 if ( bIsNegativeDuration
)
1033 fTempTime
= -fTempTime
;
1041 /** convert ISO "duration" string to double; negative durations allowed */
1042 bool Converter::convertDuration(double& rfTime
,
1043 std::string_view rString
)
1045 std::string_view aTrimmed
= o3tl::trim(rString
);
1046 const char* pStr
= aTrimmed
.data();
1048 return convertDurationHelper(rfTime
, pStr
);
1051 /** convert util::Duration to ISO8601 "duration" string */
1052 void Converter::convertDuration(OUStringBuffer
& rBuffer
,
1053 const ::util::Duration
& rDuration
)
1055 if (rDuration
.Negative
)
1057 rBuffer
.append('-');
1059 rBuffer
.append('P');
1060 const bool bHaveDate(rDuration
.Years
!= 0 ||
1061 rDuration
.Months
!= 0 ||
1062 rDuration
.Days
!= 0);
1063 if (rDuration
.Years
)
1065 rBuffer
.append(static_cast<sal_Int32
>(rDuration
.Years
));
1066 rBuffer
.append('Y');
1068 if (rDuration
.Months
)
1070 rBuffer
.append(static_cast<sal_Int32
>(rDuration
.Months
));
1071 rBuffer
.append('M');
1075 rBuffer
.append(static_cast<sal_Int32
>(rDuration
.Days
));
1076 rBuffer
.append('D');
1078 if ( rDuration
.Hours
!= 0
1079 || rDuration
.Minutes
!= 0
1080 || rDuration
.Seconds
!= 0
1081 || rDuration
.NanoSeconds
!= 0 )
1083 rBuffer
.append('T'); // time separator
1084 if (rDuration
.Hours
)
1086 rBuffer
.append(static_cast<sal_Int32
>(rDuration
.Hours
));
1087 rBuffer
.append('H');
1089 if (rDuration
.Minutes
)
1091 rBuffer
.append(static_cast<sal_Int32
>(rDuration
.Minutes
));
1092 rBuffer
.append('M');
1094 if (rDuration
.Seconds
!= 0 || rDuration
.NanoSeconds
!= 0)
1096 // seconds must not be omitted (i.e. ".42S" is not valid)
1097 rBuffer
.append(static_cast<sal_Int32
>(rDuration
.Seconds
));
1098 if (rDuration
.NanoSeconds
)
1100 OSL_ENSURE(rDuration
.NanoSeconds
< 1000000000,"NanoSeconds cannot be more than 999 999 999");
1101 rBuffer
.append('.');
1102 std::ostringstream ostr
;
1105 ostr
<< rDuration
.NanoSeconds
;
1106 rBuffer
.appendAscii(ostr
.str().c_str());
1108 rBuffer
.append('S');
1111 else if (!bHaveDate
)
1113 // zero duration: XMLSchema-2 says there must be at least one component
1114 rBuffer
.append('0');
1115 rBuffer
.append('D');
1121 enum Result
{ R_NOTHING
, R_OVERFLOW
, R_SUCCESS
};
1125 template <typename V
>
1127 readUnsignedNumber(V rString
,
1128 size_t & io_rnPos
, sal_Int32
& o_rNumber
)
1130 size_t nPos(io_rnPos
);
1132 while (nPos
< rString
.size())
1134 const typename
V::value_type c
= rString
[nPos
];
1135 if (('0' > c
) || (c
> '9'))
1140 if (io_rnPos
== nPos
) // read something?
1146 const sal_Int64 nTemp
= toInt64_WithLength(rString
.data() + io_rnPos
, 10, nPos
- io_rnPos
);
1148 const bool bOverflow
= (nTemp
>= SAL_MAX_INT32
);
1152 return bOverflow
? R_OVERFLOW
: R_SUCCESS
;
1155 template<typename V
>
1157 readUnsignedNumberMaxDigits(int maxDigits
,
1158 V rString
, size_t & io_rnPos
,
1159 sal_Int32
& o_rNumber
)
1161 bool bOverflow(false);
1163 size_t nPos(io_rnPos
);
1164 OSL_ENSURE(maxDigits
>= 0, "negative amount of digits makes no sense");
1166 while (nPos
< rString
.size())
1168 const sal_Unicode c
= rString
[nPos
];
1169 if (('0' <= c
) && (c
<= '9'))
1174 nTemp
+= (c
- u
'0');
1175 if (nTemp
>= SAL_MAX_INT32
)
1189 if (io_rnPos
== nPos
) // read something?
1197 return bOverflow
? R_OVERFLOW
: R_SUCCESS
;
1200 template<typename V
>
1202 readDurationT(V rString
, size_t & io_rnPos
)
1204 if ((io_rnPos
< rString
.size()) &&
1205 (rString
[io_rnPos
] == 'T' || rString
[io_rnPos
] == 't'))
1213 template<typename V
>
1215 readDurationComponent(V rString
,
1216 size_t & io_rnPos
, sal_Int32
& io_rnTemp
, bool & io_rbTimePart
,
1217 sal_Int32
& o_rnTarget
, const sal_Unicode cLower
, const sal_Unicode cUpper
)
1219 if (io_rnPos
< rString
.size())
1221 if (cLower
== rString
[io_rnPos
] || cUpper
== rString
[io_rnPos
])
1224 if (-1 != io_rnTemp
)
1226 o_rnTarget
= io_rnTemp
;
1230 io_rbTimePart
= readDurationT(rString
, io_rnPos
);
1232 return (R_OVERFLOW
!=
1233 readUnsignedNumber(rString
, io_rnPos
, io_rnTemp
));
1244 template <typename V
>
1245 static bool convertDurationHelper(util::Duration
& rDuration
, V string
)
1249 bool bIsNegativeDuration(false);
1250 if (!string
.empty() && ('-' == string
[0]))
1252 bIsNegativeDuration
= true;
1256 if (nPos
< string
.size()
1257 && string
[nPos
] != 'P' && string
[nPos
] != 'p') // duration must start with "P"
1264 /// last read number; -1 == no valid number! always reset after using!
1265 sal_Int32
nTemp(-1);
1266 bool bTimePart(false); // have we read 'T'?
1267 bool bSuccess(false);
1268 sal_Int32
nYears(0);
1269 sal_Int32
nMonths(0);
1271 sal_Int32
nHours(0);
1272 sal_Int32
nMinutes(0);
1273 sal_Int32
nSeconds(0);
1274 sal_Int32
nNanoSeconds(0);
1276 bTimePart
= readDurationT(string
, nPos
);
1277 bSuccess
= (R_SUCCESS
== readUnsignedNumber(string
, nPos
, nTemp
));
1279 if (!bTimePart
&& bSuccess
)
1281 bSuccess
= readDurationComponent(string
, nPos
, nTemp
, bTimePart
,
1285 if (!bTimePart
&& bSuccess
)
1287 bSuccess
= readDurationComponent(string
, nPos
, nTemp
, bTimePart
,
1291 if (!bTimePart
&& bSuccess
)
1293 bSuccess
= readDurationComponent(string
, nPos
, nTemp
, bTimePart
,
1299 if (-1 == nTemp
) // a 'T' must be followed by a component
1306 bSuccess
= readDurationComponent(string
, nPos
, nTemp
, bTimePart
,
1312 bSuccess
= readDurationComponent(string
, nPos
, nTemp
, bTimePart
,
1313 nMinutes
, 'm', 'M');
1316 // eeek! seconds are icky.
1317 if ((nPos
< string
.size()) && bSuccess
)
1319 if (string
[nPos
] == '.' ||
1320 string
[nPos
] == ',')
1327 const sal_Int32
nStart(nPos
);
1328 bSuccess
= readUnsignedNumberMaxDigits(9, string
, nPos
, nTemp
) == R_SUCCESS
;
1329 if ((nPos
< string
.size()) && bSuccess
)
1333 nNanoSeconds
= nTemp
;
1334 sal_Int32 nDigits
= nPos
- nStart
;
1335 assert(nDigits
>= 0);
1336 for (; nDigits
< 9; ++nDigits
)
1341 if ('S' == string
[nPos
] || 's' == string
[nPos
])
1361 else if ('S' == string
[nPos
] || 's' == string
[nPos
])
1377 if (nPos
!= string
.size()) // string not processed completely?
1382 if (nTemp
!= -1) // unprocessed number?
1389 rDuration
.Negative
= bIsNegativeDuration
;
1390 rDuration
.Years
= static_cast<sal_Int16
>(nYears
);
1391 rDuration
.Months
= static_cast<sal_Int16
>(nMonths
);
1392 rDuration
.Days
= static_cast<sal_Int16
>(nDays
);
1393 rDuration
.Hours
= static_cast<sal_Int16
>(nHours
);
1394 rDuration
.Minutes
= static_cast<sal_Int16
>(nMinutes
);
1395 rDuration
.Seconds
= static_cast<sal_Int16
>(nSeconds
);
1396 rDuration
.NanoSeconds
= nNanoSeconds
;
1402 /** convert ISO8601 "duration" string to util::Duration */
1403 bool Converter::convertDuration(util::Duration
& rDuration
,
1404 std::u16string_view rString
)
1406 return convertDurationHelper(rDuration
, o3tl::trim(rString
));
1409 /** convert ISO8601 "duration" string to util::Duration */
1410 bool Converter::convertDuration(util::Duration
& rDuration
,
1411 std::string_view rString
)
1413 return convertDurationHelper(rDuration
, o3tl::trim(rString
));
1417 lcl_AppendTimezone(OUStringBuffer
& i_rBuffer
, int const nOffset
)
1421 i_rBuffer
.append('Z');
1427 i_rBuffer
.append('+');
1431 i_rBuffer
.append('-');
1433 const sal_Int32
nHours (abs(nOffset
) / 60);
1434 const sal_Int32
nMinutes(abs(nOffset
) % 60);
1435 SAL_WARN_IF(nHours
> 14 || (nHours
== 14 && nMinutes
> 0),
1436 "sax", "convertDateTime: timezone overflow");
1439 i_rBuffer
.append('0');
1441 i_rBuffer
.append(nHours
);
1442 i_rBuffer
.append(':');
1445 i_rBuffer
.append('0');
1447 i_rBuffer
.append(nMinutes
);
1451 /** convert util::Date to ISO "date" string */
1452 void Converter::convertDate(
1453 OUStringBuffer
& i_rBuffer
,
1454 const util::Date
& i_rDate
,
1455 sal_Int16
const*const pTimeZoneOffset
)
1457 const util::DateTime
dt(0, 0, 0, 0,
1458 i_rDate
.Day
, i_rDate
.Month
, i_rDate
.Year
, false);
1459 convertDateTime(i_rBuffer
, dt
, pTimeZoneOffset
);
1462 static void convertTime(
1463 OUStringBuffer
& i_rBuffer
,
1464 const css::util::DateTime
& i_rDateTime
)
1466 if (i_rDateTime
.Hours
< 10) {
1467 i_rBuffer
.append('0');
1469 i_rBuffer
.append( OUString::number(static_cast<sal_Int32
>(i_rDateTime
.Hours
)) + ":");
1470 if (i_rDateTime
.Minutes
< 10) {
1471 i_rBuffer
.append('0');
1473 i_rBuffer
.append( OUString::number(static_cast<sal_Int32
>(i_rDateTime
.Minutes
) ) + ":");
1474 if (i_rDateTime
.Seconds
< 10) {
1475 i_rBuffer
.append('0');
1477 i_rBuffer
.append( static_cast<sal_Int32
>(i_rDateTime
.Seconds
) );
1478 if (i_rDateTime
.NanoSeconds
> 0) {
1479 OSL_ENSURE(i_rDateTime
.NanoSeconds
< 1000000000,"NanoSeconds cannot be more than 999 999 999");
1480 i_rBuffer
.append('.');
1481 std::ostringstream ostr
;
1484 ostr
<< i_rDateTime
.NanoSeconds
;
1485 i_rBuffer
.appendAscii(ostr
.str().c_str());
1489 static void convertTimeZone(
1490 OUStringBuffer
& i_rBuffer
,
1491 const css::util::DateTime
& i_rDateTime
,
1492 sal_Int16
const* pTimeZoneOffset
)
1494 if (pTimeZoneOffset
)
1496 lcl_AppendTimezone(i_rBuffer
, *pTimeZoneOffset
);
1498 else if (i_rDateTime
.IsUTC
)
1500 lcl_AppendTimezone(i_rBuffer
, 0);
1504 /** convert util::DateTime to ISO "time" or "dateTime" string */
1505 void Converter::convertTimeOrDateTime(
1506 OUStringBuffer
& i_rBuffer
,
1507 const css::util::DateTime
& i_rDateTime
)
1509 if (i_rDateTime
.Year
== 0 ||
1510 i_rDateTime
.Month
< 1 || i_rDateTime
.Month
> 12 ||
1511 i_rDateTime
.Day
< 1 || i_rDateTime
.Day
> 31)
1513 convertTime(i_rBuffer
, i_rDateTime
);
1514 convertTimeZone(i_rBuffer
, i_rDateTime
, nullptr);
1518 convertDateTime(i_rBuffer
, i_rDateTime
, nullptr, true);
1522 /** convert util::DateTime to ISO "date" or "dateTime" string */
1523 void Converter::convertDateTime(
1524 OUStringBuffer
& i_rBuffer
,
1525 const css::util::DateTime
& i_rDateTime
,
1526 sal_Int16
const*const pTimeZoneOffset
,
1527 bool i_bAddTimeIf0AM
)
1529 const sal_Unicode
dash('-');
1530 const sal_Unicode
zero('0');
1532 sal_Int32
const nYear(abs(i_rDateTime
.Year
));
1533 if (i_rDateTime
.Year
< 0) {
1534 i_rBuffer
.append(dash
); // negative
1537 i_rBuffer
.append(zero
);
1540 i_rBuffer
.append(zero
);
1543 i_rBuffer
.append(zero
);
1545 i_rBuffer
.append( OUString::number(nYear
) + OUStringChar(dash
) );
1546 if( i_rDateTime
.Month
< 10 ) {
1547 i_rBuffer
.append(zero
);
1549 i_rBuffer
.append( OUString::number(i_rDateTime
.Month
) + OUStringChar(dash
) );
1550 if( i_rDateTime
.Day
< 10 ) {
1551 i_rBuffer
.append(zero
);
1553 i_rBuffer
.append( static_cast<sal_Int32
>(i_rDateTime
.Day
) );
1555 if( i_rDateTime
.Seconds
!= 0 ||
1556 i_rDateTime
.Minutes
!= 0 ||
1557 i_rDateTime
.Hours
!= 0 ||
1560 i_rBuffer
.append('T');
1561 convertTime(i_rBuffer
, i_rDateTime
);
1564 convertTimeZone(i_rBuffer
, i_rDateTime
, pTimeZoneOffset
);
1567 /** convert ISO "date" or "dateTime" string to util::DateTime */
1568 bool Converter::parseDateTime( util::DateTime
& rDateTime
,
1569 std::u16string_view rString
)
1572 return parseDateOrDateTime(nullptr, rDateTime
, isDateTime
, nullptr,
1576 /** convert ISO "date" or "dateTime" string to util::DateTime */
1577 bool Converter::parseDateTime( util::DateTime
& rDateTime
,
1578 std::string_view rString
)
1581 return parseDateOrDateTime(nullptr, rDateTime
, isDateTime
, nullptr,
1585 static bool lcl_isLeapYear(const sal_uInt32 nYear
)
1587 return ((nYear
% 4) == 0)
1588 && (((nYear
% 100) != 0) || ((nYear
% 400) == 0));
1592 lcl_MaxDaysPerMonth(const sal_Int32 nMonth
, const sal_Int32 nYear
)
1594 static const sal_uInt16 s_MaxDaysPerMonth
[12] =
1595 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
1596 assert(0 < nMonth
&& nMonth
<= 12);
1597 if ((2 == nMonth
) && lcl_isLeapYear(nYear
))
1601 return s_MaxDaysPerMonth
[nMonth
- 1];
1604 static void lcl_ConvertToUTC(
1605 sal_Int16
& o_rYear
, sal_uInt16
& o_rMonth
, sal_uInt16
& o_rDay
,
1606 sal_uInt16
& o_rHours
, sal_uInt16
& o_rMinutes
,
1607 int const nSourceOffset
)
1609 sal_Int16
nOffsetHours(abs(nSourceOffset
) / 60);
1610 sal_Int16
const nOffsetMinutes(abs(nSourceOffset
) % 60);
1611 o_rMinutes
+= nOffsetMinutes
;
1612 if (nSourceOffset
< 0)
1614 o_rMinutes
+= nOffsetMinutes
;
1615 if (60 <= o_rMinutes
)
1620 o_rHours
+= nOffsetHours
;
1625 sal_Int16
nDayAdd(0);
1626 while (24 <= o_rHours
)
1633 return; // handle time without date - don't adjust what isn't there
1636 sal_Int16
const nDaysInMonth(lcl_MaxDaysPerMonth(o_rMonth
, o_rYear
));
1637 if (o_rDay
<= nDaysInMonth
)
1641 o_rDay
-= nDaysInMonth
;
1648 ++o_rYear
; // works for negative year too
1650 else if (0 < nSourceOffset
)
1652 // argh everything is unsigned
1653 if (o_rMinutes
< nOffsetMinutes
)
1658 o_rMinutes
-= nOffsetMinutes
;
1659 sal_Int16
nDaySubtract(0);
1660 while (o_rHours
< nOffsetHours
)
1665 o_rHours
-= nOffsetHours
;
1668 return; // handle time without date - don't adjust what isn't there
1670 if (nDaySubtract
< o_rDay
)
1672 o_rDay
-= nDaySubtract
;
1675 sal_Int16
const nPrevMonth((o_rMonth
== 1) ? 12 : o_rMonth
- 1);
1676 sal_Int16
const nDaysInMonth(lcl_MaxDaysPerMonth(nPrevMonth
, o_rYear
));
1677 o_rDay
+= nDaysInMonth
;
1682 --o_rYear
; // works for negative year too
1684 o_rDay
-= nDaySubtract
;
1688 template <typename V
>
1690 readDateTimeComponent(V rString
,
1691 size_t & io_rnPos
, sal_Int32
& o_rnTarget
,
1692 const sal_Int32 nMinLength
, const bool bExactLength
)
1694 const size_t nOldPos(io_rnPos
);
1696 if (R_SUCCESS
!= readUnsignedNumber
<V
>(rString
, io_rnPos
, nTemp
))
1700 const sal_Int32
nTokenLength(io_rnPos
- nOldPos
);
1701 if ((nTokenLength
< nMinLength
) ||
1702 (bExactLength
&& (nTokenLength
> nMinLength
)))
1704 return false; // bad length
1710 /** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
1711 template<typename V
>
1712 static bool lcl_parseDate(
1714 sal_Int32
& nYear
, sal_Int32
& nMonth
, sal_Int32
& nDay
,
1718 bool const bIgnoreInvalidOrMissingDate
)
1720 bool bSuccess
= true;
1722 if (string
.size() > nPos
)
1724 if ('-' == string
[nPos
])
1732 // While W3C XMLSchema specifies years with a minimum of 4 digits, be
1733 // lenient in what we accept for years < 1000. One digit is acceptable
1734 // if the remainders match.
1735 bSuccess
= readDateTimeComponent
<V
>(string
, nPos
, nYear
, 1, false);
1736 if (!bIgnoreInvalidOrMissingDate
)
1738 bSuccess
&= (0 < nYear
);
1740 bSuccess
&= (nPos
< string
.size()); // not last token
1742 if (bSuccess
&& ('-' != string
[nPos
])) // separator
1750 bSuccess
= readDateTimeComponent
<V
>(string
, nPos
, nMonth
, 2, true);
1751 if (!bIgnoreInvalidOrMissingDate
)
1753 bSuccess
&= (0 < nMonth
);
1755 bSuccess
&= (nMonth
<= 12);
1756 bSuccess
&= (nPos
< string
.size()); // not last token
1758 if (bSuccess
&& ('-' != string
[nPos
])) // separator
1766 bSuccess
= readDateTimeComponent(string
, nPos
, nDay
, 2, true);
1767 if (!bIgnoreInvalidOrMissingDate
)
1769 bSuccess
&= (0 < nDay
);
1771 if (nMonth
> 0) // not possible to check if month was missing
1773 bSuccess
&= (nDay
<= lcl_MaxDaysPerMonth(nMonth
, nYear
));
1775 else assert(bIgnoreInvalidOrMissingDate
);
1778 if (bSuccess
&& (nPos
< string
.size()))
1780 if ('T' == string
[nPos
] || 't' == string
[nPos
]) // time separator
1790 /** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
1791 template <typename V
>
1792 static bool lcl_parseDateTime(
1793 util::Date
*const pDate
, util::DateTime
& rDateTime
,
1795 std::optional
<sal_Int16
> *const pTimeZoneOffset
,
1797 bool const bIgnoreInvalidOrMissingDate
)
1799 bool bSuccess
= true;
1801 string
= o3tl::trim(string
);
1803 bool isNegative(false);
1805 sal_Int32
nMonth(0);
1808 bool bHaveTime(false);
1810 if ( !bIgnoreInvalidOrMissingDate
1811 || string
.find(':') == V::npos
// no time?
1812 || (string
.find('-') != V::npos
1813 && string
.find('-') < string
.find(':')))
1815 bSuccess
&= lcl_parseDate
<V
>(isNegative
, nYear
, nMonth
, nDay
,
1816 bHaveTime
, nPos
, string
, bIgnoreInvalidOrMissingDate
);
1823 sal_Int32
nHours(0);
1824 sal_Int32
nMinutes(0);
1825 sal_Int32
nSeconds(0);
1826 sal_Int32
nNanoSeconds(0);
1827 if (bSuccess
&& bHaveTime
)
1830 bSuccess
= readDateTimeComponent(string
, nPos
, nHours
, 2, true);
1831 bSuccess
&= (0 <= nHours
) && (nHours
<= 24);
1832 bSuccess
&= (nPos
< string
.size()); // not last token
1834 if (bSuccess
&& (':' != string
[nPos
])) // separator
1842 bSuccess
= readDateTimeComponent(string
, nPos
, nMinutes
, 2, true);
1843 bSuccess
&= (0 <= nMinutes
) && (nMinutes
< 60);
1844 bSuccess
&= (nPos
< string
.size()); // not last token
1846 if (bSuccess
&& (':' != string
[nPos
])) // separator
1854 bSuccess
= readDateTimeComponent(string
, nPos
, nSeconds
, 2, true);
1855 bSuccess
&= (0 <= nSeconds
) && (nSeconds
< 60);
1857 if (bSuccess
&& (nPos
< string
.size()) &&
1858 ('.' == string
[nPos
] || ',' == string
[nPos
])) // fraction separator
1861 const sal_Int32
nStart(nPos
);
1863 if (R_NOTHING
== readUnsignedNumberMaxDigits
<V
>(9, string
, nPos
, nTemp
))
1869 sal_Int32 nDigits
= std::min
<sal_Int32
>(nPos
- nStart
, 9);
1870 assert(nDigits
> 0);
1871 for (; nDigits
< 9; ++nDigits
)
1875 nNanoSeconds
= nTemp
;
1879 if (bSuccess
&& (nHours
== 24))
1881 if (!((0 == nMinutes
) && (0 == nSeconds
) && (0 == nNanoSeconds
)))
1883 bSuccess
= false; // only 24:00:00 is valid
1888 bool bHaveTimezone(false);
1889 bool bHaveTimezonePlus(false);
1890 bool bHaveTimezoneMinus(false);
1891 if (bSuccess
&& (nPos
< string
.size()))
1893 const sal_Unicode
c(string
[nPos
]);
1896 bHaveTimezone
= true;
1897 bHaveTimezonePlus
= true;
1902 bHaveTimezone
= true;
1903 bHaveTimezoneMinus
= true;
1906 else if ('Z' == c
|| 'z' == c
)
1908 bHaveTimezone
= true;
1916 sal_Int32
nTimezoneHours(0);
1917 sal_Int32
nTimezoneMinutes(0);
1918 if (bSuccess
&& (bHaveTimezonePlus
|| bHaveTimezoneMinus
))
1920 bSuccess
= readDateTimeComponent
<V
>(
1921 string
, nPos
, nTimezoneHours
, 2, true);
1922 bSuccess
&= (0 <= nTimezoneHours
) && (nTimezoneHours
<= 14);
1923 bSuccess
&= (nPos
< string
.size()); // not last token
1924 if (bSuccess
&& (':' != string
[nPos
])) // separator
1932 bSuccess
= readDateTimeComponent
<V
>(
1933 string
, nPos
, nTimezoneMinutes
, 2, true);
1934 bSuccess
&= (0 <= nTimezoneMinutes
) && (nTimezoneMinutes
< 60);
1936 if (bSuccess
&& (nTimezoneHours
== 14))
1938 if (0 != nTimezoneMinutes
)
1940 bSuccess
= false; // only +-14:00 is valid
1945 bSuccess
&= (nPos
== string
.size()); // trailing junk?
1949 sal_Int16
const nTimezoneOffset
= (bHaveTimezoneMinus
? -1 : +1)
1950 * ((nTimezoneHours
* 60) + nTimezoneMinutes
);
1951 if (!pDate
|| bHaveTime
) // time is optional
1954 (isNegative
? -1 : +1) * static_cast<sal_Int16
>(nYear
);
1955 rDateTime
.Month
= static_cast<sal_uInt16
>(nMonth
);
1956 rDateTime
.Day
= static_cast<sal_uInt16
>(nDay
);
1957 rDateTime
.Hours
= static_cast<sal_uInt16
>(nHours
);
1958 rDateTime
.Minutes
= static_cast<sal_uInt16
>(nMinutes
);
1959 rDateTime
.Seconds
= static_cast<sal_uInt16
>(nSeconds
);
1960 rDateTime
.NanoSeconds
= static_cast<sal_uInt32
>(nNanoSeconds
);
1963 if (pTimeZoneOffset
)
1965 *pTimeZoneOffset
= nTimezoneOffset
;
1966 rDateTime
.IsUTC
= (0 == nTimezoneOffset
);
1970 lcl_ConvertToUTC(rDateTime
.Year
, rDateTime
.Month
,
1971 rDateTime
.Day
, rDateTime
.Hours
, rDateTime
.Minutes
,
1973 rDateTime
.IsUTC
= true;
1978 if (pTimeZoneOffset
)
1980 pTimeZoneOffset
->reset();
1982 rDateTime
.IsUTC
= false;
1984 rbDateTime
= bHaveTime
;
1989 (isNegative
? -1 : +1) * static_cast<sal_Int16
>(nYear
);
1990 pDate
->Month
= static_cast<sal_uInt16
>(nMonth
);
1991 pDate
->Day
= static_cast<sal_uInt16
>(nDay
);
1994 if (pTimeZoneOffset
)
1996 *pTimeZoneOffset
= nTimezoneOffset
;
2000 // a Date cannot be adjusted
2001 SAL_INFO("sax", "dropping timezone");
2006 if (pTimeZoneOffset
)
2008 pTimeZoneOffset
->reset();
2017 /** convert ISO "time" or "dateTime" string to util::DateTime */
2018 bool Converter::parseTimeOrDateTime(
2019 util::DateTime
& rDateTime
,
2020 std::u16string_view rString
)
2023 return lcl_parseDateTime(
2024 nullptr, rDateTime
, dummy
, nullptr, rString
, true);
2027 /** convert ISO "time" or "dateTime" string to util::DateTime */
2028 bool Converter::parseTimeOrDateTime(
2029 util::DateTime
& rDateTime
,
2030 std::string_view rString
)
2033 return lcl_parseDateTime(
2034 nullptr, rDateTime
, dummy
, nullptr, rString
, true);
2037 /** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
2038 bool Converter::parseDateOrDateTime(
2039 util::Date
*const pDate
, util::DateTime
& rDateTime
,
2041 std::optional
<sal_Int16
> *const pTimeZoneOffset
,
2042 std::u16string_view rString
)
2044 return lcl_parseDateTime(
2045 pDate
, rDateTime
, rbDateTime
, pTimeZoneOffset
, rString
, false);
2048 /** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
2049 bool Converter::parseDateOrDateTime(
2050 util::Date
*const pDate
, util::DateTime
& rDateTime
,
2052 std::optional
<sal_Int16
> *const pTimeZoneOffset
,
2053 std::string_view rString
)
2055 return lcl_parseDateTime(
2056 pDate
, rDateTime
, rbDateTime
, pTimeZoneOffset
, rString
, false);
2059 /** gets the position of the first comma after npos in the string
2060 rStr. Commas inside '"' pairs are not matched */
2061 sal_Int32
Converter::indexOfComma( std::u16string_view rStr
,
2064 sal_Unicode cQuote
= 0;
2065 sal_Int32 nLen
= rStr
.size();
2066 for( ; nPos
< nLen
; nPos
++ )
2068 sal_Unicode c
= rStr
[nPos
];
2074 else if( '\'' == cQuote
)
2081 else if( '\"' == cQuote
)
2095 double Converter::GetConversionFactor(OUStringBuffer
& rUnit
, sal_Int16 nSourceUnit
, sal_Int16 nTargetUnit
)
2097 double fRetval(1.0);
2101 if(nSourceUnit
!= nTargetUnit
)
2103 const o3tl::Length eFrom
= Measure2O3tlUnit(nSourceUnit
);
2104 const o3tl::Length eTo
= Measure2O3tlUnit(nTargetUnit
);
2105 fRetval
= o3tl::convert(1.0, eFrom
, eTo
);
2107 if (const auto sUnit
= Measure2UnitString(nTargetUnit
); sUnit
.size() > 0)
2108 rUnit
.appendAscii(sUnit
.data(), sUnit
.size());
2114 double Converter::GetConversionFactor(OStringBuffer
& rUnit
, sal_Int16 nSourceUnit
, sal_Int16 nTargetUnit
)
2116 double fRetval(1.0);
2120 if(nSourceUnit
!= nTargetUnit
)
2122 const o3tl::Length eFrom
= Measure2O3tlUnit(nSourceUnit
);
2123 const o3tl::Length eTo
= Measure2O3tlUnit(nTargetUnit
);
2124 fRetval
= o3tl::convert(1.0, eFrom
, eTo
);
2126 if (const auto sUnit
= Measure2UnitString(nTargetUnit
); sUnit
.size() > 0)
2127 rUnit
.append(sUnit
.data(), sUnit
.size());
2133 template<typename V
>
2134 static sal_Int16
lcl_GetUnitFromString(V rString
, sal_Int16 nDefaultUnit
)
2137 sal_Int32 nLen
= rString
.size();
2138 sal_Int16 nRetUnit
= nDefaultUnit
;
2141 while( nPos
< nLen
&& ' ' == rString
[nPos
] )
2145 if( nPos
< nLen
&& '-' == rString
[nPos
] )
2149 while( nPos
< nLen
&& '0' <= rString
[nPos
] && '9' >= rString
[nPos
] )
2152 if( nPos
< nLen
&& '.' == rString
[nPos
] )
2155 while( nPos
< nLen
&& '0' <= rString
[nPos
] && '9' >= rString
[nPos
] )
2160 while( nPos
< nLen
&& ' ' == rString
[nPos
] )
2165 switch(rString
[nPos
])
2169 nRetUnit
= MeasureUnit::PERCENT
;
2175 if(nPos
+1 < nLen
&& (rString
[nPos
+1] == 'm'
2176 || rString
[nPos
+1] == 'M'))
2177 nRetUnit
= MeasureUnit::CM
;
2183 // CSS1_EMS or CSS1_EMX later
2189 if(nPos
+1 < nLen
&& (rString
[nPos
+1] == 'n'
2190 || rString
[nPos
+1] == 'N'))
2191 nRetUnit
= MeasureUnit::INCH
;
2197 if(nPos
+1 < nLen
&& (rString
[nPos
+1] == 'm'
2198 || rString
[nPos
+1] == 'M'))
2199 nRetUnit
= MeasureUnit::MM
;
2205 if(nPos
+1 < nLen
&& (rString
[nPos
+1] == 't'
2206 || rString
[nPos
+1] == 'T'))
2207 nRetUnit
= MeasureUnit::POINT
;
2208 if(nPos
+1 < nLen
&& (rString
[nPos
+1] == 'c'
2209 || rString
[nPos
+1] == 'C'))
2210 nRetUnit
= MeasureUnit::TWIP
;
2219 sal_Int16
Converter::GetUnitFromString(std::u16string_view rString
, sal_Int16 nDefaultUnit
)
2221 return lcl_GetUnitFromString(rString
, nDefaultUnit
);
2223 sal_Int16
Converter::GetUnitFromString(std::string_view rString
, sal_Int16 nDefaultUnit
)
2225 return lcl_GetUnitFromString(rString
, nDefaultUnit
);
2228 bool Converter::convertAny(OUStringBuffer
& rsValue
,
2229 OUStringBuffer
& rsType
,
2230 const css::uno::Any
& rValue
)
2232 bool bConverted
= false;
2234 rsValue
.setLength(0);
2235 rsType
.setLength (0);
2237 switch (rValue
.getValueTypeClass())
2239 case css::uno::TypeClass_BYTE
:
2240 case css::uno::TypeClass_SHORT
:
2241 case css::uno::TypeClass_UNSIGNED_SHORT
:
2242 case css::uno::TypeClass_LONG
:
2243 case css::uno::TypeClass_UNSIGNED_LONG
:
2245 sal_Int32 nTempValue
= 0;
2246 if (rValue
>>= nTempValue
)
2248 rsType
.append("integer");
2250 rsValue
.append(nTempValue
);
2255 case css::uno::TypeClass_BOOLEAN
:
2257 bool bTempValue
= false;
2258 if (rValue
>>= bTempValue
)
2260 rsType
.append("boolean");
2262 ::sax::Converter::convertBool(rsValue
, bTempValue
);
2267 case css::uno::TypeClass_FLOAT
:
2268 case css::uno::TypeClass_DOUBLE
:
2270 double fTempValue
= 0.0;
2271 if (rValue
>>= fTempValue
)
2273 rsType
.append("float");
2275 ::sax::Converter::convertDouble(rsValue
, fTempValue
);
2280 case css::uno::TypeClass_STRING
:
2282 OUString sTempValue
;
2283 if (rValue
>>= sTempValue
)
2285 rsType
.append("string");
2287 rsValue
.append(sTempValue
);
2292 case css::uno::TypeClass_STRUCT
:
2294 css::util::Date aDate
;
2295 css::util::Time aTime
;
2296 css::util::DateTime aDateTime
;
2298 if (rValue
>>= aDate
)
2300 rsType
.append("date");
2302 css::util::DateTime aTempValue
;
2303 aTempValue
.Day
= aDate
.Day
;
2304 aTempValue
.Month
= aDate
.Month
;
2305 aTempValue
.Year
= aDate
.Year
;
2306 aTempValue
.NanoSeconds
= 0;
2307 aTempValue
.Seconds
= 0;
2308 aTempValue
.Minutes
= 0;
2309 aTempValue
.Hours
= 0;
2310 ::sax::Converter::convertDateTime(rsValue
, aTempValue
, nullptr);
2313 if (rValue
>>= aTime
)
2315 rsType
.append("time");
2317 css::util::Duration aTempValue
;
2318 aTempValue
.Days
= 0;
2319 aTempValue
.Months
= 0;
2320 aTempValue
.Years
= 0;
2321 aTempValue
.NanoSeconds
= aTime
.NanoSeconds
;
2322 aTempValue
.Seconds
= aTime
.Seconds
;
2323 aTempValue
.Minutes
= aTime
.Minutes
;
2324 aTempValue
.Hours
= aTime
.Hours
;
2325 ::sax::Converter::convertDuration(rsValue
, aTempValue
);
2328 if (rValue
>>= aDateTime
)
2330 rsType
.append("date");
2332 ::sax::Converter::convertDateTime(rsValue
, aDateTime
, nullptr);
2343 void Converter::convertBytesToHexBinary(OUStringBuffer
& rBuffer
, const void* pBytes
,
2346 rBuffer
.setLength(0);
2347 rBuffer
.ensureCapacity(nBytes
* 2);
2348 auto pChars
= static_cast<const unsigned char*>(pBytes
);
2349 for (sal_Int32 i
= 0; i
< nBytes
; ++i
)
2351 sal_Int32 c
= *pChars
++;
2353 rBuffer
.append('0');
2354 rBuffer
.append(c
, 16);
2360 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */