diff options
author | Mike Kaganski <mike.kaganski@collabora.com> | 2021-02-13 13:55:22 +0300 |
---|---|---|
committer | Mike Kaganski <mike.kaganski@collabora.com> | 2021-02-14 12:50:01 +0100 |
commit | cfff893b9c82843a90aac4ecdb3a3936721b74a0 (patch) | |
tree | 2859340e329ea6dbffe5ae9c7eba0f67a88c57af /sax | |
parent | 20305894243e24eb383ab9feefebf4a0e9f2644f (diff) |
Move unit conversion code to o3tl, and unify on that in more places
This also allows to easily add more units, both of length and for other
unit categories.
The conversion for "Line" unit (312 twip) is questionable. Corresponding
entries in aImplFactor in vcl/source/control/field.cxx were inconsistent
(45/11 in; 10/13 pc; 156/10 pt). They were added without explanation in
commit c85db626029fd8a5e0dfcb312937279df32339a0. I haven't found a spec
of the unit (https://en.wikipedia.org/wiki/Line_(unit) is not specific).
I used the definition based on "by pt", "by mm/100", "by char" (they all
were consistent); "by pc" seems inverted; "by twip" was half as much.
This accepted conversion makes unit test for tdf#79236 pass.
Change-Id: Iae5a21d915fa8e934a1f47f8ba9f6df03b79a9fd
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/110839
Tested-by: Mike Kaganski <mike.kaganski@collabora.com>
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Diffstat (limited to 'sax')
-rw-r--r-- | sax/source/tools/converter.cxx | 585 |
1 files changed, 113 insertions, 472 deletions
diff --git a/sax/source/tools/converter.cxx b/sax/source/tools/converter.cxx index 4f7c686a1aba..18731b45ac77 100644 --- a/sax/source/tools/converter.cxx +++ b/sax/source/tools/converter.cxx @@ -28,7 +28,9 @@ #include <rtl/ustrbuf.hxx> #include <rtl/math.hxx> +#include <rtl/character.hxx> #include <sal/log.hxx> +#include <o3tl/unit_conversion.hxx> #include <osl/diagnose.h> #include <tools/long.hxx> @@ -43,11 +45,11 @@ using namespace ::com::sun::star::i18n; namespace sax { -const char* const gpsMM = "mm"; -const char* const gpsCM = "cm"; -const char* const gpsPT = "pt"; -const char* const gpsINCH = "in"; -const char* const gpsPC = "pc"; +const std::string_view gpsMM = "mm"; +const std::string_view gpsCM = "cm"; +const std::string_view gpsPT = "pt"; +const std::string_view gpsINCH = "in"; +const std::string_view gpsPC = "pc"; const sal_Int8 XML_MAXDIGITSCOUNT_TIME = 14; @@ -60,6 +62,62 @@ static sal_Int64 toInt64_WithLength(const char * str, sal_Int16 radix, sal_Int32 return rtl_str_toInt64_WithLength(str, radix, nStrLength); } +namespace +{ +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; + } +} + +template <typename V> bool wordEndsWith(V string, std::string_view expected) +{ + V substr = string.substr(0, expected.size()); + return std::equal(substr.begin(), substr.end(), expected.begin(), expected.end(), + [](sal_uInt32 c1, sal_uInt32 c2) { return rtl::toAsciiLowerCase(c1) == c2; }) + && (string.size() == expected.size() || string[expected.size()] == ' '); +} + +} /** convert string to measure using optional min and max values*/ template<typename V> @@ -137,127 +195,70 @@ static bool lcl_convertMeasure( sal_Int32& rValue, OSL_ENSURE( MeasureUnit::TWIP == nTargetUnit || MeasureUnit::POINT == nTargetUnit || MeasureUnit::MM_100TH == nTargetUnit || MeasureUnit::MM_10TH == nTargetUnit || MeasureUnit::PIXEL == nTargetUnit, "unit is not supported"); - const char *aCmpsL[3] = { nullptr, nullptr, nullptr }; - const char *aCmpsU[3] = { nullptr, nullptr, nullptr }; - double aScales[3] = { 1., 1., 1. }; + + o3tl::Length eFrom = o3tl::Length::invalid; if( MeasureUnit::TWIP == nTargetUnit ) { - switch( rString[nPos] ) + switch (rtl::toAsciiLowerCase<sal_uInt32>(rString[nPos])) { case u'c': - case u'C': - aCmpsL[0] = "cm"; - aCmpsU[0] = "CM"; - aScales[0] = (72.*20.)/2.54; // twip + if (wordEndsWith(rString.substr(nPos + 1), "m")) + eFrom = o3tl::Length::cm; break; case u'i': - case u'I': - aCmpsL[0] = "in"; - aCmpsU[0] = "IN"; - aScales[0] = 72.*20.; // twip + if (wordEndsWith(rString.substr(nPos + 1), "n")) + eFrom = o3tl::Length::in; break; case u'm': - case u'M': - aCmpsL[0] = "mm"; - aCmpsU[0] = "MM"; - aScales[0] = (72.*20.)/25.4; // twip + if (wordEndsWith(rString.substr(nPos + 1), "m")) + eFrom = o3tl::Length::mm; break; case u'p': - case u'P': - aCmpsL[0] = "pt"; - aCmpsU[0] = "PT"; - aScales[0] = 20.; // twip - - aCmpsL[1] = "pc"; - aCmpsU[1] = "PC"; - aScales[1] = 12.*20.; // twip + if (wordEndsWith(rString.substr(nPos + 1), "t")) + eFrom = o3tl::Length::pt; + else if (wordEndsWith(rString.substr(nPos + 1), "c")) + eFrom = o3tl::Length::pc; break; } } else if( MeasureUnit::MM_100TH == nTargetUnit || MeasureUnit::MM_10TH == nTargetUnit ) { - double nScaleFactor = (MeasureUnit::MM_100TH == nTargetUnit) ? 100.0 : 10.0; - switch( rString[nPos] ) + switch (rtl::toAsciiLowerCase<sal_uInt32>(rString[nPos])) { case u'c': - case u'C': - aCmpsL[0] = "cm"; - aCmpsU[0] = "CM"; - aScales[0] = 10.0 * nScaleFactor; // mm/100 + if (wordEndsWith(rString.substr(nPos + 1), "m")) + eFrom = o3tl::Length::cm; break; case u'i': - case u'I': - aCmpsL[0] = "in"; - aCmpsU[0] = "IN"; - aScales[0] = 1000.*2.54; // mm/100 + if (wordEndsWith(rString.substr(nPos + 1), "n")) + eFrom = o3tl::Length::in; break; case u'm': - case u'M': - aCmpsL[0] = "mm"; - aCmpsU[0] = "MM"; - aScales[0] = 1.0 * nScaleFactor; // mm/100 + if (wordEndsWith(rString.substr(nPos + 1), "m")) + eFrom = o3tl::Length::mm; break; case u'p': - case u'P': - aCmpsL[0] = "pt"; - aCmpsU[0] = "PT"; - aScales[0] = (10.0 * nScaleFactor*2.54)/72.; // mm/100 - - aCmpsL[1] = "pc"; - aCmpsU[1] = "PC"; - aScales[1] = (10.0 * nScaleFactor*2.54)/12.; // mm/100 - - aCmpsL[2] = "px"; - aCmpsU[2] = "PX"; - aScales[2] = 0.28 * nScaleFactor; // mm/100 + if (wordEndsWith(rString.substr(nPos + 1), "t")) + eFrom = o3tl::Length::pt; + else if (wordEndsWith(rString.substr(nPos + 1), "c")) + eFrom = o3tl::Length::pc; + else if (wordEndsWith(rString.substr(nPos + 1), "x")) + eFrom = o3tl::Length::px; break; } } else if( MeasureUnit::POINT == nTargetUnit ) { - if( rString[nPos] == 'p' || rString[nPos] == 'P' ) - { - aCmpsL[0] = "pt"; - aCmpsU[0] = "PT"; - aScales[0] = 1; - } - } - - if( aCmpsL[0] == nullptr ) - return false; - - double nScale = 0.; - for( sal_uInt16 i= 0; i < 3; i++ ) - { - sal_Int32 nTmp = nPos; // come back to the initial position before each iteration - const char *pL = aCmpsL[i]; - if( pL ) - { - const char *pU = aCmpsU[i]; - while( nTmp < nLen && *pL ) - { - sal_Unicode c = rString[nTmp]; - if( c != *pL && c != *pU ) - break; - pL++; - pU++; - nTmp++; - } - if( !*pL && (nTmp == nLen || ' ' == rString[nTmp]) ) - { - nScale = aScales[i]; - break; - } - } + if (wordEndsWith(rString.substr(nPos), "pt")) + eFrom = o3tl::Length::pt; } - if( 0. == nScale ) + if (eFrom == o3tl::Length::invalid) return false; // TODO: check overflow - if( nScale != 1. ) - nVal *= nScale; + nVal = o3tl::convert(nVal, eFrom, Measure2O3tlUnit(nTargetUnit)); } } @@ -320,14 +321,13 @@ void Converter::convertMeasure( OUStringBuffer& rBuffer, rBuffer.append( '-' ); } - // The new length is (nVal * nMul)/(nDiv*nFac*10) - tools::Long nMul = 1000; - tools::Long nDiv = 1; - tools::Long nFac = 100; - const char* psUnit = nullptr; + 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: @@ -335,25 +335,19 @@ void Converter::convertMeasure( OUStringBuffer& rBuffer, OSL_ENSURE( MeasureUnit::INCH == nTargetUnit,"output unit not supported for twip values" ); [[fallthrough]]; case MeasureUnit::MM: - // 0.01mm = 0.57twip (exactly) - nMul = 25400; // 25.4 * 1000 - nDiv = 1440; // 72 * 20; + eTo = o3tl::Length::mm; nFac = 100; psUnit = gpsMM; break; case MeasureUnit::CM: - // 0.001cm = 0.57twip (exactly) - nMul = 25400; // 2.54 * 10000 - nDiv = 1440; // 72 * 20; + eTo = o3tl::Length::cm; nFac = 1000; psUnit = gpsCM; break; case MeasureUnit::POINT: - // 0.01pt = 0.2twip (exactly) - nMul = 1000; - nDiv = 20; + eTo = o3tl::Length::pt; nFac = 100; psUnit = gpsPT; break; @@ -362,9 +356,6 @@ void Converter::convertMeasure( OUStringBuffer& rBuffer, default: OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for twip values" ); - // 0.0001in = 0.144twip (exactly) - nMul = 100000; - nDiv = 1440; // 72 * 20; nFac = 10000; psUnit = gpsINCH; break; @@ -375,8 +366,7 @@ void Converter::convertMeasure( OUStringBuffer& rBuffer, // 1pt = 1pt (exactly) OSL_ENSURE( MeasureUnit::POINT == nTargetUnit, "output unit not supported for pt values" ); - nMul = 10; - nDiv = 1; + eFrom = eTo = o3tl::Length::pt; nFac = 1; psUnit = gpsPT; break; @@ -384,6 +374,7 @@ void Converter::convertMeasure( OUStringBuffer& rBuffer, case MeasureUnit::MM_100TH: { int nFac2 = (MeasureUnit::MM_100TH == nSourceUnit) ? 100 : 10; + eFrom = Measure2O3tlUnit(nSourceUnit); switch( nTargetUnit ) { case MeasureUnit::MM_100TH: @@ -392,25 +383,19 @@ void Converter::convertMeasure( OUStringBuffer& rBuffer, "output unit not supported for 1/100mm values" ); [[fallthrough]]; case MeasureUnit::MM: - // 0.01mm = 1 mm/100 (exactly) - nMul = 10; - nDiv = 1; + eTo = o3tl::Length::mm; nFac = nFac2; psUnit = gpsMM; break; case MeasureUnit::CM: - // 0.001mm = 1 mm/100 (exactly) - nMul = 10; - nDiv = 1; // 72 * 20; + eTo = o3tl::Length::cm; nFac = 10*nFac2; psUnit = gpsCM; break; case MeasureUnit::POINT: - // 0.01pt = 0.35 mm/100 (exactly) - nMul = 72000; - nDiv = 2540; + eTo = o3tl::Length::pt; nFac = nFac2; psUnit = gpsPT; break; @@ -419,9 +404,6 @@ void Converter::convertMeasure( OUStringBuffer& rBuffer, default: OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/100mm values" ); - // 0.0001in = 0.254 mm/100 (exactly) - nMul = 100000; - nDiv = 2540; nFac = 100*nFac2; psUnit = gpsINCH; break; @@ -434,11 +416,7 @@ void Converter::convertMeasure( OUStringBuffer& rBuffer, break; } - OSL_ENSURE(nValue <= SAL_MAX_INT64 / nMul, "convertMeasure: overflow"); - nValue *= nMul; - nValue /= nDiv; - nValue += 5; - nValue /= 10; + nValue = o3tl::convert(nValue * nFac, eFrom, eTo); rBuffer.append( static_cast<sal_Int64>(nValue / nFac) ); if (nFac > 1 && (nValue % nFac) != 0) @@ -451,8 +429,8 @@ void Converter::convertMeasure( OUStringBuffer& rBuffer, } } - if( psUnit ) - rBuffer.appendAscii( psUnit ); + if (psUnit.length() > 0) + rBuffer.appendAscii(psUnit.data(), psUnit.length()); } /** convert string to boolean */ @@ -2240,349 +2218,12 @@ double Converter::GetConversionFactor(OUStringBuffer& rUnit, sal_Int16 nSourceUn if(nSourceUnit != nTargetUnit) { - const char* psUnit = nullptr; - - switch(nSourceUnit) - { - case MeasureUnit::TWIP: - { - switch(nTargetUnit) - { - case MeasureUnit::MM_100TH: - { - // 0.01mm = 0.57twip (exactly) - fRetval = ((25400.0 / 1440.0) / 10.0); - break; - } - case MeasureUnit::MM_10TH: - { - // 0.01mm = 0.57twip (exactly) - fRetval = ((25400.0 / 1440.0) / 100.0); - break; - } - case MeasureUnit::MM: - { - // 0.01mm = 0.57twip (exactly) - fRetval = ((25400.0 / 1440.0) / 1000.0); - psUnit = gpsMM; - break; - } - case MeasureUnit::CM: - { - // 0.001cm = 0.57twip (exactly) - fRetval = ((25400.0 / 1440.0) / 10000.0); - psUnit = gpsCM; - break; - } - case MeasureUnit::POINT: - { - // 0.01pt = 0.2twip (exactly) - fRetval = ((1000.0 / 20.0) / 1000.0); - psUnit = gpsPT; - break; - } - case MeasureUnit::INCH: - default: - { - OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for twip values"); - // 0.0001in = 0.144twip (exactly) - fRetval = ((100000.0 / 1440.0) / 100000.0); - psUnit = gpsINCH; - break; - } - } - break; - } - case MeasureUnit::POINT: - { - switch(nTargetUnit) - { - case MeasureUnit::MM_100TH: - { - // 1mm = 72 / 25.4 pt (exactly) - fRetval = ( 2540.0 / 72.0 ); - break; - } - case MeasureUnit::MM_10TH: - { - // 1mm = 72 / 25.4 pt (exactly) - fRetval = ( 254.0 / 72.0 ); - break; - } - case MeasureUnit::MM: - { - // 1mm = 72 / 25.4 pt (exactly) - fRetval = ( 25.4 / 72.0 ); - psUnit = gpsMM; - break; - - } - case MeasureUnit::CM: - { - // 1cm = 72 / 2.54 pt (exactly) - fRetval = ( 2.54 / 72.0 ); - psUnit = gpsCM; - break; - } - case MeasureUnit::TWIP: - { - // 1twip = 72 / 1440 pt (exactly) - fRetval = 20.0; // 1440.0 / 72.0 - psUnit = gpsPC; - break; - } - case MeasureUnit::INCH: - default: - { - OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for pt values"); - // 1in = 72 pt (exactly) - fRetval = ( 1.0 / 72.0 ); - psUnit = gpsINCH; - break; - } - } - break; - } - case MeasureUnit::MM_10TH: - { - switch(nTargetUnit) - { - case MeasureUnit::MM_100TH: - { - fRetval = 10.0; - break; - } - case MeasureUnit::MM: - { - // 0.01mm = 1 mm/100 (exactly) - fRetval = ((10.0 / 1.0) / 100.0); - psUnit = gpsMM; - break; - } - case MeasureUnit::CM: - { - fRetval = ((10.0 / 1.0) / 1000.0); - psUnit = gpsCM; - break; - } - case MeasureUnit::POINT: - { - // 0.01pt = 0.35 mm/100 (exactly) - fRetval = ((72000.0 / 2540.0) / 100.0); - psUnit = gpsPT; - break; - } - case MeasureUnit::TWIP: - { - fRetval = ((20.0 * 72000.0 / 2540.0) / 100.0); - psUnit = gpsPC; - break; - } - case MeasureUnit::INCH: - default: - { - OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/10mm values"); - // 0.0001in = 0.254 mm/100 (exactly) - fRetval = ((100000.0 / 2540.0) / 10000.0); - psUnit = gpsINCH; - break; - } - } - break; - } - case MeasureUnit::MM_100TH: - { - switch(nTargetUnit) - { - case MeasureUnit::MM_10TH: - { - fRetval = ((10.0 / 1.0) / 100.0); - break; - } - case MeasureUnit::MM: - { - // 0.01mm = 1 mm/100 (exactly) - fRetval = ((10.0 / 1.0) / 1000.0); - psUnit = gpsMM; - break; - } - case MeasureUnit::CM: - { - fRetval = ((10.0 / 1.0) / 10000.0); - psUnit = gpsCM; - break; - } - case MeasureUnit::POINT: - { - // 0.01pt = 0.35 mm/100 (exactly) - fRetval = ((72000.0 / 2540.0) / 1000.0); - psUnit = gpsPT; - break; - } - case MeasureUnit::TWIP: - { - fRetval = ((20.0 * 72000.0 / 2540.0) / 1000.0); - psUnit = gpsPC; - break; - } - case MeasureUnit::INCH: - default: - { - OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for 1/100mm values"); - // 0.0001in = 0.254 mm/100 (exactly) - fRetval = ((100000.0 / 2540.0) / 100000.0); - psUnit = gpsINCH; - break; - } - } - break; - } - case MeasureUnit::MM: - { - switch(nTargetUnit) - { - case MeasureUnit::MM_100TH: - { - fRetval = 100.0; - break; - } - case MeasureUnit::MM_10TH: - { - fRetval = 10.0; - break; - } - case MeasureUnit::CM: - { - fRetval = 0.1; - psUnit = gpsCM; - break; - } - case MeasureUnit::POINT: - { - fRetval = 72.0 / (2.54 * 10); - psUnit = gpsPT; - break; - } - case MeasureUnit::TWIP: - { - fRetval = (20.0 * 72.0) / (2.54 * 10); - psUnit = gpsPC; - break; - } - case MeasureUnit::INCH: - default: - { - OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for cm values"); - fRetval = 1 / (2.54 * 10); - psUnit = gpsINCH; - break; - } - } - break; - } - case MeasureUnit::CM: - { - switch(nTargetUnit) - { - case MeasureUnit::MM_100TH: - { - fRetval = 1000.0; - break; - } - case MeasureUnit::MM_10TH: - { - fRetval = 100.0; - break; - } - case MeasureUnit::MM: - { - fRetval = 10.0; - psUnit = gpsMM; - break; - } - case MeasureUnit::CM: - { - break; - } - case MeasureUnit::POINT: - { - fRetval = 72.0 / 2.54; - psUnit = gpsPT; - break; - } - case MeasureUnit::TWIP: - { - fRetval = (20.0 * 72.0) / 2.54; - psUnit = gpsPC; - break; - } - case MeasureUnit::INCH: - default: - { - OSL_ENSURE( MeasureUnit::INCH == nTargetUnit, "output unit not supported for cm values"); - fRetval = 1 / 2.54; - psUnit = gpsINCH; - break; - } - } - break; - } - case MeasureUnit::INCH: - { - switch (nTargetUnit) - { - case MeasureUnit::MM_100TH: - { - fRetval = 2540; - break; - } - case MeasureUnit::MM_10TH: - { - fRetval = 254; - break; - } - case MeasureUnit::MM: - { - fRetval = 25.4; - psUnit = gpsMM; - break; - } - case MeasureUnit::CM: - { - fRetval = 2.54; - psUnit = gpsCM; - break; - } - case MeasureUnit::POINT: - { - fRetval = 72.0; - psUnit = gpsPT; - break; - } - case MeasureUnit::TWIP: - { - fRetval = 72.0 * 20.0; - psUnit = gpsPC; - break; - } - default: - { - OSL_FAIL("output unit not supported for in values"); - fRetval = 1; - psUnit = gpsINCH; - break; - } - } - break; - } - default: - OSL_ENSURE(false, "sax::Converter::GetConversionFactor(): " - "source unit not supported"); - break; - } + const o3tl::Length eFrom = Measure2O3tlUnit(nSourceUnit); + const o3tl::Length eTo = Measure2O3tlUnit(nTargetUnit); + fRetval = o3tl::convert(1.0, eFrom, eTo); - if( psUnit ) - rUnit.appendAscii( psUnit ); + if (const auto sUnit = Measure2UnitString(nTargetUnit); sUnit.size() > 0) + rUnit.appendAscii(sUnit.data(), sUnit.size()); } return fRetval; |