office-gobmx/include/svx/svdtrans.hxx
Regina Henschel 7e23cbdbb6 tdf#149551 separate TextRotateAngle from TextPreRotateAngle
The import filter had only one member for both and had it mapped to
TextPreRotateAngle. That resulted in lost text area rotation when a
shape had both types of rotations, and sheared text when only text
area rotation existed.

The patch introduces a new 'moTextAreaRotation' member for the 'rot'
attribute of the <bodyPr> element. It is mapped to 'TextRotateAngle'
property. It becomes the 'draw:text-rotate-angle' attribute of the
<draw:enhanced-geometry> element for a shape and 'style:rotate-angle'
for chart elements in ODF. It must also be used to simulate 'upright'
and is used for the 'rot' attribute in <txXfrm> of diagram shapes.

The 'moRotation' member is now only used for the 'TextPreRotateAngle'
property. That angle describes a rotation of the text without changing
the text area. Valid values are multiples of 90deg. It is used for
simulating vertical writing modes that are not yet implemented. It has
no corresponding attribute in ODF. To make the meaning clear in code,
the member is renamed to 'moTextPreRotation'.

MS Office recalutes a diagram on file opening from layout.xml and
data.xml. That is not yet implemented in LO, but we use drawing.xml,
which gives the state of the diagram at time of saving. The patch
handles the 'rot' attribute of <txXfrm> element as well. It has to be
mapped to moTextAreaRotation, because it might contain angles, which
are not multiples of 90deg, for example diagram 'Gear'.

The <off> and <ext> child elements of <txXfrm> describe the actual used
text area rectangle. The existing import calculates the difference of
the actual used text area rectangle to the predefined one and
incorporates it into the TextArea*Distance attributes. The patch adds
calculating the current values of the text area rectangle as it would
be using the preset markup. At that time in import there is no
SdrObjCustomShape object, thus we cannot use its GetTextBounds() method.
So it is down manually, covering most of those types, which are used in
diagrams of MS Office.

Remarks to unit tests:
Now the rotation introduced by txXfrm is no longer in
TextPreRotateAngle, but in TextRotateAngle. According changes are in
SdImportTest::testN86510_2
Test, testFdo87488

Now the correct preset text area rectangles are used. That requires
adaption of the needed distances. Done in
SdImportTest::testBnc870237()
SdImportTest::testTdf93830()
SdImportTestSmartArt::testTdf134221()

The buggy 'upright' export is fixed. Adaption in
ScExportTest2::testTdf137000_handle_upright()

Change-Id: I79df1559f88b76e96988fe745304dc4162de6316
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136447
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
2022-07-20 08:16:03 +02:00

286 lines
12 KiB
C++

/* -*- 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/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#pragma once
#include <rtl/ustring.hxx>
#include <svx/svxdllapi.h>
#include <tools/degree.hxx>
#include <tools/fldunit.hxx>
#include <tools/fract.hxx>
#include <tools/gen.hxx>
#include <tools/helpers.hxx>
#include <tools/mapunit.hxx>
#include <tools/poly.hxx>
// That maximum shear angle
constexpr Degree100 SDRMAXSHEAR(8900);
class XPolygon;
class XPolyPolygon;
inline void MovePoly(tools::Polygon& rPoly, const Size& S) { rPoly.Move(S.Width(),S.Height()); }
void MoveXPoly(XPolygon& rPoly, const Size& S);
SVXCORE_DLLPUBLIC void ResizeRect(tools::Rectangle& rRect, const Point& rRef, const Fraction& xFact, const Fraction& yFact);
inline void ResizePoint(Point& rPnt, const Point& rRef, const Fraction& xFract, const Fraction& yFract);
void ResizePoly(tools::Polygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact);
void ResizeXPoly(XPolygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact);
inline void RotatePoint(Point& rPnt, const Point& rRef, double sn, double cs);
SVXCORE_DLLPUBLIC void RotatePoly(tools::Polygon& rPoly, const Point& rRef, double sn, double cs);
void RotateXPoly(XPolygon& rPoly, const Point& rRef, double sn, double cs);
void RotateXPoly(XPolyPolygon& rPoly, const Point& rRef, double sn, double cs);
void MirrorPoint(Point& rPnt, const Point& rRef1, const Point& rRef2);
void MirrorXPoly(XPolygon& rPoly, const Point& rRef1, const Point& rRef2);
inline void ShearPoint(Point& rPnt, const Point& rRef, double tn, bool bVShear = false);
SVXCORE_DLLPUBLIC void ShearPoly(tools::Polygon& rPoly, const Point& rRef, double tn);
void ShearXPoly(XPolygon& rPoly, const Point& rRef, double tn, bool bVShear = false);
/**
* rPnt.X/rPnt.Y is set to rCenter.X or rCenter.Y!
* We then only need to rotate rPnt by rCenter.
*
* @return the returned angle is in rad
*/
inline double GetCrookAngle(Point& rPnt, const Point& rCenter, const Point& rRad, bool bVertical);
/**
* The following methods accept a point of an XPolygon, whereas the neighbouring
* control points of the actual point are passed in pC1/pC2.
* Via rSin/rCos, sin(nAngle) and cos(nAngle) are returned.
*
* @return the returned angle is in rad
*/
double CrookRotateXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter,
const Point& rRad, double& rSin, double& rCos, bool bVert);
double CrookSlantXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter,
const Point& rRad, double& rSin, double& rCos, bool bVert);
double CrookStretchXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter,
const Point& rRad, double& rSin, double& rCos, bool bVert,
const tools::Rectangle& rRefRect);
void CrookRotatePoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert);
void CrookSlantPoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert);
void CrookStretchPoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert, const tools::Rectangle& rRefRect);
void CrookRotatePoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert);
void CrookSlantPoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert);
void CrookStretchPoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert, const tools::Rectangle& rRefRect);
/**************************************************************************************************/
/* Inline */
/**************************************************************************************************/
inline void ResizePoint(Point& rPnt, const Point& rRef, const Fraction& xFract, const Fraction& yFract)
{
double nxFract = xFract.IsValid() ? static_cast<double>(xFract) : 1.0;
double nyFract = yFract.IsValid() ? static_cast<double>(yFract) : 1.0;
rPnt.setX(rRef.X() + FRound( (rPnt.X() - rRef.X()) * nxFract ));
rPnt.setY(rRef.Y() + FRound( (rPnt.Y() - rRef.Y()) * nyFract ));
}
inline void RotatePoint(Point& rPnt, const Point& rRef, double sn, double cs)
{
tools::Long dx=rPnt.X()-rRef.X();
tools::Long dy=rPnt.Y()-rRef.Y();
rPnt.setX(FRound(rRef.X()+dx*cs+dy*sn));
rPnt.setY(FRound(rRef.Y()+dy*cs-dx*sn));
}
inline void ShearPoint(Point& rPnt, const Point& rRef, double tn, bool bVShear)
{
if (!bVShear) { // Horizontal
if (rPnt.Y()!=rRef.Y()) { // else not needed
rPnt.AdjustX(-FRound((rPnt.Y()-rRef.Y())*tn));
}
} else { // or else vertical
if (rPnt.X()!=rRef.X()) { // else not needed
rPnt.AdjustY(-FRound((rPnt.X()-rRef.X())*tn));
}
}
}
inline double GetCrookAngle(Point& rPnt, const Point& rCenter, const Point& rRad, bool bVertical)
{
double nAngle;
if (bVertical) {
tools::Long dy=rPnt.Y()-rCenter.Y();
nAngle=static_cast<double>(dy)/static_cast<double>(rRad.Y());
rPnt.setY(rCenter.Y());
} else {
tools::Long dx=rCenter.X()-rPnt.X();
nAngle=static_cast<double>(dx)/static_cast<double>(rRad.X());
rPnt.setX(rCenter.X());
}
return nAngle;
}
/**************************************************************************************************/
/**************************************************************************************************/
/**
* The Y axis points down!
* The function negates the Y axis, when calculating the angle, such
* that GetAngle(Point(0,-1))=90 deg.
* GetAngle(Point(0,0)) returns 0.
*
* @return the returned value is in the range of -180.00..179.99 deg
* and is in 1/100 deg units
*/
SVXCORE_DLLPUBLIC Degree100 GetAngle(const Point& rPnt);
SVXCORE_DLLPUBLIC Degree100 NormAngle18000(Degree100 a); /// Normalize angle to -180.00..179.99
SVXCORE_DLLPUBLIC Degree100 NormAngle36000(Degree100 a); /// Normalize angle to 0.00..359.99
sal_uInt16 GetAngleSector(Degree100 nAngle); /// Determine sector within the cartesian coordinate system
/**
* Calculates the length of (0,0) via a^2 + b^2 = c^2
* In order to avoid overflows, we ignore some decimal places.
*/
tools::Long GetLen(const Point& rPnt);
/**
* The transformation of a rectangle into a polygon, by
* using angle parameters from GeoStat. ------------
* The point of reference is always the Point 0, meaning /1 2/
* the upper left corner of the initial rectangle. / /
* When calculating the polygon, the order is first / /
* shear and then the rotation. / /
* / / \
* / / |
* A) Initial rectangle aRect B) After applying Shear /0 3/ Rot|
* +------------------+ -------------------- ------------------
* |0 1| \0 1\ C) After applying Rotate
* | | \ \
* | | | \ \
* |3 2| | \3 2\
* +------------------+ | --------------------
* |Shr
*
* When converting the polygon back into a rect, the order is necessarily the
* other way around:
* - Calculating the rotation angle: angle of the line 0-1 in figure C) to the horizontal
* - Turning the sheared rect back (we get figure B)
* - Determining the width of the rect = length of the line 0-1 in figure B)
* - Determining the height of the rect = vertical distance between the points 0 and 3
* of figure B)
* - Determining the shear angle from the line 0-3 to the perpendicular line.
*
* We need to keep in mind that the polygon can be mirrored when it was
* transformed in the meantime (e.g. mirror or resize with negative factor).
* In that case, we first need to normalize, by swapping points (0 with 3 and 1
* with 2), so that it has the right orientation.
*
* Note: a positive shear angle means a shear with a positive visible curvature
* on the screen. Mathematically, that would be a negative curvature, as the
* Y axis runs from top to bottom on the screen.
* Rotation angle: positive means a visible left rotation.
*/
class GeoStat { // Geometric state for a rect
public:
Degree100 nRotationAngle;
Degree100 nShearAngle;
double mfTanShearAngle; // tan(nShearAngle)
double mfSinRotationAngle; // sin(nRotationAngle)
double mfCosRotationAngle; // cos(nRotationAngle)
GeoStat(): nRotationAngle(0),nShearAngle(0),mfTanShearAngle(0.0),mfSinRotationAngle(0.0),mfCosRotationAngle(1.0) {}
void RecalcSinCos();
void RecalcTan();
};
tools::Polygon Rect2Poly(const tools::Rectangle& rRect, const GeoStat& rGeo);
void Poly2Rect(const tools::Polygon& rPol, tools::Rectangle& rRect, GeoStat& rGeo);
void OrthoDistance8(const Point& rPt0, Point& rPt, bool bBigOrtho);
void OrthoDistance4(const Point& rPt0, Point& rPt, bool bBigOrtho);
// Multiplication and subsequent division
// Calculation and intermediate values are in BigInt
SVXCORE_DLLPUBLIC tools::Long BigMulDiv(tools::Long nVal, tools::Long nMul, tools::Long nDiv);
class FrPair {
Fraction aX;
Fraction aY;
public:
FrPair(const Fraction& rBoth) : aX(rBoth),aY(rBoth) {}
FrPair(const Fraction& rX, const Fraction& rY) : aX(rX),aY(rY) {}
FrPair(tools::Long nMul, tools::Long nDiv) : aX(nMul,nDiv),aY(nMul,nDiv) {}
FrPair(tools::Long xMul, tools::Long xDiv, tools::Long yMul, tools::Long yDiv): aX(xMul,xDiv),aY(yMul,yDiv) {}
const Fraction& X() const { return aX; }
const Fraction& Y() const { return aY; }
Fraction& X() { return aX; }
Fraction& Y() { return aY; }
};
// To convert units of measurement
SVXCORE_DLLPUBLIC FrPair GetMapFactor(MapUnit eS, MapUnit eD);
FrPair GetMapFactor(FieldUnit eS, FieldUnit eD);
inline bool IsMetric(MapUnit eU) {
return (eU==MapUnit::Map100thMM || eU==MapUnit::Map10thMM || eU==MapUnit::MapMM || eU==MapUnit::MapCM);
}
inline bool IsInch(MapUnit eU) {
return (eU==MapUnit::Map1000thInch || eU==MapUnit::Map100thInch || eU==MapUnit::Map10thInch || eU==MapUnit::MapInch ||
eU==MapUnit::MapPoint || eU==MapUnit::MapTwip);
}
inline bool IsMetric(FieldUnit eU) {
return (eU == FieldUnit::MM || eU == FieldUnit::CM || eU == FieldUnit::M
|| eU == FieldUnit::KM || eU == FieldUnit::MM_100TH);
}
inline bool IsInch(FieldUnit eU) {
return (eU == FieldUnit::TWIP || eU == FieldUnit::POINT
|| eU == FieldUnit::PICA || eU == FieldUnit::INCH
|| eU == FieldUnit::FOOT || eU == FieldUnit::MILE);
}
class SVXCORE_DLLPUBLIC SdrFormatter {
tools::Long nMul_;
tools::Long nDiv_;
short nComma_;
bool bDirty;
MapUnit eSrcMU;
MapUnit eDstMU;
private:
SVX_DLLPRIVATE void Undirty();
public:
SdrFormatter(MapUnit eSrc, MapUnit eDst)
: nMul_(0)
, nDiv_(0)
, nComma_(0)
, bDirty(true)
, eSrcMU(eSrc)
, eDstMU(eDst)
{
}
OUString GetStr(tools::Long nVal) const;
static OUString GetUnitStr(MapUnit eUnit);
static OUString GetUnitStr(FieldUnit eUnit);
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */