LibreOffice Module oox (master) 1
WpsContext.cxx
Go to the documentation of this file.
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 "WpgContext.hxx"
16#include <com/sun/star/beans/XPropertySet.hpp>
17#include <com/sun/star/beans/XPropertyState.hpp>
18#include <com/sun/star/container/XEnumerationAccess.hpp>
19#include <com/sun/star/drawing/HomogenMatrix3.hpp>
20#include <com/sun/star/lang/XServiceInfo.hpp>
21#include <com/sun/star/text/XText.hpp>
22#include <com/sun/star/text/XTextCursor.hpp>
23#include <com/sun/star/text/WritingMode.hpp>
24#include <svx/svdtrans.hxx>
26#include <oox/token/namespaces.hxx>
27#include <oox/token/tokens.hxx>
32
33#include <optional>
34
35using namespace com::sun::star;
36
37namespace oox::shape
38{
39WpsContext::WpsContext(ContextHandler2Helper const& rParent, uno::Reference<drawing::XShape> xShape,
40 const drawingml::ShapePtr& pMasterShapePtr,
41 const drawingml::ShapePtr& pShapePtr)
42 : ShapeContext(rParent, pMasterShapePtr, pShapePtr)
43 , mxShape(std::move(xShape))
44{
45 if (mpShapePtr)
46 mpShapePtr->setWps(true);
47
48 if (const auto pParent = dynamic_cast<const WpgContext*>(&rParent))
49 m_bHasWPGParent = pParent->isFullWPGSupport();
50 else
51 m_bHasWPGParent = false;
52}
53
54WpsContext::~WpsContext() = default;
55
57 const oox::AttributeList& rAttribs)
58{
59 switch (getBaseToken(nElementToken))
60 {
61 case XML_wsp:
62 case XML_cNvCnPr:
63 break;
64 case XML_bodyPr:
65 if (mxShape.is())
66 {
67 uno::Reference<lang::XServiceInfo> xServiceInfo(mxShape, uno::UNO_QUERY);
68 uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
69 sal_Int32 nVert = rAttribs.getToken(XML_vert, XML_horz);
70 if (nVert == XML_eaVert)
71 {
72 xPropertySet->setPropertyValue("TextWritingMode",
73 uno::Any(text::WritingMode_TB_RL));
74 }
75 else if (nVert != XML_horz)
76 {
77 // Get the existing rotation of the shape.
78 drawing::HomogenMatrix3 aMatrix;
79 xPropertySet->getPropertyValue("Transformation") >>= aMatrix;
80 basegfx::B2DHomMatrix aTransformation;
81 aTransformation.set(0, 0, aMatrix.Line1.Column1);
82 aTransformation.set(0, 1, aMatrix.Line1.Column2);
83 aTransformation.set(0, 2, aMatrix.Line1.Column3);
84 aTransformation.set(1, 0, aMatrix.Line1.Column1);
85 aTransformation.set(1, 1, aMatrix.Line2.Column2);
86 aTransformation.set(1, 2, aMatrix.Line3.Column3);
87 aTransformation.set(2, 0, aMatrix.Line1.Column1);
88 aTransformation.set(2, 1, aMatrix.Line2.Column2);
89 aTransformation.set(2, 2, aMatrix.Line3.Column3);
90 basegfx::B2DTuple aScale;
91 basegfx::B2DTuple aTranslate;
92 double fRotate = 0;
93 double fShearX = 0;
94 aTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
95
96 // If the text is not rotated the way the shape wants it already, set the angle.
97 const sal_Int32 nRotation = nVert == XML_vert270 ? -270 : -90;
98 if (static_cast<sal_Int32>(basegfx::rad2deg(fRotate))
99 != NormAngle36000(Degree100(nRotation * 100)).get() / 100)
100 {
101 comphelper::SequenceAsHashMap aCustomShapeGeometry(
102 xPropertySet->getPropertyValue("CustomShapeGeometry"));
103 aCustomShapeGeometry["TextPreRotateAngle"] <<= nRotation;
104 xPropertySet->setPropertyValue(
105 "CustomShapeGeometry",
106 uno::Any(aCustomShapeGeometry.getAsConstPropertyValueList()));
107 }
108 }
109
110 if (bool bUpright = rAttribs.getBool(XML_upright, false))
111 {
112 uno::Sequence<beans::PropertyValue> aGrabBag;
113 xPropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
114 sal_Int32 length = aGrabBag.getLength();
115 aGrabBag.realloc(length + 1);
116 auto pGrabBag = aGrabBag.getArray();
117 pGrabBag[length].Name = "Upright";
118 pGrabBag[length].Value <<= bUpright;
119 xPropertySet->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag));
120 }
121
122 if (xServiceInfo.is())
123 {
124 // Handle inset attributes for Writer textframes.
125 sal_Int32 aInsets[] = { XML_lIns, XML_tIns, XML_rIns, XML_bIns };
126 std::optional<sal_Int32> oInsets[4];
127 for (std::size_t i = 0; i < SAL_N_ELEMENTS(aInsets); ++i)
128 {
129 std::optional<OUString> oValue = rAttribs.getString(aInsets[i]);
130 if (oValue.has_value())
131 oInsets[i] = oox::drawingml::GetCoordinate(oValue.value());
132 else
133 // Defaults from the spec: left/right: 91440 EMU, top/bottom: 45720 EMU
134 oInsets[i]
135 = (aInsets[i] == XML_lIns || aInsets[i] == XML_rIns) ? 254 : 127;
136 }
137 const OUString aShapeProps[]
138 = { OUString("TextLeftDistance"), OUString("TextUpperDistance"),
139 OUString("TextRightDistance"), OUString("TextLowerDistance") };
140 for (std::size_t i = 0; i < SAL_N_ELEMENTS(aShapeProps); ++i)
141 if (oInsets[i])
142 xPropertySet->setPropertyValue(aShapeProps[i], uno::Any(*oInsets[i]));
143 }
144
145 // Handle text vertical adjustment inside a text frame
146 if (rAttribs.hasAttribute(XML_anchor))
147 {
148 drawing::TextVerticalAdjust eAdjust
149 = drawingml::GetTextVerticalAdjust(rAttribs.getToken(XML_anchor, XML_t));
150 xPropertySet->setPropertyValue("TextVerticalAdjust", uno::Any(eAdjust));
151 }
152
153 // Apply character color of the shape to the shape's textbox.
154 uno::Reference<text::XText> xText(mxShape, uno::UNO_QUERY);
155 uno::Reference<text::XTextCursor> xTextCursor = xText->createTextCursor();
156 xTextCursor->gotoStart(false);
157 xTextCursor->gotoEnd(true);
158 uno::Reference<beans::XPropertySet> xTextBoxPropertySet(xTextCursor,
159 uno::UNO_QUERY);
160 uno::Any xCharColor = xPropertySet->getPropertyValue("CharColor");
161 Color aColor = COL_AUTO;
162 if ((xCharColor >>= aColor) && aColor != COL_AUTO)
163 {
164 const uno::Reference<beans::XPropertyState> xPropertyState(xTextCursor,
165 uno::UNO_QUERY);
166 const beans::PropertyState ePropertyState
167 = xPropertyState->getPropertyState("CharColor");
168 if (ePropertyState == beans::PropertyState_DEFAULT_VALUE)
169 {
170 xTextBoxPropertySet->setPropertyValue("CharColor", xCharColor);
171 }
172 else
173 {
174 // tdf#135923 Apply character color of the shape to the textrun
175 // when the character color of the textrun is default.
176 uno::Reference<container::XEnumerationAccess> paraEnumAccess(
177 xText, uno::UNO_QUERY);
178 if (paraEnumAccess.is())
179 {
180 uno::Reference<container::XEnumeration> paraEnum(
181 paraEnumAccess->createEnumeration());
182
183 while (paraEnum->hasMoreElements())
184 {
185 uno::Reference<text::XTextRange> xParagraph(paraEnum->nextElement(),
186 uno::UNO_QUERY);
187 uno::Reference<container::XEnumerationAccess> runEnumAccess(
188 xParagraph, uno::UNO_QUERY);
189 if (!runEnumAccess.is())
190 continue;
191
192 uno::Reference<container::XEnumeration> runEnum
193 = runEnumAccess->createEnumeration();
194
195 while (runEnum->hasMoreElements())
196 {
197 uno::Reference<text::XTextRange> xRun(runEnum->nextElement(),
198 uno::UNO_QUERY);
199 const uno::Reference<beans::XPropertyState> xRunState(
200 xRun, uno::UNO_QUERY);
201 if (xRunState->getPropertyState("CharColor")
202 == beans::PropertyState_DEFAULT_VALUE)
203 {
204 uno::Reference<beans::XPropertySet> xRunPropSet(
205 xRun, uno::UNO_QUERY);
206 xRunPropSet->setPropertyValue("CharColor", xCharColor);
207 }
208 }
209 }
210 }
211 }
212 }
213
214 auto nWrappingType = rAttribs.getToken(XML_wrap, XML_square);
215 xPropertySet->setPropertyValue("TextWordWrap",
216 uno::Any(nWrappingType == XML_square));
217
218 return this;
219 }
220 else if (m_bHasWPGParent && mpShapePtr)
221 {
222 // this WPS context has to be inside a WPG shape, so the <BodyPr> element
223 // cannot be applied to mxShape member, use mpShape instead, and after the
224 // the parent shape finished, apply it for its children.
225 mpShapePtr->setWPGChild(true);
227 pTextBody.reset(new oox::drawingml::TextBody());
228
229 if (rAttribs.hasAttribute(XML_anchor))
230 {
231 drawing::TextVerticalAdjust eAdjust
232 = drawingml::GetTextVerticalAdjust(rAttribs.getToken(XML_anchor, XML_t));
233 pTextBody->getTextProperties().meVA = eAdjust;
234 }
235
236 sal_Int32 aInsets[] = { XML_lIns, XML_tIns, XML_rIns, XML_bIns };
237 for (int i = 0; i < 4; ++i)
238 {
239 if (rAttribs.hasAttribute(XML_lIns))
240 {
241 std::optional<OUString> oValue = rAttribs.getString(aInsets[i]);
242 if (oValue.has_value())
243 pTextBody->getTextProperties().moInsets[i]
244 = oox::drawingml::GetCoordinate(oValue.value());
245 else
246 // Defaults from the spec: left/right: 91440 EMU, top/bottom: 45720 EMU
247 pTextBody->getTextProperties().moInsets[i]
248 = (aInsets[i] == XML_lIns || aInsets[i] == XML_rIns) ? 254 : 127;
249 }
250 }
251
252 mpShapePtr->setTextBody(pTextBody);
253 }
254 break;
255 case XML_noAutofit:
256 case XML_spAutoFit:
257 {
258 uno::Reference<lang::XServiceInfo> xServiceInfo(mxShape, uno::UNO_QUERY);
259 // We can't use oox::drawingml::TextBodyPropertiesContext here, as this
260 // is a child context of bodyPr, so the shape is already sent: we need
261 // to alter the XShape directly.
262 uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
263 if (xPropertySet.is())
264 {
265 if (xServiceInfo->supportsService("com.sun.star.text.TextFrame"))
266 xPropertySet->setPropertyValue(
267 "FrameIsAutomaticHeight",
268 uno::Any(getBaseToken(nElementToken) == XML_spAutoFit));
269 else
270 xPropertySet->setPropertyValue(
271 "TextAutoGrowHeight",
272 uno::Any(getBaseToken(nElementToken) == XML_spAutoFit));
273 }
274 }
275 break;
276 case XML_prstTxWarp:
277 if (rAttribs.hasAttribute(XML_prst))
278 {
279 uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
280 if (xPropertySet.is())
281 {
282 std::optional<OUString> presetShapeName = rAttribs.getString(XML_prst);
283 const OUString& preset = presetShapeName.value();
284 comphelper::SequenceAsHashMap aCustomShapeGeometry(
285 xPropertySet->getPropertyValue("CustomShapeGeometry"));
286 aCustomShapeGeometry["PresetTextWarp"] <<= preset;
287 xPropertySet->setPropertyValue(
288 "CustomShapeGeometry",
289 uno::Any(aCustomShapeGeometry.getAsConstPropertyValueList()));
290 }
291 }
292 break;
293 case XML_txbx:
294 {
295 mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
296 mpShapePtr->setTextBox(true);
297 //in case if the textbox is linked, save the attributes
298 //for further processing.
299 if (rAttribs.hasAttribute(XML_id))
300 {
301 std::optional<OUString> id = rAttribs.getString(XML_id);
302 if (id.has_value())
303 {
304 oox::drawingml::LinkedTxbxAttr linkedTxtBoxAttr;
305 linkedTxtBoxAttr.id = id.value().toInt32();
306 mpShapePtr->setTxbxHasLinkedTxtBox(true);
307 mpShapePtr->setLinkedTxbxAttributes(linkedTxtBoxAttr);
308 }
309 }
310 return this;
311 }
312 break;
313 case XML_linkedTxbx:
314 {
315 //in case if the textbox is linked, save the attributes
316 //for further processing.
317 mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
318 mpShapePtr->setTextBox(true);
319 std::optional<OUString> id = rAttribs.getString(XML_id);
320 std::optional<OUString> seq = rAttribs.getString(XML_seq);
321 if (id.has_value() && seq.has_value())
322 {
323 oox::drawingml::LinkedTxbxAttr linkedTxtBoxAttr;
324 linkedTxtBoxAttr.id = id.value().toInt32();
325 linkedTxtBoxAttr.seq = seq.value().toInt32();
326 mpShapePtr->setTxbxHasLinkedTxtBox(true);
327 mpShapePtr->setLinkedTxbxAttributes(linkedTxtBoxAttr);
328 }
329 }
330 break;
331 default:
332 return ShapeContext::onCreateContext(nElementToken, rAttribs);
333 }
334 return nullptr;
335}
336}
337
338/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
void set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
css::uno::Sequence< css::beans::PropertyValue > getAsConstPropertyValueList() const
Provides access to attribute values of an element.
bool hasAttribute(sal_Int32 nAttrToken) const
Returns true, if the specified attribute is present.
std::optional< OUString > getString(sal_Int32 nAttrToken) const
Returns the string value of the specified attribute.
std::optional< bool > getBool(sal_Int32 nAttrToken) const
Returns the boolean value of the specified attribute.
std::optional< sal_Int32 > getToken(sal_Int32 nAttrToken) const
Returns the token identifier of the value of the specified attribute.
Helper class that provides a context stack.
virtual ::oox::core::ContextHandlerRef onCreateContext(::sal_Int32 Element, const ::oox::AttributeList &rAttribs) override
Wpg is the drawingML equivalent of v:group.
Definition: WpgContext.hxx:20
oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElementToken, const oox::AttributeList &rAttribs) override
Will be called to create a context handler for the passed element.
Definition: WpsContext.cxx:56
WpsContext(oox::core::ContextHandler2Helper const &rParent, css::uno::Reference< css::drawing::XShape > xShape, oox::drawingml::ShapePtr const &pMasterShapePtr, oox::drawingml::ShapePtr const &pShapePtr)
Definition: WpsContext.cxx:39
css::uno::Reference< css::drawing::XShape > mxShape
Definition: WpsContext.hxx:38
constexpr ::Color COL_AUTO(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
#define SAL_N_ELEMENTS(arr)
constexpr double rad2deg(double v)
TextVerticalAdjust GetTextVerticalAdjust(sal_Int32 nToken)
sal_Int32 GetCoordinate(sal_Int32 nValue)
converts EMUs into 1/100th mmm
std::shared_ptr< Shape > ShapePtr
std::shared_ptr< TextBody > TextBodyPtr
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
uno::Reference< drawing::XShape > const mxShape
Attributes for a linked textbox.
Definition: shape.hxx:101
SVXCORE_DLLPUBLIC Degree100 NormAngle36000(Degree100 a)