1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10 #include "WpsContext.hxx"
11 #include <basegfx/matrix/b2dhommatrix.hxx>
12 #include <basegfx/tuple/b2dtuple.hxx>
13 #include <comphelper/sequenceashashmap.hxx>
14 #include <drawingml/customshapeproperties.hxx>
15 #include <com/sun/star/beans/XPropertySet.hpp>
16 #include <com/sun/star/beans/XPropertyState.hpp>
17 #include <com/sun/star/drawing/HomogenMatrix3.hpp>
18 #include <com/sun/star/lang/XServiceInfo.hpp>
19 #include <com/sun/star/text/XText.hpp>
20 #include <com/sun/star/text/XTextCursor.hpp>
21 #include <com/sun/star/text/WritingMode.hpp>
22 #include <svx/svdtrans.hxx>
23 #include <oox/helper/attributelist.hxx>
24 #include <oox/token/namespaces.hxx>
25 #include <oox/token/tokens.hxx>
26 #include <oox/drawingml/shape.hxx>
27
28 #include <optional>
29
30 using namespace com::sun::star;
31
32 namespace oox::shape
33 {
WpsContext(ContextHandler2Helper const & rParent,uno::Reference<drawing::XShape> xShape,const drawingml::ShapePtr & pMasterShapePtr,const drawingml::ShapePtr & pShapePtr)34 WpsContext::WpsContext(ContextHandler2Helper const& rParent, uno::Reference<drawing::XShape> xShape,
35 const drawingml::ShapePtr& pMasterShapePtr,
36 const drawingml::ShapePtr& pShapePtr)
37 : ShapeContext(rParent, pMasterShapePtr, pShapePtr)
38 , mxShape(std::move(xShape))
39 {
40 mpShapePtr->setWps(true);
41 }
42
43 WpsContext::~WpsContext() = default;
44
onCreateContext(sal_Int32 nElementToken,const oox::AttributeList & rAttribs)45 oox::core::ContextHandlerRef WpsContext::onCreateContext(sal_Int32 nElementToken,
46 const oox::AttributeList& rAttribs)
47 {
48 switch (getBaseToken(nElementToken))
49 {
50 case XML_wsp:
51 case XML_cNvCnPr:
52 break;
53 case XML_bodyPr:
54 if (mxShape.is())
55 {
56 uno::Reference<lang::XServiceInfo> xServiceInfo(mxShape, uno::UNO_QUERY);
57 uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
58 sal_Int32 nVert = rAttribs.getToken(XML_vert, XML_horz);
59 if (nVert == XML_eaVert)
60 {
61 xPropertySet->setPropertyValue("TextWritingMode",
62 uno::makeAny(text::WritingMode_TB_RL));
63 }
64 else if (nVert != XML_horz)
65 {
66 // Get the existing rotation of the shape.
67 drawing::HomogenMatrix3 aMatrix;
68 xPropertySet->getPropertyValue("Transformation") >>= aMatrix;
69 basegfx::B2DHomMatrix aTransformation;
70 aTransformation.set(0, 0, aMatrix.Line1.Column1);
71 aTransformation.set(0, 1, aMatrix.Line1.Column2);
72 aTransformation.set(0, 2, aMatrix.Line1.Column3);
73 aTransformation.set(1, 0, aMatrix.Line1.Column1);
74 aTransformation.set(1, 1, aMatrix.Line2.Column2);
75 aTransformation.set(1, 2, aMatrix.Line3.Column3);
76 aTransformation.set(2, 0, aMatrix.Line1.Column1);
77 aTransformation.set(2, 1, aMatrix.Line2.Column2);
78 aTransformation.set(2, 2, aMatrix.Line3.Column3);
79 basegfx::B2DTuple aScale;
80 basegfx::B2DTuple aTranslate;
81 double fRotate = 0;
82 double fShearX = 0;
83 aTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
84
85 // If the text is not rotated the way the shape wants it already, set the angle.
86 const sal_Int32 nRotation = nVert == XML_vert270 ? -270 : -90;
87 if (static_cast<sal_Int32>(basegfx::rad2deg(fRotate))
88 != NormAngle36000(Degree100(nRotation * 100)).get() / 100)
89 {
90 comphelper::SequenceAsHashMap aCustomShapeGeometry(
91 xPropertySet->getPropertyValue("CustomShapeGeometry"));
92 aCustomShapeGeometry["TextPreRotateAngle"] <<= nRotation;
93 xPropertySet->setPropertyValue(
94 "CustomShapeGeometry",
95 uno::makeAny(aCustomShapeGeometry.getAsConstPropertyValueList()));
96 }
97 }
98
99 if (bool bUpright = rAttribs.getBool(XML_upright, false))
100 {
101 uno::Sequence<beans::PropertyValue> aGrabBag;
102 xPropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
103 sal_Int32 length = aGrabBag.getLength();
104 aGrabBag.realloc(length + 1);
105 aGrabBag[length].Name = "Upright";
106 aGrabBag[length].Value <<= bUpright;
107 xPropertySet->setPropertyValue("InteropGrabBag", uno::makeAny(aGrabBag));
108 }
109
110 if (xServiceInfo.is())
111 {
112 // Handle inset attributes for Writer textframes.
113 sal_Int32 aInsets[] = { XML_lIns, XML_tIns, XML_rIns, XML_bIns };
114 std::optional<sal_Int32> oInsets[4];
115 for (std::size_t i = 0; i < SAL_N_ELEMENTS(aInsets); ++i)
116 {
117 OptValue<OUString> oValue = rAttribs.getString(aInsets[i]);
118 if (oValue.has())
119 oInsets[i] = oox::drawingml::GetCoordinate(oValue.get());
120 else
121 // Defaults from the spec: left/right: 91440 EMU, top/bottom: 45720 EMU
122 oInsets[i]
123 = (aInsets[i] == XML_lIns || aInsets[i] == XML_rIns) ? 254 : 127;
124 }
125 const OUString aShapeProps[]
126 = { OUString("TextLeftDistance"), OUString("TextUpperDistance"),
127 OUString("TextRightDistance"), OUString("TextLowerDistance") };
128 for (std::size_t i = 0; i < SAL_N_ELEMENTS(aShapeProps); ++i)
129 if (oInsets[i])
130 xPropertySet->setPropertyValue(aShapeProps[i],
131 uno::makeAny(*oInsets[i]));
132 }
133
134 // Handle text vertical adjustment inside a text frame
135 if (rAttribs.hasAttribute(XML_anchor))
136 {
137 drawing::TextVerticalAdjust eAdjust
138 = drawingml::GetTextVerticalAdjust(rAttribs.getToken(XML_anchor, XML_t));
139 xPropertySet->setPropertyValue("TextVerticalAdjust", uno::makeAny(eAdjust));
140 }
141
142 // Apply character color of the shape to the shape's textbox.
143 uno::Reference<text::XText> xText(mxShape, uno::UNO_QUERY);
144 uno::Reference<text::XTextCursor> xTextCursor = xText->createTextCursor();
145 xTextCursor->gotoStart(false);
146 xTextCursor->gotoEnd(true);
147 const uno::Reference<beans::XPropertyState> xPropertyState(xTextCursor,
148 uno::UNO_QUERY);
149 const beans::PropertyState ePropertyState
150 = xPropertyState->getPropertyState("CharColor");
151 if (ePropertyState == beans::PropertyState_DEFAULT_VALUE)
152 {
153 uno::Reference<beans::XPropertySet> xTextBoxPropertySet(xTextCursor,
154 uno::UNO_QUERY);
155 uno::Any xCharColor = xPropertySet->getPropertyValue("CharColor");
156 Color aColor = COL_AUTO;
157 if (xCharColor >>= aColor)
158 {
159 if (aColor != COL_AUTO)
160 xTextBoxPropertySet->setPropertyValue("CharColor", xCharColor);
161 }
162 }
163
164 auto nWrappingType = rAttribs.getToken(XML_wrap, XML_square);
165 xPropertySet->setPropertyValue("TextWordWrap",
166 uno::makeAny(nWrappingType == XML_square));
167
168 return this;
169 }
170 break;
171 case XML_noAutofit:
172 case XML_spAutoFit:
173 {
174 uno::Reference<lang::XServiceInfo> xServiceInfo(mxShape, uno::UNO_QUERY);
175 // We can't use oox::drawingml::TextBodyPropertiesContext here, as this
176 // is a child context of bodyPr, so the shape is already sent: we need
177 // to alter the XShape directly.
178 uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
179 if (xPropertySet.is())
180 {
181 if (xServiceInfo->supportsService("com.sun.star.text.TextFrame"))
182 xPropertySet->setPropertyValue(
183 "FrameIsAutomaticHeight",
184 uno::makeAny(getBaseToken(nElementToken) == XML_spAutoFit));
185 else
186 xPropertySet->setPropertyValue(
187 "TextAutoGrowHeight",
188 uno::makeAny(getBaseToken(nElementToken) == XML_spAutoFit));
189 }
190 }
191 break;
192 case XML_prstTxWarp:
193 if (rAttribs.hasAttribute(XML_prst))
194 {
195 uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
196 if (xPropertySet.is())
197 {
198 oox::OptValue<OUString> presetShapeName = rAttribs.getString(XML_prst);
199 const OUString& preset = presetShapeName.get();
200 comphelper::SequenceAsHashMap aCustomShapeGeometry(
201 xPropertySet->getPropertyValue("CustomShapeGeometry"));
202 aCustomShapeGeometry["PresetTextWarp"] <<= preset;
203 xPropertySet->setPropertyValue(
204 "CustomShapeGeometry",
205 uno::makeAny(aCustomShapeGeometry.getAsConstPropertyValueList()));
206 }
207 }
208 break;
209 case XML_txbx:
210 {
211 mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
212 mpShapePtr->setTextBox(true);
213 //in case if the textbox is linked, save the attributes
214 //for further processing.
215 if (rAttribs.hasAttribute(XML_id))
216 {
217 OptValue<OUString> id = rAttribs.getString(XML_id);
218 if (id.has())
219 {
220 oox::drawingml::LinkedTxbxAttr linkedTxtBoxAttr;
221 linkedTxtBoxAttr.id = id.get().toInt32();
222 mpShapePtr->setTxbxHasLinkedTxtBox(true);
223 mpShapePtr->setLinkedTxbxAttributes(linkedTxtBoxAttr);
224 }
225 }
226 return this;
227 }
228 break;
229 case XML_linkedTxbx:
230 {
231 //in case if the textbox is linked, save the attributes
232 //for further processing.
233 mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
234 mpShapePtr->setTextBox(true);
235 OptValue<OUString> id = rAttribs.getString(XML_id);
236 OptValue<OUString> seq = rAttribs.getString(XML_seq);
237 if (id.has() && seq.has())
238 {
239 oox::drawingml::LinkedTxbxAttr linkedTxtBoxAttr;
240 linkedTxtBoxAttr.id = id.get().toInt32();
241 linkedTxtBoxAttr.seq = seq.get().toInt32();
242 mpShapePtr->setTxbxHasLinkedTxtBox(true);
243 mpShapePtr->setLinkedTxbxAttributes(linkedTxtBoxAttr);
244 }
245 }
246 break;
247 default:
248 return ShapeContext::onCreateContext(nElementToken, rAttribs);
249 }
250 return nullptr;
251 }
252 }
253
254 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
255