tdf#171225: don't lose camera rotation angle for mnShapePresetType < 0
For the case when mnShapePresetType >= 0, the code setting it was added
in commit c50e44b270 (tdf#126060 Handle
text camera z rotation while pptx import., 2019-10-16). But it omitted
the other case.
This change also improves positioning of the rotated text, initially
implemented in that commit. The rotation is now added to the block text
decomposition as a separate transform primitive. The text center is now
obtained directly from the decomposition. After the change, I see about
1-pixel difference compared to PowerPoint render at 100% zoom (before
that, the offset was tens of millimeters).
testTdf128212 had to be corrected, because the metafile now has a new
element in the parsed XML, and the final positioning has changed (it is
better now). Unfortunately, I don't know what would it now produce if
the fix is broken, so I had to remove respective comment.
This change does not fix tdf#128206.
Change-Id: I86cbd0710744b14e8b9b68a436affc5b08703a12
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201286
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
This commit is contained in:
@@ -227,6 +227,8 @@ void CustomShapeProperties::pushToPropSet(
|
||||
aPropertyMap.setProperty( PROP_MirroredY, mbMirroredY );
|
||||
if( mnTextPreRotateAngle )
|
||||
aPropertyMap.setProperty( PROP_TextPreRotateAngle, mnTextPreRotateAngle );
|
||||
if (mnTextCameraZRotateAngle)
|
||||
aPropertyMap.setProperty(PROP_TextCameraZRotateAngle, mnTextCameraZRotateAngle);
|
||||
if (moTextAreaRotateAngle.has_value())
|
||||
aPropertyMap.setProperty(PROP_TextRotateAngle, moTextAreaRotateAngle.value());
|
||||
// Note 1: If Equations are defined - they are processed using internal div by 360 coordinates
|
||||
|
||||
@@ -105,11 +105,25 @@ CPPUNIT_TEST_FIXTURE(SdLayoutTest, testTdf128212)
|
||||
createSdImpressDoc("pptx/tdf128212.pptx");
|
||||
xmlDocUniquePtr pXmlDoc = parseLayout();
|
||||
|
||||
// Without the fix in place, this test would have failed with
|
||||
// - Expected: 7795
|
||||
// - Actual : 12068
|
||||
assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/textarray", "x", u"4523");
|
||||
assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/textarray", "y", u"7795");
|
||||
// The position of the rotated text depends on the calculated text size. This depends on
|
||||
// rendering, so it differs a bit on different platforms. Hence rather big delta here.
|
||||
|
||||
// translation
|
||||
assertXPath(pXmlDoc, "//push[@flags='PushMapMode']", 1);
|
||||
assertXPath(pXmlDoc, "//push[@flags='PushMapMode']/mapmode", "mapunit", u"MapRelative");
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(
|
||||
331.0, getXPath(pXmlDoc, "//push[@flags='PushMapMode']/mapmode", "x").toDouble(), 3.0);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(
|
||||
9420.0, getXPath(pXmlDoc, "//push[@flags='PushMapMode']/mapmode", "y").toDouble(), 10.0);
|
||||
// no scaling
|
||||
assertXPath(pXmlDoc, "//push[@flags='PushMapMode']/mapmode", "scalex", u"(1/1)");
|
||||
assertXPath(pXmlDoc, "//push[@flags='PushMapMode']/mapmode", "scaley", u"(1/1)");
|
||||
|
||||
// text position
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(
|
||||
4760.0, getXPath(pXmlDoc, "//push[@flags='PushMapMode']/textarray", "x").toDouble(), 3.0);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(
|
||||
-2250.0, getXPath(pXmlDoc, "//push[@flags='PushMapMode']/textarray", "y").toDouble(), 3.0);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(SdLayoutTest, testColumnsLayout)
|
||||
|
||||
Binary file not shown.
@@ -17,6 +17,7 @@
|
||||
#include <svx/sdr/contact/displayinfo.hxx>
|
||||
#include <svx/sdr/contact/viewcontact.hxx>
|
||||
#include <svx/sdr/contact/viewobjectcontact.hxx>
|
||||
#include <svx/svdoashp.hxx>
|
||||
#include <svx/svdpage.hxx>
|
||||
#include <svx/unopage.hxx>
|
||||
#include <vcl/virdev.hxx>
|
||||
@@ -189,6 +190,57 @@ CPPUNIT_TEST_FIXTURE(SdrTest, testSlideBackground)
|
||||
// i.e. the rendering did not find the bitmap.
|
||||
assertXPath(pDocument, "//bitmap", 1);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(SdrTest, test3DRotatedText)
|
||||
{
|
||||
// The document contains a shape with text "Vertical" and a 3D scene camera rotation of 90 deg.
|
||||
// The text should appear rotated and positioned ~at the top-left of the text frame.
|
||||
|
||||
loadFromFile(u"3d_rotated_text.pptx");
|
||||
|
||||
// verify the camera rotation was imported correctly
|
||||
auto xDrawPages = mxComponent.queryThrow<drawing::XDrawPagesSupplier>()->getDrawPages();
|
||||
auto xDrawPage = xDrawPages->getByIndex(0).queryThrow<drawing::XDrawPage>();
|
||||
auto pDrawPage = dynamic_cast<SvxDrawPage*>(xDrawPage.get());
|
||||
CPPUNIT_ASSERT(pDrawPage);
|
||||
auto* pSdrTextObj = static_cast<SdrTextObj*>(pDrawPage->GetSdrPage()->GetObj(0));
|
||||
CPPUNIT_ASSERT(pSdrTextObj);
|
||||
CPPUNIT_ASSERT_EQUAL(90.0, pSdrTextObj->GetCameraZRotation());
|
||||
|
||||
auto aPrimitives = renderPageToPrimitives(xDrawPage);
|
||||
svx::ExtendedPrimitive2dXmlDump aDumper;
|
||||
xmlDocUniquePtr pDocument = aDumper.dumpAndParse(aPrimitives);
|
||||
CPPUNIT_ASSERT(pDocument);
|
||||
|
||||
assertXPath(pDocument, "//textsimpleportion", "text", u"Vertical");
|
||||
// x/y are the unrotated text position (in page coordinates, before the transform)
|
||||
assertXPath(pDocument, "//textsimpleportion", "x", u"7799");
|
||||
assertXPath(pDocument, "//textsimpleportion", "y", u"6303");
|
||||
|
||||
assertXPath(pDocument, "//transform", 1);
|
||||
double fXY11 = getXPath(pDocument, "//transform", "xy11").toDouble();
|
||||
double fXY12 = getXPath(pDocument, "//transform", "xy12").toDouble();
|
||||
double fXY13 = getXPath(pDocument, "//transform", "xy13").toDouble();
|
||||
double fXY21 = getXPath(pDocument, "//transform", "xy21").toDouble();
|
||||
double fXY22 = getXPath(pDocument, "//transform", "xy22").toDouble();
|
||||
double fXY23 = getXPath(pDocument, "//transform", "xy23").toDouble();
|
||||
basegfx::B2DHomMatrix aTransform(fXY11, fXY12, fXY13, fXY21, fXY22, fXY23);
|
||||
basegfx::B2DTuple aScale, aTranslate;
|
||||
double fRotate, fShearX;
|
||||
aTransform.decompose(aScale, aTranslate, fRotate, fShearX);
|
||||
|
||||
// no scaling, no shear
|
||||
CPPUNIT_ASSERT_EQUAL(1.0, aScale.getX());
|
||||
CPPUNIT_ASSERT_EQUAL(1.0, aScale.getY());
|
||||
CPPUNIT_ASSERT_EQUAL(0.0, fShearX);
|
||||
|
||||
// The text is rotated 90 degrees counterclockwise around its unrotated center.
|
||||
CPPUNIT_ASSERT_EQUAL(-M_PI_2, fRotate);
|
||||
// The translation values reflect current state; they may change a bit (the position of the
|
||||
// rotated text is not pixel-perfect; it is about one pixel off compared to Powerpoint).
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(2744.0, aTranslate.getX(), 10.0);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(14896.0, aTranslate.getY(), 10.0);
|
||||
}
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
|
||||
#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
|
||||
#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
|
||||
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
|
||||
#include <svx/unoapi.hxx>
|
||||
#include <drawinglayer/geometry/viewinformation2d.hxx>
|
||||
#include <editeng/outlobj.hxx>
|
||||
@@ -596,23 +597,6 @@ void SdrTextObj::impDecomposeBlockTextPrimitive(
|
||||
const double fStartInY(bVerticalWriting && !bTopToBottom ? aAdjustTranslate.getY() + aOutlinerScale.getY() : aAdjustTranslate.getY());
|
||||
basegfx::B2DHomMatrix aNewTransformA(basegfx::utils::createTranslateB2DHomMatrix(fStartInX, fStartInY));
|
||||
|
||||
// Apply the camera rotation. It have to be applied after adjustment of
|
||||
// the text (top, bottom, center, left, right).
|
||||
if(GetCameraZRotation() != 0)
|
||||
{
|
||||
// First find the text rect.
|
||||
basegfx::B2DRange aTextRectangle(/*x1=*/aTranslate.getX() + aAdjustTranslate.getX(),
|
||||
/*y1=*/aTranslate.getY() + aAdjustTranslate.getY(),
|
||||
/*x2=*/aTranslate.getX() + aOutlinerScale.getX() - aAdjustTranslate.getX(),
|
||||
/*y2=*/aTranslate.getY() + aOutlinerScale.getY() - aAdjustTranslate.getY());
|
||||
|
||||
// Rotate the text from the center point.
|
||||
basegfx::B2DVector aTranslateToCenter(aTextRectangle.getWidth() / 2, aTextRectangle.getHeight() / 2);
|
||||
aNewTransformA.translate(-aTranslateToCenter.getX(), -aTranslateToCenter.getY());
|
||||
aNewTransformA.rotate(basegfx::deg2rad(360.0 - GetCameraZRotation() ));
|
||||
aNewTransformA.translate(aTranslateToCenter.getX(), aTranslateToCenter.getY());
|
||||
}
|
||||
|
||||
// mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
|
||||
// move the null point which was top left to bottom right.
|
||||
const bool bMirrorX(aScale.getX() < 0.0);
|
||||
@@ -639,6 +623,20 @@ void SdrTextObj::impDecomposeBlockTextPrimitive(
|
||||
rOutliner.StripPortions(aBreakup);
|
||||
rTarget = aBreakup.getTextPortionPrimitives();
|
||||
|
||||
// Apply 3D camera Z rotation as a post-processing step
|
||||
if (GetCameraZRotation() != 0 && !rTarget.empty())
|
||||
{
|
||||
// Rotate around the center of the unrotated text content
|
||||
const drawinglayer::geometry::ViewInformation2D aViewInfoLocal;
|
||||
const basegfx::B2DRange aOrigBounds = rTarget.getB2DRange(aViewInfoLocal);
|
||||
const basegfx::B2DHomMatrix aRotationMat(basegfx::utils::createRotateAroundPoint(
|
||||
aOrigBounds.getCenter(), basegfx::deg2rad(360.0 - GetCameraZRotation())));
|
||||
|
||||
rtl::Reference pRotation(
|
||||
new drawinglayer::primitive2d::TransformPrimitive2D(aRotationMat, std::move(rTarget)));
|
||||
rTarget = drawinglayer::primitive2d::Primitive2DContainer{ pRotation };
|
||||
}
|
||||
|
||||
// cleanup outliner
|
||||
rOutliner.SetBackgroundColor(aOriginalBackColor);
|
||||
rOutliner.Clear();
|
||||
|
||||
Reference in New Issue
Block a user