summaryrefslogtreecommitdiff
path: root/oox
diff options
context:
space:
mode:
authorRegina Henschel <rb.henschel@t-online.de>2024-03-15 00:00:51 +0100
committerRegina Henschel <rb.henschel@t-online.de>2024-03-20 18:06:35 +0100
commit2eabd56ff5f65733c6fa1bcef14d4a39c21dddc7 (patch)
tree7e33716d2e9eb5c29aba17444db7e601c8d48e4d /oox
parentf29222eaf385891620d4868827b27e734752018e (diff)
tdf#70039 import material of extruded shapes
This patch continues the tdf#70039 related commits 98b06ed3 and 6e5529d7. It adds further property-value pairs to the property map which is later used in CustomShapeProperties.pushToPropSet() for property Extrusion. OOXML defines a set of 15 material presets in section 20.1.10.50 ST_PresetMaterialType. The specification lists some values and it has example pictures. The example shape uses Bevel. That is not implemented, thus the examples are not really usable. Microsoft specifies the values it uses in section 2.1.1331 in MS-OI29500] - v20231113. The values used in the patch are based on these resources. MS Office defines the material by the properties 'Specular-', 'Diffuse-', 'Ambient-' and 'Emissive-Color, 'Specular Power', 'Blinn Hightlight', 'Diffuse-' and 'Alpha-Fresnel', and 'Metal'. But ODF, API and current implementation have for material only the properties 'Diffusion', 'Specularity', 'Shininess', 'Metal' and 'MetalType'. The patch tries to map the values as well as possible. The mapping works well for the legacy-foo materials which reflect the material in binary MS Office and is base of ODF and our implementation. But the preset has also material where transparency is added to the reflected light or the reflected light is blended or brightened with White. That is not possible currently and such shapes look different than in MS Office. These are especially the materials in the 'Translucent' section in the UI of MS Office. The material type 'flat' uses 'Emissive Color'. Such is not available at all. So material 'flat' looks noticable different too. More details about the used values is contained in the attachment in the bug report. Change-Id: I16a242446cbe98efcbdf5452058e1a3bd88dcf56 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164853 Tested-by: Jenkins Reviewed-by: Patrick Luby <guibomacdev@gmail.com> Reviewed-by: Regina Henschel <rb.henschel@t-online.de>
Diffstat (limited to 'oox')
-rw-r--r--oox/inc/drawingml/scene3dhelper.hxx13
-rw-r--r--oox/qa/unit/data/Scene3d_material_highlight.pptxbin0 -> 19845 bytes
-rw-r--r--oox/qa/unit/data/Scene3d_material_wireframe.pptxbin0 -> 15288 bytes
-rw-r--r--oox/qa/unit/testscene3d.cxx168
-rw-r--r--oox/source/drawingml/scene3dhelper.cxx93
-rw-r--r--oox/source/drawingml/shape.cxx4
6 files changed, 223 insertions, 55 deletions
diff --git a/oox/inc/drawingml/scene3dhelper.hxx b/oox/inc/drawingml/scene3dhelper.hxx
index af72a0e0cf2a..0f79825b13ca 100644
--- a/oox/inc/drawingml/scene3dhelper.hxx
+++ b/oox/inc/drawingml/scene3dhelper.hxx
@@ -50,6 +50,19 @@ public:
void setLightingProperties(const oox::drawingml::Shape3DPropertiesPtr p3DProperties,
const double& rRotZ, oox::PropertyMap& rPropertyMap);
+ /** Puts the material properties of the 3D-shape into the property map
+ @details OOXML and MSO describe the material properties using Specular Color (RGBA), Diffuse
+ Color (RGBA), Ambient Color (RGBA), Emissive Color (RGBA), Specular Power (positive int),
+ Diffuse Fresnel (int), Alpha Fresnel (int), Metal (bool) and Blinn Highlight (bool). ODF
+ and API have Diffusion (double), Specularity (double), Shininess (double), Metal (bool),
+ MetalType (enum). Thus a directly use of the MSO values is not possible and some material
+ types cannot be rendered. The method uses workarounds and approximations.
+ @param [in] p3DProperties contains besides other properties the material preset name
+ @param [in, out] rPropertyMap has properties directly usable in
+ CustomShapeProperties.pushToPropSet() for property Extrusion.*/
+ static void setMaterialProperties(const oox::drawingml::Shape3DPropertiesPtr p3DProperties,
+ oox::PropertyMap& rPropertyMap);
+
private:
/** Calculates angles suitable for API from OOXML scene3d angles.
@param [in] nLat, nLon, nRev in unit 1/60000 deg with same orientation as the attributes lat,
diff --git a/oox/qa/unit/data/Scene3d_material_highlight.pptx b/oox/qa/unit/data/Scene3d_material_highlight.pptx
new file mode 100644
index 000000000000..c299c21bc68c
--- /dev/null
+++ b/oox/qa/unit/data/Scene3d_material_highlight.pptx
Binary files differ
diff --git a/oox/qa/unit/data/Scene3d_material_wireframe.pptx b/oox/qa/unit/data/Scene3d_material_wireframe.pptx
new file mode 100644
index 000000000000..f72baeb25178
--- /dev/null
+++ b/oox/qa/unit/data/Scene3d_material_wireframe.pptx
Binary files differ
diff --git a/oox/qa/unit/testscene3d.cxx b/oox/qa/unit/testscene3d.cxx
index 5ca452ea3d21..48b968ba23cd 100644
--- a/oox/qa/unit/testscene3d.cxx
+++ b/oox/qa/unit/testscene3d.cxx
@@ -43,7 +43,7 @@ protected:
uno::Reference<drawing::XShape> getShape(sal_uInt8 nShapeIndex, sal_uInt8 nPageIndex);
// Converts the shape 0 on page 0 to a bitmap and returns this bitmap.
// It assumes, that shape 0 on page 0 is the only shape.
- void getShapeAsBitmap(BitmapEx& rBMP);
+ void getShapeAsBitmap(BitmapEx& rBMP, sal_uInt8 nShapeIndex);
};
uno::Reference<drawing::XShape> TestScene3d::getShape(sal_uInt8 nShapeIndex, sal_uInt8 nPageIndex)
@@ -60,20 +60,20 @@ uno::Reference<drawing::XShape> TestScene3d::getShape(sal_uInt8 nShapeIndex, sal
return xShape;
}
-void TestScene3d::getShapeAsBitmap(BitmapEx& rBMP)
+void TestScene3d::getShapeAsBitmap(BitmapEx& rBMP, sal_uInt8 nShapeIndex)
{
SfxViewShell* pViewShell = SfxViewShell::Current();
SdrView* pSdrView = pViewShell->GetDrawView();
// Mark object and convert it to bitmap
- uno::Reference<drawing::XShape> xShape3D(getShape(0, 0));
+ uno::Reference<drawing::XShape> xShape3D(getShape(nShapeIndex, 0));
SdrObject* pSdrShape(SdrObject::getSdrObjectFromXShape(xShape3D));
pSdrView->MarkObj(pSdrShape, pSdrView->GetSdrPageView());
dispatchCommand(mxComponent, ".uno:ConvertIntoBitmap", {});
pSdrView->UnmarkAll();
// Get graphic
- uno::Reference<drawing::XShape> xShapeBmp(getShape(0, 0));
+ uno::Reference<drawing::XShape> xShapeBmp(getShape(nShapeIndex, 0));
SdrGrafObj* pGrafObj = dynamic_cast<SdrGrafObj*>(SdrObject::getSdrObjectFromXShape(xShapeBmp));
CPPUNIT_ASSERT_MESSAGE("No image object created", pGrafObj);
const Graphic& rGraphic = pGrafObj->GetGraphic();
@@ -81,6 +81,28 @@ void TestScene3d::getShapeAsBitmap(BitmapEx& rBMP)
CPPUNIT_ASSERT_MESSAGE("No bitmap", !rBMP.IsEmpty());
}
+namespace
+{
+void lcl_AssertColorsApproximateEqual(const ::Color& aExpected, const ::Color& aActual)
+{
+ // Currently (March 2024), the import of lighting and material is only approximately possible.
+ // Thus colors are not identical. When the import will be improved, the tolerances should be
+ // reduced. The test uses HSB instead of RGB, because differences in hue are more irritating and
+ // should be detected as a priority. That is not possible with GetColorError() method.
+ sal_uInt16 nExpH;
+ sal_uInt16 nExpS;
+ sal_uInt16 nExpB;
+ sal_uInt16 nActH;
+ sal_uInt16 nActS;
+ sal_uInt16 nActB;
+ aExpected.RGBtoHSB(nExpH, nExpS, nExpB);
+ aActual.RGBtoHSB(nActH, nActS, nActB);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Hue", nExpH, nActH, 2);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Saturation", nExpS, nActS, 13);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Brightness", nExpB, nActB, 11);
+}
+} // end anonymous namespace
+
CPPUNIT_TEST_FIXTURE(TestScene3d, test_isometricRightUp)
{
// Given a document with a scene3d element on a shape. Without the fix, the shape was imported as
@@ -357,21 +379,17 @@ CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_modernCamera)
// The test assumes rendering with ShadeMode_FLAT.
loadFromFile(u"Scene3d_lightRig_modernCamera.pptx");
BitmapEx aBMP;
- getShapeAsBitmap(aBMP);
+ getShapeAsBitmap(aBMP, 0);
// Size in pixel depends on dpi. Thus calculate positions relative to size.
// Color in center
sal_Int32 nX = 0.5 * aBMP.GetSizePixel().Width();
sal_Int32 nY = 0.5 * aBMP.GetSizePixel().Height();
- ::Color aExpectedCenter(248, 226, 212);
- CPPUNIT_ASSERT_MESSAGE("center color wrong",
- aExpectedCenter.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
+ lcl_AssertColorsApproximateEqual(::Color(247, 225, 211), aBMP.GetPixelColor(nX, nY));
// Color left
nX = 0.046122 * aBMP.GetSizePixel().Width();
nY = 0.5 * aBMP.GetSizePixel().Height();
- ::Color aExpectedLeft(0, 105, 48);
- CPPUNIT_ASSERT_MESSAGE("left color wrong",
- aExpectedLeft.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
+ lcl_AssertColorsApproximateEqual(::Color(0, 103, 47), aBMP.GetPixelColor(nX, nY));
}
CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_legacyCamera)
@@ -383,21 +401,17 @@ CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_legacyCamera)
// The test assumes rendering with ShadeMode_FLAT.
loadFromFile(u"Scene3d_lightRig_legacyCamera.pptx");
BitmapEx aBMP;
- getShapeAsBitmap(aBMP);
+ getShapeAsBitmap(aBMP, 0);
// Size in pixel depends on dpi. Thus calculate positions relative to size.
// Color in center
sal_Int32 nX = 0.5 * aBMP.GetSizePixel().Width();
sal_Int32 nY = 0.5 * aBMP.GetSizePixel().Height();
- ::Color aExpectedCenter(96, 88, 82);
- CPPUNIT_ASSERT_MESSAGE("center color wrong",
- aExpectedCenter.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
+ lcl_AssertColorsApproximateEqual(::Color(94, 86, 80), aBMP.GetPixelColor(nX, nY));
// Color left
nX = 0.046122 * aBMP.GetSizePixel().Width();
nY = 0.5 * aBMP.GetSizePixel().Height();
- ::Color aExpectedLeft(0, 180, 82);
- CPPUNIT_ASSERT_MESSAGE("left color wrong",
- aExpectedLeft.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
+ lcl_AssertColorsApproximateEqual(::Color(5, 185, 87), aBMP.GetPixelColor(nX, nY));
}
CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_default)
@@ -408,27 +422,21 @@ CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_default)
// The test assumes rendering with ShadeMode_FLAT.
loadFromFile(u"Scene3d_lightRig_default.pptx");
BitmapEx aBMP;
- getShapeAsBitmap(aBMP);
+ getShapeAsBitmap(aBMP, 0);
// Size in pixel depends on dpi. Thus calculate positions relative to size.
// Front color
sal_Int32 nX = 0.93811 * aBMP.GetSizePixel().Width();
sal_Int32 nY = 0.49904 * aBMP.GetSizePixel().Height();
- ::Color aExpectedFront(165, 187, 150);
- CPPUNIT_ASSERT_MESSAGE("front color wrong",
- aExpectedFront.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
+ lcl_AssertColorsApproximateEqual(::Color(165, 187, 150), aBMP.GetPixelColor(nX, nY));
// Left color
nX = 0.078176 * aBMP.GetSizePixel().Width();
nY = 0.49904 * aBMP.GetSizePixel().Height();
- ::Color aExpectedLeft(255, 189, 74);
- CPPUNIT_ASSERT_MESSAGE("left color wrong",
- aExpectedLeft.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
+ lcl_AssertColorsApproximateEqual(::Color(255, 189, 74), aBMP.GetPixelColor(nX, nY));
// Top color
nX = 0.48860 * aBMP.GetSizePixel().Width();
nY = 0.069098 * aBMP.GetSizePixel().Height();
- ::Color aExpectedTop(189, 100, 39);
- CPPUNIT_ASSERT_MESSAGE("top color wrong",
- aExpectedTop.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
+ lcl_AssertColorsApproximateEqual(::Color(189, 100, 39), aBMP.GetPixelColor(nX, nY));
}
CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_dir_rotation)
@@ -439,27 +447,21 @@ CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_dir_rotation)
// and MSO. The test assumes rendering with ShadeMode_FLAT.
loadFromFile(u"Scene3d_lightRig_dir_rotation.pptx");
BitmapEx aBMP;
- getShapeAsBitmap(aBMP);
+ getShapeAsBitmap(aBMP, 0);
// Size in pixel depends on dpi. Thus calculate positions relative to size.
// Front color
sal_Int32 nX = 0.93811 * aBMP.GetSizePixel().Width();
sal_Int32 nY = 0.49904 * aBMP.GetSizePixel().Height();
- ::Color aExpectedFront(165, 187, 150);
- CPPUNIT_ASSERT_MESSAGE("front color wrong",
- aExpectedFront.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
+ lcl_AssertColorsApproximateEqual(::Color(165, 187, 150), aBMP.GetPixelColor(nX, nY));
// Left color
nX = 0.078176 * aBMP.GetSizePixel().Width();
nY = 0.49904 * aBMP.GetSizePixel().Height();
- ::Color aExpectedLeft(206, 108, 42);
- CPPUNIT_ASSERT_MESSAGE("left color wrong",
- aExpectedLeft.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
+ lcl_AssertColorsApproximateEqual(::Color(206, 108, 42), aBMP.GetPixelColor(nX, nY));
// Top color
nX = 0.48860 * aBMP.GetSizePixel().Width();
nY = 0.069098 * aBMP.GetSizePixel().Height();
- ::Color aExpectedTop(255, 189, 74);
- CPPUNIT_ASSERT_MESSAGE("top color wrong",
- aExpectedTop.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
+ lcl_AssertColorsApproximateEqual(::Color(255, 189, 74), aBMP.GetPixelColor(nX, nY));
}
CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_rot_rotation)
@@ -469,32 +471,88 @@ CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_rot_rotation)
// The test assumes rendering with ShadeMode_FLAT.
loadFromFile(u"Scene3d_lightRig_rot_rotation.pptx");
BitmapEx aBMP;
- getShapeAsBitmap(aBMP);
+ getShapeAsBitmap(aBMP, 0);
// Size in pixel depends on dpi. Thus calculate positions relative to size.
// Front color, same as in MS Office
sal_Int32 nX = 0.93811 * aBMP.GetSizePixel().Width();
sal_Int32 nY = 0.49904 * aBMP.GetSizePixel().Height();
- ::Color aExpectedFront(88, 100, 80);
- CPPUNIT_ASSERT_MESSAGE("center color wrong",
- aExpectedFront.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
- // Left color, different from MS Office
- // The rotation brings the second light in a position, that it contributes to the left face.
- // Because the light is specular in MS Office, but current LibreOffice cannot make a second
- // light specular, the colors in MS Office and LibreOffice differ noticeably. MS Office has
- // here rgb(255, 214, 99). The expected color is the color in LibreOffice as of March 2024.
- // The test needs to be updated, when LibreOffice rendering is improved.
+ lcl_AssertColorsApproximateEqual(::Color(88, 100, 80), aBMP.GetPixelColor(nX, nY));
+ // Left color
nX = 0.078176 * aBMP.GetSizePixel().Width();
nY = 0.49904 * aBMP.GetSizePixel().Height();
- ::Color aExpectedLeft(255, 191, 75);
- CPPUNIT_ASSERT_MESSAGE("left color wrong",
- aExpectedLeft.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
- // Top color, same as in MS Office
+ lcl_AssertColorsApproximateEqual(::Color(255, 214, 99), aBMP.GetPixelColor(nX, nY));
+ // Top color
nX = 0.48860 * aBMP.GetSizePixel().Width();
nY = 0.069098 * aBMP.GetSizePixel().Height();
- ::Color aExpectedTop(106, 56, 22);
- CPPUNIT_ASSERT_MESSAGE("top color wrong",
- aExpectedTop.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7);
+ lcl_AssertColorsApproximateEqual(::Color(106, 56, 22), aBMP.GetPixelColor(nX, nY));
+}
+
+CPPUNIT_TEST_FIXTURE(TestScene3d, test_material_highlight)
+{
+ // The file contains six shapes with same geometry and fill and line color. The scenes use the
+ // camera 'orthographicFront' and the lightRig 'twoPt'. The test looks at an area of highlight
+ // and at an area outside the hightlight.
+
+ loadFromFile(u"Scene3d_material_highlight.pptx");
+
+ BitmapEx aBMP;
+ getShapeAsBitmap(aBMP, 0); // material legacyPlastic
+ sal_Int32 nX = 0.75 * aBMP.GetSizePixel().Width();
+ sal_Int32 nYhigh = 0.25 * aBMP.GetSizePixel().Height();
+ sal_Int32 nYsoft = 0.75 * aBMP.GetSizePixel().Height();
+ lcl_AssertColorsApproximateEqual(::Color(255, 255, 255), aBMP.GetPixelColor(nX, nYhigh));
+ lcl_AssertColorsApproximateEqual(::Color(130, 95, 70), aBMP.GetPixelColor(nX, nYsoft));
+
+ // same geometry, thus nX, nYhigh and nYsoft unchanged
+ getShapeAsBitmap(aBMP, 1); // material warmMatte
+ lcl_AssertColorsApproximateEqual(::Color(253, 200, 164), aBMP.GetPixelColor(nX, nYhigh));
+ lcl_AssertColorsApproximateEqual(::Color(132, 96, 71), aBMP.GetPixelColor(nX, nYsoft));
+
+ getShapeAsBitmap(aBMP, 2); // material metal
+ lcl_AssertColorsApproximateEqual(::Color(255, 255, 255), aBMP.GetPixelColor(nX, nYhigh));
+ lcl_AssertColorsApproximateEqual(::Color(132, 96, 71), aBMP.GetPixelColor(nX, nYsoft));
+
+ getShapeAsBitmap(aBMP, 3); // material matte
+ lcl_AssertColorsApproximateEqual(::Color(190, 138, 102), aBMP.GetPixelColor(nX, nYhigh));
+ lcl_AssertColorsApproximateEqual(::Color(130, 95, 70), aBMP.GetPixelColor(nX, nYsoft));
+
+ getShapeAsBitmap(aBMP, 4); // material dkEdge
+ lcl_AssertColorsApproximateEqual(::Color(255, 255, 255), aBMP.GetPixelColor(nX, nYhigh));
+ lcl_AssertColorsApproximateEqual(::Color(115, 84, 62), aBMP.GetPixelColor(nX, nYsoft));
+
+ getShapeAsBitmap(aBMP, 5); // material legacyMetal
+ lcl_AssertColorsApproximateEqual(::Color(255, 255, 220), aBMP.GetPixelColor(nX, nYhigh));
+ lcl_AssertColorsApproximateEqual(::Color(86, 63, 46), aBMP.GetPixelColor(nX, nYsoft));
+}
+
+CPPUNIT_TEST_FIXTURE(TestScene3d, test_material_wireframe)
+{
+ // Given a document with a shape in 3D mode with material legacyWireframe.
+ // It uses a projection "Oblique: Top Left".
+ loadFromFile(u"Scene3d_material_wireframe.pptx");
+ uno::Reference<drawing::XShape> xShape(getShape(0, 0));
+
+ // Make sure the export to ODF has the corresponding attributes.
+ save("impress8");
+ xmlDocUniquePtr pXmlDoc = parseExport("content.xml");
+
+ assertXPath(pXmlDoc,
+ "/office:document-content/office:body/office:presentation/draw:page/"
+ "draw:custom-shape/draw:enhanced-geometry"_ostr,
+ "extrusion-origin"_ostr, "-0.5 -0.5");
+ assertXPath(pXmlDoc,
+ "/office:document-content/office:body/office:presentation/draw:page/"
+ "draw:custom-shape/draw:enhanced-geometry"_ostr,
+ "extrusion-skew"_ostr, "30 -45");
+ assertXPath(pXmlDoc,
+ "/office:document-content/office:body/office:presentation/draw:page/"
+ "draw:custom-shape/draw:enhanced-geometry"_ostr,
+ "projection"_ostr, "parallel");
+ assertXPath(pXmlDoc,
+ "/office:document-content/office:body/office:presentation/draw:page/"
+ "draw:custom-shape/draw:enhanced-geometry"_ostr,
+ "shade-mode"_ostr, "draft");
}
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/oox/source/drawingml/scene3dhelper.cxx b/oox/source/drawingml/scene3dhelper.cxx
index d2b03ae97485..eff649a71cd7 100644
--- a/oox/source/drawingml/scene3dhelper.cxx
+++ b/oox/source/drawingml/scene3dhelper.cxx
@@ -20,12 +20,15 @@
#include <com/sun/star/drawing/Direction3D.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeParameter.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeMetalType.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
#include <com/sun/star/drawing/Position3D.hpp>
#include <com/sun/star/drawing/ProjectionMode.hpp>
#include <com/sun/star/drawing/ShadeMode.hpp>
+#include <cmath>
+
namespace oox
{
/** This struct is used to hold values from the OOXML camera preset types.*/
@@ -941,6 +944,96 @@ void Scene3DHelper::setLightingProperties(const oox::drawingml::Shape3DPropertie
rPropertyMap.setProperty(oox::PROP_SecondLightLevel, 0.0); // prevent defaults.
}
+namespace
+/** This struct is used to hold material values for extruded custom shapes. Because we cannot yet
+ render all material propertes MS Office uses, the values are adapted to our current abilities.*/
+{
+struct MaterialValues
+{
+ std::u16string_view msMaterialPrstName; // identifies the material type
+ // Corresponds to MS Office 'Diffuse Color' and 'Ambient Color'.
+ double fDiffusion;
+ double fSpecularity; // Corresponds to MS Office 'Specular Color'.
+ // Corresponds to our 'Shininess' as 2^(Shininess/10) = nSpecularPower.
+ sal_uInt8 nSpecularPower;
+ bool bMetal; // Corresponds to MS Office 'Metal'
+ // constants com::sun::star::drawing::EnhancedCustomShapeMetalType
+ // MetalMSCompatible belongs to 'legacyMetal' material type.
+ std::optional<sal_Int16> oMetalType; // MetalODF = 0, MetalMSCompatible = 1
+ // MS Office properties 'Emissive Color', 'Diffuse Fresnel', 'Alpha Fresnel' and 'Blinn Highlight'
+ // are not contained.
+};
+} // end anonymous namespace
+
+// OOXML standard has a fixed amount of 15 material types. The type 'legacyWireframe' is special and
+// thus is handled separately. A spreadsheet with further remarks is attached to tdf#70039.
+constexpr sal_uInt16 nPrstMaterialCount(14);
+constexpr MaterialValues aPrstMaterialArray[nPrstMaterialCount]
+ = { { u"clear", 100, 60, 20, false, {} },
+ { u"dkEdge", 70, 100, 35, false, {} },
+ { u"flat", 100, 80, 50, false, {} },
+ { u"legacyMatte", 100, 0, 0, false, {} },
+ { u"legacyMetal", 66.69921875, 122.0703125, 32, true, { 1 } },
+ { u"legacyPlastic", 100, 122.0703125, 32, false, {} },
+ { u"matte", 100, 0, 0, false, {} },
+ { u"metal", 100, 100, 12, true, { 0 } },
+ { u"plastic", 100, 60, 12, true, { 0 } },
+ { u"powder", 100, 30, 10, false, {} },
+ { u"softEdge", 100, 100, 35, false, {} },
+ { u"softmetal", 100, 100, 8, true, { 0 } },
+ { u"translucentPowder", 100, 30, 10, true, { 0 } },
+ { u"warmMatte", 100, 30, 8, false, {} } };
+
+void Scene3DHelper::setMaterialProperties(const oox::drawingml::Shape3DPropertiesPtr p3DProperties,
+ oox::PropertyMap& rPropertyMap)
+{
+ if (!p3DProperties)
+ return;
+
+ // PowerPoint does not write aus prstMaterial="warmMatte", but handles it as default.
+ const sal_Int32 nMaterialID = (*p3DProperties).mnMaterial.value_or(XML_warmMatte); // token
+
+ // special handling for 'legacyWireframe'
+ if (nMaterialID == XML_legacyWireframe)
+ {
+ // This is handled via shade mode of the scene.
+ rPropertyMap.setProperty(oox::PROP_ShadeMode, css::drawing::ShadeMode_DRAFT);
+ // Notice, the color of the strokes will be different from MS Office, because LO uses the
+ // shape line color even if the line style is 'none', whereas MS Office uses contour color or
+ // Black.
+ return;
+ }
+
+ sal_Int16 nIdx(0); // Index into aPrstMaterialArray
+ while (nIdx < nPrstMaterialCount
+ && aPrstMaterialArray[nIdx].msMaterialPrstName
+ != oox::drawingml::Generic3DProperties::getPresetMaterialTypeString(nMaterialID))
+ ++nIdx;
+ if (nIdx >= nPrstMaterialCount)
+ return; // error in document
+
+ // extrusion-diffuse, extrusion-specularity-loext
+ rPropertyMap.setProperty(oox::PROP_Diffusion, aPrstMaterialArray[nIdx].fDiffusion);
+ rPropertyMap.setProperty(oox::PROP_Specularity, aPrstMaterialArray[nIdx].fSpecularity);
+
+ // extrusion-shininess
+ double fShininess = 0.0;
+ // Conversion 2^(fShininess/10) = nSpecularPower
+ if (aPrstMaterialArray[nIdx].nSpecularPower > 0)
+ fShininess = 10.0 * std::log2(aPrstMaterialArray[nIdx].nSpecularPower);
+ rPropertyMap.setProperty(oox::PROP_Shininess, fShininess);
+
+ // extrusion-metal, extrusion-metal-type
+ rPropertyMap.setProperty(oox::PROP_Metal, aPrstMaterialArray[nIdx].bMetal);
+ if (aPrstMaterialArray[nIdx].bMetal)
+ {
+ sal_Int16 eMetalType = aPrstMaterialArray[nIdx].oMetalType.value_or(0) == 1
+ ? css::drawing::EnhancedCustomShapeMetalType::MetalMSCompatible
+ : css::drawing::EnhancedCustomShapeMetalType::MetalODF;
+ rPropertyMap.setProperty(oox::PROP_MetalType, eMetalType);
+ }
+}
+
} // end namespace oox
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/shape.cxx b/oox/source/drawingml/shape.cxx
index 195b85f893df..963391806c40 100644
--- a/oox/source/drawingml/shape.cxx
+++ b/oox/source/drawingml/shape.cxx
@@ -1002,8 +1002,12 @@ Reference< XShape > const & Shape::createAndInsert(
}
if (bHas3DEffect)
+ {
aScene3DHelper.setLightingProperties(mp3DPropertiesPtr, fShapeRotateInclCamera,
getCustomShapeProperties()->getExtrusionPropertyMap());
+ oox::Scene3DHelper::setMaterialProperties(
+ mp3DPropertiesPtr, getCustomShapeProperties()->getExtrusionPropertyMap());
+ }
if (bIsCroppedGraphic || bIs3DGraphic)
{