tdf#70039 convert 3D effects to extrusion
ODF allows a 3D mode for custom shapes. That can be used to render some of the 3D effects possible in MS Office. MS Office has not published, how they calculate the 3D-scenes. Thus most principles and values are found by experiments. My assumptions are contained as comments. This current solution does not work well for perspectiveFront camera with rotation on only y-axis or on only x-axis. If someone has an idea, what is wrong in my solution or what MS Office might specially do, please tell me. The tests do not cover whether the rendering in LO is the same as in MS Office. I have no idea how to write such tests. To test manually: In Powerpoint: Copy the shape and set the copy to wireframe. Cut the copy and insert it as svg image. Move the image so that the lines cover the original shape. Save it. In LibreOffice: Open the file and set the shape to wireframe. Now you can easily compare the rendering of PowerPoint and LibreOffice. Extrusion can be used for images, that have a 3D-scene applied like in tdf#45495. That would work with this patch, but the related places are commented out because of tdf#159515. This patch does not cover lighting and material and it does not contain export. 3D-text is not contained in the patch. There are principle problems with 3D-text. Thus a solution requieres a lot, including additions to the ODF standard. The comments in tdf#70039 contain more about aspects, where MS Office and ODF are in principle incompatible in regard to 3D-effects. Change-Id: I8a5da536ade2a4b67630af221ea47e0288450188 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162594 Reviewed-by: Sarper Akdemir <sarper.akdemir.extern@allotropia.de> Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
This commit is contained in:
parent
81adba0eee
commit
6e5529d738
16 changed files with 1008 additions and 14 deletions
53
oox/CppunitTest_oox_testscene3d.mk
Normal file
53
oox/CppunitTest_oox_testscene3d.mk
Normal file
|
@ -0,0 +1,53 @@
|
|||
# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
|
||||
#*************************************************************************
|
||||
#
|
||||
# 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/.
|
||||
#
|
||||
#*************************************************************************
|
||||
|
||||
$(eval $(call gb_CppunitTest_CppunitTest,oox_testscene3d))
|
||||
|
||||
$(eval $(call gb_CppunitTest_use_externals,oox_testscene3d,\
|
||||
boost_headers \
|
||||
libxml2 \
|
||||
))
|
||||
|
||||
$(eval $(call gb_CppunitTest_add_exception_objects,oox_testscene3d, \
|
||||
oox/qa/unit/testscene3d \
|
||||
))
|
||||
|
||||
$(eval $(call gb_CppunitTest_use_libraries,oox_testscene3d, \
|
||||
comphelper \
|
||||
cppu \
|
||||
cppuhelper \
|
||||
oox \
|
||||
sal \
|
||||
subsequenttest \
|
||||
test \
|
||||
unotest \
|
||||
utl \
|
||||
tl \
|
||||
))
|
||||
|
||||
$(eval $(call gb_CppunitTest_use_sdk_api,oox_testscene3d))
|
||||
|
||||
$(eval $(call gb_CppunitTest_use_ure,oox_testscene3d))
|
||||
$(eval $(call gb_CppunitTest_use_vcl,oox_testscene3d))
|
||||
|
||||
$(eval $(call gb_CppunitTest_use_rdb,oox_testscene3d,services))
|
||||
|
||||
$(eval $(call gb_CppunitTest_use_custom_headers,oox_testscene3d,\
|
||||
officecfg/registry \
|
||||
))
|
||||
|
||||
$(eval $(call gb_CppunitTest_use_configuration,oox_testscene3d))
|
||||
|
||||
$(eval $(call gb_CppunitTest_add_arguments,oox_testscene3d, \
|
||||
-env:arg-env=$(gb_Helper_LIBRARY_PATH_VAR)"$$$${$(gb_Helper_LIBRARY_PATH_VAR)+=$$$$$(gb_Helper_LIBRARY_PATH_VAR)}" \
|
||||
))
|
||||
|
||||
# vim: set noet sw=4 ts=4:
|
|
@ -181,6 +181,7 @@ $(eval $(call gb_Library_add_exception_objects,oox,\
|
|||
oox/source/drawingml/objectdefaultcontext \
|
||||
oox/source/drawingml/presetgeometrynames \
|
||||
oox/source/drawingml/scene3dcontext \
|
||||
oox/source/drawingml/scene3dhelper \
|
||||
oox/source/drawingml/shapecontext \
|
||||
oox/source/drawingml/shape \
|
||||
oox/source/drawingml/shape3dproperties \
|
||||
|
|
|
@ -35,6 +35,7 @@ $(eval $(call gb_Module_add_check_targets,oox,\
|
|||
CppunitTest_oox_shape \
|
||||
CppunitTest_oox_export \
|
||||
CppunitTest_oox_mcgr \
|
||||
CppunitTest_oox_testscene3d \
|
||||
CppunitTest_oox_wpc_drawing_canvas \
|
||||
))
|
||||
endif
|
||||
|
|
|
@ -124,6 +124,7 @@ public:
|
|||
|
||||
sal_Int32 getArcNum() { return mnArcNum++; }
|
||||
sal_Int32 countArcTo() { return mnArcNum; }
|
||||
PropertyMap& getExtrusionPropertyMap() { return maExtrusionPropertyMap; }
|
||||
|
||||
/**
|
||||
Returns whether or not the current CustomShapeProperties
|
||||
|
@ -156,6 +157,7 @@ private:
|
|||
static void initializePresetDataMap();
|
||||
|
||||
sal_Int32 mnArcNum;
|
||||
PropertyMap maExtrusionPropertyMap;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
96
oox/inc/drawingml/scene3dhelper.hxx
Executable file
96
oox/inc/drawingml/scene3dhelper.hxx
Executable file
|
@ -0,0 +1,96 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* 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/.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drawingml/shape3dproperties.hxx>
|
||||
#include <oox/drawingml/drawingmltypes.hxx>
|
||||
#include <oox/helper/propertymap.hxx>
|
||||
|
||||
namespace oox
|
||||
{
|
||||
class Scene3DHelper
|
||||
{
|
||||
public:
|
||||
/** Creates extrusion properties in rPropertyMap from the 3D information in p3DProperties and
|
||||
gives values which have to be set as shape properties.
|
||||
@param [in] p3DProperties a pointer to Shape3DProperties
|
||||
@param [in] rnMSOShapeRotation a MSO shape rotation angle in 1/60000 deg
|
||||
@param [in, out] rPropertyMap a map that is filled with properties directly usable in
|
||||
CustomShapeProperties.pushToPropSet() for property Extrusion.
|
||||
@param [out] rRotZ the angle for shape rotation around z-axis in rad, with orientation
|
||||
same as API shape property RotateAngle. It has the shape rotation from rnMSOShapeRotation
|
||||
integrated.
|
||||
@param [out] rExtrusionColor a complex color from which the color of the extruded faces can
|
||||
be created. The extrusion color is not handled as extrusion property but as secondary
|
||||
color in the style of the shape. If no 3D exist or the camera is invalid, the parameter
|
||||
value is unchanged.
|
||||
@return Returns true if extrusion properties are generated. Returns false if there is no 3D
|
||||
or if rendering without 3D is currently better. rPropertyMap is unchanged in such case.
|
||||
*/
|
||||
bool setExtrusionProperties(const oox::drawingml::Shape3DPropertiesPtr p3DProperties,
|
||||
const sal_Int32& rnMSOShapeRotation, oox::PropertyMap& rPropertyMap,
|
||||
double& rRotZ, oox::drawingml::Color& rExtrusionColor);
|
||||
|
||||
private:
|
||||
/** Calculates angles suitable for API from OOXML scene3d angles.
|
||||
@param [in] nLat, nLon, nRev in unit 1/60000 deg with same orientation as the attributes lat,
|
||||
lon and rev of the <rot> child element of the <scene3d> element in OOXML markup.
|
||||
@param [out] fX, fY, fZ values in unit radians with correct orientation for API properties
|
||||
EnhancedCustomShapeExtrusion::RotateAngle and RotationDescriptor::RotateAngle*/
|
||||
static void getAPIAnglesFromOOXAngle(const sal_Int32 nLat, const sal_Int32 nLon,
|
||||
const sal_Int32 nRev, double& fX, double& fY, double& fZ);
|
||||
|
||||
/** Calculates angles suitable for API from Shape3DProperties.
|
||||
@details It considers the preset camera in case the optional, direct rotation angles have no
|
||||
value. It integrates the given rnMSOShapeRotation into fZ.
|
||||
@param [in] p3DProperties a pointer to Shape3DProperties
|
||||
@param [in] rnMSOShapeRotation rotation in 1/60000 deg as given in 'rot' attribute of 'xfrm'
|
||||
element in OOXML.
|
||||
@param [out] fX, fY, fZ values in unit radians with correct orientation for API properties
|
||||
EnhancedCustomShapeExtrusion::RotateAngle and RotationDescriptor::RotateAngle*/
|
||||
void getAPIAnglesFrom3DProperties(const oox::drawingml::Shape3DPropertiesPtr p3DProperties,
|
||||
const sal_Int32& rnMSOShapeRotation, double& fX, double& fY,
|
||||
double& fZ);
|
||||
|
||||
/** Adds the rotation angles fX and fY as property RotateAngle to the map.
|
||||
@param [in, out] rPropertyMap a map to add the RotateAngle property
|
||||
@param [in] fX, fY rotation angle in unit rad with correct orientation for the property.*/
|
||||
static void addRotateAngleToMap(oox::PropertyMap& rPropertyMap, const double fX,
|
||||
const double fY);
|
||||
|
||||
/** Adds the Depth property to the map.
|
||||
@details The second component is relative, whereas the z-position in OOXML is absolute. Uses
|
||||
360EMU depth in case of zero Depth as otherwise no relative position is possible.
|
||||
@param [in] p3DProperties a pointer to Shape3DProperties
|
||||
@param [in, out] rPropertyMap a map to add the Depth property*/
|
||||
static void addExtrusionDepthToMap(const oox::drawingml::Shape3DPropertiesPtr p3DProperties,
|
||||
oox::PropertyMap& rPropertyMap);
|
||||
|
||||
/** Adds the projection mode itself and the associated camera parameters to the map.
|
||||
@details Both modes add ProjectionMode and Origin properties. Adds Skew property in case of
|
||||
mode PARALLEL and ViewPoint property in case of mode PERSPECTIVE. The Skew angles includes the
|
||||
shape rotation because MSOffice rotates after creating the projection and ODF before.
|
||||
@param [in] p3DProperties a pointer to Shape3DProperties
|
||||
@param [in, out] rPropertyMap a map to add ProjectMode, Origin and Skew or ViewPoint
|
||||
properties.
|
||||
@param [in] bIsParallel true for mode PARALLEL, false for PRERPECTIVE
|
||||
@param [in] rnMSOShapeRotation shape rotation in 1/60000 degree*/
|
||||
void addProjectionGeometryToMap(const oox::drawingml::Shape3DPropertiesPtr p3DProperties,
|
||||
oox::PropertyMap& rPropertyMap, const bool bIsParallel,
|
||||
const sal_Int32 rnMSOShapeRotation);
|
||||
|
||||
// Index into array aPrstCameraValuesArray.
|
||||
sal_Int16 mnPrstCameraIndex = -1; // '-1' means invalid or not yet searched
|
||||
}; // end class Scene3DHelper
|
||||
|
||||
} // end namespace oox
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
BIN
oox/qa/unit/data/Scene3d_isometricRightUp.pptx
Normal file
BIN
oox/qa/unit/data/Scene3d_isometricRightUp.pptx
Normal file
Binary file not shown.
BIN
oox/qa/unit/data/Scene3d_legacyObliqueBottomRight.pptx
Normal file
BIN
oox/qa/unit/data/Scene3d_legacyObliqueBottomRight.pptx
Normal file
Binary file not shown.
BIN
oox/qa/unit/data/Scene3d_legacyPerspectiveTopRight.pptx
Normal file
BIN
oox/qa/unit/data/Scene3d_legacyPerspectiveTopRight.pptx
Normal file
Binary file not shown.
BIN
oox/qa/unit/data/Scene3d_obliqueTopRight.pptx
Normal file
BIN
oox/qa/unit/data/Scene3d_obliqueTopRight.pptx
Normal file
Binary file not shown.
BIN
oox/qa/unit/data/Scene3d_orthographicFront.pptx
Normal file
BIN
oox/qa/unit/data/Scene3d_orthographicFront.pptx
Normal file
Binary file not shown.
BIN
oox/qa/unit/data/Scene3d_perspectiveContrastingLeft.pptx
Normal file
BIN
oox/qa/unit/data/Scene3d_perspectiveContrastingLeft.pptx
Normal file
Binary file not shown.
320
oox/qa/unit/testscene3d.cxx
Executable file
320
oox/qa/unit/testscene3d.cxx
Executable file
|
@ -0,0 +1,320 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
#include <test/unoapixml_test.hxx>
|
||||
|
||||
#include <com/sun/star/awt/Rectangle.hpp>
|
||||
#include <com/sun/star/beans/XPropertySet.hpp>
|
||||
#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
|
||||
#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
|
||||
#include <com/sun/star/drawing/Position3D.hpp>
|
||||
#include <com/sun/star/drawing/ProjectionMode.hpp>
|
||||
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
|
||||
#include <comphelper/sequenceashashmap.hxx>
|
||||
#include <editeng/unoprnms.hxx>
|
||||
|
||||
using namespace css;
|
||||
|
||||
/// Covers tests for scene3d import and export, available since LO 24.8., see tdf#70039
|
||||
class TestScene3d : public UnoApiXmlTest
|
||||
{
|
||||
public:
|
||||
TestScene3d()
|
||||
: UnoApiXmlTest("/oox/qa/unit/data/")
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
// get shape with nShapeIndex from page nPageIndex
|
||||
uno::Reference<drawing::XShape> getShape(sal_uInt8 nShapeIndex, sal_uInt8 nPageIndex);
|
||||
};
|
||||
|
||||
uno::Reference<drawing::XShape> TestScene3d::getShape(sal_uInt8 nShapeIndex, sal_uInt8 nPageIndex)
|
||||
{
|
||||
uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent,
|
||||
uno::UNO_QUERY_THROW);
|
||||
CPPUNIT_ASSERT_MESSAGE("Could not get XDrawPagesSupplier", xDrawPagesSupplier.is());
|
||||
uno::Reference<drawing::XDrawPages> xDrawPages(xDrawPagesSupplier->getDrawPages());
|
||||
uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPages->getByIndex(nPageIndex),
|
||||
uno::UNO_QUERY_THROW);
|
||||
CPPUNIT_ASSERT_MESSAGE("Could not get xDrawPage", xDrawPage.is());
|
||||
uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(nShapeIndex), uno::UNO_QUERY);
|
||||
CPPUNIT_ASSERT_MESSAGE("Could not get xShape", xShape.is());
|
||||
return xShape;
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(TestScene3d, test_isometricRightUp)
|
||||
{
|
||||
// Given a document with a scene3d element on a shape. Without the fix, the shape was imported as
|
||||
// flat 2D shape without extrusion. This test covers some basic properties.
|
||||
loadFromFile(u"Scene3d_isometricRightUp.pptx");
|
||||
uno::Reference<drawing::XShape> xShape(getShape(1, 0)); // shape 1 on page 0
|
||||
|
||||
// Prepare property maps
|
||||
uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
|
||||
uno::Sequence<beans::PropertyValue> aGeoPropSeq;
|
||||
xShapeProps->getPropertyValue(u"CustomShapeGeometry"_ustr) >>= aGeoPropSeq;
|
||||
comphelper::SequenceAsHashMap aGeoPropMap(aGeoPropSeq);
|
||||
uno::Sequence<beans::PropertyValue> aExtrusionSeq;
|
||||
aGeoPropMap.getValue(u"Extrusion"_ustr) >>= aExtrusionSeq;
|
||||
comphelper::SequenceAsHashMap aExtrusionPropMap(aExtrusionSeq);
|
||||
|
||||
// Make sure extrusion is on.
|
||||
bool bIsExtruded(false);
|
||||
aExtrusionPropMap.getValue(u"Extrusion"_ustr) >>= bIsExtruded;
|
||||
CPPUNIT_ASSERT_MESSAGE("error: Extrusion not enabled", bIsExtruded);
|
||||
|
||||
// Make sure the extrusion properties correspond to the camera preset type.
|
||||
// projection mode should be PARALLEL
|
||||
drawing::ProjectionMode eProjectionMode = drawing::ProjectionMode_PERSPECTIVE;
|
||||
aExtrusionPropMap.getValue(u"ProjectionMode"_ustr) >>= eProjectionMode;
|
||||
CPPUNIT_ASSERT_EQUAL(drawing::ProjectionMode_PARALLEL, eProjectionMode);
|
||||
|
||||
// For Skew, Origin, RotateAngle and Depth
|
||||
drawing::EnhancedCustomShapeParameterPair aParaPair;
|
||||
|
||||
// Should be front projection, which means Skew amount and angle are zero.
|
||||
aExtrusionPropMap.getValue(u"Skew"_ustr) >>= aParaPair;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, aParaPair.First.Value.get<double>(), 1E-14);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, aParaPair.Second.Value.get<double>(), 1E-14);
|
||||
|
||||
// .. and Origin x and y are zero.
|
||||
aExtrusionPropMap.getValue(u"Origin"_ustr) >>= aParaPair;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, aParaPair.First.Value.get<double>(), 1E-14);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, aParaPair.Second.Value.get<double>(), 1E-14);
|
||||
|
||||
// Rotation around x-axis should be -35deg, around y-axis should be -45deg.
|
||||
aExtrusionPropMap.getValue(UNO_NAME_MISC_OBJ_ROTATEANGLE) >>= aParaPair;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-35.0, aParaPair.First.Value.get<double>(), 1E-14);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-45.0, aParaPair.Second.Value.get<double>(), 1E-14);
|
||||
|
||||
// Depth should be 36pt = 1270Hmm without shift
|
||||
aExtrusionPropMap.getValue(u"Depth"_ustr) >>= aParaPair;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1270.0, aParaPair.First.Value.get<double>(), 1E-14);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, aParaPair.Second.Value.get<double>(), 1E-14);
|
||||
|
||||
// This shape does not use an extrusion color.
|
||||
bool bIsExtrusionColorEnabled(true);
|
||||
aExtrusionPropMap.getValue(u"Color"_ustr) >>= bIsExtrusionColorEnabled;
|
||||
CPPUNIT_ASSERT_MESSAGE("error: Extrusion color enabled", !bIsExtrusionColorEnabled);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(TestScene3d, test_legacyObliqueBottomRight)
|
||||
{
|
||||
// The legacy preset camera types correspond to the geometry available in the UI of LibreOffice.
|
||||
// They are not available in the UI of MS Office, but if given, user can change some properties.
|
||||
// The test includes some of them.
|
||||
|
||||
loadFromFile(u"Scene3d_legacyObliqueBottomRight.pptx");
|
||||
uno::Reference<drawing::XShape> xShape(getShape(1, 0)); // shape 1 on page 0
|
||||
|
||||
// Prepare property maps
|
||||
uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
|
||||
uno::Sequence<beans::PropertyValue> aGeoPropSeq;
|
||||
xShapeProps->getPropertyValue(u"CustomShapeGeometry"_ustr) >>= aGeoPropSeq;
|
||||
comphelper::SequenceAsHashMap aGeoPropMap(aGeoPropSeq);
|
||||
uno::Sequence<beans::PropertyValue> aExtrusionSeq;
|
||||
aGeoPropMap.getValue(u"Extrusion"_ustr) >>= aExtrusionSeq;
|
||||
comphelper::SequenceAsHashMap aExtrusionPropMap(aExtrusionSeq);
|
||||
|
||||
// For Skew, Origin, RotateAngle and Depth
|
||||
drawing::EnhancedCustomShapeParameterPair aParaPair;
|
||||
|
||||
// Should be a slanted projection with shifted origin
|
||||
aExtrusionPropMap.getValue(u"Skew"_ustr) >>= aParaPair;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(50.0, aParaPair.First.Value.get<double>(), 1E-14);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(135.0, aParaPair.Second.Value.get<double>(), 1E-14);
|
||||
aExtrusionPropMap.getValue(u"Origin"_ustr) >>= aParaPair;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, aParaPair.First.Value.get<double>(), 1E-14);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, aParaPair.Second.Value.get<double>(), 1E-14);
|
||||
|
||||
// The shape has a rotation. Rotations on x-axis and y-axis belong to the extrusion rotation,
|
||||
// rotation on z-axis belongs to the shape rotation.
|
||||
aExtrusionPropMap.getValue(UNO_NAME_MISC_OBJ_ROTATEANGLE) >>= aParaPair;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-3.45117839701884, aParaPair.First.Value.get<double>(), 1E-12);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(9.3912858020435, aParaPair.Second.Value.get<double>(), 1E-12);
|
||||
sal_Int32 nZRotate; // unit 1/100 degree
|
||||
xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_ROTATEANGLE) >>= nZRotate;
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(2028), nZRotate);
|
||||
|
||||
// Depth should be 36pt = 1270Hmm. The second value corresponds to the 'Distance from ground'
|
||||
// setting in the UI of MS Office, only that it is relative to the depth in ODF.
|
||||
aExtrusionPropMap.getValue(u"Depth"_ustr) >>= aParaPair;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(1270.0, aParaPair.First.Value.get<double>(), 1E-14);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, aParaPair.Second.Value.get<double>(), 1E-14);
|
||||
|
||||
// This shape uses an extrusion color. The extrusion color itself does not belong to the
|
||||
// extrusion but to the shape fill properties. The color is not a complex color but still RGB.
|
||||
bool bIsExtrusionColorEnabled(false);
|
||||
aExtrusionPropMap.getValue(u"Color"_ustr) >>= bIsExtrusionColorEnabled;
|
||||
CPPUNIT_ASSERT_MESSAGE("error: Extrusion color is not enabled", bIsExtrusionColorEnabled);
|
||||
Color nColor;
|
||||
xShapeProps->getPropertyValue(UNO_NAME_FILLCOLOR_2) >>= nColor;
|
||||
CPPUNIT_ASSERT_EQUAL(Color(0x89c064), nColor);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(TestScene3d, test_obliqueTopRight)
|
||||
{
|
||||
// MS Office applies the shape rotation after the camera rotations in case of oblique preset
|
||||
// camera types. That needs to be converted to the 'first shape rotation' specification of ODF.
|
||||
// That conversion results in angles not available in the UI of LibreOffice, but LO can
|
||||
// render them. The shape has got these rotation in the UI of MS Office: 60° shape ,
|
||||
// 50° on x-axis, 10° on y-axis and 40° on z-axis.
|
||||
loadFromFile(u"Scene3d_obliqueTopRight.pptx");
|
||||
uno::Reference<drawing::XShape> xShape(getShape(0, 0)); // shape 0 on page 0
|
||||
|
||||
// Prepare property maps
|
||||
uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
|
||||
uno::Sequence<beans::PropertyValue> aGeoPropSeq;
|
||||
xShapeProps->getPropertyValue(u"CustomShapeGeometry"_ustr) >>= aGeoPropSeq;
|
||||
comphelper::SequenceAsHashMap aGeoPropMap(aGeoPropSeq);
|
||||
uno::Sequence<beans::PropertyValue> aExtrusionSeq;
|
||||
aGeoPropMap.getValue(u"Extrusion"_ustr) >>= aExtrusionSeq;
|
||||
comphelper::SequenceAsHashMap aExtrusionPropMap(aExtrusionSeq);
|
||||
|
||||
// For Skew and RotateAngle
|
||||
drawing::EnhancedCustomShapeParameterPair aParaPair;
|
||||
|
||||
// Should be an oblique projection with non default skew values
|
||||
aExtrusionPropMap.getValue(u"Skew"_ustr) >>= aParaPair;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(30.0, aParaPair.First.Value.get<double>(), 1E-14);
|
||||
// -135° from preset camera type, minus 60° shape rotation
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-195.0, aParaPair.Second.Value.get<double>(), 1E-14);
|
||||
|
||||
// Because of the needed conversions the resulting angles are very different from
|
||||
// those set in the UI of MS Office.
|
||||
aExtrusionPropMap.getValue(UNO_NAME_MISC_OBJ_ROTATEANGLE) >>= aParaPair;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-45.7369327638437, aParaPair.First.Value.get<double>(), 1E-12);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(24.9102537477636, aParaPair.Second.Value.get<double>(), 1E-12);
|
||||
sal_Int32 nZRotate; // unit 1/100 degree
|
||||
xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_ROTATEANGLE) >>= nZRotate;
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(34597), nZRotate);
|
||||
|
||||
// The test uses BoundRect to verify position and size of the resulting scene.
|
||||
// The tolerance 10 is estimated and can be adjusted if required for HiDPI.
|
||||
awt::Rectangle aBoundRect;
|
||||
xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(8325, aBoundRect.Width, 10);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(7804, aBoundRect.Height, 10);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(8272, aBoundRect.X, 10);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(3510, aBoundRect.Y, 10);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(TestScene3d, test_orthographicFront)
|
||||
{
|
||||
// MS Office users might use the z-rotation instead of the shape rotation without intending an
|
||||
// extrusion. As of March 2024 we import the shape as 2D shape in such case, because extrusion
|
||||
// looses the stroke, see tdf#159334.
|
||||
loadFromFile(u"Scene3d_orthographicFront.pptx");
|
||||
uno::Reference<drawing::XShape> xShape(getShape(0, 0)); // shape 0 on page 0
|
||||
|
||||
// Make sure that in case extrusion properties exist, the extrusion is disabled.
|
||||
uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
|
||||
uno::Sequence<beans::PropertyValue> aGeoPropSeq;
|
||||
xShapeProps->getPropertyValue(u"CustomShapeGeometry"_ustr) >>= aGeoPropSeq;
|
||||
comphelper::SequenceAsHashMap aGeoPropMap(aGeoPropSeq);
|
||||
uno::Sequence<beans::PropertyValue> aExtrusionSeq;
|
||||
if (aGeoPropMap.getValue(u"Extrusion"_ustr) >>= aExtrusionSeq)
|
||||
{
|
||||
comphelper::SequenceAsHashMap aExtrusionPropMap(aExtrusionSeq);
|
||||
bool bIsExtruded(true);
|
||||
aExtrusionPropMap.getValue(u"Extrusion"_ustr) >>= bIsExtruded;
|
||||
CPPUNIT_ASSERT_MESSAGE("error: Extrusion is enabled", !bIsExtruded);
|
||||
}
|
||||
|
||||
// Make sure the shape is nevertheless rotated.
|
||||
sal_Int32 nZRotate; // unit 1/100 degree
|
||||
xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_ROTATEANGLE) >>= nZRotate;
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(9000), nZRotate);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(TestScene3d, test_perspectiveContrastingLeft)
|
||||
{
|
||||
// The file contains a shape with the preset camera perspectiveContrastingLeft.
|
||||
// Such shape cannot be produced in the UI of LibreOffice, but it can be rendered.
|
||||
loadFromFile(u"Scene3d_perspectiveContrastingLeft.pptx");
|
||||
uno::Reference<drawing::XShape> xShape(getShape(0, 0)); // shape 0 on page 0
|
||||
|
||||
// Prepare property maps
|
||||
uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
|
||||
uno::Sequence<beans::PropertyValue> aGeoPropSeq;
|
||||
xShapeProps->getPropertyValue(u"CustomShapeGeometry"_ustr) >>= aGeoPropSeq;
|
||||
comphelper::SequenceAsHashMap aGeoPropMap(aGeoPropSeq);
|
||||
uno::Sequence<beans::PropertyValue> aExtrusionSeq;
|
||||
aGeoPropMap.getValue(u"Extrusion"_ustr) >>= aExtrusionSeq;
|
||||
comphelper::SequenceAsHashMap aExtrusionPropMap(aExtrusionSeq);
|
||||
|
||||
// projection mode should be PERSPECTIVE
|
||||
drawing::ProjectionMode eProjectionMode = drawing::ProjectionMode_PARALLEL;
|
||||
aExtrusionPropMap.getValue(u"ProjectionMode"_ustr) >>= eProjectionMode;
|
||||
CPPUNIT_ASSERT_EQUAL(drawing::ProjectionMode_PERSPECTIVE, eProjectionMode);
|
||||
|
||||
// A perspective projection needs a viewPoint. MS Office has it on the z-axis.
|
||||
drawing::Position3D aViewPoint;
|
||||
aExtrusionPropMap.getValue(u"ViewPoint"_ustr) >>= aViewPoint;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, aViewPoint.PositionX, 1E-12);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, aViewPoint.PositionY, 1E-12);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(38451.0, aViewPoint.PositionZ, 1E-12);
|
||||
|
||||
// Check rotation angles
|
||||
drawing::EnhancedCustomShapeParameterPair aParaPair;
|
||||
aExtrusionPropMap.getValue(UNO_NAME_MISC_OBJ_ROTATEANGLE) >>= aParaPair;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-6.94093344831102, aParaPair.First.Value.get<double>(), 1E-12);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(44.4431265782766, aParaPair.Second.Value.get<double>(), 1E-12);
|
||||
sal_Int32 nZRotate; // unit 1/100 degree
|
||||
xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_ROTATEANGLE) >>= nZRotate;
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(35504), nZRotate);
|
||||
|
||||
// Use BoundRect as workaround, because there is no easy way to test the rendered line geometry.
|
||||
// The tolerance 10 is estimated and can be adjusted if required for HiDPI.
|
||||
awt::Rectangle aBoundRect;
|
||||
xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(6614, aBoundRect.Width, 10);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(8577, aBoundRect.Height, 10);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(5725, aBoundRect.X, 10);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(4213, aBoundRect.Y, 10);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(TestScene3d, test_legacyPerspectiveTopRight)
|
||||
{
|
||||
// The file contains a shape with the preset camera legacyPerspectiveTopLeft. These kind of
|
||||
// camera corresponds directly to the extrusions available in the UI of LibreOffice. The
|
||||
// non-frontal view is not done by rotation but by shifting the view point horizontal and
|
||||
// vertical. That is tested here.
|
||||
loadFromFile(u"Scene3d_legacyPerspectiveTopRight.pptx");
|
||||
uno::Reference<drawing::XShape> xShape(getShape(0, 0)); // shape 0 on page 0
|
||||
|
||||
// Prepare property maps
|
||||
uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
|
||||
uno::Sequence<beans::PropertyValue> aGeoPropSeq;
|
||||
xShapeProps->getPropertyValue(u"CustomShapeGeometry"_ustr) >>= aGeoPropSeq;
|
||||
comphelper::SequenceAsHashMap aGeoPropMap(aGeoPropSeq);
|
||||
uno::Sequence<beans::PropertyValue> aExtrusionSeq;
|
||||
aGeoPropMap.getValue(u"Extrusion"_ustr) >>= aExtrusionSeq;
|
||||
comphelper::SequenceAsHashMap aExtrusionPropMap(aExtrusionSeq);
|
||||
|
||||
// shifted view point
|
||||
drawing::Position3D aViewPoint;
|
||||
aExtrusionPropMap.getValue(u"ViewPoint"_ustr) >>= aViewPoint;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(3472.0, aViewPoint.PositionX, 1E-12);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(-3472.0, aViewPoint.PositionY, 1E-12);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(25000.0, aViewPoint.PositionZ, 1E-12);
|
||||
|
||||
// Use BoundRect as workaround, because there is no easy way to test the rendered line geometry.
|
||||
// The tolerance 10 is estimated and can be adjusted if required for HiDPI.
|
||||
awt::Rectangle aBoundRect;
|
||||
xShapeProps->getPropertyValue(UNO_NAME_MISC_OBJ_BOUNDRECT) >>= aBoundRect;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(7004, aBoundRect.Width, 10);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(7004, aBoundRect.Height, 10);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(6292, aBoundRect.X, 10);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(3138, aBoundRect.Y, 10);
|
||||
}
|
||||
CPPUNIT_PLUGIN_IMPLEMENT();
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
|
@ -129,6 +129,11 @@ void CustomShapeProperties::pushToPropSet(
|
|||
aPropertyMap.setProperty( PROP_TextCameraZRotateAngle, mnTextCameraZRotateAngle );
|
||||
if (moTextAreaRotateAngle.has_value())
|
||||
aPropertyMap.setProperty(PROP_TextRotateAngle, moTextAreaRotateAngle.value());
|
||||
if (!maExtrusionPropertyMap.empty())
|
||||
{
|
||||
Sequence< PropertyValue > aExtrusionSequence = maExtrusionPropertyMap.makePropertyValueSequence();
|
||||
aPropertyMap.setAnyProperty( PROP_Extrusion, css::uno::Any(aExtrusionSequence));
|
||||
}
|
||||
Sequence< PropertyValue > aSeq = aPropertyMap.makePropertyValueSequence();
|
||||
aPropSet.setProperty( PROP_CustomShapeGeometry, aSeq );
|
||||
|
||||
|
@ -361,6 +366,11 @@ void CustomShapeProperties::pushToPropSet(
|
|||
aHandlesRange[ i ] = aHandle.makePropertyValueSequence();
|
||||
}
|
||||
aPropertyMap.setProperty( PROP_Handles, aHandles);
|
||||
if (!maExtrusionPropertyMap.empty())
|
||||
{
|
||||
Sequence< PropertyValue > aExtrusionSequence = maExtrusionPropertyMap.makePropertyValueSequence();
|
||||
aPropertyMap.setProperty( PROP_Extrusion, aExtrusionSequence);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Note that the script oox/source/drawingml/customshapes/generatePresetsData.pl looks
|
||||
|
|
442
oox/source/drawingml/scene3dhelper.cxx
Executable file
442
oox/source/drawingml/scene3dhelper.cxx
Executable file
|
@ -0,0 +1,442 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* 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/.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drawingml/scene3dhelper.hxx>
|
||||
|
||||
#include <basegfx/matrix/b3dhommatrix.hxx>
|
||||
#include <basegfx/numeric/ftools.hxx>
|
||||
#include <oox/drawingml/drawingmltypes.hxx>
|
||||
#include <oox/helper/propertymap.hxx>
|
||||
#include <oox/token/properties.hxx>
|
||||
#include <oox/token/tokens.hxx>
|
||||
|
||||
#include <com/sun/star/drawing/EnhancedCustomShapeParameter.hpp>
|
||||
#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
|
||||
#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
|
||||
#include <com/sun/star/drawing/Position3D.hpp>
|
||||
#include <com/sun/star/drawing/ProjectionMode.hpp>
|
||||
|
||||
namespace oox
|
||||
{
|
||||
/** This struct is used to hold values from the OOXML camera preset types.*/
|
||||
namespace
|
||||
{
|
||||
struct PrstCameraValues
|
||||
{
|
||||
std::u16string_view msCameraPrstName; // identifies the value set
|
||||
|
||||
bool mbIsParallel;
|
||||
|
||||
// values as shown in the UI of MS Office, converted to 1/60000 deg
|
||||
double mfRotateAngleX; // unit 1/60000 degree
|
||||
double mfRotateAngleY; // unit 1/60000 degree
|
||||
double mfRotateAngleZ; // unit 1/60000 degree
|
||||
|
||||
// Position of origin relative to the bounding box of the transformed 2D shape.
|
||||
// LibreOffice can handle values outside the ODF range.
|
||||
double mfOriginX; // ODF range [-0.5 (left).. 0.5 (right)], fraction of width
|
||||
double mfOriginY; // ODF range [-0.5 (top) 0.5 (bottom)], fraction of height
|
||||
|
||||
// mandatory for PARALLEL, ignored for PERSPECTIVE
|
||||
double mfSkewAmount; // range 0 to 100, percent of depth used as slant length
|
||||
double mfSkewAngle; // unit degree
|
||||
|
||||
// mandatory for PERSPECTIVE, ignored for PARALLEL
|
||||
// API type ::com::sun::star::drawing::Position3D; unit 1/100 mm
|
||||
double mfViewPointX; // shift from Origin
|
||||
double mfViewPointY; // shift from Origin
|
||||
double mfViewPointZ; // absolute z-coordinate
|
||||
|
||||
// The OOXML camera attribute "zoom" is not contained, because it is not set in preset camera
|
||||
// types and LO cannot render it in custom shape extrusion scene.
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
// The values were found experimental using MS Office. A spreadsheet with remarks is attached
|
||||
// to tdf#70039.
|
||||
constexpr sal_uInt16 nCameraPresetCount(62); // Fixed, specified in OOXML standard.
|
||||
constexpr PrstCameraValues aPrstCameraValuesArray[nCameraPresetCount] = {
|
||||
{ u"isometricBottomDown", true, 2124000, 18882000, 17988000, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricBottomUp", true, 2124000, 2718000, 3612000, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricLeftDown", true, 2100000, 2700000, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricLeftUp", true, 19500000, 2700000, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricOffAxis1Left", true, 1080000, 3840000, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricOffAxis1Right", true, 1080000, 20040000, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricOffAxis1Top", true, 18078000, 18390000, 3456000, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricOffAxis2Left", true, 1080000, 1560000, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricOffAxis2Right", true, 1080000, 17760000, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricOffAxis2Top", true, 18078000, 3210000, 18144000, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricOffAxis3Bottom", true, 3522000, 18390000, 18144000, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricOffAxis3Left", true, 20520000, 3840000, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricOffAxis3Right", true, 20520000, 20040000, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricOffAxis4Bottom", true, 3522000, 3210000, 3456000, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricOffAxis4Left", true, 20520000, 1560000, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricOffAxis4Right", true, 20520000, 17760000, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricRightDown", true, 19500000, 18900000, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricRightUp", true, 2100000, 18900000, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricTopDown", true, 19476000, 2718000, 17988000, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"isometricTopUp", true, 19476000, 18882000, 3612000, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"legacyObliqueBottom", true, 0, 0, 0, 0, 0.5, 50, 90, 0, 0, 0 },
|
||||
{ u"legacyObliqueBottomLeft", true, 0, 0, 0, -0.5, 0.5, 50, 45, 0, 0, 0 },
|
||||
{ u"legacyObliqueBottomRight", true, 0, 0, 0, 0.5, 0.5, 50, 135, 0, 0, 0 },
|
||||
{ u"legacyObliqueFront", true, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"legacyObliqueLeft", true, 0, 0, 0, -0.5, 0, 50, -360, 0, 0, 0 },
|
||||
{ u"legacyObliqueRight", true, 0, 0, 0, 0.5, 0, 50, 180, 0, 0, 0 },
|
||||
{ u"legacyObliqueTop", true, 0, 0, 0, 0, -0.5, 50, -90, 0, 0, 0 },
|
||||
{ u"legacyObliqueTopLeft", true, 0, 0, 0, -0.5, -0.5, 50, -45, 0, 0, 0 },
|
||||
{ u"legacyObliqueTopRight", true, 0, 0, 0, 0.5, -0.5, 50, -135, 0, 0, 0 },
|
||||
{ u"legacyPerspectiveBottom", false, 0, 0, 0, 0, 0.5, 50, 90, 0, 3472, 25000 },
|
||||
{ u"legacyPerspectiveBottomLeft", false, 0, 0, 0, -0.5, 0.5, 50, 45, -3472, 3472, 25000 },
|
||||
{ u"legacyPerspectiveBottomRight", false, 0, 0, 0, 0.5, 0.5, 50, 135, 3472, 3472, 25000 },
|
||||
{ u"legacyPerspectiveFront", false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25000 },
|
||||
{ u"legacyPerspectiveLeft", false, 0, 0, 0, -0.5, 0, 50, -360, -3472, 0, 25000 },
|
||||
{ u"legacyPerspectiveRight", false, 0, 0, 0, 0.5, 0, 50, 180, 3472, 0, 25000 },
|
||||
{ u"legacyPerspectiveTop", false, 0, 0, 0, 0, -0.5, 50, -90, 0, -3472, 25000 },
|
||||
{ u"legacyPerspectiveTopLeft", false, 0, 0, 0, -0.5, -0.5, 50, -45, -3472, -3472, 25000 },
|
||||
{ u"legacyPerspectiveTopRight", false, 0, 0, 0, 0.5, -0.5, 50, -135, 3472, -3472, 25000 },
|
||||
{ u"obliqueBottom", true, 0, 0, 0, 0, 0.5, 30, 90, 0, 0, 0 },
|
||||
{ u"obliqueBottomLeft", true, 0, 0, 0, -0.5, 0.5, 30, 45, 0, 0, 0 },
|
||||
{ u"obliqueBottomRight", true, 0, 0, 0, 0.5, 0.5, 30, 135, 0, 0, 0 },
|
||||
{ u"obliqueLeft", true, 0, 0, 0, -0.5, 0, 30, -360, 0, 0, 0 },
|
||||
{ u"obliqueRight", true, 0, 0, 0, 0.5, 0, 30, 180, 0, 0, 0 },
|
||||
{ u"obliqueTop", true, 0, 0, 0, 0, -0.5, 30, -90, 0, 0, 0 },
|
||||
{ u"obliqueTopLeft", true, 0, 0, 0, -0.5, -0.5, 30, -45, 0, 0, 0 },
|
||||
{ u"obliqueTopRight", true, 0, 0, 0, 0.5, -0.5, 30, -135, 0, 0, 0 },
|
||||
{ u"orthographicFront", true, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ u"perspectiveAbove", false, 20400000, 0, 0, 0, 0, 0, 0, 0, 0, 38451 },
|
||||
{ u"perspectiveAboveLeftFacing", false, 2358000, 858000, 20466000, 0, 0, 0, 0, 0, 0, 38451 },
|
||||
{ u"perspectiveAboveRightFacing", false, 2358000, 20742000, 1134000, 0, 0, 0, 0, 0, 0, 38451 },
|
||||
{ u"perspectiveBelow", false, 1200000, 0, 0, 0, 0, 0, 0, 0, 0, 38451 },
|
||||
{ u"perspectiveContrastingLeftFacing", false, 624000, 2634000, 21384000, 0, 0, 0, 0, 0, 0,
|
||||
38451 },
|
||||
{ u"perspectiveContrastingRightFacing", false, 624000, 18966000, 216000, 0, 0, 0, 0, 0, 0,
|
||||
38451 },
|
||||
{ u"perspectiveFront", false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38451 },
|
||||
{ u"perspectiveHeroicExtremeLeftFacing", false, 486000, 2070000, 21426000, 0, 0, 0, 0, 0, 0,
|
||||
18981 },
|
||||
{ u"perspectiveHeroicExtremeRightFacing", false, 486000, 19530000, 174000, 0, 0, 0, 0, 0, 0,
|
||||
18981 },
|
||||
{ u"perspectiveHeroicLeftFacing", false, 20940000, 858000, 156000, 0, 0, 0, 0, 0, 0, 38451 },
|
||||
{ u"perspectiveHeroicRightFacing", false, 20940000, 20742000, 21444000, 0, 0, 0, 0, 0, 0,
|
||||
38451 },
|
||||
{ u"perspectiveLeft", false, 0, 1200000, 0, 0, 0, 0, 0, 0, 0, 38451 },
|
||||
{ u"perspectiveRelaxed", false, 18576000, 0, 0, 0, 0, 0, 0, 0, 0, 38451 },
|
||||
{ u"perspectiveRelaxedModerately", false, 19488000, 0, 0, 0, 0, 0, 0, 0, 0, 38451 },
|
||||
{ u"perspectiveRight", false, 0, 20400000, 0, 0, 0, 0, 0, 0, 0, 38451 }
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
/** Searches for the item in aPrstCameraValuesArray with given sPresetName.
|
||||
@param [in] sPresetName name as specified in OOXML standard
|
||||
@return returns the index if item exists, otherwise -1*/
|
||||
sal_Int16 getPrstCameraIndex(std::u16string_view sPresetName)
|
||||
{
|
||||
sal_Int16 nIt(0);
|
||||
while (nIt < nCameraPresetCount && aPrstCameraValuesArray[nIt].msCameraPrstName != sPresetName)
|
||||
++nIt;
|
||||
if (nIt >= nCameraPresetCount)
|
||||
{
|
||||
nIt = -1; // Error is handled by caller
|
||||
}
|
||||
return nIt;
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
void Scene3DHelper::getAPIAnglesFromOOXAngle(const sal_Int32 nLat, const sal_Int32 nLon,
|
||||
const sal_Int32 nRev, double& fX, double& fY,
|
||||
double& fZ)
|
||||
{
|
||||
// MS Office applies the rotations in the order first around y-axis by nLon, then around x-axis
|
||||
// by nLat and last around z-axis by nRev. The extrusion mode in ODF and also the API
|
||||
// first rotate around the z-axis, then around the y-axis and last around the x-axis. In ODF, the
|
||||
// rotation around the z-axis is integrated into the shape transformation and the others are
|
||||
// specified in the enhanced geometry of the shape.
|
||||
// The orientation of the resulting angles equals the orientation in API, but the angles are in
|
||||
// radians.
|
||||
|
||||
// First we build the total rotation matrix from the OOX angles. y-axis points down.
|
||||
basegfx::B3DHomMatrix aXMat;
|
||||
const double fLatRad = basegfx::deg2rad<60000>(nLat);
|
||||
aXMat.set(1, 1, cos(fLatRad));
|
||||
aXMat.set(2, 2, cos(fLatRad));
|
||||
aXMat.set(1, 2, sin(fLatRad));
|
||||
aXMat.set(2, 1, -sin(fLatRad));
|
||||
|
||||
basegfx::B3DHomMatrix aYMat;
|
||||
const double fLonRad = basegfx::deg2rad<60000>(nLon);
|
||||
aYMat.set(0, 0, cos(fLonRad));
|
||||
aYMat.set(2, 2, cos(fLonRad));
|
||||
aYMat.set(0, 2, -sin(fLonRad));
|
||||
aYMat.set(2, 0, sin(fLonRad));
|
||||
|
||||
basegfx::B3DHomMatrix aZMat;
|
||||
const double fRevRad = basegfx::deg2rad<60000>(nRev);
|
||||
aZMat.set(0, 0, cos(fRevRad));
|
||||
aZMat.set(1, 1, cos(fRevRad));
|
||||
aZMat.set(0, 1, sin(fRevRad));
|
||||
aZMat.set(1, 0, -sin(fRevRad));
|
||||
basegfx::B3DHomMatrix aTotalMat = aZMat * aXMat * aYMat;
|
||||
|
||||
// Now we decompose it so that rotation around z-axis is the first rotation. We know it is a
|
||||
// orthonormal matrix, so some steps seen in B3DHomMatrix::decompose() are not needed.
|
||||
// The solution fY2 = pi - fY results in the same projection, thus we do not consider it.
|
||||
fY = std::asin(-aTotalMat.get(0, 2));
|
||||
|
||||
if (basegfx::fTools::equalZero(cos(fY)))
|
||||
{
|
||||
// This case has zeros at positions (0,0), (0,1), (1,2) and (2,2) in aTotalMat.
|
||||
// This special case means, that the shape is rotated around the y-axis so, that the user
|
||||
// looks on the extruded faces. Front face and back face are orthogonal to the xy-plane. The
|
||||
// rotation around the x-axis cannot be distinguished from an initial rotation of the shape
|
||||
// outside 3D. Thus there exist no unique solution.
|
||||
fX = 0.0;
|
||||
fZ = std::atan2(aTotalMat.get(2, 1), aTotalMat.get(1, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
fX = std::atan2(-aTotalMat.get(1, 2) / cos(fY), aTotalMat.get(2, 2) / cos(fY));
|
||||
fZ = std::atan2(aTotalMat.get(0, 1) / cos(fY), aTotalMat.get(0, 0) / cos(fY));
|
||||
}
|
||||
}
|
||||
|
||||
void Scene3DHelper::getAPIAnglesFrom3DProperties(
|
||||
const oox::drawingml::Shape3DPropertiesPtr p3DProperties, const sal_Int32& rnMSOShapeRotation,
|
||||
double& fX, double& fY, double& fZ)
|
||||
{
|
||||
if (!p3DProperties)
|
||||
return;
|
||||
|
||||
// on x-axis, unit 1/60000 deg
|
||||
sal_Int32 nLatitude = (*p3DProperties).maCameraRotation.mnLatitude.value_or(0);
|
||||
// on y-axis, unit 1/60000 deg
|
||||
sal_Int32 nLongitude = (*p3DProperties).maCameraRotation.mnLongitude.value_or(0);
|
||||
// on z-axis, unit 1/60000 deg
|
||||
sal_Int32 nRevolution = (*p3DProperties).maCameraRotation.mnRevolution.value_or(0);
|
||||
|
||||
// Some projection types need special treatment:
|
||||
if (29 <= mnPrstCameraIndex && mnPrstCameraIndex <= 37)
|
||||
{
|
||||
// legacyPerspective. MS Office does not use z-rotation but writes it to file. We need to
|
||||
// ignore it. The preset cameras have no rotation.
|
||||
nRevolution = 0;
|
||||
}
|
||||
else if (47 <= mnPrstCameraIndex && mnPrstCameraIndex <= 61)
|
||||
{
|
||||
// perspective. MS Office has a strange rendering behavior: If the shape rotation is not zero
|
||||
// and the angle for rotation on x-axis (=latitude) is >90deg and <=270deg, then MSO renders
|
||||
// the shape with an addition 180deg rotation on the z-axis. This happens only with user
|
||||
// entered angles.
|
||||
if (rnMSOShapeRotation != 0 && nLatitude > 5400000 && nLatitude <= 16200000)
|
||||
nRevolution += 10800000;
|
||||
}
|
||||
|
||||
// In case attributes lat, lon and rev of the <rot> child element of the <scene3d> element in
|
||||
// OOXML markup are given, they overwrite the values from the preset camera type. Otherwise the
|
||||
// values from the preset camera are used. OOXML requires that all three attributes must exist at
|
||||
// the same time. Thus it is enough to test one of them.
|
||||
if (!(*p3DProperties).maCameraRotation.mnRevolution.has_value())
|
||||
{
|
||||
// The angles are given in 1/60000 deg in aPrstCameraValuesArray.
|
||||
nLatitude = aPrstCameraValuesArray[mnPrstCameraIndex].mfRotateAngleX;
|
||||
nLongitude = aPrstCameraValuesArray[mnPrstCameraIndex].mfRotateAngleY;
|
||||
nRevolution = aPrstCameraValuesArray[mnPrstCameraIndex].mfRotateAngleZ;
|
||||
}
|
||||
|
||||
// MS Office applies the shape rotation after the rotations from camera in case of non-legacy
|
||||
// cameras, and before for legacy cameras. ODF specifies to first rotate the shape. Thus we need
|
||||
// to add shape rotation to nRevolution in case of non-legacy cameras. The shape rotation has
|
||||
// opposite orientation than camera z-rotation.
|
||||
bool bIsLegacyCamera = 20 <= mnPrstCameraIndex && mnPrstCameraIndex <= 37;
|
||||
if (!bIsLegacyCamera)
|
||||
nRevolution -= rnMSOShapeRotation;
|
||||
|
||||
// Now calculate the angles for LO rotation order and orientation.
|
||||
Scene3DHelper::getAPIAnglesFromOOXAngle(nLatitude, nLongitude, nRevolution, fX, fY, fZ);
|
||||
|
||||
if (bIsLegacyCamera)
|
||||
fZ -= basegfx::deg2rad<60000>(rnMSOShapeRotation);
|
||||
}
|
||||
|
||||
void Scene3DHelper::addRotateAngleToMap(oox::PropertyMap& rPropertyMap, const double fX,
|
||||
const double fY)
|
||||
{
|
||||
css::drawing::EnhancedCustomShapeParameterPair aAnglePair;
|
||||
aAnglePair.First.Value <<= basegfx::rad2deg(fX);
|
||||
aAnglePair.First.Type = css::drawing::EnhancedCustomShapeParameterType::NORMAL;
|
||||
aAnglePair.Second.Value <<= basegfx::rad2deg(fY);
|
||||
aAnglePair.Second.Type = css::drawing::EnhancedCustomShapeParameterType::NORMAL;
|
||||
rPropertyMap.setAnyProperty(oox::PROP_RotateAngle, css::uno::Any(aAnglePair));
|
||||
}
|
||||
|
||||
void Scene3DHelper::addExtrusionDepthToMap(const oox::drawingml::Shape3DPropertiesPtr p3DProperties,
|
||||
oox::PropertyMap& rPropertyMap)
|
||||
{
|
||||
// Amount of extrusion and its position relative to the original shape face. This moves the
|
||||
// shape inside the scene.
|
||||
// The GetExtrusionDepth() method in EnhancedCustomShape3d.cxx expects type double for both.
|
||||
sal_Int32 nDepthAmount = (*p3DProperties).mnExtrusionH.value_or(0); // unit EMU
|
||||
double fDepthAmount = o3tl::convert(nDepthAmount, o3tl::Length::emu, o3tl::Length::mm100);
|
||||
sal_Int32 nZPosition = (*p3DProperties).mnShapeZ.value_or(0); // unit EMU
|
||||
double fZPosition = o3tl::convert(nZPosition, o3tl::Length::emu, o3tl::Length::mm100);
|
||||
double fDepthRelPos = 0.0;
|
||||
if (nDepthAmount == 0 && nZPosition != 0)
|
||||
{
|
||||
// We cannot express the position relative to the extrusion depth.
|
||||
// Use an artifical, small depth of 1Hmm
|
||||
fDepthRelPos = fZPosition;
|
||||
fDepthAmount = 1.0; // unit Hmm
|
||||
}
|
||||
else if (nDepthAmount != 0)
|
||||
fDepthRelPos = fZPosition / fDepthAmount;
|
||||
|
||||
css::drawing::EnhancedCustomShapeParameterPair aPair;
|
||||
css::drawing::EnhancedCustomShapeParameter& rDepthAmount = aPair.First;
|
||||
rDepthAmount.Value <<= fDepthAmount;
|
||||
rDepthAmount.Type = css::drawing::EnhancedCustomShapeParameterType::NORMAL;
|
||||
css::drawing::EnhancedCustomShapeParameter& rDepthFraction = aPair.Second;
|
||||
rDepthFraction.Value <<= fDepthRelPos;
|
||||
rDepthFraction.Type = css::drawing::EnhancedCustomShapeParameterType::NORMAL;
|
||||
rPropertyMap.setProperty(oox::PROP_Depth, aPair);
|
||||
}
|
||||
|
||||
void Scene3DHelper::addProjectionGeometryToMap(
|
||||
const oox::drawingml::Shape3DPropertiesPtr p3DProperties, oox::PropertyMap& rPropertyMap,
|
||||
const bool bIsParallel, const sal_Int32 rnMSOShapeRotation)
|
||||
{
|
||||
// origin is needed for parallel and perspective as well
|
||||
css::drawing::EnhancedCustomShapeParameterPair aOrigin;
|
||||
aOrigin.First.Value <<= aPrstCameraValuesArray[mnPrstCameraIndex].mfOriginX;
|
||||
aOrigin.First.Type = css::drawing::EnhancedCustomShapeParameterType::NORMAL;
|
||||
aOrigin.Second.Value <<= aPrstCameraValuesArray[mnPrstCameraIndex].mfOriginY;
|
||||
aOrigin.Second.Type = css::drawing::EnhancedCustomShapeParameterType::NORMAL;
|
||||
rPropertyMap.setProperty(oox::PROP_Origin, aOrigin);
|
||||
|
||||
if (bIsParallel)
|
||||
{
|
||||
// PARALLEL needs API property Skew.
|
||||
// orthographicFront and isometric projections do not use skew. We write it nevertheless
|
||||
// to prevent LO defaults. Zeros are contained in aPrstCameraValuesArray for these cases.
|
||||
double fSkewAngle = aPrstCameraValuesArray[mnPrstCameraIndex].mfSkewAngle; // unit degree
|
||||
double fSkewAmount = aPrstCameraValuesArray[mnPrstCameraIndex].mfSkewAmount;
|
||||
// oblique projections (index [38..45]) need special treatment. MS Office rotates around the
|
||||
// z-axis after the projection was created. Thus the rotation affects the skew direction. ODF
|
||||
// rotates the shape before creating the projection. Thus we need to incorporate the shape
|
||||
// rotation into the shew angle.
|
||||
if (38 <= mnPrstCameraIndex && mnPrstCameraIndex <= 45)
|
||||
{
|
||||
fSkewAngle -= rnMSOShapeRotation / 60000.0;
|
||||
}
|
||||
css::drawing::EnhancedCustomShapeParameterPair aSkew;
|
||||
aSkew.First.Value <<= fSkewAmount;
|
||||
aSkew.First.Type = css::drawing::EnhancedCustomShapeParameterType::NORMAL;
|
||||
aSkew.Second.Value <<= fSkewAngle;
|
||||
aSkew.Second.Type = css::drawing::EnhancedCustomShapeParameterType::NORMAL;
|
||||
rPropertyMap.setProperty(oox::PROP_Skew, aSkew);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PERSPECTIVE needs API property ViewPoint.
|
||||
css::drawing::Position3D aViewPoint;
|
||||
|
||||
// x- and y-coordinate depend on preset camera type.
|
||||
aViewPoint.PositionX = aPrstCameraValuesArray[mnPrstCameraIndex].mfViewPointX;
|
||||
aViewPoint.PositionY = aPrstCameraValuesArray[mnPrstCameraIndex].mfViewPointY;
|
||||
|
||||
// The z-coordinate is determined bei a field of view angle in OOXML and by a
|
||||
// distance in LibreOffice. MS Office users can change its value.
|
||||
if ((*p3DProperties).mfFieldOfVision.has_value())
|
||||
{
|
||||
double fFov = (*p3DProperties).mfFieldOfVision.value();
|
||||
fFov = std::clamp(fFov, 0.5, 179.5);
|
||||
// 15976 = 25000 * tan(32.5°) as in legacy. Better ideas to calculate the distance are
|
||||
// welcome.
|
||||
aViewPoint.PositionZ = 15976.0 / tan(basegfx::deg2rad(fFov / 2.0));
|
||||
}
|
||||
else
|
||||
aViewPoint.PositionZ = aPrstCameraValuesArray[mnPrstCameraIndex].mfViewPointZ;
|
||||
|
||||
rPropertyMap.setProperty(oox::PROP_ViewPoint, aViewPoint);
|
||||
}
|
||||
// ToDo: It is possible in OOXML to set a 3D-scene on a group. It is not clear yet how that can
|
||||
// be mimicked in LO. In case of perspective projection, it produces a horizontal or vertical
|
||||
// shift of the viewpoint in relation to the shapes of the group, for example.
|
||||
}
|
||||
|
||||
bool Scene3DHelper::setExtrusionProperties(const oox::drawingml::Shape3DPropertiesPtr p3DProperties,
|
||||
const sal_Int32& rnMSOShapeRotation,
|
||||
oox::PropertyMap& rPropertyMap, double& rRotZ,
|
||||
oox::drawingml::Color& rExtrusionColor)
|
||||
{
|
||||
// We convert rnMSOShapeRotation, so that Shape::createAndInsert() can use rRotZ the same way in
|
||||
// all cases.
|
||||
rRotZ = basegfx::deg2rad<60000>(-rnMSOShapeRotation);
|
||||
|
||||
if (!p3DProperties || (p3DProperties && !(*p3DProperties).mnPreset.has_value()))
|
||||
return false;
|
||||
|
||||
const sal_Int32 nCameraPrstID((*p3DProperties).mnPreset.value());
|
||||
mnPrstCameraIndex
|
||||
= getPrstCameraIndex(oox::drawingml::Generic3DProperties::getCameraPrstName(nCameraPrstID));
|
||||
if (mnPrstCameraIndex < 0 or mnPrstCameraIndex >= nCameraPresetCount)
|
||||
return false; // error in document. OOXML specifies a fixed set of preset camera types.
|
||||
|
||||
// We use extrusion, if there is a rotation around x-axis or y-axis,
|
||||
// or if there is no such rotation but we have a perspective projection with true depth,
|
||||
// or we have a parallel projection other than a 'front' type.
|
||||
// In other cases the rendering as normal shape is better than any current extrusion.
|
||||
double fX;
|
||||
double fY;
|
||||
Scene3DHelper::getAPIAnglesFrom3DProperties(p3DProperties, rnMSOShapeRotation, fX, fY, rRotZ);
|
||||
sal_Int32 nDepthAmount = (*p3DProperties).mnExtrusionH.value_or(0);
|
||||
bool bIsParallel = aPrstCameraValuesArray[mnPrstCameraIndex].mbIsParallel;
|
||||
bool bIsParallelFrontType
|
||||
= (nCameraPrstID == XML_legacyObliqueFront) || (nCameraPrstID == XML_orthographicFront);
|
||||
bool bCreateExtrusion = (!basegfx::fTools::equalZero(fX) || !basegfx::fTools::equalZero(fY))
|
||||
|| (!bIsParallel && nDepthAmount > 0)
|
||||
|| (bIsParallel && !bIsParallelFrontType);
|
||||
|
||||
// Extrusion color is not handled as extrusion property but as shape property. Thus deliver it
|
||||
// in any case, so that Shape::createAndInsert() knows about it.
|
||||
rExtrusionColor = (*p3DProperties).maExtrusionColor;
|
||||
|
||||
if (!bCreateExtrusion)
|
||||
return false;
|
||||
|
||||
// Create the extrusion properties in rPropertyMap so that they can be directly used.
|
||||
// Turn extrusion on
|
||||
rPropertyMap.setProperty(oox::PROP_Extrusion, true);
|
||||
|
||||
// Dummy value. Will be changed from evaluating the material properties, when implemented.
|
||||
rPropertyMap.setProperty(oox::PROP_Diffusion, 100.0);
|
||||
|
||||
// Camera properties
|
||||
css::drawing::ProjectionMode eProjectionMode = bIsParallel
|
||||
? css::drawing::ProjectionMode_PARALLEL
|
||||
: css::drawing::ProjectionMode_PERSPECTIVE;
|
||||
rPropertyMap.setProperty(oox::PROP_ProjectionMode, eProjectionMode);
|
||||
|
||||
Scene3DHelper::addRotateAngleToMap(rPropertyMap, fX, fY);
|
||||
|
||||
Scene3DHelper::addProjectionGeometryToMap(p3DProperties, rPropertyMap, bIsParallel,
|
||||
rnMSOShapeRotation);
|
||||
|
||||
// Shape properties
|
||||
Scene3DHelper::addExtrusionDepthToMap(p3DProperties, rPropertyMap);
|
||||
|
||||
// Extrusion color enabled?
|
||||
rPropertyMap.setProperty(oox::PROP_Color, rExtrusionColor.isUsed());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end namespace oox
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
|
@ -28,6 +28,7 @@
|
|||
#include <drawingml/lineproperties.hxx>
|
||||
#include <drawingml/presetgeometrynames.hxx>
|
||||
#include <drawingml/shape3dproperties.hxx>
|
||||
#include <drawingml/scene3dhelper.hxx>
|
||||
#include "effectproperties.hxx"
|
||||
#include <oox/drawingml/shapepropertymap.hxx>
|
||||
#include <drawingml/textbody.hxx>
|
||||
|
@ -842,7 +843,7 @@ void lcl_doSpecialMSOWidthHeightToggle(basegfx::B2DHomMatrix& aTransformation)
|
|||
return;
|
||||
}
|
||||
|
||||
void lcl_RotateAtCenter(basegfx::B2DHomMatrix& aTransformation,sal_Int32 nMSORotationAngle)
|
||||
void lcl_RotateAtCenter(basegfx::B2DHomMatrix& aTransformation, sal_Int32 nMSORotationAngle)
|
||||
{
|
||||
if (nMSORotationAngle == 0)
|
||||
return;
|
||||
|
@ -854,6 +855,15 @@ void lcl_RotateAtCenter(basegfx::B2DHomMatrix& aTransformation,sal_Int32 nMSORot
|
|||
aTransformation.translate(aCenter);
|
||||
return;
|
||||
}
|
||||
|
||||
Degree100 lcl_MSORotateAngleToAPIAngle(const sal_Int32 nMSORotationAngle)
|
||||
{
|
||||
// Converts a shape rotation angle from MSO to angle for API property RotateAngle
|
||||
// from unit 1/60000 deg to unit 1/100 deg
|
||||
Degree100 nAngle(nMSORotationAngle / 600);
|
||||
// API RotateAngle has opposite direction than nMSORotationAngle, thus 'minus'.
|
||||
return NormAngle36000(-nAngle);
|
||||
}
|
||||
}
|
||||
|
||||
Reference< XShape > const & Shape::createAndInsert(
|
||||
|
@ -937,7 +947,43 @@ Reference< XShape > const & Shape::createAndInsert(
|
|||
|
||||
bool bIsCustomShape = (aServiceName == "com.sun.star.drawing.CustomShape" || bIsCroppedGraphic);
|
||||
bool bIsConnectorShape = (aServiceName == "com.sun.star.drawing.ConnectorShape");
|
||||
if(bIsCroppedGraphic)
|
||||
|
||||
// Look for 3D. Its z-rotation and extrusion color become shape properties. Do it early as
|
||||
// graphics might use 3D too and in that case should be imported as custom shape as well.
|
||||
double fShapeRotateInclCamera = 0.0; // unit rad; same orientation as shape property RotateAngle
|
||||
Color aExtrusionColor;
|
||||
Scene3DHelper aScene3DHelper;
|
||||
bool bHas3DEffect = aScene3DHelper.setExtrusionProperties(
|
||||
mp3DPropertiesPtr, mnRotation, getCustomShapeProperties()->getExtrusionPropertyMap(),
|
||||
fShapeRotateInclCamera, aExtrusionColor);
|
||||
// Currently the other places use unit 1/60000deg and MSO shape rotate orientation, thus convert.
|
||||
sal_Int32 nShapeRotateInclCamera = -basegfx::rad2deg<60000>(fShapeRotateInclCamera);
|
||||
|
||||
bool bIs3DGraphic = aServiceName == "com.sun.star.drawing.GraphicObjectShape" && bHas3DEffect;
|
||||
if (bIs3DGraphic)
|
||||
{
|
||||
// The parts which use bIs3DGraphic are commented out for now (3x) because the export does
|
||||
// not re-create the image, and because the rendering of images with transparent parts is
|
||||
// broken in extrusion mode (tdf#159515). In principle it works that way to get 3D-effects
|
||||
// on images.
|
||||
SAL_INFO("oox.drawingml",
|
||||
"Shape::createAndInsert: image with 3D effect conversion disabled");
|
||||
}
|
||||
// bIsCustomShape |= bIs3DGraphic;
|
||||
|
||||
// The extrusion color does not belong to the extrusion properties but is secondary color in
|
||||
// the style of the shape, FillColor2 in API.
|
||||
if (aExtrusionColor.isUsed())
|
||||
{
|
||||
// FillColor2 is not yet transformed to ComplexColor.
|
||||
::Color aColor = aExtrusionColor.getColor(rFilterBase.getGraphicHelper());
|
||||
maShapeProperties.setProperty(PROP_FillColor2, aColor);
|
||||
}
|
||||
// ToDo: MS Office 'automatic' color uses line color if it exists, LO uses fill color. We might
|
||||
// need to change color here in case of 'automatic'.
|
||||
|
||||
// if (bIsCroppedGraphic || bIs3DGraphic), disabled for now, see comment #965
|
||||
if (bIsCroppedGraphic)
|
||||
{
|
||||
aServiceName = "com.sun.star.drawing.CustomShape";
|
||||
mpGraphicPropertiesPtr->mbIsCustomShape = true;
|
||||
|
@ -1057,11 +1103,10 @@ Reference< XShape > const & Shape::createAndInsert(
|
|||
// The flip contained in aParentScale will affect orientation of object rotation angle.
|
||||
sal_Int16 nOrientation = ((aParentScale.getX() < 0) != (aParentScale.getY() < 0)) ? -1 : 1;
|
||||
// ToDo: Not sure about the restrictions given by bUseRotationTransform.
|
||||
// Since LibreOffice doesn't have 3D camera options for 2D shapes, rotate the shape opposite of
|
||||
// the camera Z axis rotation, in order to produce the same visual result from MSO
|
||||
const sal_Int32 nCameraRotation = get3DProperties().maCameraRotation.mnRevolution.value_or(0);
|
||||
if (bUseRotationTransform && (mnRotation != 0 || nCameraRotation != 0))
|
||||
lcl_RotateAtCenter(aTransformation, nOrientation * (mnRotation - nCameraRotation));
|
||||
if (bUseRotationTransform && nShapeRotateInclCamera != 0)
|
||||
{
|
||||
lcl_RotateAtCenter(aTransformation, nOrientation * nShapeRotateInclCamera);
|
||||
}
|
||||
|
||||
if (fParentRotate != 0.0)
|
||||
aTransformation.rotate(fParentRotate);
|
||||
|
@ -1319,8 +1364,9 @@ Reference< XShape > const & Shape::createAndInsert(
|
|||
// applying properties
|
||||
aShapeProps.assignUsed( getShapeProperties() );
|
||||
aShapeProps.assignUsed( maDefaultShapeProperties );
|
||||
if(mnRotation != 0 && bIsCustomShape)
|
||||
aShapeProps.setProperty( PROP_RotateAngle, sal_Int32( NormAngle36000( Degree100(mnRotation / -600) ) ));
|
||||
if(nShapeRotateInclCamera != 0 && bIsCustomShape)
|
||||
aShapeProps.setProperty(PROP_RotateAngle,
|
||||
sal_Int32(lcl_MSORotateAngleToAPIAngle(nShapeRotateInclCamera)));
|
||||
if( bIsEmbMedia ||
|
||||
bIsCustomShape ||
|
||||
aServiceName == "com.sun.star.drawing.GraphicObjectShape" ||
|
||||
|
@ -1341,7 +1387,8 @@ Reference< XShape > const & Shape::createAndInsert(
|
|||
FillProperties aFillProperties = getActualFillProperties(pTheme, &rShapeOrParentShapeFillProps);
|
||||
if (getFillProperties().moFillType.has_value() && getFillProperties().moFillType.value() == XML_grpFill)
|
||||
getFillProperties().assignUsed(aFillProperties);
|
||||
if(!bIsCroppedGraphic)
|
||||
// if(!bIsCroppedGraphic && !bIs3DGraphic), disabled for now, see comment #960
|
||||
if (!bIsCroppedGraphic)
|
||||
aFillProperties.pushToPropMap(aShapeProps, rGraphicHelper, mnRotation, nFillPhClr,
|
||||
css::awt::Size(aShapeRectHmm.Width, aShapeRectHmm.Height),
|
||||
nFillPhClrTheme, mbFlipH, mbFlipV, bIsCustomShape);
|
||||
|
@ -1834,11 +1881,10 @@ Reference< XShape > const & Shape::createAndInsert(
|
|||
|
||||
// tdf#133037: a bit hackish: force Shape to rotate in the opposite direction the camera would rotate
|
||||
PropertySet aPropertySet(mxShape);
|
||||
if ( !bUseRotationTransform && (mnRotation != 0 || nCameraRotation != 0) )
|
||||
if ( !bUseRotationTransform && (nShapeRotateInclCamera !=0) )
|
||||
{
|
||||
// use the same logic for rotation from VML exporter (SimpleShape::implConvertAndInsert at vmlshape.cxx)
|
||||
Degree100 nAngle = NormAngle36000( Degree100((mnRotation - nCameraRotation) / -600) );
|
||||
aPropertySet.setAnyProperty( PROP_RotateAngle, Any( sal_Int32( nAngle.get() ) ) );
|
||||
Degree100 nAngle(lcl_MSORotateAngleToAPIAngle(nShapeRotateInclCamera));
|
||||
aPropertySet.setAnyProperty(PROP_RotateAngle, Any( sal_Int32(nAngle)));
|
||||
aPropertySet.setAnyProperty( PROP_HoriOrientPosition, Any( maPosition.X ) );
|
||||
aPropertySet.setAnyProperty( PROP_VertOrientPosition, Any( maPosition.Y ) );
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ BottomBorder
|
|||
BottomBorderComplexColor
|
||||
BottomBorderDistance
|
||||
BottomMargin
|
||||
Brightness
|
||||
BuiltInUnit
|
||||
BulletChar
|
||||
BulletColor
|
||||
|
@ -141,9 +142,11 @@ DefaultState
|
|||
DefaultText
|
||||
DefaultValue
|
||||
DeletedLegendEntries
|
||||
Depth
|
||||
DiagonalBLTR
|
||||
DiagonalTLBR
|
||||
DialogLibraries
|
||||
Diffusion
|
||||
DisableComplexChartTypes
|
||||
DisableDataTableDialog
|
||||
DisplayLabels
|
||||
|
@ -168,6 +171,7 @@ ExternalDocLinks
|
|||
ExternalLinks
|
||||
ExtrapolateBackward
|
||||
ExtrapolateForward
|
||||
Extrusion
|
||||
FileFormat
|
||||
FillBackground
|
||||
FillBitmapMode
|
||||
|
@ -179,6 +183,7 @@ FillBitmapSizeX
|
|||
FillBitmapSizeY
|
||||
FillBitmap
|
||||
FillColor
|
||||
FillColor2
|
||||
FillColorTheme
|
||||
FillColorLumMod
|
||||
FillColorLumOff
|
||||
|
@ -195,6 +200,9 @@ FillUseSlideBackground
|
|||
Filter
|
||||
FilterCriteriaSource
|
||||
FilterOptions
|
||||
FirstLightDirection
|
||||
FirstLightHarsh
|
||||
FirstLightLevel
|
||||
FirstLineOffset
|
||||
FirstPageFooterContent
|
||||
FirstPageHeaderContent
|
||||
|
@ -321,6 +329,7 @@ LeftMargin
|
|||
LeftPageFooterContent
|
||||
LeftPageHeaderContent
|
||||
LegacyFragment
|
||||
LightFace
|
||||
LineCap
|
||||
LineColor
|
||||
LineComplexColor
|
||||
|
@ -348,6 +357,8 @@ MaxFieldCount
|
|||
MaxTextLen
|
||||
MediaType
|
||||
MediaURL
|
||||
Metal
|
||||
MetalType
|
||||
MinorTickmarks
|
||||
MirroredX
|
||||
MirroredY
|
||||
|
@ -370,10 +381,12 @@ NumberingIsNumber
|
|||
NumberingLevel
|
||||
NumberingRules
|
||||
NumberingType
|
||||
NumberOfLineSegments
|
||||
Offset
|
||||
OpCodeMap
|
||||
Opaque
|
||||
Orientation
|
||||
Origin
|
||||
OutputPosition
|
||||
Outline
|
||||
OverlapSequence
|
||||
|
@ -423,6 +436,7 @@ PrivateStream
|
|||
PrivateTempFileURL
|
||||
ProgressValueMax
|
||||
ProgressValueMin
|
||||
ProjectionMode
|
||||
Protected
|
||||
RadiusRangeMaximum
|
||||
RadiusRangeMinimum
|
||||
|
@ -460,6 +474,7 @@ RightPageHeaderContent
|
|||
Role
|
||||
RotateAngle
|
||||
RotateReference
|
||||
RotationCenter
|
||||
RotationHorizontal
|
||||
RotationVertical
|
||||
RowGrand
|
||||
|
@ -476,9 +491,13 @@ ScrollValue
|
|||
ScrollValueMax
|
||||
ScrollValueMin
|
||||
ScrollWidth
|
||||
SecondLightDirection
|
||||
SecondLightHarsh
|
||||
SecondLightLevel
|
||||
Segments
|
||||
SelectedItems
|
||||
SelectedPage
|
||||
ShadeMode
|
||||
Shadow
|
||||
ShadowAlignment
|
||||
ShadowColor
|
||||
|
@ -489,6 +508,7 @@ ShadowTransparence
|
|||
ShadowBlur
|
||||
ShadowXDistance
|
||||
ShadowYDistance
|
||||
Shininess
|
||||
Show
|
||||
ShowBorder
|
||||
ShowCharts
|
||||
|
@ -518,10 +538,12 @@ Size100thMM
|
|||
SizePixel
|
||||
SizeProtect
|
||||
SizeType
|
||||
Skew
|
||||
SkipDuplicates
|
||||
SortInfo
|
||||
Sound
|
||||
SoundOn
|
||||
Specularity
|
||||
Speed
|
||||
Spin
|
||||
SpinIncrement
|
||||
|
@ -614,6 +636,7 @@ VerticalAlign
|
|||
VerticalSplitMode
|
||||
VerticalSplitPositionTwips
|
||||
ViewBox
|
||||
ViewPoint
|
||||
Visible
|
||||
VisibleFlag
|
||||
VisibleSize
|
||||
|
|
Loading…
Reference in a new issue