diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx index a5928223ff1c..5e9d069c3290 100644 --- a/oox/source/export/shapes.cxx +++ b/oox/source/export/shapes.cxx @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,7 @@ #include #include #include +#include using namespace ::css; using namespace ::css::beans; @@ -697,6 +699,21 @@ static sal_Int32 lcl_NormalizeAngle( sal_Int32 nAngle ) return nAngle < 0 ? ( nAngle + 360 ) : nAngle ; } +static sal_Int32 lcl_CircleAngle2CustomShapeEllipseAngleOOX(const sal_Int32 nInternAngle, const sal_Int32 nWidth, const sal_Int32 nHeight) +{ + if (nWidth != 0 || nHeight != 0) + { + double fAngle = basegfx::deg2rad(nInternAngle / 100.0); // intern 1/100 deg to degree to rad + fAngle = atan2(nHeight * sin(fAngle), nWidth * cos(fAngle)); // circle to ellipse + fAngle = basegfx::rad2deg(fAngle) * 60000.0; // rad to degree to OOXML angle unit + sal_Int32 nAngle = basegfx::fround(fAngle); // normalize + nAngle = nAngle % 21600000; + return nAngle < 0 ? (nAngle + 21600000) : nAngle; + } + else // should be handled by caller, dummy value + return 0; +} + ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape ) { // First check, if this is a Fontwork-shape. For DrawingML, such a shape is a @@ -1055,7 +1072,7 @@ ShapeExport& ShapeExport::WriteEllipseShape( const Reference< XShape >& xShape ) pFS->startElementNS(mnXmlNamespace, (GetDocumentType() != DOCUMENT_DOCX ? XML_sp : XML_wsp)); - // TODO: arc, section, cut, connector + // TODO: connector ? // non visual shape properties if (GetDocumentType() != DOCUMENT_DOCX) @@ -1071,13 +1088,64 @@ ShapeExport& ShapeExport::WriteEllipseShape( const Reference< XShape >& xShape ) else pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr); + Reference< XPropertySet > xProps( xShape, UNO_QUERY ); + CircleKind eCircleKind(CircleKind_FULL); + if (xProps.is()) + xProps->getPropertyValue("CircleKind" ) >>= eCircleKind; + // visual shape properties pFS->startElementNS( mnXmlNamespace, XML_spPr ); WriteShapeTransformation( xShape, XML_a ); - WritePresetShape( "ellipse" ); - Reference< XPropertySet > xProps( xShape, UNO_QUERY ); + + if (CircleKind_FULL == eCircleKind) + WritePresetShape("ellipse"); + else + { + sal_Int32 nStartAngleIntern(9000); + sal_Int32 nEndAngleIntern(0); + if (xProps.is()) + { + xProps->getPropertyValue("CircleStartAngle" ) >>= nStartAngleIntern; + xProps->getPropertyValue("CircleEndAngle") >>= nEndAngleIntern; + } + std::vector< std::pair> aAvList; + awt::Size aSize = xShape->getSize(); + if (aSize.Width != 0 || aSize.Height != 0) + { + // Our arc has 90° up, OOXML has 90° down, so mirror it. + // API angles are 1/100 degree. + sal_Int32 nStartAngleOOXML(lcl_CircleAngle2CustomShapeEllipseAngleOOX(36000 - nEndAngleIntern, aSize.Width, aSize.Height)); + sal_Int32 nEndAngleOOXML(lcl_CircleAngle2CustomShapeEllipseAngleOOX(36000 - nStartAngleIntern, aSize.Width, aSize.Height)); + lcl_AppendAdjustmentValue( aAvList, 1, nStartAngleOOXML); + lcl_AppendAdjustmentValue( aAvList, 2, nEndAngleOOXML); + } + switch (eCircleKind) + { + case CircleKind_ARC : + WritePresetShape("arc", aAvList); + break; + case CircleKind_SECTION : + WritePresetShape("pie", aAvList); + break; + case CircleKind_CUT : + WritePresetShape("chord", aAvList); + break; + default : + WritePresetShape("ellipse"); + } + } if( xProps.is() ) { + if (CircleKind_ARC == eCircleKind) + { + // An arc in ODF is never filled, even if a fill style other than + // "none" is set. OOXML arc can be filled, so set fill explicit to + // NONE, otherwise some hidden or inherited filling is shown. + FillStyle eFillStyle(FillStyle_NONE); + uno::Any aNewValue; + aNewValue <<= eFillStyle; + xProps->setPropertyValue("FillStyle", aNewValue); + } WriteFill( xProps ); WriteOutline( xProps ); } diff --git a/sd/qa/unit/data/odp/tdf99497_CircleKind.odp b/sd/qa/unit/data/odp/tdf99497_CircleKind.odp new file mode 100644 index 000000000000..f3749dfdffb0 Binary files /dev/null and b/sd/qa/unit/data/odp/tdf99497_CircleKind.odp differ diff --git a/sd/qa/unit/export-tests-ooxml2.cxx b/sd/qa/unit/export-tests-ooxml2.cxx index dc1c4246ff35..0916ac95f3ff 100644 --- a/sd/qa/unit/export-tests-ooxml2.cxx +++ b/sd/qa/unit/export-tests-ooxml2.cxx @@ -193,6 +193,7 @@ public: void testTdf111789(); void testTdf100348_convert_Fontwork2TextWarp(); void testTdf1225573_FontWorkScaleX(); + void testTdf99497_keepAppearanceOfCircleKind(); /// SmartArt animated elements void testTdf104792(); void testTdf90627(); @@ -288,6 +289,7 @@ public: CPPUNIT_TEST(testTdf111789); CPPUNIT_TEST(testTdf100348_convert_Fontwork2TextWarp); CPPUNIT_TEST(testTdf1225573_FontWorkScaleX); + CPPUNIT_TEST(testTdf99497_keepAppearanceOfCircleKind); CPPUNIT_TEST(testTdf104792); CPPUNIT_TEST(testTdf90627); CPPUNIT_TEST(testTdf104786); @@ -2538,6 +2540,46 @@ void SdOOXMLExportTest2::testTdf126741() xDocShRef->DoClose(); } +void SdOOXMLExportTest2::testTdf99497_keepAppearanceOfCircleKind() +{ + // Error was, that all CircleKind were exported to 'ellipse'. + // Resulting pptx has to contain the customshapes of the corresponding kind + // slide 1 ARC -> arc, slide 2 CUT -> chord, slide 3 SECTION -> pie + // Adjustment values need to exist and their values need to correspond to the + // orignal angles. Shape 'arc' needs to be unfilled. + const OUString sPath("/sd/qa/unit/data/odp/tdf99497_CircleKind.odp"); + ::sd::DrawDocShellRef xDocShRef = loadURL(m_directories.getURLFromSrc(sPath), ODP); + utl::TempFile tempFile; + xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile); + + // slide 1 45° -> adj1 = 20493903, 270° -> adj2 = 5400000, exists + xmlDocPtr pXmlDocContent1 = parseExport(tempFile, "ppt/slides/slide1.xml"); + const OString sPathStart1("/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:prstGeom"); + assertXPath(pXmlDocContent1, sPathStart1 + "[@prst='arc']"); + const OString sPathAdj1(sPathStart1 + "/a:avLst/a:gd"); + assertXPath(pXmlDocContent1, sPathAdj1 + "[@name='adj1' and @fmla='val 20493903']"); + assertXPath(pXmlDocContent1, sPathAdj1 + "[@name='adj2' and @fmla='val 5400000']"); + assertXPath(pXmlDocContent1, "/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:noFill"); + + // slide 2 270° -> adj1 = 5400000, 180° -> adj2 = 10800000 + xmlDocPtr pXmlDocContent2 = parseExport(tempFile, "ppt/slides/slide2.xml"); + const OString sPathStart2("/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:prstGeom"); + assertXPath(pXmlDocContent2, sPathStart2 + "[@prst='chord']"); + const OString sPathAdj2(sPathStart2 + "/a:avLst/a:gd"); + assertXPath(pXmlDocContent2, sPathAdj2 + "[@name='adj1' and @fmla='val 5400000']"); + assertXPath(pXmlDocContent2, sPathAdj2 + "[@name='adj2' and @fmla='val 10800000']"); + + // slide 3 120° -> adj1 = 12600000, 30° -> adj2 = 20946396 + xmlDocPtr pXmlDocContent3 = parseExport(tempFile, "ppt/slides/slide3.xml"); + const OString sPathStart3("/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:prstGeom"); + assertXPath(pXmlDocContent3, sPathStart3 + "[@prst='pie']"); + const OString sPathAdj3(sPathStart3 + "/a:avLst/a:gd"); + assertXPath(pXmlDocContent3, sPathAdj3 + "[@name='adj1' and @fmla='val 12600000']"); + assertXPath(pXmlDocContent3, sPathAdj3 + "[@name='adj2' and @fmla='val 20946396']"); + + xDocShRef->DoClose(); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SdOOXMLExportTest2); CPPUNIT_PLUGIN_IMPLEMENT();