summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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