summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRegina Henschel <rb.henschel@t-online.de>2022-01-13 13:26:25 +0100
committerRegina Henschel <rb.henschel@t-online.de>2022-02-17 14:14:01 +0100
commit157b027c4cfca2582b1c1bdb66992560084ac008 (patch)
tree41ae4086069acc86fb36ccbb23a1eb82bfa5117c
parent424cd919c4234d6e6fd4da89acd90197771f7b9d (diff)
tdf#145700 Improve lighting in extruded custom shapes
The fix tries to make rendering similar to MS Office. The ODF standard follows closely the extrusion in RTF and MS binary format. Rendering uses the 3D scene engine. The main problem was, that the z-component of the direction was interpreted with opposite sign. As result the maximum of a light was at false position. Especially a direction from the observer to the object has produced a light behind the shape and so looks as if light was off. The wrong z-direction has produced lighting, which was less intensive than in MS Office. To compensate that, a third light source was added as workaround. That part is removed. Second problem was wrong use of 3D-scene D3DMaterialSpecularIntensity and D3DMaterialSpecular (= UI Specular color). That was not only wrong in OOo but in my previous patch too. D3DMaterialSpecularIntensity corresponds to MS property 'c3DShininess'. Relationship is Intensity = 2^c3DShininess. D3DMaterialSpecular is calculated from MS property c3DSpecularAmt and and c3DKeyIntensity. The light source was missing, but needs to be included, because c3DSpecularAmt is 'the ratio of incident to specular light that is reflected on a shape'. The old unit tests are adapted to this change. MS gives no information how it softens a light in case of harsh=false. ODF specifies it as 'implementation-defined'. The patch uses four additional lights, which have directions in 60° angle to the original light. The light intensity is distributed. That comes near to rendering in MS Office. Changing our 3D engine to provide 'soft' lights was not doable for me. The way MS Office renders a 'metal' surface is different from ODF specification. To distinguish the kinds, I have introduced a new property MetalType. I have discussed it with the ODF TC (see minutes from 2022-02-07) and got the advise to use namespaced values. Therefore the datatype is not boolean. The 'Surface' drop-down in the extrusion bar is changed to make the two kinds of rendering 'Metal' available to the user. If a user sets surface 'Metal' in the UI of MS Office, it sets not only fc3DMetallic but reduces the value of c3DDiffuseAmt in addition. Our 3D-scene engine has the corresponding ODF attribute dr3d:diffuse-color not implemented. To get a similar rendering I change the material color of the 3D-objects as workaround. Change-Id: Ia986b9c318b4c79688e0c0e2d858215b9d612fdc Reviewed-on: https://gerrit.libreoffice.org/c/core/+/128449 Tested-by: Jenkins Reviewed-by: Regina Henschel <rb.henschel@t-online.de>
-rw-r--r--filter/source/msfilter/escherex.cxx9
-rw-r--r--filter/source/msfilter/msdffimp.cxx121
-rw-r--r--include/xmloff/xmltoken.hxx1
-rw-r--r--offapi/UnoApi_offapi.mk1
-rw-r--r--offapi/com/sun/star/drawing/EnhancedCustomShapeExtrusion.idl11
-rwxr-xr-xoffapi/com/sun/star/drawing/EnhancedCustomShapeMetalType.idl36
-rw-r--r--schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng9
-rw-r--r--svx/qa/unit/customshapes.cxx109
-rw-r--r--svx/qa/unit/data/tdf145700_3D_FirstLightHarsh.docbin0 -> 27136 bytes
-rw-r--r--svx/qa/unit/data/tdf145700_3D_FrontLightDim.docbin0 -> 27136 bytes
-rw-r--r--svx/qa/unit/data/tdf145700_3D_NonUI.docbin0 -> 29184 bytes
-rw-r--r--svx/qa/unit/svdraw.cxx54
-rw-r--r--svx/source/customshapes/EnhancedCustomShape3d.cxx363
-rw-r--r--svx/source/tbxctrls/extrusioncontrols.cxx8
-rw-r--r--svx/source/tbxctrls/extrusioncontrols.hxx1
-rw-r--r--svx/source/toolbars/extrusionbar.cxx126
-rw-r--r--svx/uiconfig/ui/surfacewindow.ui25
-rw-r--r--xmloff/inc/EnhancedCustomShapeToken.hxx2
-rw-r--r--xmloff/qa/unit/data/tdf145700_3D_metal_type_MSCompatible.docbin0 -> 27136 bytes
-rw-r--r--xmloff/qa/unit/draw.cxx100
-rw-r--r--xmloff/source/core/xmltoken.cxx1
-rw-r--r--xmloff/source/draw/EnhancedCustomShapeToken.cxx2
-rw-r--r--xmloff/source/draw/shapeexport.cxx20
-rw-r--r--xmloff/source/draw/ximpcustomshape.cxx13
-rw-r--r--xmloff/source/token/tokens.txt1
25 files changed, 831 insertions, 182 deletions
diff --git a/filter/source/msfilter/escherex.cxx b/filter/source/msfilter/escherex.cxx
index 9ea8ab91a7e0..3f817a72bcb1 100644
--- a/filter/source/msfilter/escherex.cxx
+++ b/filter/source/msfilter/escherex.cxx
@@ -91,6 +91,7 @@
#include <sal/log.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/numeric/ftools.hxx>
#include <osl/diagnose.h>
#include <algorithm>
@@ -2858,7 +2859,11 @@ void EscherPropertyContainer::CreateCustomShapeProperties( const MSO_SPT eShapeT
{
double fExtrusionShininess = 0;
if ( rrProp.Value >>= fExtrusionShininess )
- AddOpt( DFF_Prop_c3DShininess, static_cast<sal_Int32>( fExtrusionShininess * 655.36 ) );
+ {
+ // ODF to MS Office conversion invers to msdffimp.cxx
+ fExtrusionShininess = basegfx::fround(fExtrusionShininess / 10.0);
+ AddOpt( DFF_Prop_c3DShininess, static_cast<sal_Int32>(fExtrusionShininess) );
+ }
}
else if ( rrProp.Name == "Skew" )
{
@@ -2875,7 +2880,7 @@ void EscherPropertyContainer::CreateCustomShapeProperties( const MSO_SPT eShapeT
{
double fExtrusionSpecularity = 0;
if ( rrProp.Value >>= fExtrusionSpecularity )
- AddOpt( DFF_Prop_c3DSpecularAmt, static_cast<sal_Int32>( fExtrusionSpecularity * 1333 ) );
+ AddOpt( DFF_Prop_c3DSpecularAmt, static_cast<sal_Int32>( fExtrusionSpecularity * 655.36 ) );
}
else if ( rrProp.Name == "ProjectionMode" )
{
diff --git a/filter/source/msfilter/msdffimp.cxx b/filter/source/msfilter/msdffimp.cxx
index a0ecad074ac6..76813d38df24 100644
--- a/filter/source/msfilter/msdffimp.cxx
+++ b/filter/source/msfilter/msdffimp.cxx
@@ -149,6 +149,7 @@
#include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeTextPathMode.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeMetalType.hpp>
#include <com/sun/star/beans/PropertyValues.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
@@ -1675,14 +1676,20 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt
aExtrusionPropVec.push_back( aProp );
// "Brightness"
+ // MS Office default 0x00004E20 16.16 FixedPoint, 20000/65536=0.30517, ODF default 33%.
+ // Thus must set value even if default.
+ double fBrightness = 20000.0;
if ( IsProperty( DFF_Prop_c3DAmbientIntensity ) )
{
- double fBrightness = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DAmbientIntensity, 0 ));
- fBrightness /= 655.36;
- aProp.Name = "Brightness";
- aProp.Value <<= fBrightness;
- aExtrusionPropVec.push_back( aProp );
+ // Value must be in range 0.0 to 1.0 in MS Office binary specification, but larger
+ // values are in fact interpreted.
+ fBrightness = GetPropertyValue( DFF_Prop_c3DAmbientIntensity, 0 );
}
+ fBrightness /= 655.36;
+ aProp.Name = "Brightness";
+ aProp.Value <<= fBrightness;
+ aExtrusionPropVec.push_back( aProp );
+
// "Depth" in 1/100mm
if ( IsProperty( DFF_Prop_c3DExtrudeBackward ) || IsProperty( DFF_Prop_c3DExtrudeForward ) )
{
@@ -1700,14 +1707,17 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt
aExtrusionPropVec.push_back( aProp );
}
// "Diffusion"
+ // ODF default is 0%, MS Office default is 100%. Thus must set value even if default.
+ double fDiffusion = 100;
if ( IsProperty( DFF_Prop_c3DDiffuseAmt ) )
{
- double fDiffusion = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DDiffuseAmt, 0 ));
+ fDiffusion = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DDiffuseAmt, 0 ));
fDiffusion /= 655.36;
- aProp.Name = "Diffusion";
- aProp.Value <<= fDiffusion;
- aExtrusionPropVec.push_back( aProp );
}
+ aProp.Name = "Diffusion";
+ aProp.Value <<= fDiffusion;
+ aExtrusionPropVec.push_back( aProp );
+
// "NumberOfLineSegments"
if ( IsProperty( DFF_Prop_c3DTolerance ) )
{
@@ -1730,24 +1740,35 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt
aProp.Name = "SecondLightHarsh";
aProp.Value <<= bExtrusionSecondLightHarsh;
aExtrusionPropVec.push_back( aProp );
+
// "FirstLightLevel"
+ // MS Office default 0x00009470 16.16 FixedPoint, 38000/65536 = 0.5798, ODF default 66%.
+ // Thus must set value even if default.
+ double fFirstLightLevel = 38000.0;
if ( IsProperty( DFF_Prop_c3DKeyIntensity ) )
{
- double fFirstLightLevel = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DKeyIntensity, 0 ));
- fFirstLightLevel /= 655.36;
- aProp.Name = "FirstLightLevel";
- aProp.Value <<= fFirstLightLevel;
- aExtrusionPropVec.push_back( aProp );
+ // value<0 and value>1 are allowed in MS Office. Clamp such in ODF export, not here.
+ fFirstLightLevel = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DKeyIntensity, 0 ));
}
+ fFirstLightLevel /= 655.36;
+ aProp.Name = "FirstLightLevel";
+ aProp.Value <<= fFirstLightLevel;
+ aExtrusionPropVec.push_back( aProp );
+
// "SecondLightLevel"
+ // MS Office default 0x00009470 16.16 FixedPoint, 38000/65536 = 0.5798, ODF default 66%.
+ // Thus must set value even if default.
+ double fSecondLightLevel = 38000.0;
if ( IsProperty( DFF_Prop_c3DFillIntensity ) )
{
- double fSecondLightLevel = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DFillIntensity, 0 ));
- fSecondLightLevel /= 655.36;
- aProp.Name = "SecondLightLevel";
- aProp.Value <<= fSecondLightLevel;
- aExtrusionPropVec.push_back( aProp );
+ // value<0 and value>1 are allowed in MS Office. Clamp such in ODF export, not here.
+ fSecondLightLevel = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DFillIntensity, 0 ));
}
+ fSecondLightLevel /= 655.36;
+ aProp.Name = "SecondLightLevel";
+ aProp.Value <<= fSecondLightLevel;
+ aExtrusionPropVec.push_back( aProp );
+
// "FirstLightDirection"
if ( IsProperty( DFF_Prop_c3DKeyX ) || IsProperty( DFF_Prop_c3DKeyY ) || IsProperty( DFF_Prop_c3DKeyZ ) )
{
@@ -1776,6 +1797,10 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt
aProp.Name = "Metal";
aProp.Value <<= bExtrusionMetal;
aExtrusionPropVec.push_back( aProp );
+ aProp.Name = "MetalType";
+ aProp.Value <<= css::drawing::EnhancedCustomShapeMetalType::MetalMSCompatible;
+ aExtrusionPropVec.push_back(aProp);
+
// "ShadeMode"
if ( IsProperty( DFF_Prop_c3DRenderMode ) )
{
@@ -1788,7 +1813,7 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt
aProp.Value <<= eExtrusionShadeMode;
aExtrusionPropVec.push_back( aProp );
}
- // "RotateAngle" in Grad
+ // "RotateAngle" in Degree
if ( IsProperty( DFF_Prop_c3DXRotationAngle ) || IsProperty( DFF_Prop_c3DYRotationAngle ) )
{
double fAngleX = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DXRotationAngle, 0 ))) / 65536.0;
@@ -1821,34 +1846,44 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt
}
}
// "Shininess"
+ // MS Office default 5, ODF default 50%.
if ( IsProperty( DFF_Prop_c3DShininess ) )
{
double fShininess = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DShininess, 0 ));
- fShininess /= 655.36;
+ fShininess *= 10.0; // error in [MS ODRAW] (2021), type is not FixedPoint but long.
aProp.Name = "Shininess";
aProp.Value <<= fShininess;
aExtrusionPropVec.push_back( aProp );
}
+
// "Skew"
+ // MS Office angle file value is 16.16 FixedPoint, default 0xFF790000,
+ // -8847360/65536=-135, ODF default 45. Thus must set value even if default.
+ double fSkewAngle = -135.0;
+ // MS Office amount file value is signed integer in range 0xFFFFFF9C to 0x00000064,
+ // default 0x00000032, ODF default 50.0
+ double fSkewAmount = 50.0;
if ( IsProperty( DFF_Prop_c3DSkewAmount ) || IsProperty( DFF_Prop_c3DSkewAngle ) )
{
- double fSkewAmount = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSkewAmount, 50 ));
- double fSkewAngle = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSkewAngle, sal::static_int_cast< sal_uInt32 >(-135 * 65536) ))) / 65536.0;
+ fSkewAmount = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSkewAmount, 50 ));
+ fSkewAngle = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSkewAngle, sal::static_int_cast< sal_uInt32 >(-135 * 65536) ));
+ fSkewAngle /= 65536.0;
+ }
+ EnhancedCustomShapeParameterPair aSkewPair;
+ aSkewPair.First.Value <<= fSkewAmount;
+ aSkewPair.First.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aSkewPair.Second.Value <<= fSkewAngle;
+ aSkewPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aProp.Name = "Skew";
+ aProp.Value <<= aSkewPair;
+ aExtrusionPropVec.push_back( aProp );
- EnhancedCustomShapeParameterPair aSkewPair;
- aSkewPair.First.Value <<= fSkewAmount;
- aSkewPair.First.Type = EnhancedCustomShapeParameterType::NORMAL;
- aSkewPair.Second.Value <<= fSkewAngle;
- aSkewPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL;
- aProp.Name = "Skew";
- aProp.Value <<= aSkewPair;
- aExtrusionPropVec.push_back( aProp );
- }
// "Specularity"
+ // Type Fixed point 16.16, percent in API
if ( IsProperty( DFF_Prop_c3DSpecularAmt ) )
{
double fSpecularity = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSpecularAmt, 0 ));
- fSpecularity /= 1333;
+ fSpecularity /= 655.36;
aProp.Name = "Specularity";
aProp.Value <<= fSpecularity;
aExtrusionPropVec.push_back( aProp );
@@ -1860,16 +1895,22 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt
aExtrusionPropVec.push_back( aProp );
// "ViewPoint" in 1/100mm
+ // MS Office default 1250000 EMU=3472.222 Hmm, ODF default 3.5cm
+ // Thus must set value even if default.
+ double fViewX = 1250000.0 / 360.0;
+ double fViewY = -1250000.0 / 360.0;;
+ double fViewZ = 9000000.0 / 360.0;
if ( IsProperty( DFF_Prop_c3DXViewpoint ) || IsProperty( DFF_Prop_c3DYViewpoint ) || IsProperty( DFF_Prop_c3DZViewpoint ) )
{
- double fViewX = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DXViewpoint, 1250000 ))) / 360.0;
- double fViewY = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DYViewpoint, sal_uInt32(-1250000) )))/ 360.0;
- double fViewZ = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DZViewpoint, 9000000 ))) / 360.0;
- css::drawing::Position3D aExtrusionViewPoint( fViewX, fViewY, fViewZ );
- aProp.Name = "ViewPoint";
- aProp.Value <<= aExtrusionViewPoint;
- aExtrusionPropVec.push_back( aProp );
+ fViewX = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DXViewpoint, 1250000 ))) / 360.0;
+ fViewY = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DYViewpoint, sal_uInt32(-1250000) )))/ 360.0;
+ fViewZ = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DZViewpoint, 9000000 ))) / 360.0;
}
+ css::drawing::Position3D aExtrusionViewPoint( fViewX, fViewY, fViewZ );
+ aProp.Name = "ViewPoint";
+ aProp.Value <<= aExtrusionViewPoint;
+ aExtrusionPropVec.push_back( aProp );
+
// "Origin"
if ( IsProperty( DFF_Prop_c3DOriginX ) || IsProperty( DFF_Prop_c3DOriginY ) )
{
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index c17ffe8af69a..7e278b4cffc3 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -2455,6 +2455,7 @@ namespace xmloff::token {
XML_EXTRUSION_FIRST_LIGHT_DIRECTION,
XML_EXTRUSION_SECOND_LIGHT_DIRECTION,
XML_EXTRUSION_METAL,
+ XML_EXTRUSION_METAL_TYPE,
XML_EXTRUSION_ROTATION_ANGLE,
XML_EXTRUSION_ROTATION_CENTER,
XML_EXTRUSION_SHININESS,
diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk
index 2f4240e75e33..28b3a41f280b 100644
--- a/offapi/UnoApi_offapi.mk
+++ b/offapi/UnoApi_offapi.mk
@@ -2295,6 +2295,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/drawing,\
EnhancedCustomShapeSegmentCommand \
EnhancedCustomShapeTextFrame \
EnhancedCustomShapeTextPathMode \
+ EnhancedCustomShapeMetalType \
EscapeDirection \
FillStyle \
FlagSequence \
diff --git a/offapi/com/sun/star/drawing/EnhancedCustomShapeExtrusion.idl b/offapi/com/sun/star/drawing/EnhancedCustomShapeExtrusion.idl
index bfe99f4029fd..4b589c4aa1d8 100644
--- a/offapi/com/sun/star/drawing/EnhancedCustomShapeExtrusion.idl
+++ b/offapi/com/sun/star/drawing/EnhancedCustomShapeExtrusion.idl
@@ -94,11 +94,20 @@ service EnhancedCustomShapeExtrusion
*/
[optional, property] boolean Metal;
+ /** Specifies in case of Metal=true the way the rendering of the shape is modified.
+ <p>Note: Currently not usable in ODF strict.</p>
+
+ @see EnhancedCustomShapeMetalType
+
+ @since LibreOffice 7.4
+ */
+ [optional, property] short MetalType;
+
/** This property defines the shade mode.
*/
[optional, property] ::com::sun::star::drawing::ShadeMode ShadeMode;
- /** This attributes specifies the rotation angle about the x-axis in grad.
+ /** This attributes specifies the rotation angle about the x-axis in degrees.
The order of rotation is: z-axis, y-axis and then x-axis. The z-axis is
specified by the draw:rotate-angle.
*/
diff --git a/offapi/com/sun/star/drawing/EnhancedCustomShapeMetalType.idl b/offapi/com/sun/star/drawing/EnhancedCustomShapeMetalType.idl
new file mode 100755
index 000000000000..aea502ccd31b
--- /dev/null
+++ b/offapi/com/sun/star/drawing/EnhancedCustomShapeMetalType.idl
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ */
+#ifndef __com_sun_star_drawing_EnhancedCustomShapeMetalType_idl__
+#define __com_sun_star_drawing_EnhancedCustomShapeMetalType_idl__
+
+
+ module com { module sun { module star { module drawing {
+
+/** These constants define the way the attribute Metal of service
+ EnhancedCustomShapeExtrusion is interpreted for rendering the shape.
+ @since LibreOffice 7.4
+ */
+constants EnhancedCustomShapeMetalType
+{
+ /** The rendering of the shape is modified as specified in the ODF standard.
+ */
+ const short MetalODF = 0;
+
+ /** The rendering of the shape is modified to get a similar rendering as in Microsoft Office for objects, which have the fc3DMetallic flag in Rich Text Format or binary MS Office format set.
+ */
+ const short MetalMSCompatible = 1;
+};
+
+
+}; }; }; };
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ \ No newline at end of file
diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
index ce941afd1b77..2df8d41c5eb9 100644
--- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
@@ -2395,6 +2395,15 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
</rng:optional>
</rng:define>
+ <!-- https://issues.oasis-open.org/browse/OFFICE-4123 -->
+ <rng:define name="draw-enhanced-geometry-attlist" combine="interleave">
+ <rng:optional>
+ <rng:attribute name="loext:extrusion-metal-type">
+ <rng:ref name="namespacedToken"/>
+ </rng:attribute>
+ </rng:optional>
+ </rng:define>
+
<!-- TODO no proposal -->
<rng:define name="draw-custom-shape-attlist" combine="interleave">
<rng:ref name="common-draw-rel-size-attlist"/>
diff --git a/svx/qa/unit/customshapes.cxx b/svx/qa/unit/customshapes.cxx
index b420ecebe2e4..081a6b7789e0 100644
--- a/svx/qa/unit/customshapes.cxx
+++ b/svx/qa/unit/customshapes.cxx
@@ -14,16 +14,18 @@
#include <test/bootstrapfixture.hxx>
#include <unotest/macros_test.hxx>
#include <rtl/ustring.hxx>
-#include <editeng/unoprnms.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/point/b2dpoint.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <editeng/unoprnms.hxx>
#include <sfx2/request.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/viewsh.hxx>
#include <svl/intitem.hxx>
#include <svx/EnhancedCustomShape2d.hxx>
#include <svx/extrusionbar.hxx>
+#include <svx/graphichelper.hxx>
#include <svx/svdoashp.hxx>
#include <svx/svdopath.hxx>
#include <svx/svdview.hxx>
@@ -31,13 +33,16 @@
#include <svx/unoapi.hxx>
#include <unotools/mediadescriptor.hxx>
#include <unotools/tempfile.hxx>
+#include <vcl/filter/PngImageReader.hxx>
+#include <vcl/BitmapReadAccess.hxx>
#include <cppunit/TestAssert.h>
+#include <com/sun/star/awt/Rectangle.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/GraphicExportFilter.hpp>
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
#include <com/sun/star/drawing/XDrawPage.hpp>
-#include <com/sun/star/awt/Rectangle.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/XStorable.hpp>
@@ -127,6 +132,106 @@ void lcl_AssertRectEqualWithTolerance(std::string_view sInfo, const tools::Recta
std::abs(rExpected.GetHeight() - rActual.GetHeight()) <= nTolerance);
}
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145700_3D_NonUI)
+{
+ // The document contains first light soft, no ambient color, no second light and shininess 6.
+ // Such settings are not available in the UI. It tests the actual color, not the geometry.
+ // Load document
+ OUString aURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf145700_3D_NonUI.doc";
+ mxComponent = loadFromDesktop(aURL, "com.sun.star.text.TextDocument");
+
+ // Generate bitmap from shape
+ uno::Reference<drawing::XShape> xShape = getShape(0);
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ GraphicHelper::SaveShapeAsGraphicToPath(mxComponent, xShape, "image/png", aTempFile.GetURL());
+
+ // Read bitmap and test color
+ // The expected values are taken from an image generated by Word
+ // Without the changed methods the colors were in range RGB(17,11,17) to RGB(87,55,89).
+ SvFileStream aFileStream(aTempFile.GetURL(), StreamMode::READ);
+ vcl::PngImageReader aPNGReader(aFileStream);
+ BitmapEx aBMPEx = aPNGReader.read();
+ Bitmap aBMP = aBMPEx.GetBitmap();
+ Bitmap::ScopedReadAccess pRead(aBMP);
+ Size aSize = aBMP.GetSizePixel();
+ // GetColor(Y,X)
+ Color aActualColor = pRead->GetColor(aSize.Height() / 2, aSize.Width() * 0.125);
+ Color aExpectedColor(107, 67, 109);
+ sal_uInt16 nColorDistance = aExpectedColor.GetColorError(aActualColor);
+ CPPUNIT_ASSERT_LESS(sal_uInt16(6), nColorDistance);
+ // The current solution for soft light still can be improved. nColorDistance is high.
+ aActualColor = pRead->GetColor(aSize.Height() / 2, aSize.Width() * 0.45);
+ aExpectedColor = Color(179, 113, 183);
+ nColorDistance = aExpectedColor.GetColorError(aActualColor);
+ CPPUNIT_ASSERT_LESS(sal_uInt16(54), nColorDistance);
+ // This point tests whether shininess is read and used. With default shininess it would be white.
+ aActualColor = pRead->GetColor(aSize.Height() / 2, aSize.Width() * 0.72);
+ aExpectedColor = Color(255, 231, 255);
+ nColorDistance = aExpectedColor.GetColorError(aActualColor);
+ CPPUNIT_ASSERT_LESS(sal_uInt16(14), nColorDistance);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145700_3D_FrontLightDim)
+{
+ // This tests the actual color, not the geometry.
+ // Load document
+ OUString aURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf145700_3D_FrontLightDim.doc";
+ mxComponent = loadFromDesktop(aURL, "com.sun.star.text.TextDocument");
+
+ // Generate bitmap from shape
+ uno::Reference<drawing::XShape> xShape = getShape(0);
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ GraphicHelper::SaveShapeAsGraphicToPath(mxComponent, xShape, "image/png", aTempFile.GetURL());
+
+ // Read bitmap and test color
+ // The expected values are taken from an image generated by Word
+ // Without the changed methods the nColorDistance was 476 and 173 respecitively.
+ SvFileStream aFileStream(aTempFile.GetURL(), StreamMode::READ);
+ vcl::PngImageReader aPNGReader(aFileStream);
+ BitmapEx aBMPEx = aPNGReader.read();
+ Bitmap aBMP = aBMPEx.GetBitmap();
+ Bitmap::ScopedReadAccess pRead(aBMP);
+ Size aSize = aBMP.GetSizePixel();
+ // GetColor(Y,X)
+ Color aActualColor = pRead->GetColor(aSize.Height() / 2, aSize.Width() * 0.4);
+ Color aExpectedColor(240, 224, 229);
+ sal_uInt16 nColorDistance = aExpectedColor.GetColorError(aActualColor);
+ CPPUNIT_ASSERT_LESS(sal_uInt16(9), nColorDistance);
+ aActualColor = pRead->GetColor(aSize.Height() / 2, aSize.Width() * 0.9);
+ aExpectedColor = Color(96, 90, 92);
+ nColorDistance = aExpectedColor.GetColorError(aActualColor);
+ CPPUNIT_ASSERT_LESS(sal_uInt16(6), nColorDistance);
+}
+
+CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145700_3D_FirstLightHarsh)
+{
+ // Load document
+ OUString aURL
+ = m_directories.getURLFromSrc(sDataDirectory) + "tdf145700_3D_FirstLightHarsh.doc";
+ mxComponent = loadFromDesktop(aURL, "com.sun.star.text.TextDocument");
+
+ // Generate bitmap from shape
+ uno::Reference<drawing::XShape> xShape = getShape(0);
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ GraphicHelper::SaveShapeAsGraphicToPath(mxComponent, xShape, "image/png", aTempFile.GetURL());
+
+ // Read bitmap and test color in center
+ SvFileStream aFileStream(aTempFile.GetURL(), StreamMode::READ);
+ vcl::PngImageReader aPNGReader(aFileStream);
+ BitmapEx aBMPEx = aPNGReader.read();
+ Bitmap aBMP = aBMPEx.GetBitmap();
+ Bitmap::ScopedReadAccess pRead(aBMP);
+ Size aSize = aBMP.GetSizePixel();
+ // GetColor(Y,X)
+ const Color aActualColor = pRead->GetColor(aSize.Height() / 2, aSize.Width() / 2);
+ const Color aExpectedColor(211, 247, 255); // from image generated by Word
+ sal_uInt16 nColorDistance = aExpectedColor.GetColorError(aActualColor);
+ CPPUNIT_ASSERT_LESS(sal_uInt16(3), nColorDistance);
+}
+
CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf145956_Origin_Relative_BoundRect)
{
// The ViewPoint is relative to point Origin. The coordinates of point Origin are fractions of
diff --git a/svx/qa/unit/data/tdf145700_3D_FirstLightHarsh.doc b/svx/qa/unit/data/tdf145700_3D_FirstLightHarsh.doc
new file mode 100644
index 000000000000..28b8b018d4e6
--- /dev/null
+++ b/svx/qa/unit/data/tdf145700_3D_FirstLightHarsh.doc
Binary files differ
diff --git a/svx/qa/unit/data/tdf145700_3D_FrontLightDim.doc b/svx/qa/unit/data/tdf145700_3D_FrontLightDim.doc
new file mode 100644
index 000000000000..5849e3eac6ba
--- /dev/null
+++ b/svx/qa/unit/data/tdf145700_3D_FrontLightDim.doc
Binary files differ
diff --git a/svx/qa/unit/data/tdf145700_3D_NonUI.doc b/svx/qa/unit/data/tdf145700_3D_NonUI.doc
new file mode 100644
index 000000000000..d62d57cf02cc
--- /dev/null
+++ b/svx/qa/unit/data/tdf145700_3D_NonUI.doc
Binary files differ
diff --git a/svx/qa/unit/svdraw.cxx b/svx/qa/unit/svdraw.cxx
index cab6b56b0ae7..275f75507d64 100644
--- a/svx/qa/unit/svdraw.cxx
+++ b/svx/qa/unit/svdraw.cxx
@@ -398,8 +398,9 @@ CPPUNIT_TEST_FIXTURE(SvdrawTest, testFontWorks)
assertXPath(pXmlDoc, "//scene", "projectionMode", "Perspective");
assertXPath(pXmlDoc, "//scene/extrude3D[1]/fill", "color", "#ff0000");
assertXPath(pXmlDoc, "//scene/extrude3D[1]/object3Dattributes/material", "color", "#ff0000");
+ // ODF default 50% is repesented by Specular Intensity = 2^5. The relationship is not linear.
assertXPath(pXmlDoc, "//scene/extrude3D[1]/object3Dattributes/material", "specularIntensity",
- "20");
+ "32");
}
CPPUNIT_TEST_FIXTURE(SvdrawTest, testSurfaceMetal)
@@ -411,12 +412,12 @@ CPPUNIT_TEST_FIXTURE(SvdrawTest, testSurfaceMetal)
xmlDocUniquePtr pXmlDoc = lcl_dumpAndParseFirstObjectWithAssert(pSdrPage);
- // ODF specifies specular color as rgb(200,200,200) and adding 15 to specularity for metal=true
- // without patch the specular color was #ffffff
- assertXPath(pXmlDoc, "(//material)[1]", "specular", "#c8c8c8");
- // specularIntensity = 100 - (80 + 15), with nominal value 80 in the file
- // without patch specularIntensity was 15
- assertXPath(pXmlDoc, "(//material)[1]", "specularIntensity", "5");
+ // ODF specifies for metal = true specular color as rgb(200,200,200) and adding 15 to specularity
+ // Together with extrusion-first-light-level 67% and extrusion-specularity 80% factor is
+ // 0.67*0.8 * 200/255 = 0.42 and color #6b6b6b
+ assertXPath(pXmlDoc, "(//material)[1]", "specular", "#6b6b6b");
+ // 3D specularIntensity = 2^(50/10) + 15 = 47, with default extrusion-shininess 50%
+ assertXPath(pXmlDoc, "(//material)[1]", "specularIntensity", "47");
}
CPPUNIT_TEST_FIXTURE(SvdrawTest, testExtrusionPhong)
@@ -442,16 +443,20 @@ CPPUNIT_TEST_FIXTURE(SvdrawTest, testSurfaceMattePPT)
xmlDocUniquePtr pXmlDoc = lcl_dumpAndParseFirstObjectWithAssert(pSdrPage);
- // The preset 'matte' in the PPT user interface sets the specularity of material to 0. To get the
- // same effect in LO, specular of the lights need to be false in addition. Without patch the
- // lights 1, 2, 3 are used, with patch lights 2, 3, 4. Thereby light 4 has the same color and
- // direction as light 1, but without being specular. The dump has in both cases three lights, but
- // without number. So we test as ersatz, that the third of them has the color of light 1. Being
- // not light 1, it is never specular in LO, so no need to test.
- // 'color' was "#464646" without patch.
- assertXPath(pXmlDoc, "(//light)[3]", "color", "#aaaaaa");
- // 'specularIntensity' was "15" without patch. specularIntensity = 100 - specularity of material.
- assertXPath(pXmlDoc, "(//material)[1]", "specularIntensity", "100");
+ // The preset 'matte' sets the specularity of material to 0. But that alone does not make the
+ // rendering 'matte' in LO. To get a 'matte' effect in LO, specularity of the light need to be
+ // false in addition. To get this, first light is set off and values from first light are copied
+ // to forth light, as only first light is specular. Because first and third lights are off, the
+ // forth light is the second one in the dump. The gray color corresponding to
+ // FirstLightLevel = 38000/2^16 is #949494.
+ assertXPath(pXmlDoc, "(//material)[1]", "specular", "#000000");
+ assertXPath(pXmlDoc, "(//light)[2]", "color", "#949494");
+ // To make the second light soft, part of its intensity is moved to lights 5,6,7 and 8.
+ assertXPath(pXmlDoc, "(//light)[1]", "color", "#1e1e1e");
+ assertXPath(pXmlDoc, "(//light)[3]", "color", "#3b3b3b");
+ // The 3D property specularIntensity is not related to 'extrusion-specularity' but to
+ // 'extrusion-shininess'. specularIntensity = 2^(shininess/10), here default 32.
+ assertXPath(pXmlDoc, "(//material)[1]", "specularIntensity", "32");
}
CPPUNIT_TEST_FIXTURE(SvdrawTest, testMaterialSpecular)
@@ -465,10 +470,17 @@ CPPUNIT_TEST_FIXTURE(SvdrawTest, testMaterialSpecular)
xmlDocUniquePtr pXmlDoc = lcl_dumpAndParseFirstObjectWithAssert(pSdrPage);
CPPUNIT_ASSERT(pXmlDoc);
- // The material property 'draw:extrusion-specularity' was not applied to the object but to the
- // scene. Without patch the object has always a default value 15 of specularIntensity. The file
- // has specularity=77%. It should be specularIntensity = 100-77=23 with patch.
- assertXPath(pXmlDoc, "(//material)[1]", "specularIntensity", "23");
+ // 3D specular color is derived from properties 'extrusion-specularity' and 'extrusion-first-light
+ // -level'. 3D specularIntensity is dervied from property 'draw:extrusion-shininess'. Both are
+ // object properties, not scene properties. Those were wrong in various forms before the patch.
+ // Specularity = 77% * first-light-level 67% = 0.5159, which corresponds to gray color #848484.
+ assertXPath(pXmlDoc, "(//material)[1]", "specular", "#848484");
+ // extrusion-shinines 50% corresponds to 3D specularIntensity 32, use 2^(50/10).
+ assertXPath(pXmlDoc, "(//material)[1]", "specularIntensity", "32");
+ // extrusion-first-light-level 67% corresponds to gray color #ababab, use 255 * 0.67.
+ assertXPath(pXmlDoc, "(//light)[1]", "color", "#ababab");
+ // The first light is harsh, the second light soft. So the 3D scene should have 6 lights (1+1+4).
+ assertXPath(pXmlDoc, "//light", 6);
}
}
diff --git a/svx/source/customshapes/EnhancedCustomShape3d.cxx b/svx/source/customshapes/EnhancedCustomShape3d.cxx
index c967261cfd22..0d8078fe6bd3 100644
--- a/svx/source/customshapes/EnhancedCustomShape3d.cxx
+++ b/svx/source/customshapes/EnhancedCustomShape3d.cxx
@@ -45,6 +45,7 @@
#include <com/sun/star/drawing/ShadeMode.hpp>
#include <svx/sdr/properties/properties.hxx>
#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeMetalType.hpp>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/range/b2drange.hxx>
#include <sdr/primitive2d/sdrattributecreator.hxx>
@@ -53,6 +54,7 @@
#include <svx/xlnwtit.hxx>
#include <svx/xlntrit.hxx>
#include <svx/xfltrit.hxx>
+#include <basegfx/color/bcolor.hxx>
using namespace com::sun::star;
using namespace com::sun::star::uno;
@@ -90,6 +92,9 @@ void GetSkew( const SdrCustomShapeGeometryItem& rItem, double& rSkewAmount, doub
if ( ! ( pAny && ( *pAny >>= aSkewParaPair ) && ( aSkewParaPair.First.Value >>= rSkewAmount ) && ( aSkewParaPair.Second.Value >>= rSkewAngle ) ) )
{
rSkewAmount = 50;
+ // ODF default is 45, but older ODF documents expect -135 as default. For intermediate
+ // solution see tdf#141301 and tdf#141127.
+ // MS Office default -135 is set in msdffimp.cxx to make import independent from setting here.
rSkewAngle = -135;
}
rSkewAngle = basegfx::deg2rad(rSkewAngle);
@@ -170,6 +175,56 @@ drawing::Direction3D GetDirection3D( const SdrCustomShapeGeometryItem& rItem, co
return aRetValue;
}
+sal_Int16 GetMetalType(const SdrCustomShapeGeometryItem& rItem, const sal_Int16 eDefault)
+{
+ sal_Int16 aRetValue(eDefault);
+ const Any* pAny = rItem.GetPropertyValueByName("Extrusion", "MetalType");
+ if (pAny)
+ *pAny >>= aRetValue;
+ return aRetValue;
+}
+
+// Calculates the light directions for the additional lights, which are used to emulate soft
+// lights of MS Office. Method needs to be documented in the Wiki
+// https://wiki.documentfoundation.org/Development/ODF_Implementer_Notes in part
+// List_of_LibreOffice_ODF_implementation-defined_items
+// The method expects vector rLight to be normalized and results normalized vectors.
+void lcl_SoftLightsDirection(const basegfx::B3DVector& rLight, basegfx::B3DVector& rSoftUp,
+ basegfx::B3DVector& rSoftDown, basegfx::B3DVector& rSoftRight,
+ basegfx::B3DVector& rSoftLeft)
+{
+ constexpr double fAngle = basegfx::deg2rad(60); // angle between regular light and soft light
+
+ // We first create directions around (0|0|1) and then rotate them to the light position.
+ rSoftUp = basegfx::B3DVector(0.0, sin(fAngle), cos(fAngle));
+ rSoftDown = basegfx::B3DVector(0.0, -sin(fAngle), cos(fAngle));
+ rSoftRight = basegfx::B3DVector(sin(fAngle), 0.0, cos(fAngle));
+ rSoftLeft = basegfx::B3DVector(-sin(fAngle), 0.0, cos(fAngle));
+
+ basegfx::B3DHomMatrix aRotateMat;
+ aRotateMat.rotate(0.0, 0.0, M_PI_4);
+ if (rLight.getX() == 0.0 && rLight.getZ() == 0.0)
+ {
+ // Special case with light from top or bottom
+ if (rLight.getY() >= 0.0)
+ aRotateMat.rotate(-M_PI_2, 0.0, 0.0);
+ else
+ aRotateMat.rotate(M_PI_2, 0.0, 0.0);
+ }
+ else
+ {
+ // Azimuth from z-axis to x-axis. (0|0|1) to (1|0|0) is 90deg.
+ double fAzimuth = atan2(rLight.getX(), rLight.getZ());
+ // Elevation from xz-plane to y-axis. (0|0|1) to (0|1|0) is 90deg.
+ double fElevation = atan2(rLight.getY(), std::hypot(rLight.getX(), rLight.getZ()));
+ aRotateMat.rotate(-fElevation, fAzimuth, 0.0);
+ }
+
+ rSoftUp = aRotateMat * rSoftUp;
+ rSoftDown = aRotateMat * rSoftDown;
+ rSoftRight = aRotateMat * rSoftRight;
+ rSoftLeft = aRotateMat * rSoftLeft;
+}
}
SdrObject* EnhancedCustomShape3d::Create3DObject(
@@ -256,7 +311,7 @@ SdrObject* EnhancedCustomShape3d::Create3DObject(
pScene->GetProperties().SetObjectItem( Svx3DShadeModeItem(static_cast<sal_uInt16>(eShadeMode)));
aSet.Put( makeSvx3DPercentDiagonalItem( 0 ) );
aSet.Put( Svx3DTextureModeItem( 1 ) );
- // SPECIFIC needed for ShadeMode_SMOOTH and ShadeMode_PHONG, otherwise FLAT is faster
+ // SPECIFIC needed for ShadeMode_SMOOTH and ShadeMode_PHONG, otherwise FLAT is faster.
if (eShadeMode == drawing::ShadeMode_SMOOTH || eShadeMode == drawing::ShadeMode_PHONG)
aSet.Put( Svx3DNormalsKindItem(static_cast<sal_uInt16>(drawing::NormalsKind_SPECIFIC)));
else
@@ -703,105 +758,259 @@ SdrObject* EnhancedCustomShape3d::Create3DObject(
pScene->SetLogicRect(a2DProjectionResult.GetBoundRect());
- // light
-
-
- drawing::Direction3D aFirstLightDirectionDefault( 50000, 0, 10000 );
- drawing::Direction3D aFirstLightDirection( GetDirection3D( rGeometryItem, "FirstLightDirection", aFirstLightDirectionDefault ) );
- if ( aFirstLightDirection.DirectionZ == 0.0 )
- aFirstLightDirection.DirectionZ = 1.0;
-
- double fLightIntensity = GetDouble( rGeometryItem, "FirstLightLevel", 43712.0 / 655.36 ) / 100.0;
+ // light and material
- bool bFirstLightHarsh = GetBool( rGeometryItem, "FirstLightHarsh", true );
+ // "LightFace" has nothing corresponding in 3D rendering engine.
+ /* bool bLightFace = */ GetBool(rGeometryItem, "LightFace", true); // default in ODF
- drawing::Direction3D aSecondLightDirectionDefault( -50000, 0, 10000 );
- drawing::Direction3D aSecondLightDirection( GetDirection3D( rGeometryItem, "SecondLightDirection", aSecondLightDirectionDefault ) );
- if ( aSecondLightDirection.DirectionZ == 0.0 )
- aSecondLightDirection.DirectionZ = -1;
+ // Light directions
- double fLight2Intensity = GetDouble( rGeometryItem, "SecondLightLevel", 43712.0 / 655.36 ) / 100.0;
+ drawing::Direction3D aFirstLightDirectionDefault(50000.0, 0.0, 10000.0);
+ drawing::Direction3D aFirstLightDirection(GetDirection3D( rGeometryItem, "FirstLightDirection", aFirstLightDirectionDefault));
+ if (aFirstLightDirection.DirectionX == 0.0 && aFirstLightDirection.DirectionY == 0.0
+ && aFirstLightDirection.DirectionZ == 0.0)
+ aFirstLightDirection.DirectionZ = 1.0;
+ basegfx::B3DVector aLight1Vector(aFirstLightDirection.DirectionX, -aFirstLightDirection.DirectionY, aFirstLightDirection.DirectionZ);
+ aLight1Vector.normalize();
+
+ drawing::Direction3D aSecondLightDirectionDefault(-50000.0, 0.0, 10000.0);
+ drawing::Direction3D aSecondLightDirection(GetDirection3D( rGeometryItem, "SecondLightDirection", aSecondLightDirectionDefault));
+ if (aSecondLightDirection.DirectionX == 0.0 && aSecondLightDirection.DirectionY == 0.0
+ && aSecondLightDirection.DirectionZ == 0.0)
+ aSecondLightDirection.DirectionZ = 1.0;
+ basegfx::B3DVector aLight2Vector(aSecondLightDirection.DirectionX, -aSecondLightDirection.DirectionY, aSecondLightDirection.DirectionZ);
+ aLight2Vector.normalize();
+
+ // Light Intensity
+
+ // For "FirstLight" the 3D-Scene light "1" is regulary used. In case of surface "Matte"
+ // the light 4 is used instead. For "SecondLight" the 3D-Scene light "2" is regulary used.
+ // In case first or second light is not harsh, the lights 5 to 8 are used in addition
+ // to get a soft light appearance.
+ // The 3D-Scene light "3" is currently not used.
+
+ // ODF default 66%. MS Office default 38000/65536=0.579 is set in import filter.
+ double fLight1Intensity = GetDouble(rGeometryItem, "FirstLightLevel", 66) / 100.0;
+ // ODF and MS Office have both default 'true'.
+ bool bFirstLightHarsh = GetBool(rGeometryItem, "FirstLightHarsh", true);
+ // ODF default 66%. MS Office default 38000/65536=0.579 is set in import filter
+ double fLight2Intensity = GetDouble(rGeometryItem, "SecondLightLevel", 66) / 100.0;
+ // ODF has default 'true'. MS Office default 'false' is set in import.
+ bool bSecondLightHarsh = GetBool(rGeometryItem, "SecondLightHarsh", true);
+
+ // ODF default 33%. MS Office default 20000/65536=0.305 is set in import filter.
+ double fAmbientIntensity = GetDouble(rGeometryItem, "Brightness", 33) / 100.0;
+
+ double fLight1IntensityForSpecular(fLight1Intensity); // remember original value
+ if (!bFirstLightHarsh || !bSecondLightHarsh) // might need softing lights
+ {
+ bool bNeedSoftLights(false); // catch case of lights with zero intensity.
+ basegfx::B3DVector aLight5Vector;
+ basegfx::B3DVector aLight6Vector;
+ basegfx::B3DVector aLight7Vector;
+ basegfx::B3DVector aLight8Vector;
+ // The needed light intensities depend on the angle between regular light and
+ // additional lights, currently for 60deg.
+ Color aHoriSoftLightColor;
+ Color aVertSoftLightColor;
+
+ if (!bSecondLightHarsh && fLight2Intensity > 0.0
+ && (bFirstLightHarsh || fLight1Intensity == 0.0)) // only second light soft
+ {
+ // That is default for shapes generated in the UI, for LO and MS Office as well.
+ bNeedSoftLights = true;
+ double fLight2SoftIntensity = fLight2Intensity * 0.40;
+ aHoriSoftLightColor = Color(basegfx::BColor(fLight2SoftIntensity).clamp());
+ aVertSoftLightColor = aHoriSoftLightColor;
+ fLight2Intensity *= 0.2;
+
+ lcl_SoftLightsDirection(aLight2Vector, aLight5Vector, aLight6Vector,
+ aLight7Vector, aLight8Vector);
+ }
+ else if (!bFirstLightHarsh && fLight1Intensity > 0.0
+ && (bSecondLightHarsh || fLight2Intensity == 0.0)) // only first light soft
+ {
+ bNeedSoftLights = true;
+ double fLight1SoftIntensity = fLight1Intensity * 0.40;
+ aHoriSoftLightColor = Color(basegfx::BColor(fLight1SoftIntensity).clamp());
+ aVertSoftLightColor = aHoriSoftLightColor;
+ fLight1Intensity *= 0.2;
+
+ lcl_SoftLightsDirection(aLight1Vector, aLight5Vector, aLight6Vector,
+ aLight7Vector, aLight8Vector);
+ }
+ else if (!bFirstLightHarsh && fLight1Intensity > 0.0 && !bSecondLightHarsh
+ && fLight2Intensity > 0.0) // both lights soft
+ {
+ bNeedSoftLights = true;
+ // We do not hat enough lights. We use two soft lights for FirstLight and two for
+ // SecondLight and double intensity.
+ double fLight1SoftIntensity = fLight1Intensity * 0.8;
+ fLight1Intensity *= 0.4;
+ aHoriSoftLightColor = Color(basegfx::BColor(fLight1SoftIntensity).clamp());
+ basegfx::B3DVector aDummy1, aDummy2;
+ lcl_SoftLightsDirection(aLight1Vector, aDummy1, aDummy2, aLight7Vector,
+ aLight8Vector);
+
+ double fLight2SoftIntensity = fLight2Intensity * 0.8;
+ aVertSoftLightColor = Color(basegfx::BColor(fLight2SoftIntensity).clamp());
+ fLight2Intensity *= 0.4;
+ lcl_SoftLightsDirection(aLight2Vector, aLight5Vector, aLight6Vector, aDummy1,
+ aDummy2);
+ }
- /* sal_Bool bLight2Harsh = */ GetBool( rGeometryItem, "SecondLightHarsh", false );
- /* sal_Bool bLightFace = */ GetBool( rGeometryItem, "LightFace", false );
+ if (bNeedSoftLights)
+ {
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightDirection5Item(aLight5Vector));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightcolor5Item(aVertSoftLightColor));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff5Item(true));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightDirection6Item(aLight6Vector));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightcolor6Item(aVertSoftLightColor));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff6Item(true));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightDirection7Item(aLight7Vector));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightcolor7Item(aHoriSoftLightColor));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff7Item(true));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightDirection8Item(aLight8Vector));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightcolor8Item(aHoriSoftLightColor));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff8Item(true));
+ }
+ }
- double fAmbientIntensity = GetDouble( rGeometryItem, "Brightness", 22178.0 / 655.36 ) / 100.0;
- bool bMetal = GetBool( rGeometryItem, "Metal", false );
+ // ToDo: MSO seems to add half of the surplus to ambient color. ODF restricts value to <1.
+ if (fLight1Intensity > 1.0)
+ {
+ fAmbientIntensity += (fLight1Intensity - 1.0) / 2.0;
+ }
- // Currently needed for import from binary MS Office.
- // ToDo: Create a solution in the filters.
- // MS Office adds black to diffuse and ambient color in case of metal. Use an
- // approximating ersatz.
- if (bMetal)
+ // ToDo: How to handle fAmbientIntensity larger 1.0 ? Perhaps lighten object color?
+
+ // Now set the regulary 3D-scene light attributes.
+ Color aAmbientColor(basegfx::BColor(fAmbientIntensity).clamp());
+ pScene->GetProperties().SetObjectItem(makeSvx3DAmbientcolorItem(aAmbientColor));
+
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection1Item(aLight1Vector));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff1Item(fLight1Intensity > 0.0));
+ Color aLight1Color(basegfx::BColor(fLight1Intensity).clamp());
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor1Item(aLight1Color));
+
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection2Item(aLight2Vector));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff2Item(fLight2Intensity > 0.0));
+ Color aLight2Color(basegfx::BColor(fLight2Intensity).clamp());
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor2Item(aLight2Color));
+
+ // Object reactions on light
+ // Diffusion, Specular-Color and -Intensity are object properties, not scene properties.
+ // Surface flag "Metal" is an object property too.
+
+ // Property "Diffusion" would correspond to style attribute "drd3:diffuse-color".
+ // But that is not implemented. We cannot ignore the attribute because MS Office sets
+ // attribute c3DDiffuseAmt to 43712 (Type Fixed 16.16, approx 66,9%) instead of MSO
+ // default 65536 (100%), if the user sets surface 'Metal' in the UI of MS Office.
+ // We will change the material color of the 3D object as ersatz.
+ // ODF data type is percent with default 0%. MSO default is set in import filter.
+ double fDiffusion = GetDouble(rGeometryItem, "Diffusion", 0.0) / 100.0;
+
+ // ODF standard specifies for value true: "the specular color for the shading of an
+ // extruded shape is gray (red, green and blue values of 200) instead of white and 15% is
+ // added to the specularity."
+ // Neither 'specularity' nor 'specular color' is clearly defined in the standard. ODF term
+ // 'specularity' seems to correspond to UI field 'Specular Intensity' for 3D scenes.
+ // MS Office uses current material color in case 'Metal' is set. To detect, whether
+ // rendering similar to MS Office has to be used the property 'MetalType' is used. It is
+ // set on import and in the extrusion bar.
+ bool bMetal = GetBool(rGeometryItem, "Metal", false);
+ sal_Int16 eMetalType(
+ GetMetalType(rGeometryItem, drawing::EnhancedCustomShapeMetalType::MetalODF));
+ bool bMetalMSCompatible
+ = eMetalType == drawing::EnhancedCustomShapeMetalType::MetalMSCompatible;
+
+ // Property "Specularity" corresponds to 3D object style attribute dr3d:specular-color.
+ double fSpecularity = GetDouble(rGeometryItem, "Specularity", 0) / 100.0;
+
+ if (bMetal && !bMetalMSCompatible)
{
- fAmbientIntensity -= 0.15; // Estimated value. Adapt it if necessary.
- fAmbientIntensity = std::clamp(fAmbientIntensity, 0.0, 1.0);
- fLight2Intensity -= 0.15;
- fLight2Intensity = std::clamp(fLight2Intensity, 0.0, 1.0);
+ fSpecularity *= 200.0 / 255.0;
}
- sal_uInt16 nAmbientColor = static_cast<sal_uInt16>( fAmbientIntensity * 255.0 );
- if ( nAmbientColor > 255 )
- nAmbientColor = 255;
- Color aGlobalAmbientColor( static_cast<sal_uInt8>(nAmbientColor), static_cast<sal_uInt8>(nAmbientColor), static_cast<sal_uInt8>(nAmbientColor) );
- pScene->GetProperties().SetObjectItem( makeSvx3DAmbientcolorItem( aGlobalAmbientColor ) );
-
- sal_uInt8 nSpotLight1 = static_cast<sal_uInt8>( fLightIntensity * 255.0 );
- basegfx::B3DVector aSpotLight1( aFirstLightDirection.DirectionX, - ( aFirstLightDirection.DirectionY ), -( aFirstLightDirection.DirectionZ ) );
- aSpotLight1.normalize();
- pScene->GetProperties().SetObjectItem( makeSvx3DLightOnOff1Item( true ) );
- Color aAmbientSpot1Color( nSpotLight1, nSpotLight1, nSpotLight1 );
- pScene->GetProperties().SetObjectItem( makeSvx3DLightcolor1Item( aAmbientSpot1Color ) );
- pScene->GetProperties().SetObjectItem( makeSvx3DLightDirection1Item( aSpotLight1 ) );
-
- sal_uInt8 nSpotLight2 = static_cast<sal_uInt8>( fLight2Intensity * 255.0 );
- basegfx::B3DVector aSpotLight2( aSecondLightDirection.DirectionX, -aSecondLightDirection.DirectionY, -aSecondLightDirection.DirectionZ );
- aSpotLight2.normalize();
- pScene->GetProperties().SetObjectItem( makeSvx3DLightOnOff2Item( true ) );
- Color aAmbientSpot2Color( nSpotLight2, nSpotLight2, nSpotLight2 );
- pScene->GetProperties().SetObjectItem( makeSvx3DLightcolor2Item( aAmbientSpot2Color ) );
- pScene->GetProperties().SetObjectItem( makeSvx3DLightDirection2Item( aSpotLight2 ) );
-
- // Currently needed for import from binary MS Office.
- // ToDo: Create a solution in the filters.
- // Binary MS Office creates brighter shapes than our 3D engine with same values.
- sal_uInt8 nSpotLight3 = 70;
- basegfx::B3DVector aSpotLight3( 0.0, 0.0, 1.0 );
- pScene->GetProperties().SetObjectItem( makeSvx3DLightOnOff3Item( true ) );
- Color aAmbientSpot3Color( nSpotLight3, nSpotLight3, nSpotLight3 );
- pScene->GetProperties().SetObjectItem( makeSvx3DLightcolor3Item( aAmbientSpot3Color ) );
- pScene->GetProperties().SetObjectItem( makeSvx3DLightDirection3Item( aSpotLight3 ) );
-
- double fSpecular = GetDouble( rGeometryItem, "Specularity", 0 );
- // ODF specifies 'white', OOXML uses shape fill color in some presets
- Color aSpecularCol(255, 255, 255);
- if ( bMetal )
+ // MS Office seems to render as if 'Specular Color' = Specularity * Light1Intensity.
+ double fShadingFactor = fLight1IntensityForSpecular * fSpecularity;
+ Color aSpecularCol(basegfx::BColor(fShadingFactor).clamp());
+ // In case of bMetalMSCompatible the color will be recalculated in the below loop.
+
+ // Shininess ODF default 50 (unit %). MS Office default 5, import filter makes *10.
+ // Shininess corresponds to "Specular Intensity" with the nonlinear relationship
+ // "Specular Intensity" = 2^c3DShininess = 2^("Shininess" / 10)
+ double fShininess = GetDouble(rGeometryItem, "Shininess", 50) / 10.0;
+ fShininess = std::clamp<double>(pow(2, fShininess), 0.0, 100.0);
+ sal_uInt16 nIntensity = static_cast<sal_uInt16>(basegfx::fround(fShininess));
+ if (bMetal && !bMetalMSCompatible)
{
- // values as specified in ODF
- aSpecularCol = Color( 200, 200, 200 );
- fSpecular += 15.0;
+ nIntensity += 15; // as specified in ODF
+ nIntensity = std::clamp<sal_uInt16>(nIntensity, 0, 100);
}
- sal_Int32 nIntensity = 100 - static_cast<sal_Int32>(fSpecular);
- nIntensity = std::clamp<sal_Int32>(nIntensity, 0, 100);
- // specularity is an object property, not a scene property
- SdrObjListIter aSceneIter( *pScene, SdrIterMode::DeepNoGroups );
+ SdrObjListIter aSceneIter(*pScene, SdrIterMode::DeepNoGroups);
while (aSceneIter.IsMore())
{
const SdrObject* pNext = aSceneIter.Next();
+
+ // Change material color as ersatz for missing style attribute "drd3:diffuse-color".
+ // For this ersatz we exclude case fDiffusion == 0.0, because for older documents this
+ // attribute is not written out to draw:extrusion-diffusion and ODF default 0 would
+ // produce black objects.
+ const Color& rMatColor
+ = pNext->GetProperties().GetItem(XATTR_FILLCOLOR).GetColorValue();
+ Color aOldMatColor(rMatColor);
+ if (basegfx::fTools::more(fDiffusion, 0.0)
+ && !basegfx::fTools::equal(fDiffusion, 1.0))
+ {
+ // Occurs e.g. with MS surface preset 'Metal'.
+ sal_uInt16 nHue;
+ sal_uInt16 nSaturation;
+ sal_uInt16 nBrightness;
+ rMatColor.RGBtoHSB(nHue, nSaturation, nBrightness);
+ nBrightness
+ = static_cast<sal_uInt16>(static_cast<double>(nBrightness) * fDiffusion);
+ nBrightness = std::clamp<sal_uInt16>(nBrightness, 0, 100);
+ Color aNewMatColor = Color::HSBtoRGB(nHue, nSaturation, nBrightness);
+ pNext->GetProperties().SetObjectItem(XFillColorItem("", aNewMatColor));
+ }
+
+ // Using material color instead of gray in case of MS Office compatible rendering.
+ if (bMetal && bMetalMSCompatible)
+ {
+ sal_uInt16 nHue;
+ sal_uInt16 nSaturation;
+ sal_uInt16 nBrightness;
+ aOldMatColor.RGBtoHSB(nHue, nSaturation, nBrightness);
+ nBrightness = static_cast<sal_uInt16>(static_cast<double>(nBrightness)
+ * fShadingFactor);
+ nBrightness = std::clamp<sal_uInt16>(nBrightness, 0, 100);
+ aSpecularCol = Color::HSBtoRGB(nHue, nSaturation, nBrightness);
+ }
+
pNext->GetProperties().SetObjectItem(makeSvx3DMaterialSpecularItem(aSpecularCol));
- pNext->GetProperties().SetObjectItem(makeSvx3DMaterialSpecularIntensityItem(static_cast<sal_uInt16>(nIntensity)));
+ pNext->GetProperties().SetObjectItem(
+ makeSvx3DMaterialSpecularIntensityItem(nIntensity));
}
- // fSpecular = 0 is used to indicate surface preset "matte".
- if (!bFirstLightHarsh || basegfx::fTools::equalZero(fSpecular, 0.0001))
+ // fSpecularity = 0 is used to indicate surface preset "Matte".
+ if (basegfx::fTools::equalZero(fSpecularity))
{
// First light in LO 3D engine is always specular, all other lights are never specular.
// We copy light1 values to light4 and use it instead of light1 in the 3D scene.
pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff1Item(false));
pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff4Item(true));
- pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor4Item(aAmbientSpot1Color));
- pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection4Item(aSpotLight1));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor4Item(aLight1Color));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection4Item(aLight1Vector));
}
// removing placeholder objects
diff --git a/svx/source/tbxctrls/extrusioncontrols.cxx b/svx/source/tbxctrls/extrusioncontrols.cxx
index c54004b24cec..b0d8a1238610 100644
--- a/svx/source/tbxctrls/extrusioncontrols.cxx
+++ b/svx/source/tbxctrls/extrusioncontrols.cxx
@@ -829,11 +829,13 @@ ExtrusionSurfaceWindow::ExtrusionSurfaceWindow(svt::PopupWindowController* pCont
, mxMatt(m_xBuilder->weld_radio_button("matt"))
, mxPlastic(m_xBuilder->weld_radio_button("plastic"))
, mxMetal(m_xBuilder->weld_radio_button("metal"))
+ , mxMetalMSO(m_xBuilder->weld_radio_button("metalMSO"))
{
mxWireFrame->connect_toggled(LINK(this, ExtrusionSurfaceWindow, SelectHdl));
mxMatt->connect_toggled(LINK(this, ExtrusionSurfaceWindow, SelectHdl));
mxPlastic->connect_toggled(LINK(this, ExtrusionSurfaceWindow, SelectHdl));
mxMetal->connect_toggled(LINK(this, ExtrusionSurfaceWindow, SelectHdl));
+ mxMetalMSO->connect_toggled(LINK(this, ExtrusionSurfaceWindow, SelectHdl));
AddStatusListener( g_sExtrusionSurface );
}
@@ -853,6 +855,8 @@ void ExtrusionSurfaceWindow::implSetSurface( int nSurface, bool bEnabled )
mxPlastic->set_sensitive(bEnabled);
mxMetal->set_active(nSurface == 3 && bEnabled);
mxMetal->set_sensitive(bEnabled);
+ mxMetalMSO->set_active(nSurface == 4 && bEnabled);
+ mxMetalMSO->set_sensitive(bEnabled);
}
void ExtrusionSurfaceWindow::statusChanged(
@@ -886,8 +890,10 @@ IMPL_LINK(ExtrusionSurfaceWindow, SelectHdl, weld::Toggleable&, rButton, void)
nSurface = 1;
else if (mxPlastic->get_active())
nSurface = 2;
- else
+ else if (mxMetal->get_active())
nSurface = 3;
+ else
+ nSurface = 4;
Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(
OUString(g_sExtrusionSurface).copy(5), nSurface) };
diff --git a/svx/source/tbxctrls/extrusioncontrols.hxx b/svx/source/tbxctrls/extrusioncontrols.hxx
index 7e0043487274..8b7c2e8afcc7 100644
--- a/svx/source/tbxctrls/extrusioncontrols.hxx
+++ b/svx/source/tbxctrls/extrusioncontrols.hxx
@@ -188,6 +188,7 @@ private:
std::unique_ptr<weld::RadioButton> mxMatt;
std::unique_ptr<weld::RadioButton> mxPlastic;
std::unique_ptr<weld::RadioButton> mxMetal;
+ std::unique_ptr<weld::RadioButton> mxMetalMSO;
DECL_LINK( SelectHdl, weld::Toggleable&, void );
diff --git a/svx/source/toolbars/extrusionbar.cxx b/svx/source/toolbars/extrusionbar.cxx
index 659af238d77e..26ac4805cde0 100644
--- a/svx/source/toolbars/extrusionbar.cxx
+++ b/svx/source/toolbars/extrusionbar.cxx
@@ -18,6 +18,7 @@
*/
+#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/ShadeMode.hpp>
@@ -126,9 +127,9 @@ static void impl_execute( SfxRequest const & rReq, SdrCustomShapeGeometryItem& r
{
css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, sExtrusion );
+ bool bOn(false);
if( pAny )
{
- bool bOn(false);
(*pAny) >>= bOn;
bOn = !bOn;
(*pAny) <<= bOn;
@@ -139,6 +140,23 @@ static void impl_execute( SfxRequest const & rReq, SdrCustomShapeGeometryItem& r
aPropValue.Name = sExtrusion;
aPropValue.Value <<= true;
rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+ bOn = true;
+ }
+ // draw:extrusion-diffusion has default 0% and c3DDiffuseAmt has default 100%. We set property
+ // "Diffusion" with value 100% here if it does not exist already. This forces, that the
+ // property is written to file in case an extrusion is newly created, and users of old
+ // documents, which usually do not have this property, can force the value to 100% by toggling
+ // the extrusion off and on.
+ if (bOn)
+ {
+ pAny = rGeometryItem.GetPropertyValueByName(sExtrusion, u"Diffusion");
+ if (!pAny)
+ {
+ css::beans::PropertyValue aPropValue;
+ aPropValue.Name = u"Diffusion";
+ aPropValue.Value <<= 100.0;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+ }
}
}
break;
@@ -335,52 +353,75 @@ static void impl_execute( SfxRequest const & rReq, SdrCustomShapeGeometryItem& r
break;
case 1: // matte
case 2: // plastic
- case 3: // metal
+ case 3: // metal ODF
+ case 4: // metal MS Office
if (eOldShadeMode == ShadeMode_DRAFT)
eShadeMode = ShadeMode_FLAT; // ODF default
break;
}
- bool bMetal = nSurface == 3;
-
// ODF has no dedicated property for 'surface'. MS Office binary format uses attribute
// c3DSpecularAmt to distinguish between 'matte' (=0) and 'plastic'.
- // From point of ODF, using not harsh light has similar effect.
+ // We do the same.
double fOldSpecularity = 0.0;
pAny = rGeometryItem.GetPropertyValueByName(sExtrusion, u"Specularity");
if (pAny)
*pAny >>= fOldSpecularity;
double fSpecularity = fOldSpecularity;
- bool bOldIsFirstLightHarsh = true;
- pAny = rGeometryItem.GetPropertyValueByName(sExtrusion, u"FirstLightHarsh");
- if (pAny)
- *pAny >>= bOldIsFirstLightHarsh;
- bool bIsFirstLightHarsh = bOldIsFirstLightHarsh;
switch( nSurface )
{
case 0: // wireframe
break;
case 1: // matte
fSpecularity = 0.0;
- bIsFirstLightHarsh = false;
break;
case 2: // plastic
- case 3: // metal
+ case 3: // metal ODF
+ case 4: // metal MS Office
if (basegfx::fTools::equalZero(fOldSpecularity, 0.0001))
- fSpecularity = 80; // estimated value, can be changed if necessary
- if (!bOldIsFirstLightHarsh)
- bIsFirstLightHarsh = true;
+ // MS Office uses 80000/65536. That is currently not allowed in ODF.
+ // But the ODF error will be catched in xmloff.
+ fSpecularity = 80000.0 / 655.36; // interpreted as %
break;
}
+ // MS Office binary format uses attribute c3DDiffuseAmt with value =43712 (Fixed 16.16) in
+ // addition to the 'metal' flag. For other surface kinds default = 65536 is used.
+ // We toggle between 100 and 43712.0 / 655.36 here, to get better ODF -> MSO binary.
+ // We keep other values, those might be set outside regular UI, e.g by macro.
+ double fOldDiffusion = 100.0;
+ pAny = rGeometryItem.GetPropertyValueByName(sExtrusion, u"Diffusion");
+ if (pAny)
+ *pAny >>= fOldDiffusion;
+ double fDiffusion = fOldDiffusion;
+ if (nSurface == 4)
+ {
+ if (fOldDiffusion == 100.0)
+ fDiffusion = 43712.0 / 655.36; // interpreted as %
+ }
+ else
+ {
+ if (basegfx::fTools::equalZero(fOldDiffusion - 43712.0 / 655.36, 0.0001))
+ fDiffusion = 100.0;
+ }
+
css::beans::PropertyValue aPropValue;
aPropValue.Name = "ShadeMode";
aPropValue.Value <<= eShadeMode;
rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
aPropValue.Name = "Metal";
- aPropValue.Value <<= bMetal;
- rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+ aPropValue.Value <<= nSurface == 3 || nSurface == 4;
+ rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
+
+ if (nSurface == 3 || nSurface == 4)
+ {
+ aPropValue.Name = "MetalType";
+ aPropValue.Value <<= nSurface == 4
+ ? EnhancedCustomShapeMetalType::MetalMSCompatible
+ : EnhancedCustomShapeMetalType::MetalODF;
+ rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
+ }
if (!basegfx::fTools::equalZero(fOldSpecularity - fSpecularity, 0.0001))
{
@@ -389,10 +430,10 @@ static void impl_execute( SfxRequest const & rReq, SdrCustomShapeGeometryItem& r
rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
}
- if (bOldIsFirstLightHarsh != bIsFirstLightHarsh)
+ if (!basegfx::fTools::equalZero(fOldDiffusion - fDiffusion, 0.0001))
{
- aPropValue.Name = "FirstLightHarsh";
- aPropValue.Value <<= bIsFirstLightHarsh;
+ aPropValue.Name = "Diffusion";
+ aPropValue.Value <<= fDiffusion;
rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
}
}
@@ -404,16 +445,17 @@ static void impl_execute( SfxRequest const & rReq, SdrCustomShapeGeometryItem& r
{
sal_Int32 nLevel = rReq.GetArgs()->GetItem<SfxInt32Item>(SID_EXTRUSION_LIGHTING_INTENSITY)->GetValue();
- double fBrightness;
- double fLevel1;
- double fLevel2;
+ double fBrightness; // c3DAmbientIntensity in MS Office
+ double fLevel1; // c3DKeyIntensity in MS Office
+ double fLevel2; // c3DFillIntensity in MS Office
+ // ToDo: "bright" values are different from MS Office. Should they be kept?
switch( nLevel )
{
case 0: // bright
- fBrightness = 34.0;
- fLevel1 = 66.0;
- fLevel2 = 66.0;
+ fBrightness = 33.0; // ODF default.
+ fLevel1 = 66.0; // ODF default
+ fLevel2 = 66.0; // ODF default
break;
case 1: // normal
fBrightness = 15.0;
@@ -432,10 +474,6 @@ static void impl_execute( SfxRequest const & rReq, SdrCustomShapeGeometryItem& r
aPropValue.Value <<= fBrightness;
rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
- aPropValue.Name = "SecondLightHarsh";
- aPropValue.Value <<= false;
- rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
-
aPropValue.Name = "FirstLightLevel";
aPropValue.Value <<= fLevel1;
rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
@@ -443,6 +481,12 @@ static void impl_execute( SfxRequest const & rReq, SdrCustomShapeGeometryItem& r
aPropValue.Name = "SecondLightLevel";
aPropValue.Value <<= fLevel2;
rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+
+ // If a user sets light preset 'Dim' in MS Office, MS Office sets second light to harsh.
+ // In other cases it is soft.
+ aPropValue.Name = "SecondLightHarsh";
+ aPropValue.Value <<= nLevel == 2;
+ rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
}
}
break;
@@ -665,8 +709,8 @@ static void getExtrusionDirectionState( SdrView const * pSdrView, SfxItemSet& rS
}
bool bParallel = true;
- Position3D aViewPoint( 3472, -3472, 25000 );
- double fSkewAngle = -135;
+ Position3D aViewPoint( 3472, -3472, 25000 ); // MSO default
+ double fSkewAngle = -135; // MSO default
pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "ProjectionMode" );
sal_Int16 nProjectionMode = sal_Int16();
@@ -869,25 +913,33 @@ static void getExtrusionSurfaceState( SdrView const * pSdrView, SfxItemSet& rSet
sal_Int32 nSurface = 0; // wire frame
ShadeMode eShadeMode( ShadeMode_FLAT );
- pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "ShadeMode" );
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, u"ShadeMode" );
if( pAny )
*pAny >>= eShadeMode;
if (eShadeMode != ShadeMode_DRAFT)
{
bool bMetal = false;
- pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "Metal" );
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, u"Metal" );
if( pAny )
*pAny >>= bMetal;
if( bMetal )
{
- nSurface = 3; // metal
+ nSurface = 3; // metal ODF
+ sal_Int16 eMetalType;
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, u"MetalType" );
+ if (pAny)
+ {
+ *pAny >>= eMetalType;
+ if (eMetalType == EnhancedCustomShapeMetalType::MetalMSCompatible)
+ nSurface = 4; // metal MS Office
+ }
}
else
{
double fSpecularity = 0;
- pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "Specularity" );
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, u"Specularity" );
if( pAny )
*pAny >>= fSpecularity;
@@ -951,7 +1003,7 @@ static void getExtrusionDepthState( SdrView const * pSdrView, SfxItemSet& rSet )
continue;
}
- double fDepth = 1270.0;
+ double fDepth = 1270.0; // =36pt ODF default
pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "Depth" );
if( pAny )
{
diff --git a/svx/uiconfig/ui/surfacewindow.ui b/svx/uiconfig/ui/surfacewindow.ui
index e7f612b8e7a9..52688d176f47 100644
--- a/svx/uiconfig/ui/surfacewindow.ui
+++ b/svx/uiconfig/ui/surfacewindow.ui
@@ -22,6 +22,11 @@
<property name="can_focus">False</property>
<property name="icon_name">svx/res/wireframe_16.png</property>
</object>
+ <object class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">svx/res/metal_16.png</property>
+ </object>
<object class="GtkPopover" id="SurfaceWindow">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
@@ -89,7 +94,7 @@
</child>
<child>
<object class="GtkRadioButton" id="metal">
- <property name="label" translatable="yes" context="surfacewindow|RID_SVXSTR_METAL">Me_tal</property>
+ <property name="label" translatable="yes" context="surfacewindow|RID_SVXSTR_METAL">Me_tal ODF</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -105,6 +110,24 @@
<property name="position">3</property>
</packing>
</child>
+ <child>
+ <object class="GtkRadioButton" id="metalMSO">
+ <property name="label" translatable="yes" context="surfacewindow|RID_SVXSTR_METALMSO">Meta_l MS compatible</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="image">image5</property>
+ <property name="use_underline">True</property>
+ <property name="always_show_image">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">wireframe</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
</object>
</child>
</object>
diff --git a/xmloff/inc/EnhancedCustomShapeToken.hxx b/xmloff/inc/EnhancedCustomShapeToken.hxx
index 328704f9b889..7fd86f5a5126 100644
--- a/xmloff/inc/EnhancedCustomShapeToken.hxx
+++ b/xmloff/inc/EnhancedCustomShapeToken.hxx
@@ -47,6 +47,7 @@ namespace xmloff::EnhancedCustomShapeToken {
EAS_extrusion_first_light_direction,
EAS_extrusion_second_light_direction,
EAS_extrusion_metal,
+ EAS_extrusion_metal_type,
EAS_shade_mode,
EAS_extrusion_rotation_angle,
EAS_extrusion_rotation_center,
@@ -115,6 +116,7 @@ namespace xmloff::EnhancedCustomShapeToken {
EAS_FirstLightDirection,
EAS_SecondLightDirection,
EAS_Metal,
+ EAS_MetalType,
EAS_ShadeMode,
EAS_RotateAngle,
EAS_RotationCenter,
diff --git a/xmloff/qa/unit/data/tdf145700_3D_metal_type_MSCompatible.doc b/xmloff/qa/unit/data/tdf145700_3D_metal_type_MSCompatible.doc
new file mode 100644
index 000000000000..99c433654dcd
--- /dev/null
+++ b/xmloff/qa/unit/data/tdf145700_3D_metal_type_MSCompatible.doc
Binary files differ
diff --git a/xmloff/qa/unit/draw.cxx b/xmloff/qa/unit/draw.cxx
index fc07053cbacf..04c7178ca6bf 100644
--- a/xmloff/qa/unit/draw.cxx
+++ b/xmloff/qa/unit/draw.cxx
@@ -16,15 +16,19 @@
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeMetalType.hpp>
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
#include <com/sun/star/drawing/XMasterPageTarget.hpp>
#include <com/sun/star/util/Color.hpp>
#include <com/sun/star/text/XTextRange.hpp>
#include <com/sun/star/text/XTextTable.hpp>
+#include <comphelper/configuration.hxx>
+#include <officecfg/Office/Common.hxx>
#include <unotools/mediadescriptor.hxx>
#include <unotools/tempfile.hxx>
#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/saveopt.hxx>
using namespace ::com::sun::star;
@@ -44,6 +48,7 @@ public:
void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override;
uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
void save(const OUString& rFilterName, utl::TempFile& rTempFile);
+ uno::Reference<drawing::XShape> getShape(sal_uInt8 nShapeIndex);
};
void XmloffDrawTest::setUp()
@@ -76,6 +81,17 @@ void XmloffDrawTest::save(const OUString& rFilterName, utl::TempFile& rTempFile)
validate(rTempFile.GetFileName(), test::ODF);
}
+uno::Reference<drawing::XShape> XmloffDrawTest::getShape(sal_uInt8 nShapeIndex)
+{
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent,
+ uno::UNO_QUERY_THROW);
+ uno::Reference<drawing::XDrawPages> xDrawPages(xDrawPagesSupplier->getDrawPages());
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPages->getByIndex(0), uno::UNO_QUERY_THROW);
+ uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(nShapeIndex),
+ uno::UNO_QUERY_THROW);
+ return xShape;
+}
+
CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTextBoxLoss)
{
// Load a document that has a shape with a textbox in it. Save it to ODF and reload.
@@ -242,6 +258,90 @@ CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTableInShape)
CPPUNIT_ASSERT_EQUAL(OUString("A1"), xCell->getString());
}
+// Tests for save/load of new (LO 7.4) attribute loext:extrusion-metal-type
+namespace
+{
+void lcl_assertMetalProperties(std::string_view sInfo, uno::Reference<drawing::XShape>& rxShape)
+{
+ uno::Reference<beans::XPropertySet> xShapeProps(rxShape, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aGeoPropSeq;
+ xShapeProps->getPropertyValue("CustomShapeGeometry") >>= aGeoPropSeq;
+ comphelper::SequenceAsHashMap aGeoPropMap(aGeoPropSeq);
+ uno::Sequence<beans::PropertyValue> aExtrusionSeq;
+ aGeoPropMap.getValue("Extrusion") >>= aExtrusionSeq;
+ comphelper::SequenceAsHashMap aExtrusionPropMap(aExtrusionSeq);
+
+ bool bIsMetal(false);
+ aExtrusionPropMap.getValue("Metal") >>= bIsMetal;
+ OString sMsg = OString::Concat(sInfo) + " Metal";
+ CPPUNIT_ASSERT_MESSAGE(sMsg.getStr(), bIsMetal);
+
+ sal_Int16 nMetalType(-1);
+ aExtrusionPropMap.getValue("MetalType") >>= nMetalType;
+ sMsg = OString::Concat(sInfo) + " MetalType";
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ sMsg.getStr(), css::drawing::EnhancedCustomShapeMetalType::MetalMSCompatible, nMetalType);
+}
+}
+
+CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testExtrusionMetalTypeExtended)
+{
+ // import
+ getComponent() = loadFromDesktop(m_directories.getURLFromSrc(DATA_DIRECTORY)
+ + "tdf145700_3D_metal_type_MSCompatible.doc",
+ "com.sun.star.text.TextDocument");
+ // verify properties
+ uno::Reference<drawing::XShape> xShape(getShape(0));
+ lcl_assertMetalProperties("from doc", xShape);
+
+ // Test, that new attribute is written with loext namespace. Adapt when attribute is added to ODF.
+ utl::TempFile aTempFile;
+ // The file has set c3DSpecularAmt="65536" to prevent validation error in attribute
+ // draw:extrusion-specularity. The error, that 122% is written in case of c3DSpecularAmt="80000" is
+ // not yet fixed.
+ save("writer8", aTempFile);
+
+ // assert XML.
+ std::unique_ptr<SvStream> pStream = parseExportStream(aTempFile, "content.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ assertXPath(pXmlDoc, "//draw:enhanced-geometry", "extrusion-metal", "true");
+ assertXPath(pXmlDoc,
+ "//draw:enhanced-geometry[@loext:extrusion-metal-type='loext:MetalMSCompatible']");
+
+ // reload
+ getComponent()->dispose();
+ getComponent() = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+ // verify properties
+ uno::Reference<drawing::XShape> xShapeReload(getShape(0));
+ lcl_assertMetalProperties("from ODF 1.3 extended", xShapeReload);
+}
+
+CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testExtrusionMetalTypeStrict)
+{
+ // import
+ getComponent() = loadFromDesktop(m_directories.getURLFromSrc(DATA_DIRECTORY)
+ + "tdf145700_3D_metal_type_MSCompatible.doc",
+ "com.sun.star.text.TextDocument");
+
+ // save ODF 1.3 strict and test, that new attribute is not written. Adapt when attribute is
+ // added to ODF.
+ const SvtSaveOptions::ODFDefaultVersion nCurrentODFVersion(GetODFDefaultVersion());
+ SetODFDefaultVersion(SvtSaveOptions::ODFVER_013);
+ // The file has set c3DSpecularAmt="65536" to prevent validation error in attribute
+ // draw:extrusion-specularity. The error, that 122% is written in case of c3DSpecularAmt="80000" is
+ // not yet fixed.
+ utl::TempFile aTempFile;
+ save("writer8", aTempFile);
+
+ // assert XML.
+ std::unique_ptr<SvStream> pStream = parseExportStream(aTempFile, "content.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ assertXPath(pXmlDoc, "//draw:enhanced-geometry", "extrusion-metal", "true");
+ assertXPath(pXmlDoc, "//draw:enhanced-geometry[@loext:extrusion-metal-type]", 0);
+
+ SetODFDefaultVersion(nCurrentODFVersion);
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index 298dd431a0fe..f3ae86566e43 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -2473,6 +2473,7 @@ namespace xmloff::token {
TOKEN( "extrusion-first-light-direction" , XML_EXTRUSION_FIRST_LIGHT_DIRECTION ),
TOKEN( "extrusion-second-light-direction" , XML_EXTRUSION_SECOND_LIGHT_DIRECTION ),
TOKEN( "extrusion-metal" , XML_EXTRUSION_METAL ),
+ TOKEN( "extrusion-metal-type" , XML_EXTRUSION_METAL_TYPE ),
TOKEN( "extrusion-rotation-angle" , XML_EXTRUSION_ROTATION_ANGLE ),
TOKEN( "extrusion-rotation-center" , XML_EXTRUSION_ROTATION_CENTER ),
TOKEN( "extrusion-shininess" , XML_EXTRUSION_SHININESS ),
diff --git a/xmloff/source/draw/EnhancedCustomShapeToken.cxx b/xmloff/source/draw/EnhancedCustomShapeToken.cxx
index 38ca0df48e6a..700e29fc71fd 100644
--- a/xmloff/source/draw/EnhancedCustomShapeToken.cxx
+++ b/xmloff/source/draw/EnhancedCustomShapeToken.cxx
@@ -59,6 +59,7 @@ const TokenTable pTokenTableArray[] =
{ "extrusion-first-light-direction", EAS_extrusion_first_light_direction },
{ "extrusion-second-light-direction", EAS_extrusion_second_light_direction },
{ "extrusion-metal", EAS_extrusion_metal },
+ { "extrusion-metal-type", EAS_extrusion_metal_type },
{ "shade-mode", EAS_shade_mode },
{ "extrusion-rotation-angle", EAS_extrusion_rotation_angle },
{ "extrusion-rotation-center", EAS_extrusion_rotation_center },
@@ -127,6 +128,7 @@ const TokenTable pTokenTableArray[] =
{ "FirstLightDirection", EAS_FirstLightDirection },
{ "SecondLightDirection", EAS_SecondLightDirection },
{ "Metal", EAS_Metal },
+ { "MetalType", EAS_MetalType },
{ "ShadeMode", EAS_ShadeMode },
{ "RotateAngle", EAS_RotateAngle },
{ "RotationCenter", EAS_RotationCenter },
diff --git a/xmloff/source/draw/shapeexport.cxx b/xmloff/source/draw/shapeexport.cxx
index f1c23d3ed0b2..ea33330994b1 100644
--- a/xmloff/source/draw/shapeexport.cxx
+++ b/xmloff/source/draw/shapeexport.cxx
@@ -46,6 +46,7 @@
#include <com/sun/star/drawing/EnhancedCustomShapeGluePointType.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeMetalType.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
@@ -4453,6 +4454,25 @@ static void ImpExportEnhancedGeometry( SvXMLExport& rExport, const uno::Referenc
bExtrusionMetal ? GetXMLToken( XML_TRUE ) : GetXMLToken( XML_FALSE ) );
}
break;
+ case EAS_MetalType :
+ {
+ // export only if ODF extensions are enabled
+ sal_Int16 eMetalType;
+ if (rProp.Value >>= eMetalType)
+ {
+ SvtSaveOptions::ODFSaneDefaultVersion eVersion = rExport.getSaneDefaultVersion();
+ if (eVersion > SvtSaveOptions::ODFSVER_013
+ && (eVersion & SvtSaveOptions::ODFSVER_EXTENDED))
+ {
+ if (eMetalType == drawing::EnhancedCustomShapeMetalType::MetalMSCompatible)
+ aStr = "loext:MetalMSCompatible";
+ else
+ aStr = "draw:MetalODF";
+ rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_EXTRUSION_METAL_TYPE, aStr);
+ }
+ }
+ }
+ break;
case EAS_ShadeMode :
{
// shadeMode
diff --git a/xmloff/source/draw/ximpcustomshape.cxx b/xmloff/source/draw/ximpcustomshape.cxx
index c8dbf70ba143..df86901b9635 100644
--- a/xmloff/source/draw/ximpcustomshape.cxx
+++ b/xmloff/source/draw/ximpcustomshape.cxx
@@ -40,6 +40,7 @@
#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
#include <com/sun/star/drawing/EnhancedCustomShapeTextPathMode.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeMetalType.hpp>
#include <com/sun/star/drawing/ProjectionMode.hpp>
#include <com/sun/star/drawing/Position3D.hpp>
#include <sax/tools/converter.hxx>
@@ -982,6 +983,18 @@ void XMLEnhancedCustomShapeContext::startFastElement(
case EAS_extrusion_metal :
GetBool( maExtrusion, aIter.toView(), EAS_Metal );
break;
+ case EAS_extrusion_metal_type :
+ {
+ OUString rValue = aIter.toString();
+ sal_Int16 eMetalType(drawing::EnhancedCustomShapeMetalType::MetalODF);
+ if (rValue == "loext:MetalMSCompatible")
+ eMetalType = drawing::EnhancedCustomShapeMetalType::MetalMSCompatible;
+ beans::PropertyValue aProp;
+ aProp.Name = EASGet(EAS_MetalType);
+ aProp.Value <<= eMetalType;
+ maExtrusion.push_back(aProp);
+ }
+ break;
case EAS_shade_mode :
{
drawing::ShadeMode eShadeMode( drawing::ShadeMode_FLAT );
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index a23326c71f17..f610bfbff47e 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -2320,6 +2320,7 @@ extrusion-second-light-level
extrusion-first-light-direction
extrusion-second-light-direction
extrusion-metal
+extrusion-metal-type
extrusion-rotation-angle
extrusion-rotation-center
extrusion-shininess