/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
o3tl::Length Measure2O3tlUnit(sal_Int16 nUnit)
{ switch (nUnit)
{ case MeasureUnit::TWIP: return o3tl::Length::twip; case MeasureUnit::POINT: return o3tl::Length::pt; case MeasureUnit::MM_10TH: return o3tl::Length::mm10; case MeasureUnit::MM_100TH: return o3tl::Length::mm100; case MeasureUnit::MM: return o3tl::Length::mm; case MeasureUnit::CM: return o3tl::Length::cm; default:
SAL_WARN("sax", "unit not supported for length");
[[fallthrough]]; case MeasureUnit::INCH: return o3tl::Length::in;
}
}
std::string_view Measure2UnitString(sal_Int16 nUnit)
{ switch (nUnit)
{ case MeasureUnit::TWIP: return gpsPC; // ?? case MeasureUnit::POINT: return gpsPT; case MeasureUnit::MM_10TH: case MeasureUnit::MM_100TH: return {}; case MeasureUnit::MM: return gpsMM; case MeasureUnit::CM: return gpsCM; case MeasureUnit::INCH: default: return gpsINCH;
}
}
/** parse unit substring into measure unit*/ template <class V> static std::optional<sal_Int16> lcl_parseMeasureUnit(const V& rString)
{ if (rString.empty())
{ return std::nullopt;
}
switch (rtl::toAsciiLowerCase<sal_uInt32>(rString[0]))
{ case u'%': return MeasureUnit::PERCENT;
case u'c': if (wordEndsWith(rString.substr(1), "m")) return MeasureUnit::CM; break;
case u'e': if (wordEndsWith(rString.substr(1), "m")) return MeasureUnit::FONT_EM; break;
case u'i': if (wordEndsWith(rString.substr(1), "c")) return MeasureUnit::FONT_CJK_ADVANCE; if (wordEndsWith(rString.substr(1), "n")) return MeasureUnit::INCH; break;
case u'm': if (wordEndsWith(rString.substr(1), "m")) return MeasureUnit::MM; break;
case u'p': if (wordEndsWith(rString.substr(1), "c")) return MeasureUnit::PICA; if (wordEndsWith(rString.substr(1), "t")) return MeasureUnit::POINT; if (wordEndsWith(rString.substr(1), "x")) return MeasureUnit::PIXEL; break;
}
return;
}
sal_Int64 nValue(nMeasure); // extend to 64-bit first to avoid overflow // the sign is processed separately if (nValue < 0)
{
nValue = -nValue;
rBuffer.append( '-' );
}
o3tl::Length eFrom = o3tl::Length::in, eTo = o3tl::Length::in; int nFac = 100; // used to get specific number of decimals (2 by default)
std::string_view psUnit; switch( nSourceUnit )
{ case MeasureUnit::TWIP:
eFrom = o3tl::Length::twip; switch( nTargetUnit )
{ case MeasureUnit::MM_100TH: case MeasureUnit::MM_10TH:
OSL_ENSURE( MeasureUnit::INCH == nTargetUnit,"output unit not supported for twip values" );
[[fallthrough]]; case MeasureUnit::MM:
eTo = o3tl::Length::mm;
nFac = 100;
psUnit = gpsMM; break;
case MeasureUnit::CM:
eTo = o3tl::Length::cm;
nFac = 1000;
psUnit = gpsCM; break;
case MeasureUnit::POINT:
eTo = o3tl::Length::pt;
nFac = 100;
psUnit = gpsPT; break;
case MeasureUnit::INCH: default:
OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for twip values" );
nFac = 10000;
psUnit = gpsINCH; break;
} break;
case MeasureUnit::POINT: // 1pt = 1pt (exactly)
OSL_ENSURE( MeasureUnit::POINT == nTargetUnit, "output unit not supported for pt values" );
eFrom = eTo = o3tl::Length::pt;
nFac = 1;
psUnit = gpsPT; break; case MeasureUnit::MM_10TH: case MeasureUnit::MM_100TH:
{ int nFac2 = (MeasureUnit::MM_100TH == nSourceUnit) ? 100 : 10;
eFrom = Measure2O3tlUnit(nSourceUnit); switch( nTargetUnit )
{ case MeasureUnit::MM_100TH: case MeasureUnit::MM_10TH:
OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/100mm values" );
[[fallthrough]]; case MeasureUnit::MM:
eTo = o3tl::Length::mm;
nFac = nFac2;
psUnit = gpsMM; break;
case MeasureUnit::CM:
eTo = o3tl::Length::cm;
nFac = 10*nFac2;
psUnit = gpsCM; break;
case MeasureUnit::POINT:
eTo = o3tl::Length::pt;
nFac = nFac2;
psUnit = gpsPT; break;
case MeasureUnit::INCH: default:
OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/100mm values" );
nFac = 100*nFac2;
psUnit = gpsINCH; break;
} break;
} default:
OSL_ENSURE(false, "sax::Converter::convertMeasure(): " "source unit not supported"); break;
}
/** convert measure with given unit to string with given unit*/ void Converter::convertMeasureUnit(OUStringBuffer& rBuffer, double dValue,
std::optional<sal_Int16> nValueUnit)
{
::rtl::math::doubleToUStringBuffer(rBuffer, dValue, rtl_math_StringFormat_Automatic,
rtl_math_DecimalPlaces_Max, '.', true);
if (nValueUnit.has_value())
{ if (auto it = stConvertMeasureUnitStrMap.find(*nValueUnit);
it != stConvertMeasureUnitStrMap.end())
{
rBuffer.appendAscii(it->second.data(), it->second.length());
}
}
}
/** convert number, 10th of degrees with range [0..3600] to SVG angle */ void Converter::convert10thDegAngle(OUStringBuffer& rBuffer, sal_Int16 const nAngle, constbool isWrongOOo10thDegAngle)
{ if (isWrongOOo10thDegAngle)
{
rBuffer.append(static_cast<sal_Int32>(nAngle));
} else
{ double fAngle(double(nAngle) / 10.0);
::sax::Converter::convertDouble(rBuffer, fAngle);
rBuffer.append("deg");
}
}
/** convert SVG angle to number in 10th of degrees */ bool Converter::convert10thDegAngle(sal_Int16& rAngle, std::u16string_view rString, boolconst isWrongOOo10thDegAngle)
{ // ODF 1.1 leaves it undefined what the number means, but ODF 1.2 says it's // degrees, while OOo has historically used 10th of degrees :( // So import degrees when we see the "deg" suffix but continue with 10th of // degrees for now for the sake of existing OOo/LO documents, until the // new versions that can read "deg" suffix are widely deployed and we can // start to write the "deg" suffix. double fAngle(0.0);
std::u16string_view aRest; bool bRet = ::sax::Converter::convertDouble(fAngle, rString, &aRest); if (bRet)
{ if (aRest == u"deg")
fAngle *= 10.0; elseif (aRest == u"grad")
fAngle *= 9.0; // 360deg = 400grad elseif (aRest == u"rad")
fAngle = basegfx::rad2deg<10>(fAngle); else// no explicit unit
{ // isWrongOOo10thDegAngle = true: nothing to do here. Wrong, but backward compatible. if (!aRest.empty())
{ // Wrong unit. Don't change rAngle, rely on callers checking boolean return returnfalse;
} if (!isWrongOOo10thDegAngle)
fAngle *= 10.0; // conform to ODF 1.2 and newer
}
fAngle = std::clamp<double>(basegfx::fround(fAngle), SHRT_MIN, SHRT_MAX);
rAngle = static_cast<sal_Int16>(fAngle);
} return bRet;
}
/** convert SVG angle to number, 10th of degrees with range [0..3600] */ bool Converter::convert10thDegAngle(sal_Int16& rAngle, std::string_view rString, boolconst isWrongOOo10thDegAngle)
{ // ODF 1.1 leaves it undefined what the number means, but ODF 1.2 says it's // degrees, while OOo has historically used 10th of degrees :( // So import degrees when we see the "deg" suffix but continue with 10th of // degrees for now for the sake of existing OOo/LO documents, until the // new versions that can read "deg" suffix are widely deployed and we can // start to write the "deg" suffix. double fAngle(0.0);
std::string_view aRest; bool bRet = ::sax::Converter::convertDouble(fAngle, rString, &aRest); if (bRet)
{ if (aRest == "deg")
fAngle *= 10.0; elseif (aRest == "grad")
fAngle *= 9.0; // 360deg = 400grad elseif (aRest == "rad")
fAngle = basegfx::rad2deg<10>(fAngle); else// no explicit unit
{ // isWrongOOo10thDegAngle = true: nothing to do here. Wrong, but backward compatible. if (!aRest.empty())
{ // Wrong unit. Don't change rAngle, rely on callers checking boolean return returnfalse;
} if (!isWrongOOo10thDegAngle)
fAngle *= 10.0; // conform to ODF 1.2 and newer
}
fAngle = std::clamp<double>(basegfx::fround(fAngle), SHRT_MIN, SHRT_MAX);
rAngle = static_cast<sal_Int16>(fAngle);
} return bRet;
}
/** convert SVG angle to number, in degrees, range [0..360] */ bool Converter::convertAngle(double& rAngle, std::u16string_view rString)
{ // ODF uses in several places angles in data type 'angle' (18.3.1, ODF 1.3). That is a double // followed by unit identifier deg, grad or rad or a unitless value in degrees. // This method converts ODF 'angle' to double degrees and normalizes it to range // [0..360]. Further type converting and range restriction are done by the caller.
std::u16string_view aRest; bool bRet = ::sax::Converter::convertDouble(rAngle, rString, &aRest); if (bRet)
{ //degrees if (aRest == u"grad")
rAngle *= 0.9; // 360deg = 400grad elseif (aRest == u"rad")
rAngle = basegfx::rad2deg(rAngle); elseif (aRest != u"deg" && !aRest.empty())
{ // Wrong unit
rAngle = 0; returnfalse;
} // degrees in range [0..360]
rAngle = basegfx::snapToZeroRange(rAngle, 360.0);
} return bRet;
}
/** convert SVG angle to number, in degrees, range [0..360] */ bool Converter::convertAngle(double& rAngle, std::string_view rString)
{ // ODF uses in several places angles in data type 'angle' (18.3.1, ODF 1.3). That is a double // followed by unit identifier deg, grad or rad or a unitless value in degrees. // This method converts ODF 'angle' to double degrees and normalizes it to range // [0..360]. Further type converting and range restriction are done by the caller.
std::string_view aRest; bool bRet = ::sax::Converter::convertDouble(rAngle, rString, &aRest); if (bRet)
{ // degrees if (aRest == "grad")
rAngle *= 0.9; // 360deg = 400grad elseif (aRest == "rad")
rAngle = basegfx::rad2deg(rAngle); elseif (aRest != "deg" && !aRest.empty())
{ // Wrong unit
rAngle = 0; returnfalse;
} // degrees in range [0..360]
rAngle = basegfx::snapToZeroRange(rAngle, 360.0);
} return bRet;
}
// take care of negative durations as specified in: // XML Schema, W3C Working Draft 07 April 2000, section 3.2.6.1 if (fValue < 0.0)
{
rBuffer.append('-');
fValue = - fValue;
}
while ( bSuccess && !bDone )
{
sal_Unicode c = *(pStr++); if ( !c ) // end
bDone = true; elseif ( '0' <= c && '9' >= c )
{ if ( nTemp >= SAL_MAX_INT32 / 10 )
bSuccess = false; else
{ if ( !bIsFraction )
{
nTemp *= 10;
nTemp += (c - u'0');
} else
{
sDoubleStr.append(c);
}
}
} elseif ( bTimePart )
{ if ( c == 'H' || c == 'h' )
{
nHours = nTemp;
nTemp = 0;
} elseif ( c == 'M' || c == 'm')
{
nMins = nTemp;
nTemp = 0;
} elseif ( (c == ',') || (c == '.') )
{
nSecs = nTemp;
nTemp = 0;
bIsFraction = true;
sDoubleStr = "0.";
} elseif ( c == 'S' || c == 's' )
{ if ( !bIsFraction )
{
nSecs = nTemp;
nTemp = 0;
sDoubleStr = "0.0";
}
} else
bSuccess = false; // invalid character
} else
{ if ( c == 'T' || c == 't' ) // "T" starts time part
bTimePart = true; elseif ( c == 'D' || c == 'd')
{
nDays = nTemp;
nTemp = 0;
} elseif ( c == 'Y' || c == 'y' || c == 'M' || c == 'm' )
{ //! how many days is a year or month?
OSL_FAIL( "years or months in duration: not implemented");
bSuccess = false;
} else
bSuccess = false; // invalid character
}
}
if ( bSuccess )
{ // Calculate similar to ImpSvNumberInputScan::GetTimeRef: first, sum whole seconds, add // second fraction, and finally, divide. Produces less rounding errors than calculating // fractions of a day from seconds, minutes, hours separately, and then adding together. double seconds = nDays * tools::Time::secondPerDay + nHours * tools::Time::secondPerHour
+ nMins * tools::Time::secondPerMinute + nSecs
+ o3tl::toDouble(sDoubleStr); double fTempTime = seconds / tools::Time::secondPerDay;
/** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */ template<typename V> staticbool lcl_parseDate( bool & isNegative,
sal_Int32 & nYear, sal_Int32 & nMonth, sal_Int32 & nDay, bool & bHaveTime,
size_t & nPos,
V string, boolconst bIgnoreInvalidOrMissingDate)
{ bool bSuccess = true;
if (string.size() > nPos)
{ if ('-' == string[nPos])
{
isNegative = true;
++nPos;
}
}
{ // While W3C XMLSchema specifies years with a minimum of 4 digits, be // lenient in what we accept for years < 1000. One digit is acceptable // if the remainders match.
bSuccess = readDateTimeComponent<V>(string, nPos, nYear, 1, false); if (!bIgnoreInvalidOrMissingDate)
{
bSuccess &= (0 < nYear);
}
bSuccess &= (nYear <= (isNegative ? 32768 : 32767)); // Must fit into css::util::Date
bSuccess &= (nPos < string.size()); // not last token
} if (bSuccess && ('-' != string[nPos])) // separator
{
bSuccess = false;
} if (bSuccess)
{
++nPos;
bSuccess = readDateTimeComponent<V>(string, nPos, nMonth, 2, true); if (!bIgnoreInvalidOrMissingDate)
{
bSuccess &= (0 < nMonth);
}
bSuccess &= (nMonth <= 12);
bSuccess &= (nPos < string.size()); // not last token
} if (bSuccess && ('-' != string[nPos])) // separator
{
bSuccess = false;
} if (bSuccess)
{
++nPos;
bSuccess = readDateTimeComponent(string, nPos, nDay, 2, true); if (!bIgnoreInvalidOrMissingDate)
{
bSuccess &= (0 < nDay);
} if (nMonth > 0) // not possible to check if month was missing
{
sal_Int32 nMaxDay
= comphelper::date::getDaysInMonth(nMonth, isNegative ? -nYear : nYear);
bSuccess &= (nDay <= nMaxDay);
} else assert(bIgnoreInvalidOrMissingDate);
}
if (bSuccess && (nPos < string.size()))
{ if ('T' == string[nPos] || 't' == string[nPos]) // time separator
{
bHaveTime = true;
++nPos;
}
}
return bSuccess;
}
/** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */ template <typename V> staticbool lcl_parseDateTime(
util::Date *const pDate, util::DateTime & rDateTime, bool & rbDateTime,
std::optional<sal_Int16> *const pTimeZoneOffset,
V string, boolconst bIgnoreInvalidOrMissingDate)
{ bool bSuccess = true;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.