summaryrefslogtreecommitdiffstats
path: root/oox/source/drawingml/transform2dcontext.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'oox/source/drawingml/transform2dcontext.cxx')
-rw-r--r--oox/source/drawingml/transform2dcontext.cxx393
1 files changed, 393 insertions, 0 deletions
diff --git a/oox/source/drawingml/transform2dcontext.cxx b/oox/source/drawingml/transform2dcontext.cxx
new file mode 100644
index 0000000000..1cd67d1192
--- /dev/null
+++ b/oox/source/drawingml/transform2dcontext.cxx
@@ -0,0 +1,393 @@
+/* -*- 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 .
+ */
+
+#include <cmath>
+
+#include <drawingml/transform2dcontext.hxx>
+
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <drawingml/customshapeproperties.hxx>
+#include <drawingml/textbody.hxx>
+#include <oox/drawingml/shape.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+
+#include <com/sun/star/awt/Rectangle.hpp>
+
+using namespace ::com::sun::star;
+using ::oox::core::ContextHandlerRef;
+
+namespace oox::drawingml {
+
+/** context to import a CT_Transform2D */
+Transform2DContext::Transform2DContext( ContextHandler2Helper const & rParent, const AttributeList& rAttribs, Shape& rShape, bool btxXfrm )
+: ContextHandler2( rParent )
+, mrShape( rShape )
+, mbtxXfrm ( btxXfrm )
+{
+ if( !btxXfrm )
+ {
+ mrShape.setRotation( rAttribs.getInteger( XML_rot, 0 ) ); // 60000ths of a degree Positive angles are clockwise; negative angles are counter-clockwise
+ mrShape.setFlip( rAttribs.getBool( XML_flipH, false ), rAttribs.getBool( XML_flipV, false ) );
+ }
+ else
+ {
+ if (rAttribs.hasAttribute(XML_rot) && mrShape.getTextBody())
+ {
+ mno_txXfrmRot = rAttribs.getInteger(XML_rot, 0);
+ sal_Int32 nTextAreaRot = mrShape.getTextBody()->getTextProperties().moTextAreaRotation.value_or(0);
+ mrShape.getTextBody()->getTextProperties().moTextAreaRotation = mno_txXfrmRot.value() + nTextAreaRot;
+ }
+ }
+}
+
+namespace
+{
+bool ConstructPresetTextRectangle(Shape& rShape, awt::Rectangle& rRect)
+{
+ // When we are here, we have neither xShape nor a SdrObject. So need to manually calc the text
+ // area rectangle defined in the preset in OOXML standard, but only for those types of shapes
+ // where we know, that MS Office SmartArt presets do not use the default text area rectangle.
+ const sal_Int32 nType = rShape.getCustomShapeProperties()->getShapePresetType();
+ switch (nType)
+ {
+ case XML_ellipse:
+ // The preset text rectangle touches the perimeter of the ellipse at 45deg.
+ rRect.X = rShape.getPosition().X + rShape.getSize().Width * ((1.0 - M_SQRT1_2) / 2.0);
+ rRect.Y = rShape.getPosition().Y + rShape.getSize().Height * ((1.0 - M_SQRT1_2) / 2.0);
+ rRect.Width = rShape.getSize().Width * M_SQRT1_2;
+ rRect.Height = rShape.getSize().Height * M_SQRT1_2;
+ return true;
+ case XML_roundRect:
+ case XML_round2SameRect:
+ {
+ // Second handle of round2SameRect used in preset diagrams has value 0.
+ auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
+ double fAdj = aAdjGdList.empty() ? 16667 : aAdjGdList[0].maFormula.toDouble();
+ sal_Int32 nWidth = rShape.getSize().Width;
+ sal_Int32 nHeight = rShape.getSize().Height;
+ if (nWidth == 0 || nHeight == 0)
+ return false;
+ double fMaxAdj = 50000.0 * nWidth / std::min(nWidth, nHeight);
+ fAdj = std::clamp<double>(fAdj, 0, fMaxAdj);
+ sal_Int32 nTextLeft = std::min(nWidth, nHeight) * fAdj / 100000.0 * 0.29289;
+ sal_Int32 nTextTop = nTextLeft;
+ rRect.X = rShape.getPosition().X + nTextLeft;
+ rRect.Y = rShape.getPosition().Y + nTextTop;
+ rRect.Width = nWidth - 2 * nTextLeft;
+ rRect.Height = nHeight - (nType == XML_roundRect ? 2 : 1) * nTextTop;
+ return true;
+ }
+ case XML_trapezoid:
+ {
+ auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
+ double fAdj = aAdjGdList.empty() ? 25000 : aAdjGdList[0].maFormula.toDouble();
+ sal_Int32 nWidth = rShape.getSize().Width;
+ sal_Int32 nHeight = rShape.getSize().Height;
+ if (nWidth == 0 || nHeight == 0)
+ return false;
+ double fMaxAdj = 50000.0 * nWidth / std::min(nWidth, nHeight);
+ fAdj = std::clamp<double>(fAdj, 0, fMaxAdj);
+ sal_Int32 nTextLeft = nWidth / 3.0 * fAdj / fMaxAdj;
+ sal_Int32 nTextTop = nHeight / 3.0 * fAdj / fMaxAdj;
+ rRect.X = rShape.getPosition().X + nTextLeft;
+ rRect.Y = rShape.getPosition().Y + nTextTop;
+ rRect.Width = nWidth - 2 * nTextLeft;
+ rRect.Height = nHeight - 2 * nTextTop;
+ return true;
+ }
+ case XML_flowChartManualOperation:
+ {
+ sal_Int32 nWidth = rShape.getSize().Width;
+ sal_Int32 nTextLeft = nWidth / 5;
+ rRect.X = rShape.getPosition().X + nTextLeft;
+ rRect.Y = rShape.getPosition().Y;
+ rRect.Width = nWidth - 2 * nTextLeft;
+ rRect.Height = rShape.getSize().Height;
+ return true;
+ }
+ case XML_pie:
+ case XML_rect:
+ case XML_wedgeRectCallout:
+ {
+ // When tdf#149918 is fixed, pie will need its own case
+ rRect.X = rShape.getPosition().X;
+ rRect.Y = rShape.getPosition().Y;
+ rRect.Width = rShape.getSize().Width;
+ rRect.Height = rShape.getSize().Height;
+ return true;
+ }
+ case XML_gear6:
+ {
+ // The identifiers here reflect the guides name value in presetShapeDefinitions.xml
+ double w = rShape.getSize().Width;
+ double h = rShape.getSize().Height;
+ if (w <= 0 || h <= 0)
+ return false;
+ double a1(15000.0);
+ double a2(3526.0);
+ auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
+ if (aAdjGdList.size() == 2)
+ {
+ a1 = aAdjGdList[0].maFormula.toDouble();
+ a2 = aAdjGdList[1].maFormula.toDouble();
+ a1 = std::clamp<double>(a1, 0, 20000);
+ a2 = std::clamp<double>(a2, 0, 5358);
+ }
+ double th = std::min(w, h) * a1 / 100000.0;
+ double l2 = std::min(w, h) * a2 / 100000.0 / 2.0;
+ double l3 = th / 2.0 + l2;
+
+ double rh = h / 2.0 - th;
+ double rw = w / 2.0 - th;
+
+ double maxr = std::min(rw, rh);
+ double ha = atan2(l3, maxr);
+
+ double aA1 = basegfx::deg2rad(330) - ha;
+ double ta11 = rw * cos(aA1);
+ double ta12 = rh * sin(aA1);
+ double bA1 = atan2(ta12, ta11);
+ double cta1 = rh * cos(bA1);
+ double sta1 = rw * sin(bA1);
+ double ma1 = std::hypot(cta1, sta1);
+ double na1 = rw * rh / ma1;
+ double dxa1 = na1 * cos(bA1);
+ double dya1 = na1 * sin(bA1);
+
+ double xA1 = w / 2.0 + dxa1; // r
+ double yA1 = h / 2.0 + dya1; // t
+ double yD2 = h - yA1; // b
+ double xD5 = w - xA1; // l
+
+ rRect.X = rShape.getPosition().X + xD5;
+ rRect.Y = rShape.getPosition().Y + yA1;
+ rRect.Width = xA1 - xD5;
+ rRect.Height = yD2 - yA1;
+ return true;
+ }
+ case XML_hexagon:
+ {
+ auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
+ double fAdj = aAdjGdList.empty() ? 25000 : aAdjGdList[0].maFormula.toDouble();
+ sal_Int32 nWidth = rShape.getSize().Width;
+ sal_Int32 nHeight = rShape.getSize().Height;
+ if (nWidth == 0 || nHeight == 0)
+ return false;
+ double fMaxAdj = 50000.0 * nWidth / std::min(nWidth, nHeight);
+ fAdj = std::clamp<double>(fAdj, 0, fMaxAdj);
+ double fFactor = fAdj / fMaxAdj / 6.0 + 1.0 / 12.0;
+ sal_Int32 nTextLeft = nWidth * fFactor;
+ sal_Int32 nTextTop = nHeight * fFactor;
+ rRect.X = rShape.getPosition().X + nTextLeft;
+ rRect.Y = rShape.getPosition().Y + nTextTop;
+ rRect.Width = nWidth - 2 * nTextLeft;
+ rRect.Height = nHeight - 2 * nTextTop;
+ return true;
+ }
+ case XML_round1Rect:
+ {
+ sal_Int32 nWidth = rShape.getSize().Width;
+ sal_Int32 nHeight = rShape.getSize().Height;
+ if (nWidth == 0 || nHeight == 0)
+ return false;
+ auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
+ double fAdj = aAdjGdList.empty() ? 16667.0 : aAdjGdList[0].maFormula.toDouble();
+ fAdj = std::clamp<double>(fAdj, 0.0, 50000.0);
+ double fDx = std::min(nWidth, nHeight) * fAdj / 100000.0 * 0.29289;
+ rRect.X = rShape.getPosition().X;
+ rRect.Y = rShape.getPosition().Y;
+ rRect.Width = nWidth - fDx;
+ rRect.Height = nHeight;
+ return true;
+ }
+ case XML_rightArrow:
+ {
+ // The identifiers here reflect the guides name value in presetShapeDefinitions.xml
+ sal_Int32 nWidth = rShape.getSize().Width;
+ sal_Int32 nHeight = rShape.getSize().Height;
+ if (nWidth == 0 || nHeight == 0)
+ return false;
+ double a1(50000.0);
+ double a2(50000.0);
+ auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
+ if (aAdjGdList.size() == 2)
+ {
+ a1 = aAdjGdList[0].maFormula.toDouble();
+ a2 = aAdjGdList[1].maFormula.toDouble();
+ a1 = std::clamp<double>(a1, 0, 100000);
+ }
+ double maxAdj2 = 100000.0 * nWidth / std::min(nWidth, nHeight);
+ a2 = std::clamp<double>(a2, 0, maxAdj2);
+ double dx1 = std::min(nWidth, nHeight) * a2 / 100000.0;
+ double x1 = nWidth - dx1;
+ double dy1 = nHeight * a1 / 200000.0;
+ double y1 = nHeight / 2.0 - dy1; // top
+ double y2 = nHeight / 2.0 + dy1; // bottom
+ double dx2 = y1 * dx1 / (nHeight / 2.0);
+ double x2 = x1 + dx2; // right
+ rRect.X = rShape.getPosition().X; // left = 0
+ rRect.Y = rShape.getPosition().Y + y1;
+ rRect.Width = x2;
+ rRect.Height = y2 - y1;
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+basegfx::B2DPoint getCenter(const awt::Rectangle& rRect)
+{
+ return basegfx::B2DPoint(rRect.X + rRect.Width / 2.0, rRect.Y + rRect.Height / 2.0);
+}
+} // end namespace
+
+ContextHandlerRef Transform2DContext::onCreateContext( sal_Int32 aElementToken, const AttributeList& rAttribs )
+{
+ if (mbtxXfrm)
+ {
+ // The child elements <a:off> and <a:ext> of a <dsp:txXfrm> element describe the position and
+ // size of the text area rectangle. We cannot change the text area rectangle directly, because
+ // currently we depend on the geometry definition of the preset. As workaround we change the
+ // indents to move and scale the text block. The needed shifts are calculated here as moTextOff
+ // and used in TextBodyProperties::pushTextDistances().
+ awt::Rectangle aPresetTextRectangle;
+ if (!ConstructPresetTextRectangle(mrShape, aPresetTextRectangle))
+ return nullptr; // faulty shape or text area calculation not implemented
+
+ switch (aElementToken)
+ {
+ case A_TOKEN(off):
+ {
+ // need <a:ext> too, so only save values here.
+ const OUString sXValue = rAttribs.getStringDefaulted(XML_x);
+ const OUString sYValue = rAttribs.getStringDefaulted(XML_y);
+ if (!sXValue.isEmpty() && !sYValue.isEmpty())
+ {
+ mno_txXfrmOffX = sXValue.toInt32();
+ mno_txXfrmOffY = sYValue.toInt32();
+ }
+ }
+ break;
+ case A_TOKEN(ext):
+ {
+ // Build text frame from txXfrm element
+ awt::Rectangle aUnrotatedTxXfrm = aPresetTextRectangle; // dummy initialize
+ const OUString sCXValue = rAttribs.getStringDefaulted(XML_cx);
+ const OUString sCYValue = rAttribs.getStringDefaulted(XML_cy);
+ if (!sCXValue.isEmpty() && !sCYValue.isEmpty())
+ {
+ aUnrotatedTxXfrm.Width = sCXValue.toInt32();
+ aUnrotatedTxXfrm.Height = sCYValue.toInt32();
+ }
+ if (mno_txXfrmOffX.has_value() && mno_txXfrmOffY.has_value())
+ {
+ aUnrotatedTxXfrm.X = mno_txXfrmOffX.value();
+ aUnrotatedTxXfrm.Y = mno_txXfrmOffY.value();
+ }
+
+ // Has the txXfrm an own rotation beyond compensation of the shape rotation?
+ // Happens e.g. in diagram type 'Detailed Process'.
+ sal_Int32 nAngleDiff
+ = (mrShape.getRotation() + mno_txXfrmRot.value_or(0)) % 21600000;
+ if (nAngleDiff != 0)
+ {
+ // Rectangle aUnrotatedTxXfrm rotates around its center not around text area
+ // center from preset. We shift aUnrotatedTxXfrm so that it is at the original
+ // position after rotation of text area rectangle from preset.
+ basegfx::B2DPoint aXfrmCenter(getCenter(aUnrotatedTxXfrm));
+ basegfx::B2DPoint aPresetCenter(getCenter(aPresetTextRectangle));
+
+ if (!aXfrmCenter.equal(aPresetCenter))
+ {
+ double fAngleRad = basegfx::deg2rad(nAngleDiff / 60000.0);
+ basegfx::B2DHomMatrix aRotMatrix(
+ basegfx::utils::createRotateAroundPoint(aPresetCenter, -fAngleRad));
+ basegfx::B2DPoint aNewCenter(aRotMatrix * aXfrmCenter);
+ aUnrotatedTxXfrm.X += aNewCenter.getX() - aXfrmCenter.getX();
+ aUnrotatedTxXfrm.Y += aNewCenter.getY() - aXfrmCenter.getY();
+ }
+ }
+
+ if(mrShape.getTextBody())
+ {
+ // Calculate indent offsets
+ sal_Int32 nOffsetLeft = aUnrotatedTxXfrm.X - aPresetTextRectangle.X;
+ sal_Int32 nOffsetTop = aUnrotatedTxXfrm.Y - aPresetTextRectangle.Y;
+ sal_Int32 nOffsetRight
+ = aPresetTextRectangle.Width - aUnrotatedTxXfrm.Width - nOffsetLeft;
+ sal_Int32 nOffsetBottom
+ = aPresetTextRectangle.Height - aUnrotatedTxXfrm.Height - nOffsetTop;
+
+ if (nOffsetLeft)
+ mrShape.getTextBody()->getTextProperties().moTextOffLeft
+ = GetCoordinate(nOffsetLeft);
+ if (nOffsetTop)
+ mrShape.getTextBody()->getTextProperties().moTextOffUpper
+ = GetCoordinate(nOffsetTop);
+ if (nOffsetRight)
+ mrShape.getTextBody()->getTextProperties().moTextOffRight
+ = GetCoordinate(nOffsetRight);
+ if (nOffsetBottom)
+ mrShape.getTextBody()->getTextProperties().moTextOffLower
+ = GetCoordinate(nOffsetBottom);
+ }
+ }
+ break;
+ }
+ return nullptr;
+ } // end of case mbtxXfrm
+
+ switch( aElementToken )
+ {
+ case A_TOKEN( off ): // horz/vert translation
+ mrShape.setPosition( awt::Point( rAttribs.getInteger( XML_x, 0 ), rAttribs.getInteger( XML_y, 0 ) ) );
+ break;
+ case A_TOKEN( ext ): // horz/vert size
+ mrShape.setSize( awt::Size( rAttribs.getInteger( XML_cx, 0 ), rAttribs.getInteger( XML_cy, 0 ) ) );
+ break;
+ case A_TOKEN( chOff ): // horz/vert translation of children
+ mrShape.setChildPosition( awt::Point( rAttribs.getInteger( XML_x, 0 ), rAttribs.getInteger( XML_y, 0 ) ) );
+ break;
+ case A_TOKEN( chExt ): // horz/vert size of children
+ {
+ sal_Int32 nChExtCx = rAttribs.getInteger(XML_cx, 0);
+
+ if(nChExtCx == 0)
+ nChExtCx = mrShape.getSize().Width;
+
+ sal_Int32 nChExtCy = rAttribs.getInteger(XML_cy, 0);
+
+ if(nChExtCy == 0)
+ nChExtCy = mrShape.getSize().Height;
+
+ mrShape.setChildSize(awt::Size(nChExtCx, nChExtCy));
+ }
+ break;
+ }
+
+ return nullptr;
+}
+
+} // namespace oox::drawingml
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */