diff --git a/include/sax/tools/converter.hxx b/include/sax/tools/converter.hxx
index bb97acc595ac..e6568faffbd6 100644
--- a/include/sax/tools/converter.hxx
+++ b/include/sax/tools/converter.hxx
@@ -210,6 +210,14 @@ public:
static bool convert10thDegAngle(sal_Int16& rAngle, std::string_view rString,
bool isWrongOOo10thDegAngle);
+ /** convert SVG angle to number, in 1/nFactor of degrees, range [0..nFactor*360[ */
+ static bool convertAngle(double& rAngle, std::u16string_view rString,
+ const sal_uInt16& nFactor = 1);
+
+ /** convert SVG angle to number, in 1/nFactor of degrees, range [0..nFactor*360[ */
+ static bool convertAngle(double& rAngle, std::string_view rString,
+ const sal_uInt16& nFactor = 1);
+
/** convert double to XMLSchema-2 "duration" string; negative durations allowed */
static void convertDuration(OUStringBuffer& rBuffer,
const double fTime);
diff --git a/sax/Library_sax.mk b/sax/Library_sax.mk
index f65fcf05b669..7188041f1bf4 100644
--- a/sax/Library_sax.mk
+++ b/sax/Library_sax.mk
@@ -26,6 +26,7 @@ $(eval $(call gb_Library_use_externals,sax,\
))
$(eval $(call gb_Library_use_libraries,sax,\
+ basegfx \
comphelper \
cppu \
cppuhelper \
diff --git a/sax/source/tools/converter.cxx b/sax/source/tools/converter.cxx
index bc2342d5b508..204033a7e64a 100644
--- a/sax/source/tools/converter.cxx
+++ b/sax/source/tools/converter.cxx
@@ -802,6 +802,56 @@ bool Converter::convert10thDegAngle(sal_Int16& rAngle, std::string_view rString,
return bRet;
}
+/** convert SVG angle to number, in 1/nFactor of degrees, range [0..nFactor*360[ */
+bool Converter::convertAngle(double& rAngle, std::u16string_view rString, const sal_uInt16& nFactor)
+{
+ // 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. LO uses angles
+ // in degrees, 1/10 of degrees and 1/100 of degrees in various data types.
+ // This method converts ODF 'angle' to double considering nFactor and normalizes it to range
+ // [0..nFactor*360[. Further type converting and range restriction are done by the caller.
+ bool bRet = ::sax::Converter::convertDouble(rAngle, rString);
+ if (bRet)
+ {
+ //degrees
+ if (std::u16string_view::npos != rString.find(u"grad"))
+ rAngle *= 0.9; // 360deg = 400grad
+ else if (std::u16string_view::npos != rString.find(u"rad"))
+ rAngle = basegfx::rad2deg(rAngle);
+ // 1/nFactor of degrees in range [0..nFactor*360]
+ if (nFactor > 0)
+ rAngle = basegfx::snapToZeroRange(rAngle * nFactor, nFactor * 360.0);
+ else
+ return false;
+ }
+ return bRet;
+}
+
+/** convert SVG angle to number, in 1/nFactor of degrees, range [0..nFactor*360[ */
+bool Converter::convertAngle(double& rAngle, std::string_view rString, const sal_uInt16& nFactor)
+{
+ // 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. LO uses angles
+ // in degrees, 1/10 of degrees and 1/100 of degrees in various data types.
+ // This method converts ODF 'angle' to double considering nFactor and normalizes it to range
+ // [0..nFactor*360[. Further type converting and range restriction are done by the caller.
+ bool bRet = ::sax::Converter::convertDouble(rAngle, rString);
+ if (bRet)
+ {
+ // degrees
+ if (std::u16string_view::npos != rString.find("grad"))
+ rAngle *= 0.9; // 360deg = 400grad
+ else if (std::u16string_view::npos != rString.find("rad"))
+ rAngle = basegfx::rad2deg(rAngle);
+ // 1/nFactor of degrees in range [0..nFactor*360]
+ if (nFactor > 0)
+ rAngle = basegfx::snapToZeroRange(rAngle * nFactor, nFactor * 360.0);
+ else
+ return false;
+ }
+ return bRet;
+}
+
/** convert double to ISO "duration" string; negative durations allowed */
void Converter::convertDuration(OUStringBuffer& rBuffer,
const double fTime)
diff --git a/xmloff/qa/unit/data/tdf161483_CircleStartEndAngle.fodg b/xmloff/qa/unit/data/tdf161483_CircleStartEndAngle.fodg
new file mode 100644
index 000000000000..66b815322be4
--- /dev/null
+++ b/xmloff/qa/unit/data/tdf161483_CircleStartEndAngle.fodg
@@ -0,0 +1,397 @@
+
+
+
+
+ LOmyBuild/25.2.0.0.alpha0$Windows_X86_64 LibreOffice_project/5a7283a0eb880c5273ea48b0d1a6f881c4297b1a
+
+ 24x16
+ Regina Henschel
+ 2024-06-23T16:09:12
+ Regina Henschel
+ 2024-06-23T16:11:32
+
+
+
+
+ -1494
+ -349
+ 24728
+ 19000
+
+
+ view1
+ true
+ false
+ false
+ false
+ true
+ false
+ true
+ true
+ true
+ 1500
+ false
+ Hw==
+ Hw==
+
+ false
+ true
+ true
+ 0
+ 0
+ true
+ true
+ true
+ 4
+ 0
+ -244
+ -2551
+ 29363
+ 16609
+ 500
+ 500
+ 100
+ 100
+ 500
+ 5
+ 500
+ 5
+ false
+ 1500
+ true
+ false
+ false
+ false
+ false
+
+
+
+
+ 1251
+ 0
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ true
+ false
+ true
+ true
+ true
+ 0
+ $(inst)/share/palette%3B$(user)/config/standard.sod
+ $(inst)/share/palette%3B$(user)/config/html.soc
+ false
+ $(inst)/share/palette%3B$(user)/config/standard.soe
+ $(inst)/share/palette%3B$(user)/config/standard.soh
+ true
+ $(inst)/share/palette%3B$(user)/config/standard.sog
+ $(inst)/share/palette%3B$(user)/config/standard.sob
+ true
+ true
+ 4
+ 0
+ false
+ low-resolution
+ false
+ false
+ false
+ false
+ true
+ 2
+ 1
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/xmloff/qa/unit/data/tdf161483_ShadowSlant.fodg b/xmloff/qa/unit/data/tdf161483_ShadowSlant.fodg
new file mode 100644
index 000000000000..c012e03fc6b3
--- /dev/null
+++ b/xmloff/qa/unit/data/tdf161483_ShadowSlant.fodg
@@ -0,0 +1,419 @@
+
+
+
+
+ LibreOffice_7.6.7/7.6.7.2$Windows_X86_64 LibreOffice_project/dd47e4b30cb7dab30588d6c79c651f218165e3c5
+ 32x24
+ Regina Henschel
+ 2024-06-21T21:28:57
+ Regina Henschel
+ 2024-06-22T18:18:28.827000000
+
+ PT9M8S3
+
+
+ -3960
+ -2799
+ 36561
+ 24977
+
+
+ view1
+ true
+ false
+ false
+ true
+ true
+ false
+ false
+ true
+ true
+ 1500
+ false
+ Hw==
+ Hw==
+
+ false
+ true
+ true
+ 0
+ 0
+ true
+ true
+ true
+ 4
+ 0
+ -3960
+ -2799
+ 37760
+ 24821
+ 500
+ 500
+ 100
+ 100
+ 500
+ 5
+ 500
+ 5
+ false
+ 1500
+ true
+ false
+ false
+ false
+ false
+
+
+
+
+ 1250
+ 0
+ EPSON6FC99C (WP-4025 Series)
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ true
+ false
+ true
+ true
+ true
+ 0
+ $(inst)/share/palette%3B$(user)/config/standard.sod
+ $(inst)/share/palette/html.soc
+ false
+ $(inst)/share/palette%3B$(user)/config/standard.soe
+ $(inst)/share/palette%3B$(user)/config/standard.soh
+ true
+ $(inst)/share/palette%3B$(user)/config/standard.sog
+ $(inst)/share/palette%3B$(user)/config/standard.sob
+ true
+ true
+ 4
+ 0
+ false
+ low-resolution
+ false
+ false
+ false
+ false
+ true
+ 2
+ 1
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/xmloff/qa/unit/draw.cxx b/xmloff/qa/unit/draw.cxx
index bfa279ca120f..f81df002c4ae 100644
--- a/xmloff/qa/unit/draw.cxx
+++ b/xmloff/qa/unit/draw.cxx
@@ -879,6 +879,58 @@ CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTdf161327_HatchAngle)
}
}
+CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTdf161483_ShadowSlant)
+{
+ // Load document with four 3D-scenes, that differ in the draw:shadow-slant value
+ loadFromFile(u"tdf161483_ShadowSlant.fodg");
+
+ // The shadow-slant angle is given in file as
+ // [0] 36 unitless
+ // [1] 36deg,
+ // [2] 40grad,
+ // [3] 1.628318530717959rad
+ // The resulting angle should be 36 in all cases.
+ // Cases [1], [2] and [3] had angle 0 without fix.
+
+ constexpr sal_Int16 nExpectedAngle = 36; // D3DSceneShadowSlant has data type 'short'
+ for (size_t i = 0; i < 4; ++i)
+ {
+ uno::Reference xShape(getShape(i));
+ uno::Reference xShapeProps(xShape, uno::UNO_QUERY);
+ sal_Int16 nActualAngle;
+ xShapeProps->getPropertyValue(u"D3DSceneShadowSlant"_ustr) >>= nActualAngle;
+ CPPUNIT_ASSERT_EQUAL(nExpectedAngle, nActualAngle);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTdf161483_CircleStartEndAngle)
+{
+ // Load document with four 'Arc' shapes, which differ in the type of start and end angles
+ loadFromFile(u"tdf161483_CircleStartEndAngle.fodg");
+
+ // The start and end angles are given in file as
+ // [0] unitless: start 337.5 end 306
+ // [1] deg: start 337.5deg end 306deg
+ // [2] grad: start 375grad end 340grad
+ // [3] rad: start 5.89048622548086rad end 5.34070751110265rad
+ // The resulting angle should be 33750 and 30600 in all cases.
+
+ // CircleStartAngle and CircleEndAngle have data type 'long', meaning Degree100
+ constexpr sal_Int32 nExpectedStartAngle = 33750;
+ constexpr sal_Int32 nExpectedEndAngle = 30600;
+ for (size_t i = 0; i < 4; ++i)
+ {
+ uno::Reference xShape(getShape(i));
+ uno::Reference xShapeProps(xShape, uno::UNO_QUERY);
+ sal_Int32 nActualStartAngle;
+ xShapeProps->getPropertyValue(u"CircleStartAngle"_ustr) >>= nActualStartAngle;
+ CPPUNIT_ASSERT_EQUAL(nExpectedStartAngle, nActualStartAngle);
+ sal_Int32 nActualEndAngle;
+ xShapeProps->getPropertyValue(u"CircleEndAngle"_ustr) >>= nActualEndAngle;
+ CPPUNIT_ASSERT_EQUAL(nExpectedEndAngle, nActualEndAngle);
+ }
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmloff/source/draw/ximp3dscene.cxx b/xmloff/source/draw/ximp3dscene.cxx
index a137cc247f4e..8c1648be4490 100644
--- a/xmloff/source/draw/ximp3dscene.cxx
+++ b/xmloff/source/draw/ximp3dscene.cxx
@@ -284,7 +284,11 @@ void SdXML3DSceneAttributesHelper::processSceneAttribute( const sax_fastparser::
}
case XML_SHADOW_SLANT:
{
- ::sax::Converter::convertNumber(mnShadowSlant, aIter.toView());
+ double fAngle = 0.0;
+ if (::sax::Converter::convertAngle(fAngle, aIter.toView()))
+ mnShadowSlant = static_cast(basegfx::fround(fAngle));
+ else
+ mnShadowSlant = 0;
return;
}
case XML_SHADE_MODE:
diff --git a/xmloff/source/draw/ximpshap.cxx b/xmloff/source/draw/ximpshap.cxx
index 27e98821f19b..89bb474f928e 100644
--- a/xmloff/source/draw/ximpshap.cxx
+++ b/xmloff/source/draw/ximpshap.cxx
@@ -1157,15 +1157,15 @@ bool SdXMLEllipseShapeContext::processAttribute( const sax_fastparser::FastAttri
case XML_ELEMENT(DRAW, XML_START_ANGLE):
{
double dStartAngle;
- if (::sax::Converter::convertDouble( dStartAngle, aIter.toView() ))
- mnStartAngle = static_cast(dStartAngle * 100.0);
+ if (::sax::Converter::convertAngle( dStartAngle, aIter.toView(), 100))
+ mnStartAngle = static_cast(basegfx::fround(dStartAngle));
break;
}
case XML_ELEMENT(DRAW, XML_END_ANGLE):
{
double dEndAngle;
- if (::sax::Converter::convertDouble( dEndAngle, aIter.toView() ))
- mnEndAngle = static_cast(dEndAngle * 100.0);
+ if (::sax::Converter::convertAngle( dEndAngle, aIter.toView(), 100))
+ mnEndAngle = static_cast(basegfx::fround(dEndAngle));
break;
}
default: