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