tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sax / source / tools / converter.cxx
blob61557575350b48a6886a1559627b2850a467773d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
27 #include <optional>
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>
40 #include <algorithm>
41 #include <map>
42 #include <string_view>
44 using namespace com::sun::star;
45 using namespace com::sun::star::uno;
46 using namespace com::sun::star::util;
49 namespace sax {
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);
72 namespace
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)
84 switch (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;
94 case MeasureUnit::MM:
95 return o3tl::Length::mm;
96 case MeasureUnit::CM:
97 return o3tl::Length::cm;
98 default:
99 SAL_WARN("sax", "unit not supported for length");
100 [[fallthrough]];
101 case MeasureUnit::INCH:
102 return o3tl::Length::in;
106 std::string_view Measure2UnitString(sal_Int16 nUnit)
108 switch (nUnit)
110 case MeasureUnit::TWIP:
111 return gpsPC; // ??
112 case MeasureUnit::POINT:
113 return gpsPT;
114 case MeasureUnit::MM_10TH:
115 case MeasureUnit::MM_100TH:
116 return {};
117 case MeasureUnit::MM:
118 return gpsMM;
119 case MeasureUnit::CM:
120 return gpsCM;
121 case MeasureUnit::INCH:
122 default:
123 return gpsINCH;
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)
140 if (rString.empty())
142 return std::nullopt;
145 switch (rtl::toAsciiLowerCase<sal_uInt32>(rString[0]))
147 case u'%':
148 return MeasureUnit::PERCENT;
150 case u'c':
151 if (wordEndsWith(rString.substr(1), "m"))
152 return MeasureUnit::CM;
153 break;
155 case u'e':
156 if (wordEndsWith(rString.substr(1), "m"))
157 return MeasureUnit::FONT_EM;
158 break;
160 case u'i':
161 if (wordEndsWith(rString.substr(1), "c"))
162 return MeasureUnit::FONT_CJK_ADVANCE;
163 if (wordEndsWith(rString.substr(1), "n"))
164 return MeasureUnit::INCH;
165 break;
167 case u'm':
168 if (wordEndsWith(rString.substr(1), "m"))
169 return MeasureUnit::MM;
170 break;
172 case u'p':
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;
179 break;
182 return std::nullopt;
185 /** parse measure string into double and measure unit*/
186 template <class V>
187 static bool lcl_parseMeasure(double& rValue, std::optional<sal_Int16>& rSourceUnit, bool& rNeg, const V& rString)
189 rValue = 0.0;
190 rSourceUnit.reset();
191 rNeg = false;
193 bool bNeg = false;
194 double nVal = 0;
196 sal_Int32 nPos = 0;
197 sal_Int32 const nLen = rString.size();
199 // skip white space
200 while( (nPos < nLen) && (rString[nPos] <= ' ') )
201 nPos++;
203 if( nPos < nLen && '-' == rString[nPos] )
205 bNeg = true;
206 nPos++;
209 // get number
210 while( nPos < nLen &&
211 '0' <= rString[nPos] &&
212 '9' >= rString[nPos] )
214 // TODO: check overflow!
215 nVal *= 10;
216 nVal += (rString[nPos] - '0');
217 nPos++;
219 if( nPos < nLen && '.' == rString[nPos] )
221 nPos++;
222 double nDiv = 1.;
224 while( nPos < nLen &&
225 '0' <= rString[nPos] &&
226 '9' >= rString[nPos] )
228 // TODO: check overflow!
229 nDiv *= 10;
230 nVal += ( static_cast<double>(rString[nPos] - '0') / nDiv );
231 nPos++;
235 // skip white space
236 while( (nPos < nLen) && (rString[nPos] <= ' ') )
237 nPos++;
239 if (nPos < nLen)
241 // Parse unit from the tail
242 auto nUnit = lcl_parseMeasureUnit(rString.substr(nPos));
243 if (!nUnit.has_value())
245 return false;
248 rSourceUnit = nUnit.value();
251 rValue = nVal;
252 rNeg = bNeg;
254 return true;
257 /** convert string to measure using optional min and max values*/
258 template <class V>
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 */)
264 double nVal = 0.0;
265 std::optional<sal_Int16> nSourceUnit;
266 bool bNeg = false;
268 if (!lcl_parseMeasure(nVal, nSourceUnit, bNeg, rString))
270 return false;
273 if (nSourceUnit.has_value())
275 if( MeasureUnit::PERCENT == nTargetUnit )
277 if (MeasureUnit::PERCENT != nSourceUnit)
278 return false;
280 else if( MeasureUnit::PIXEL == nTargetUnit )
282 if (MeasureUnit::PIXEL != nSourceUnit)
283 return false;
285 else
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;
299 break;
300 case MeasureUnit::INCH:
301 eFrom = o3tl::Length::in;
302 break;
303 case MeasureUnit::MM:
304 eFrom = o3tl::Length::mm;
305 break;
306 case MeasureUnit::POINT:
307 eFrom = o3tl::Length::pt;
308 break;
309 case MeasureUnit::PICA:
310 eFrom = o3tl::Length::pc;
311 break;
314 else if( MeasureUnit::MM_100TH == nTargetUnit || MeasureUnit::MM_10TH == nTargetUnit )
316 switch (nSourceUnit.value())
318 case MeasureUnit::CM:
319 eFrom = o3tl::Length::cm;
320 break;
321 case MeasureUnit::INCH:
322 eFrom = o3tl::Length::in;
323 break;
324 case MeasureUnit::MM:
325 eFrom = o3tl::Length::mm;
326 break;
327 case MeasureUnit::POINT:
328 eFrom = o3tl::Length::pt;
329 break;
330 case MeasureUnit::PICA:
331 eFrom = o3tl::Length::pc;
332 break;
333 case MeasureUnit::PIXEL:
334 eFrom = o3tl::Length::px;
335 break;
338 else if( MeasureUnit::POINT == nTargetUnit )
340 if (MeasureUnit::POINT == nSourceUnit)
341 eFrom = o3tl::Length::pt;
344 if (eFrom == o3tl::Length::invalid)
345 return false;
347 // TODO: check overflow
348 nVal = o3tl::convert(nVal, eFrom, Measure2O3tlUnit(nTargetUnit));
352 nVal += .5;
353 if( bNeg )
354 nVal = -nVal;
356 if( nVal <= static_cast<double>(nMin) )
357 rValue = nMin;
358 else if( nVal >= static_cast<double>(nMax) )
359 rValue = nMax;
360 else
361 rValue = static_cast<sal_Int32>(nVal);
363 return true;
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,
389 sal_Int32 nMeasure,
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( '%' );
401 return;
403 sal_Int64 nValue(nMeasure); // extend to 64-bit first to avoid overflow
404 // the sign is processed separately
405 if (nValue < 0)
407 nValue = -nValue;
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" );
423 [[fallthrough]];
424 case MeasureUnit::MM:
425 eTo = o3tl::Length::mm;
426 nFac = 100;
427 psUnit = gpsMM;
428 break;
430 case MeasureUnit::CM:
431 eTo = o3tl::Length::cm;
432 nFac = 1000;
433 psUnit = gpsCM;
434 break;
436 case MeasureUnit::POINT:
437 eTo = o3tl::Length::pt;
438 nFac = 100;
439 psUnit = gpsPT;
440 break;
442 case MeasureUnit::INCH:
443 default:
444 OSL_ENSURE( MeasureUnit::INCH == nTargetUnit,
445 "output unit not supported for twip values" );
446 nFac = 10000;
447 psUnit = gpsINCH;
448 break;
450 break;
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;
457 nFac = 1;
458 psUnit = gpsPT;
459 break;
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" );
471 [[fallthrough]];
472 case MeasureUnit::MM:
473 eTo = o3tl::Length::mm;
474 nFac = nFac2;
475 psUnit = gpsMM;
476 break;
478 case MeasureUnit::CM:
479 eTo = o3tl::Length::cm;
480 nFac = 10*nFac2;
481 psUnit = gpsCM;
482 break;
484 case MeasureUnit::POINT:
485 eTo = o3tl::Length::pt;
486 nFac = nFac2;
487 psUnit = gpsPT;
488 break;
490 case MeasureUnit::INCH:
491 default:
492 OSL_ENSURE( MeasureUnit::INCH == nTargetUnit,
493 "output unit not supported for 1/100mm values" );
494 nFac = 100*nFac2;
495 psUnit = gpsINCH;
496 break;
498 break;
500 default:
501 OSL_ENSURE(false, "sax::Converter::convertMeasure(): "
502 "source unit not supported");
503 break;
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)
514 nFac /= 10;
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)
527 bool bNeg = false;
528 bool bResult = lcl_parseMeasure(rValue, rValueUnit, bNeg, rString);
530 if (bNeg)
532 rValue = -rValue;
535 return bResult;
538 /** convert string to measure with unit*/
539 bool Converter::convertMeasureUnit(double& rValue, std::optional<sal_Int16>& rValueUnit,
540 std::string_view rString)
542 bool bNeg = false;
543 bool bResult = lcl_parseMeasure(rValue, rValueUnit, bNeg, rString);
545 if (bNeg)
547 rValue = -rValue;
550 return bResult;
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' )
634 return nChar - '0';
635 else if( nChar >= 'a' && nChar <= 'f' )
636 return nChar - 'a' + 10;
637 else if( nChar >= 'A' && nChar <= 'F' )
638 return nChar - 'A' + 10;
639 else
640 return 0;
643 /** convert string to rgb color */
644 template<typename V>
645 static bool lcl_convertColor( sal_Int32& rColor, V rValue )
647 if( rValue.size() != 7 || rValue[0] != '#' )
648 return false;
650 rColor = lcl_gethex( rValue[1] ) * 16 + lcl_gethex( rValue[2] );
651 rColor <<= 8;
653 rColor |= lcl_gethex( rValue[3] ) * 16 + lcl_gethex( rValue[4] );
654 rColor <<= 8;
656 rColor |= lcl_gethex( rValue[5] ) * 16 + lcl_gethex( rValue[6] );
658 return true;
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 )
698 rValue = 0;
699 sal_Int64 nNumber = 0;
700 bool bRet = convertNumber64(nNumber,aString,nMin,nMax);
701 if ( bRet )
702 rValue = static_cast<sal_Int32>(nNumber);
703 return bRet;
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 )
711 rValue = 0;
712 sal_Int64 nNumber = 0;
713 bool bRet = convertNumber64(nNumber,aString,nMin,nMax);
714 if ( bRet )
715 rValue = static_cast<sal_Int32>(nNumber);
716 return bRet;
719 /** convert string to 64-bit number with optional min and max values */
720 template<typename V>
721 static bool lcl_convertNumber64( sal_Int64& rValue,
722 V aString,
723 sal_Int64 nMin, sal_Int64 nMax )
725 sal_Int32 nPos = 0;
726 sal_Int32 const nLen = aString.size();
728 // skip white space
729 while( (nPos < nLen) && (aString[nPos] <= ' ') )
730 nPos++;
732 sal_Int32 nNumberStartPos = nPos;
734 if( nPos < nLen && '-' == aString[nPos] )
736 nPos++;
739 // get number
740 while( nPos < nLen &&
741 '0' <= aString[nPos] &&
742 '9' >= aString[nPos] )
744 nPos++;
747 rValue = toInt64_WithLength(aString.data() + nNumberStartPos, 10, nPos - nNumberStartPos);
749 if( rValue < nMin )
750 rValue = nMin;
751 else if( rValue > nMax )
752 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,
776 double fNumber,
777 bool bWriteUnits,
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);
785 if(bWriteUnits)
786 rBuffer.append('%');
788 else
790 OUStringBuffer sUnit;
791 double fFactor = GetConversionFactor(sUnit, nSourceUnit, nTargetUnit);
792 if(fFactor != 1.0)
793 fNumber *= fFactor;
794 ::rtl::math::doubleToUStringBuffer( rBuffer, fNumber, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true);
795 if(bWriteUnits)
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))
811 return false;
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)
818 rValue /= fFactor;
819 return true;
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))
827 return false;
829 OStringBuffer sUnit;
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)
834 rValue /= fFactor;
835 return true;
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*/',',
846 &eStatus, &pEnd);
847 if (pRest)
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;
856 const char* pEnd;
857 rValue = rtl_math_stringToDouble(rString.data(),
858 rString.data() + rString.size(),
859 /*cDecSeparator*/'.', /*cGroupSeparator*/',',
860 &eStatus, &pEnd);
861 if (pRest)
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));
874 else
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.
892 double fAngle(0.0);
893 std::u16string_view aRest;
894 bool bRet = ::sax::Converter::convertDouble(fAngle, rString, &aRest);
895 if (bRet)
897 if (aRest == u"deg")
898 fAngle *= 10.0;
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.
905 if (!aRest.empty())
907 // Wrong unit. Don't change rAngle, rely on callers checking boolean return
908 return false;
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);
916 return bRet;
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.
929 double fAngle(0.0);
930 std::string_view aRest;
931 bool bRet = ::sax::Converter::convertDouble(fAngle, rString, &aRest);
932 if (bRet)
934 if (aRest == "deg")
935 fAngle *= 10.0;
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.
942 if (!aRest.empty())
944 // Wrong unit. Don't change rAngle, rely on callers checking boolean return
945 return false;
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);
953 return bRet;
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);
965 if (bRet)
967 //degrees
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())
974 // Wrong unit
975 rAngle = 0;
976 return false;
978 // degrees in range [0..360]
979 rAngle = basegfx::snapToZeroRange(rAngle, 360.0);
981 return bRet;
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);
993 if (bRet)
995 // degrees
996 if (aRest == "grad")
997 rAngle *= 0.9; // 360deg = 400grad
998 else if (aRest == "rad")
999 rAngle = basegfx::rad2deg(rAngle);
1000 else if (aRest != "deg" && !aRest.empty())
1002 // Wrong unit
1003 rAngle = 0;
1004 return false;
1006 // degrees in range [0..360]
1007 rAngle = basegfx::snapToZeroRange(rAngle, 360.0);
1009 return bRet;
1012 /** convert double to ISO "duration" string; negative durations allowed */
1013 void Converter::convertDuration(OUStringBuffer& rBuffer,
1014 const double fTime)
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
1020 if (fValue < 0.0)
1022 rBuffer.append('-');
1023 fValue = - fValue;
1026 rBuffer.append( "PT" );
1027 fValue *= 24;
1028 double fHoursValue = ::rtl::math::approxFloor (fValue);
1029 fValue -= fHoursValue;
1030 fValue *= 60;
1031 double fMinsValue = ::rtl::math::approxFloor (fValue);
1032 fValue -= fMinsValue;
1033 fValue *= 60;
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);
1039 else
1040 fNanoSecsValue = 0.0;
1042 if (fNanoSecsValue == 1.0)
1044 fNanoSecsValue = 0.0;
1045 fSecsValue += 1.0;
1047 if (fSecsValue >= 60.0)
1049 fSecsValue -= 60.0;
1050 fMinsValue += 1.0;
1052 if (fMinsValue >= 60.0)
1054 fMinsValue -= 60.0;
1055 fHoursValue += 1.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, '.',
1073 true));
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;
1092 pStr++;
1095 if ( *pStr != 'P' && *pStr != 'p' ) // duration must start with "P"
1096 return false;
1097 pStr++;
1099 OUStringBuffer sDoubleStr;
1100 bool bSuccess = true;
1101 bool bDone = false;
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++);
1113 if ( !c ) // end
1114 bDone = true;
1115 else if ( '0' <= c && '9' >= c )
1117 if ( nTemp >= SAL_MAX_INT32 / 10 )
1118 bSuccess = false;
1119 else
1121 if ( !bIsFraction )
1123 nTemp *= 10;
1124 nTemp += (c - u'0');
1126 else
1128 sDoubleStr.append(c);
1132 else if ( bTimePart )
1134 if ( c == 'H' || c == 'h' )
1136 nHours = nTemp;
1137 nTemp = 0;
1139 else if ( c == 'M' || c == 'm')
1141 nMins = nTemp;
1142 nTemp = 0;
1144 else if ( (c == ',') || (c == '.') )
1146 nSecs = nTemp;
1147 nTemp = 0;
1148 bIsFraction = true;
1149 sDoubleStr = "0.";
1151 else if ( c == 'S' || c == 's' )
1153 if ( !bIsFraction )
1155 nSecs = nTemp;
1156 nTemp = 0;
1157 sDoubleStr = "0.0";
1160 else
1161 bSuccess = false; // invalid character
1163 else
1165 if ( c == 'T' || c == 't' ) // "T" starts time part
1166 bTimePart = true;
1167 else if ( c == 'D' || c == 'd')
1169 nDays = nTemp;
1170 nTemp = 0;
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");
1177 bSuccess = false;
1179 else
1180 bSuccess = false; // invalid character
1184 if ( bSuccess )
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;
1200 rfTime = fTempTime;
1202 return bSuccess;
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');
1237 if (rDuration.Days)
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;
1267 ostr.fill('0');
1268 ostr.width(9);
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');
1283 namespace {
1285 enum Result { R_NOTHING, R_OVERFLOW, R_SUCCESS };
1289 template <typename V>
1290 static Result
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'))
1300 break;
1301 ++nPos;
1304 if (io_rnPos == nPos) // read something?
1306 o_rNumber = -1;
1307 return R_NOTHING;
1310 const sal_Int64 nTemp = toInt64_WithLength(rString.data() + io_rnPos, 10, nPos - io_rnPos);
1312 const bool bOverflow = (nTemp >= SAL_MAX_INT32);
1314 io_rnPos = nPos;
1315 o_rNumber = nTemp;
1316 return bOverflow ? R_OVERFLOW : R_SUCCESS;
1319 template<typename V>
1320 static Result
1321 readUnsignedNumberMaxDigits(int maxDigits,
1322 V rString, size_t & io_rnPos,
1323 sal_Int32 & o_rNumber)
1325 bool bOverflow(false);
1326 sal_Int64 nTemp(0);
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'))
1335 if (maxDigits > 0)
1337 nTemp *= 10;
1338 nTemp += (c - u'0');
1339 if (nTemp >= SAL_MAX_INT32)
1341 bOverflow = true;
1343 --maxDigits;
1346 else
1348 break;
1350 ++nPos;
1353 if (io_rnPos == nPos) // read something?
1355 o_rNumber = -1;
1356 return R_NOTHING;
1359 io_rnPos = nPos;
1360 o_rNumber = nTemp;
1361 return bOverflow ? R_OVERFLOW : R_SUCCESS;
1364 template<typename V>
1365 static bool
1366 readDurationT(V rString, size_t & io_rnPos)
1368 if ((io_rnPos < rString.size()) &&
1369 (rString[io_rnPos] == 'T' || rString[io_rnPos] == 't'))
1371 ++io_rnPos;
1372 return true;
1374 return false;
1377 template<typename V>
1378 static bool
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])
1387 ++io_rnPos;
1388 if (-1 != io_rnTemp)
1390 o_rnTarget = io_rnTemp;
1391 io_rnTemp = -1;
1392 if (!io_rbTimePart)
1394 io_rbTimePart = readDurationT(rString, io_rnPos);
1396 return (R_OVERFLOW !=
1397 readUnsignedNumber(rString, io_rnPos, io_rnTemp));
1399 else
1401 return false;
1405 return true;
1408 template <typename V>
1409 static bool convertDurationHelper(util::Duration& rDuration, V string)
1411 size_t nPos(0);
1413 bool bIsNegativeDuration(false);
1414 if (!string.empty() && ('-' == string[0]))
1416 bIsNegativeDuration = true;
1417 ++nPos;
1420 if (nPos < string.size()
1421 && string[nPos] != 'P' && string[nPos] != 'p') // duration must start with "P"
1423 return false;
1426 ++nPos;
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);
1434 sal_Int32 nDays(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,
1446 nYears, 'y', 'Y');
1449 if (!bTimePart && bSuccess)
1451 bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart,
1452 nMonths, 'm', 'M');
1455 if (!bTimePart && bSuccess)
1457 bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart,
1458 nDays, 'd', 'D');
1461 if (bTimePart)
1463 if (-1 == nTemp) // a 'T' must be followed by a component
1465 bSuccess = false;
1468 if (bSuccess)
1470 bSuccess = readDurationComponent(string, nPos, nTemp, bTimePart,
1471 nHours, 'h', 'H');
1474 if (bSuccess)
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] == ',')
1486 ++nPos;
1487 if (-1 != nTemp)
1489 nSeconds = nTemp;
1490 nTemp = -1;
1491 const sal_Int32 nStart(nPos);
1492 bSuccess = readUnsignedNumberMaxDigits(9, string, nPos, nTemp) == R_SUCCESS;
1493 if ((nPos < string.size()) && bSuccess)
1495 if (-1 != nTemp)
1497 nNanoSeconds = nTemp;
1498 sal_Int32 nDigits = nPos - nStart;
1499 assert(nDigits >= 0);
1500 for (; nDigits < 9; ++nDigits)
1502 nNanoSeconds *= 10;
1504 nTemp=-1;
1505 if ('S' == string[nPos] || 's' == string[nPos])
1507 ++nPos;
1509 else
1511 bSuccess = false;
1514 else
1516 bSuccess = false;
1520 else
1522 bSuccess = false;
1525 else if ('S' == string[nPos] || 's' == string[nPos])
1527 ++nPos;
1528 if (-1 != nTemp)
1530 nSeconds = nTemp;
1531 nTemp = -1;
1533 else
1535 bSuccess = false;
1541 if (nPos != string.size()) // string not processed completely?
1543 bSuccess = false;
1546 if (nTemp != -1) // unprocessed number?
1548 bSuccess = false;
1551 if (bSuccess)
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;
1563 return bSuccess;
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));
1580 static void
1581 lcl_AppendTimezone(OUStringBuffer & i_rBuffer, int const nOffset)
1583 if (0 == nOffset)
1585 i_rBuffer.append('Z');
1587 else
1589 if (0 < nOffset)
1591 i_rBuffer.append('+');
1593 else
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");
1601 if (nHours < 10)
1603 i_rBuffer.append('0');
1605 i_rBuffer.append(nHours);
1606 i_rBuffer.append(':');
1607 if (nMinutes < 10)
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;
1646 ostr.fill('0');
1647 ostr.width(9);
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);
1680 else
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
1700 if (nYear < 1000) {
1701 i_rBuffer.append(zero);
1703 if (nYear < 100) {
1704 i_rBuffer.append(zero);
1706 if (nYear < 10) {
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 ||
1722 i_bAddTimeIf0AM )
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 )
1735 bool isDateTime;
1736 return parseDateOrDateTime(nullptr, rDateTime, isDateTime, nullptr,
1737 rString);
1740 /** convert ISO "date" or "dateTime" string to util::DateTime */
1741 bool Converter::parseDateTime( util::DateTime& rDateTime,
1742 std::string_view rString )
1744 bool isDateTime;
1745 return parseDateOrDateTime(nullptr, rDateTime, isDateTime, nullptr,
1746 rString);
1749 static bool lcl_isLeapYear(const sal_uInt32 nYear)
1751 return ((nYear % 4) == 0)
1752 && (((nYear % 100) != 0) || ((nYear % 400) == 0));
1755 static sal_uInt16
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))
1763 return 29;
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)
1781 o_rMinutes -= 60;
1782 ++nOffsetHours;
1784 o_rHours += nOffsetHours;
1785 if (o_rHours < 24)
1787 return;
1789 sal_Int16 nDayAdd(0);
1790 while (24 <= o_rHours)
1792 o_rHours -= 24;
1793 ++nDayAdd;
1795 if (o_rDay == 0)
1797 return; // handle time without date - don't adjust what isn't there
1799 o_rDay += nDayAdd;
1800 sal_Int16 const nDaysInMonth(lcl_MaxDaysPerMonth(o_rMonth, o_rYear));
1801 if (o_rDay <= nDaysInMonth)
1803 return;
1805 o_rDay -= nDaysInMonth;
1806 ++o_rMonth;
1807 if (o_rMonth <= 12)
1809 return;
1811 o_rMonth = 1;
1812 ++o_rYear; // works for negative year too
1814 else if (0 < nSourceOffset)
1816 // argh everything is unsigned
1817 if (o_rMinutes < nOffsetMinutes)
1819 o_rMinutes += 60;
1820 ++nOffsetHours;
1822 o_rMinutes -= nOffsetMinutes;
1823 sal_Int16 nDaySubtract(0);
1824 while (o_rHours < nOffsetHours)
1826 o_rHours += 24;
1827 ++nDaySubtract;
1829 o_rHours -= nOffsetHours;
1830 if (o_rDay == 0)
1832 return; // handle time without date - don't adjust what isn't there
1834 if (nDaySubtract < o_rDay)
1836 o_rDay -= nDaySubtract;
1837 return;
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;
1842 --o_rMonth;
1843 if (0 == o_rMonth)
1845 o_rMonth = 12;
1846 --o_rYear; // works for negative year too
1848 o_rDay -= nDaySubtract;
1852 template <typename V>
1853 static bool
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);
1859 sal_Int32 nTemp(0);
1860 if (R_SUCCESS != readUnsignedNumber<V>(rString, io_rnPos, nTemp))
1862 return false;
1864 const sal_Int32 nTokenLength(io_rnPos - nOldPos);
1865 if ((nTokenLength < nMinLength) ||
1866 (bExactLength && (nTokenLength > nMinLength)))
1868 return false; // bad length
1870 o_rnTarget = nTemp;
1871 return true;
1874 /** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
1875 template<typename V>
1876 static bool lcl_parseDate(
1877 bool & isNegative,
1878 sal_Int32 & nYear, sal_Int32 & nMonth, sal_Int32 & nDay,
1879 bool & bHaveTime,
1880 size_t & nPos,
1881 V string,
1882 bool const bIgnoreInvalidOrMissingDate)
1884 bool bSuccess = true;
1886 if (string.size() > nPos)
1888 if ('-' == string[nPos])
1890 isNegative = true;
1891 ++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
1908 bSuccess = false;
1910 if (bSuccess)
1912 ++nPos;
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
1924 bSuccess = false;
1926 if (bSuccess)
1928 ++nPos;
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
1946 bHaveTime = true;
1947 ++nPos;
1951 return bSuccess;
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,
1958 bool & rbDateTime,
1959 std::optional<sal_Int16> *const pTimeZoneOffset,
1960 V string,
1961 bool const bIgnoreInvalidOrMissingDate)
1963 bool bSuccess = true;
1965 string = o3tl::trim(string);
1967 bool isNegative(false);
1968 sal_Int32 nYear(0);
1969 sal_Int32 nMonth(0);
1970 sal_Int32 nDay(0);
1971 size_t nPos(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);
1982 else
1984 bHaveTime = true;
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
2000 bSuccess = false;
2002 if (bSuccess)
2004 ++nPos;
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
2012 bSuccess = false;
2014 if (bSuccess)
2016 ++nPos;
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
2024 ++nPos;
2025 const sal_Int32 nStart(nPos);
2026 sal_Int32 nTemp(0);
2027 if (R_NOTHING == readUnsignedNumberMaxDigits<V>(9, string, nPos, nTemp))
2029 bSuccess = false;
2031 if (bSuccess)
2033 sal_Int32 nDigits = std::min<sal_Int32>(nPos - nStart, 9);
2034 assert(nDigits > 0);
2035 for (; nDigits < 9; ++nDigits)
2037 nTemp *= 10;
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]);
2058 if ('+' == c)
2060 bHaveTimezone = true;
2061 bHaveTimezonePlus = true;
2062 ++nPos;
2064 else if ('-' == c)
2066 bHaveTimezone = true;
2067 bHaveTimezoneMinus = true;
2068 ++nPos;
2070 else if ('Z' == c || 'z' == c)
2072 bHaveTimezone = true;
2073 ++nPos;
2075 else
2077 bSuccess = false;
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
2090 bSuccess = false;
2092 if (bSuccess)
2094 ++nPos;
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?
2111 if (bSuccess)
2113 sal_Int16 const nTimezoneOffset = (bHaveTimezoneMinus ? -1 : +1)
2114 * ((nTimezoneHours * 60) + nTimezoneMinutes);
2115 if (!pDate || bHaveTime) // time is optional
2117 rDateTime.Year =
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);
2125 if (bHaveTimezone)
2127 if (pTimeZoneOffset)
2129 *pTimeZoneOffset = nTimezoneOffset;
2130 rDateTime.IsUTC = (0 == nTimezoneOffset);
2132 else
2134 lcl_ConvertToUTC(rDateTime.Year, rDateTime.Month,
2135 rDateTime.Day, rDateTime.Hours, rDateTime.Minutes,
2136 nTimezoneOffset);
2137 rDateTime.IsUTC = true;
2140 else
2142 if (pTimeZoneOffset)
2144 pTimeZoneOffset->reset();
2146 rDateTime.IsUTC = false;
2148 rbDateTime = bHaveTime;
2150 else
2152 pDate->Year =
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);
2156 if (bHaveTimezone)
2158 if (pTimeZoneOffset)
2160 *pTimeZoneOffset = nTimezoneOffset;
2162 else
2164 // a Date cannot be adjusted
2165 SAL_INFO("sax", "dropping timezone");
2168 else
2170 if (pTimeZoneOffset)
2172 pTimeZoneOffset->reset();
2175 rbDateTime = false;
2178 return bSuccess;
2181 /** convert ISO "time" or "dateTime" string to util::DateTime */
2182 bool Converter::parseTimeOrDateTime(
2183 util::DateTime & rDateTime,
2184 std::u16string_view rString)
2186 bool dummy;
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)
2196 bool dummy;
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,
2204 bool & rbDateTime,
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,
2215 bool & rbDateTime,
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,
2226 sal_Int32 nPos )
2228 sal_Unicode cQuote = 0;
2229 sal_Int32 nLen = rStr.size();
2230 for( ; nPos < nLen; nPos++ )
2232 sal_Unicode c = rStr[nPos];
2233 switch( c )
2235 case u'\'':
2236 if( 0 == cQuote )
2237 cQuote = c;
2238 else if( '\'' == cQuote )
2239 cQuote = 0;
2240 break;
2242 case u'"':
2243 if( 0 == cQuote )
2244 cQuote = c;
2245 else if( '\"' == cQuote )
2246 cQuote = 0;
2247 break;
2249 case u',':
2250 if( 0 == cQuote )
2251 return nPos;
2252 break;
2256 return -1;
2259 double Converter::GetConversionFactor(OUStringBuffer& rUnit, sal_Int16 nSourceUnit, sal_Int16 nTargetUnit)
2261 double fRetval(1.0);
2262 rUnit.setLength(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());
2275 return fRetval;
2278 double Converter::GetConversionFactor(OStringBuffer& rUnit, sal_Int16 nSourceUnit, sal_Int16 nTargetUnit)
2280 double fRetval(1.0);
2281 rUnit.setLength(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());
2294 return fRetval;
2297 template<typename V>
2298 static sal_Int16 lcl_GetUnitFromString(V rString, sal_Int16 nDefaultUnit)
2300 sal_Int32 nPos = 0;
2301 sal_Int32 nLen = rString.size();
2302 sal_Int16 nRetUnit = nDefaultUnit;
2304 // skip white space
2305 while( nPos < nLen && ' ' == rString[nPos] )
2306 nPos++;
2308 // skip negative
2309 if( nPos < nLen && '-' == rString[nPos] )
2310 nPos++;
2312 // skip number
2313 while( nPos < nLen && '0' <= rString[nPos] && '9' >= rString[nPos] )
2314 nPos++;
2316 if( nPos < nLen && '.' == rString[nPos] )
2318 nPos++;
2319 while( nPos < nLen && '0' <= rString[nPos] && '9' >= rString[nPos] )
2320 nPos++;
2323 // skip white space
2324 while( nPos < nLen && ' ' == rString[nPos] )
2325 nPos++;
2327 if( nPos < nLen )
2329 switch(rString[nPos])
2331 case '%' :
2333 nRetUnit = MeasureUnit::PERCENT;
2334 break;
2336 case 'c':
2337 case 'C':
2339 if(nPos+1 < nLen && (rString[nPos+1] == 'm'
2340 || rString[nPos+1] == 'M'))
2341 nRetUnit = MeasureUnit::CM;
2342 break;
2344 case 'e':
2345 case 'E':
2347 // CSS1_EMS or CSS1_EMX later
2348 break;
2350 case 'i':
2351 case 'I':
2353 if(nPos+1 < nLen && (rString[nPos+1] == 'n'
2354 || rString[nPos+1] == 'N'))
2355 nRetUnit = MeasureUnit::INCH;
2356 break;
2358 case 'm':
2359 case 'M':
2361 if(nPos+1 < nLen && (rString[nPos+1] == 'm'
2362 || rString[nPos+1] == 'M'))
2363 nRetUnit = MeasureUnit::MM;
2364 break;
2366 case 'p':
2367 case 'P':
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;
2375 break;
2380 return nRetUnit;
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");
2413 bConverted = true;
2414 rsValue.append(nTempValue);
2417 break;
2419 case css::uno::TypeClass_BOOLEAN :
2421 bool bTempValue = false;
2422 if (rValue >>= bTempValue)
2424 rsType.append("boolean");
2425 bConverted = true;
2426 ::sax::Converter::convertBool(rsValue, bTempValue);
2429 break;
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");
2438 bConverted = true;
2439 ::sax::Converter::convertDouble(rsValue, fTempValue);
2442 break;
2444 case css::uno::TypeClass_STRING :
2446 OUString sTempValue;
2447 if (rValue >>= sTempValue)
2449 rsType.append("string");
2450 bConverted = true;
2451 rsValue.append(sTempValue);
2454 break;
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");
2465 bConverted = true;
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);
2476 else
2477 if (rValue >>= aTime)
2479 rsType.append("time");
2480 bConverted = true;
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);
2491 else
2492 if (rValue >>= aDateTime)
2494 rsType.append("date");
2495 bConverted = true;
2496 ::sax::Converter::convertDateTime(rsValue, aDateTime, nullptr);
2499 break;
2500 default:
2501 break;
2504 return bConverted;
2507 void Converter::convertBytesToHexBinary(OUStringBuffer& rBuffer, const void* pBytes,
2508 sal_Int32 nBytes)
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++;
2516 if (c < 16)
2517 rBuffer.append('0');
2518 rBuffer.append(c, 16);
2524 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */