/src/libreoffice/oox/source/vml/vmlshapecontext.cxx
Line | Count | Source |
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 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include <sal/config.h> |
21 | | |
22 | | #include <string_view> |
23 | | |
24 | | #include <oox/vml/vmlshapecontext.hxx> |
25 | | |
26 | | #include <oox/core/xmlfilterbase.hxx> |
27 | | #include <oox/helper/attributelist.hxx> |
28 | | #include <oox/helper/helper.hxx> |
29 | | #include <oox/token/namespaces.hxx> |
30 | | #include <oox/token/tokens.hxx> |
31 | | #include <oox/vml/vmldrawing.hxx> |
32 | | #include <oox/vml/vmlshape.hxx> |
33 | | #include <oox/vml/vmlshapecontainer.hxx> |
34 | | #include <oox/vml/vmltextboxcontext.hxx> |
35 | | |
36 | | #include <osl/diagnose.h> |
37 | | #include <filter/msfilter/escherex.hxx> |
38 | | #include <o3tl/string_view.hxx> |
39 | | |
40 | | namespace oox::vml { |
41 | | |
42 | | using namespace ::com::sun::star; |
43 | | |
44 | | using ::oox::core::ContextHandler2; |
45 | | using ::oox::core::ContextHandler2Helper; |
46 | | using ::oox::core::ContextHandlerRef; |
47 | | |
48 | | namespace { |
49 | | |
50 | | /** Returns the boolean value from the specified VML attribute (if present). |
51 | | */ |
52 | | std::optional< bool > lclDecodeBool( const AttributeList& rAttribs, sal_Int32 nToken ) |
53 | 41.0k | { |
54 | 41.0k | std::optional< OUString > oValue = rAttribs.getString( nToken ); |
55 | 41.0k | if( oValue.has_value() ) return std::optional< bool >( ConversionHelper::decodeBool( oValue.value() ) ); |
56 | 34.2k | return std::optional< bool >(); |
57 | 41.0k | } |
58 | | |
59 | | /** Returns the percentage value from the specified VML attribute (if present). |
60 | | The value will be normalized (1.0 is returned for 100%). |
61 | | */ |
62 | | std::optional< double > lclDecodePercent( const AttributeList& rAttribs, sal_Int32 nToken, double fDefValue ) |
63 | 10.0k | { |
64 | 10.0k | std::optional< OUString > oValue = rAttribs.getString( nToken ); |
65 | 10.0k | if( oValue.has_value() ) return std::optional< double >( ConversionHelper::decodePercent( oValue.value(), fDefValue ) ); |
66 | 10.0k | return std::optional< double >(); |
67 | 10.0k | } |
68 | | |
69 | | /** #119750# Special method for opacity; it *should* be a percentage value, but there are cases |
70 | | where a value relative to 0xffff (65536) is used, ending with an 'f' |
71 | | */ |
72 | | std::optional< double > lclDecodeOpacity( const AttributeList& rAttribs, sal_Int32 nToken, double fDefValue ) |
73 | 12.0k | { |
74 | 12.0k | std::optional< OUString > oValue = rAttribs.getString( nToken ); |
75 | 12.0k | double fRetval(fDefValue); |
76 | | |
77 | 12.0k | if( oValue.has_value() ) |
78 | 39 | { |
79 | 39 | const OUString& aString(oValue.value()); |
80 | 39 | const sal_Int32 nLength(aString.getLength()); |
81 | | |
82 | 39 | if(nLength > 0) |
83 | 39 | { |
84 | 39 | if(aString.endsWith("f")) |
85 | 32 | { |
86 | 32 | fRetval = std::clamp(aString.toDouble() / 65536.0, 0.0, 1.0); |
87 | 32 | } |
88 | 7 | else |
89 | 7 | { |
90 | 7 | fRetval = ConversionHelper::decodePercent( aString, fDefValue ); |
91 | 7 | } |
92 | 39 | } |
93 | 39 | } |
94 | | |
95 | 12.0k | return std::optional< double >(fRetval); |
96 | 12.0k | } |
97 | | |
98 | | /** Returns the integer value pair from the specified VML attribute (if present). |
99 | | */ |
100 | | std::optional< Int32Pair > lclDecodeInt32Pair( const AttributeList& rAttribs, sal_Int32 nToken ) |
101 | 16.0k | { |
102 | 16.0k | std::optional< OUString > oValue = rAttribs.getString( nToken ); |
103 | 16.0k | std::optional< Int32Pair > oRetValue; |
104 | 16.0k | if( oValue.has_value() ) |
105 | 1.48k | { |
106 | 1.48k | std::u16string_view aValue1, aValue2; |
107 | 1.48k | ConversionHelper::separatePair( aValue1, aValue2, oValue.value(), ',' ); |
108 | 1.48k | oRetValue = Int32Pair( o3tl::toInt32(aValue1), o3tl::toInt32(aValue2) ); |
109 | 1.48k | } |
110 | 16.0k | return oRetValue; |
111 | 16.0k | } |
112 | | |
113 | | /** Returns the percentage pair from the specified VML attribute (if present). |
114 | | */ |
115 | | std::optional< DoublePair > lclDecodePercentPair( const AttributeList& rAttribs, sal_Int32 nToken ) |
116 | 10.2k | { |
117 | 10.2k | std::optional< OUString > oValue = rAttribs.getString( nToken ); |
118 | 10.2k | std::optional< DoublePair > oRetValue; |
119 | 10.2k | if( oValue.has_value() ) |
120 | 8 | { |
121 | 8 | std::u16string_view aValue1, aValue2; |
122 | 8 | ConversionHelper::separatePair( aValue1, aValue2, oValue.value(), ',' ); |
123 | 8 | oRetValue = DoublePair( |
124 | 8 | ConversionHelper::decodePercent( aValue1, 0.0 ), |
125 | 8 | ConversionHelper::decodePercent( aValue2, 0.0 ) ); |
126 | 8 | } |
127 | 10.2k | return oRetValue; |
128 | 10.2k | } |
129 | | |
130 | | /** Returns the boolean value from the passed string of an attribute in the x: |
131 | | namespace (VML for spreadsheets). Supported values: f, t, False, True. |
132 | | @param bDefaultForEmpty Default value for the empty string. |
133 | | */ |
134 | | bool lclDecodeVmlxBool( std::u16string_view rValue, bool bDefaultForEmpty ) |
135 | 9.51k | { |
136 | 9.51k | if( rValue.empty() ) return bDefaultForEmpty; |
137 | 4.15k | sal_Int32 nToken = AttributeConversion::decodeToken( rValue ); |
138 | | // anything else than 't' or 'True' is considered to be false, as specified |
139 | 4.15k | return (nToken == XML_t) || (nToken == XML_True); |
140 | 9.51k | } |
141 | | |
142 | | } // namespace |
143 | | |
144 | | ShapeLayoutContext::ShapeLayoutContext( ContextHandler2Helper const & rParent, Drawing& rDrawing ) : |
145 | 200 | ContextHandler2( rParent ), |
146 | 200 | mrDrawing( rDrawing ) |
147 | 200 | { |
148 | 200 | } |
149 | | |
150 | | ContextHandlerRef ShapeLayoutContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) |
151 | 200 | { |
152 | 200 | switch( nElement ) |
153 | 200 | { |
154 | 200 | case O_TOKEN( idmap ): |
155 | 200 | { |
156 | 200 | OUString aBlockIds = rAttribs.getStringDefaulted( XML_data); |
157 | 200 | sal_Int32 nIndex = 0; |
158 | 400 | while( nIndex >= 0 ) |
159 | 200 | { |
160 | 200 | std::u16string_view aToken = o3tl::trim(o3tl::getToken(aBlockIds, 0, ' ', nIndex )); |
161 | 200 | if( !aToken.empty() ) |
162 | 200 | mrDrawing.registerBlockId( o3tl::toInt32(aToken) ); |
163 | 200 | } |
164 | 200 | } |
165 | 200 | break; |
166 | 200 | } |
167 | 200 | return nullptr; |
168 | 200 | } |
169 | | |
170 | | ClientDataContext::ClientDataContext( ContextHandler2Helper const & rParent, |
171 | | ClientData& rClientData, const AttributeList& rAttribs ) : |
172 | 4.77k | ContextHandler2( rParent ), |
173 | 4.77k | mrClientData( rClientData ) |
174 | 4.77k | { |
175 | 4.77k | mrClientData.mnObjType = rAttribs.getToken( XML_ObjectType, XML_TOKEN_INVALID ); |
176 | 4.77k | } |
177 | | |
178 | | ContextHandlerRef ClientDataContext::onCreateContext( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ ) |
179 | 40.9k | { |
180 | 40.9k | if( isRootElement() ) |
181 | 40.9k | { |
182 | 40.9k | maElementText.clear(); |
183 | 40.9k | return this; |
184 | 40.9k | } |
185 | 33 | return nullptr; |
186 | 40.9k | } |
187 | | |
188 | | void ClientDataContext::onCharacters( const OUString& rChars ) |
189 | 35.4k | { |
190 | | /* Empty but existing elements have special meaning, e.g. 'true'. Collect |
191 | | existing text and convert it in onEndElement(). */ |
192 | 35.4k | maElementText = rChars; |
193 | 35.4k | } |
194 | | |
195 | | void ClientDataContext::onEndElement() |
196 | 45.6k | { |
197 | 45.6k | switch( getCurrentElement() ) |
198 | 45.6k | { |
199 | 4.58k | case VMLX_TOKEN(Anchor): |
200 | 4.58k | mrClientData.maAnchor = maElementText; |
201 | 4.58k | break; |
202 | 0 | case VMLX_TOKEN(FmlaMacro): |
203 | 0 | mrClientData.maFmlaMacro = maElementText; |
204 | 0 | break; |
205 | 0 | case VMLX_TOKEN(FmlaPict): |
206 | 0 | mrClientData.maFmlaPict = maElementText; |
207 | 0 | break; |
208 | 10 | case VMLX_TOKEN(FmlaLink): |
209 | 10 | mrClientData.maFmlaLink = maElementText; |
210 | 10 | break; |
211 | 4 | case VMLX_TOKEN(FmlaRange): |
212 | 4 | mrClientData.maFmlaRange = maElementText; |
213 | 4 | break; |
214 | 0 | case VMLX_TOKEN(FmlaGroup): |
215 | 0 | mrClientData.maFmlaGroup = maElementText; |
216 | 0 | break; |
217 | 0 | case VMLX_TOKEN(TextHAlign): |
218 | 0 | mrClientData.mnTextHAlign = AttributeConversion::decodeToken(maElementText); |
219 | 0 | break; |
220 | 19 | case VMLX_TOKEN(TextVAlign): |
221 | 19 | mrClientData.mnTextVAlign = AttributeConversion::decodeToken(maElementText); |
222 | 19 | break; |
223 | 4.67k | case VMLX_TOKEN(Column): |
224 | 4.67k | mrClientData.mnCol = maElementText.toInt32(); |
225 | 4.67k | break; |
226 | 4.68k | case VMLX_TOKEN(Row): |
227 | 4.68k | mrClientData.mnRow = maElementText.toInt32(); |
228 | 4.68k | break; |
229 | 2 | case VMLX_TOKEN(Checked): |
230 | 2 | mrClientData.mnChecked = maElementText.toInt32(); |
231 | 2 | break; |
232 | 0 | case VMLX_TOKEN(DropStyle): |
233 | 0 | mrClientData.mnDropStyle = AttributeConversion::decodeToken(maElementText); |
234 | 0 | break; |
235 | 0 | case VMLX_TOKEN(DropLines): |
236 | 0 | mrClientData.mnDropLines = maElementText.toInt32(); |
237 | 0 | break; |
238 | 10 | case VMLX_TOKEN(Val): |
239 | 10 | mrClientData.mnVal = maElementText.toInt32(); |
240 | 10 | break; |
241 | 10 | case VMLX_TOKEN(Min): |
242 | 10 | mrClientData.mnMin = maElementText.toInt32(); |
243 | 10 | break; |
244 | 10 | case VMLX_TOKEN(Max): |
245 | 10 | mrClientData.mnMax = maElementText.toInt32(); |
246 | 10 | break; |
247 | 10 | case VMLX_TOKEN(Inc): |
248 | 10 | mrClientData.mnInc = maElementText.toInt32(); |
249 | 10 | break; |
250 | 10 | case VMLX_TOKEN(Page): |
251 | 10 | mrClientData.mnPage = maElementText.toInt32(); |
252 | 10 | break; |
253 | 4 | case VMLX_TOKEN(SelType): |
254 | 4 | mrClientData.mnSelType = AttributeConversion::decodeToken(maElementText); |
255 | 4 | break; |
256 | 0 | case VMLX_TOKEN(VTEdit): |
257 | 0 | mrClientData.mnVTEdit = maElementText.toInt32(); |
258 | 0 | break; |
259 | 4.15k | case VMLX_TOKEN(PrintObject): |
260 | 4.15k | mrClientData.mbPrintObject = lclDecodeVmlxBool(maElementText, true); |
261 | 4.15k | break; |
262 | 41 | case VMLX_TOKEN(Visible): |
263 | 41 | mrClientData.mbVisible = lclDecodeVmlxBool(maElementText, true); |
264 | 41 | break; |
265 | 6 | case VMLX_TOKEN(DDE): |
266 | 6 | mrClientData.mbDde = lclDecodeVmlxBool(maElementText, true); |
267 | 6 | break; |
268 | 2 | case VMLX_TOKEN(NoThreeD): |
269 | 2 | mrClientData.mbNo3D = lclDecodeVmlxBool(maElementText, true); |
270 | 2 | break; |
271 | 4 | case VMLX_TOKEN(NoThreeD2): |
272 | 4 | mrClientData.mbNo3D2 = lclDecodeVmlxBool(maElementText, true); |
273 | 4 | break; |
274 | 0 | case VMLX_TOKEN(MultiLine): |
275 | 0 | mrClientData.mbMultiLine = lclDecodeVmlxBool(maElementText, true); |
276 | 0 | break; |
277 | 0 | case VMLX_TOKEN(VScroll): |
278 | 0 | mrClientData.mbVScroll = lclDecodeVmlxBool(maElementText, true); |
279 | 0 | break; |
280 | 0 | case VMLX_TOKEN(SecretEdit): |
281 | 0 | mrClientData.mbSecretEdit = lclDecodeVmlxBool(maElementText, true); |
282 | 0 | break; |
283 | | // Oddly, Excel will declare MoveWithCells and SizeWithCells with no value if these are false (therefore, bDefaultForEmpty == false) |
284 | | // When MoveWithCells and SizeWithCells are NOT declared they should be assumed true. |
285 | 535 | case VMLX_TOKEN(MoveWithCells): |
286 | 535 | mrClientData.mbMoveWithCells = lclDecodeVmlxBool(maElementText, false); |
287 | 535 | break; |
288 | 4.77k | case VMLX_TOKEN(SizeWithCells): |
289 | 4.77k | mrClientData.mbSizeWithCells = lclDecodeVmlxBool(maElementText, false); |
290 | 4.77k | break; |
291 | 45.6k | } |
292 | 45.6k | } |
293 | | |
294 | | ShapeContextBase::ShapeContextBase( ContextHandler2Helper const & rParent ) : |
295 | 8.02k | ContextHandler2( rParent ) |
296 | 8.02k | { |
297 | 8.02k | } |
298 | | |
299 | | ContextHandlerRef ShapeContextBase::createShapeContext( ContextHandler2Helper const & rParent, |
300 | | ShapeContainer& rShapes, sal_Int32 nElement, const AttributeList& rAttribs ) |
301 | 8.33k | { |
302 | 8.33k | switch( nElement ) |
303 | 8.33k | { |
304 | 200 | case O_TOKEN( shapelayout ): |
305 | 200 | return new ShapeLayoutContext( rParent, rShapes.getDrawing() ); |
306 | | |
307 | 802 | case VML_TOKEN( shapetype ): |
308 | 802 | return new ShapeTypeContext( rParent, rShapes.createShapeType(), rAttribs ); |
309 | 110 | case VML_TOKEN( group ): |
310 | 110 | return new GroupShapeContext( rParent, rShapes.createShape< GroupShape >(), rAttribs ); |
311 | 6.08k | case VML_TOKEN( shape ): |
312 | 6.08k | if (rAttribs.hasAttribute(XML_path) && |
313 | | // tdf#122563 skip in the case of empty path |
314 | 462 | !rAttribs.getStringDefaulted(XML_path).isEmpty()) |
315 | 462 | return new ShapeContext( rParent, rShapes.createShape< BezierShape >(), rAttribs ); |
316 | 5.62k | else |
317 | 5.62k | return new ShapeContext( rParent, rShapes.createShape< ComplexShape >(), rAttribs ); |
318 | 0 | case VML_TOKEN(background): |
319 | 562 | case VML_TOKEN( rect ): |
320 | 562 | return new RectangleShapeContext( rParent, rAttribs, rShapes.createShape< RectangleShape >() ); |
321 | 24 | case VML_TOKEN( roundrect ): |
322 | 24 | return new ShapeContext( rParent, rShapes.createShape< RectangleShape >(), rAttribs ); |
323 | 169 | case VML_TOKEN( oval ): |
324 | 169 | return new ShapeContext( rParent, rShapes.createShape< EllipseShape >(), rAttribs ); |
325 | 0 | case VML_TOKEN( polyline ): |
326 | 0 | return new ShapeContext( rParent, rShapes.createShape< PolyLineShape >(), rAttribs ); |
327 | 271 | case VML_TOKEN( line ): |
328 | 271 | return new ShapeContext( rParent, rShapes.createShape< LineShape >(), rAttribs ); |
329 | 0 | case VML_TOKEN( curve ): |
330 | 0 | return new ShapeContext( rParent, rShapes.createShape< BezierShape >(), rAttribs ); |
331 | | |
332 | | // TODO: |
333 | 0 | case VML_TOKEN( arc ): |
334 | 0 | case VML_TOKEN( diagram ): |
335 | 0 | case VML_TOKEN( image ): |
336 | 0 | return new ShapeContext( rParent, rShapes.createShape< ComplexShape >(), rAttribs ); |
337 | | |
338 | 0 | case W_TOKEN(control): |
339 | 0 | return new ControlShapeContext( rParent, rShapes, rAttribs ); |
340 | 8.33k | } |
341 | 107 | return nullptr; |
342 | 8.33k | } |
343 | | |
344 | | ShapeTypeContext::ShapeTypeContext(ContextHandler2Helper const & rParent, |
345 | | std::shared_ptr<ShapeType> const& pShapeType, |
346 | | const AttributeList& rAttribs) |
347 | 8.02k | : ShapeContextBase(rParent) |
348 | 8.02k | , m_pShapeType(pShapeType) // tdf#112311 keep it alive |
349 | 8.02k | , mrTypeModel( pShapeType->getTypeModel() ) |
350 | 8.02k | { |
351 | | // shape identifier and shape name |
352 | 8.02k | bool bHasOspid = rAttribs.hasAttribute( O_TOKEN( spid ) ); |
353 | 8.02k | mrTypeModel.maShapeId = rAttribs.getXString( bHasOspid ? O_TOKEN( spid ) : XML_id, OUString() ); |
354 | 8.02k | mrTypeModel.maLegacyId = rAttribs.getStringDefaulted( XML_id); |
355 | 8.02k | OSL_ENSURE( !mrTypeModel.maShapeId.isEmpty(), "ShapeTypeContext::ShapeTypeContext - missing shape identifier" ); |
356 | | // builtin shape type identifier |
357 | 8.02k | mrTypeModel.moShapeType = rAttribs.getInteger( O_TOKEN( spt ) ); |
358 | | // if the o:spid attribute exists, the id attribute contains the user-defined shape name |
359 | 8.02k | if( bHasOspid ) |
360 | 141 | { |
361 | 141 | mrTypeModel.maShapeName = rAttribs.getXString( XML_id, OUString() ); |
362 | | // get ShapeType and ShapeId from name for compatibility |
363 | 141 | static constexpr OUString sShapeTypePrefix = u"shapetype_"_ustr; |
364 | 141 | std::u16string_view tmp; |
365 | 141 | if( mrTypeModel.maShapeName.startsWith( sShapeTypePrefix ) ) |
366 | 0 | { |
367 | 0 | mrTypeModel.maShapeId = mrTypeModel.maShapeName; |
368 | 0 | mrTypeModel.moShapeType = o3tl::toInt32(mrTypeModel.maShapeName.subView(sShapeTypePrefix.getLength())); |
369 | 0 | } |
370 | 141 | else if (mrTypeModel.maShapeName.startsWith("_x0000_t", &tmp)) |
371 | 0 | { |
372 | 0 | mrTypeModel.maShapeId = mrTypeModel.maShapeName; |
373 | 0 | mrTypeModel.moShapeType = o3tl::toInt32(tmp); |
374 | 0 | } |
375 | 141 | } |
376 | | |
377 | | // coordinate system position/size, CSS style |
378 | 8.02k | mrTypeModel.moCoordPos = lclDecodeInt32Pair( rAttribs, XML_coordorigin ); |
379 | 8.02k | mrTypeModel.moCoordSize = lclDecodeInt32Pair( rAttribs, XML_coordsize ); |
380 | 8.02k | setStyle( rAttribs.getStringDefaulted( XML_style) ); |
381 | 8.02k | if( lclDecodeBool( rAttribs, O_TOKEN( hr )).value_or( false )) |
382 | 0 | { // MSO's handling of o:hr width is nowhere near what the spec says: |
383 | | // - o:hrpct is not in % but in 0.1% |
384 | | // - if o:hrpct is not given, 100% width is assumed |
385 | | // - given width is used only if explicit o:hrpct="0" is given |
386 | 0 | OUString hrpct = rAttribs.getString( O_TOKEN( hrpct ), u"1000"_ustr ); |
387 | 0 | if( hrpct != "0" ) |
388 | 0 | mrTypeModel.maWidthPercent = OUString::number( hrpct.toInt32() ); |
389 | 0 | mrTypeModel.maWrapDistanceLeft = "0"; |
390 | 0 | mrTypeModel.maWrapDistanceRight = "0"; |
391 | 0 | mrTypeModel.maPositionHorizontal = rAttribs.getString( O_TOKEN( hralign ), u"left"_ustr ); |
392 | 0 | mrTypeModel.moWrapType = "topAndBottom"; |
393 | 0 | } |
394 | | |
395 | | // stroke settings (may be overridden by v:stroke element later) |
396 | 8.02k | mrTypeModel.maStrokeModel.moStroked = lclDecodeBool( rAttribs, XML_stroked ); |
397 | 8.02k | mrTypeModel.maStrokeModel.moColor = rAttribs.getString( XML_strokecolor ); |
398 | 8.02k | mrTypeModel.maStrokeModel.moWeight = rAttribs.getString( XML_strokeweight ); |
399 | | |
400 | | // fill settings (may be overridden by v:fill element later) |
401 | 8.02k | mrTypeModel.maFillModel.moFilled = lclDecodeBool( rAttribs, XML_filled ); |
402 | 8.02k | mrTypeModel.maFillModel.moColor = rAttribs.getString( XML_fillcolor ); |
403 | | |
404 | | // For roundrect we may have an arcsize attribute to read |
405 | 8.02k | mrTypeModel.maArcsize = rAttribs.getStringDefaulted(XML_arcsize); |
406 | | // editas |
407 | 8.02k | mrTypeModel.maEditAs = rAttribs.getStringDefaulted(XML_editas); |
408 | | |
409 | 8.02k | mrTypeModel.maAdjustments = rAttribs.getStringDefaulted(XML_adj); |
410 | 8.02k | } |
411 | | |
412 | | ContextHandlerRef ShapeTypeContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) |
413 | 19.5k | { |
414 | 19.5k | if( isRootElement() ) switch( nElement ) |
415 | 19.5k | { |
416 | 1.80k | case VML_TOKEN( stroke ): |
417 | 1.80k | assignIfUsed( mrTypeModel.maStrokeModel.moStroked, lclDecodeBool( rAttribs, XML_on ) ); |
418 | 1.80k | mrTypeModel.maStrokeModel.maStartArrow.moArrowType = rAttribs.getToken( XML_startarrow ); |
419 | 1.80k | mrTypeModel.maStrokeModel.maStartArrow.moArrowWidth = rAttribs.getToken( XML_startarrowwidth ); |
420 | 1.80k | mrTypeModel.maStrokeModel.maStartArrow.moArrowLength = rAttribs.getToken( XML_startarrowlength ); |
421 | 1.80k | mrTypeModel.maStrokeModel.maEndArrow.moArrowType = rAttribs.getToken( XML_endarrow ); |
422 | 1.80k | mrTypeModel.maStrokeModel.maEndArrow.moArrowWidth = rAttribs.getToken( XML_endarrowwidth ); |
423 | 1.80k | mrTypeModel.maStrokeModel.maEndArrow.moArrowLength = rAttribs.getToken( XML_endarrowlength ); |
424 | 1.80k | assignIfUsed( mrTypeModel.maStrokeModel.moColor, rAttribs.getString( XML_color ) ); |
425 | 1.80k | mrTypeModel.maStrokeModel.moOpacity = lclDecodeOpacity( rAttribs, XML_opacity, 1.0 ); |
426 | 1.80k | assignIfUsed( mrTypeModel.maStrokeModel.moWeight, rAttribs.getString( XML_weight ) ); |
427 | 1.80k | mrTypeModel.maStrokeModel.moDashStyle = rAttribs.getString( XML_dashstyle ); |
428 | 1.80k | mrTypeModel.maStrokeModel.moLineStyle = rAttribs.getToken( XML_linestyle ); |
429 | 1.80k | mrTypeModel.maStrokeModel.moEndCap = rAttribs.getToken( XML_endcap ); |
430 | 1.80k | mrTypeModel.maStrokeModel.moJoinStyle = rAttribs.getToken( XML_joinstyle ); |
431 | 1.80k | break; |
432 | 5.11k | case VML_TOKEN( fill ): |
433 | 5.11k | { |
434 | | // in DOCX shapes use r:id for the relationship id |
435 | | // in XLSX they use o:relid |
436 | 5.11k | bool bHasORelId = rAttribs.hasAttribute( O_TOKEN(relid) ); |
437 | 5.11k | assignIfUsed( mrTypeModel.maFillModel.moFilled, lclDecodeBool( rAttribs, XML_on ) ); |
438 | 5.11k | assignIfUsed( mrTypeModel.maFillModel.moColor, rAttribs.getString( XML_color ) ); |
439 | 5.11k | mrTypeModel.maFillModel.moOpacity = lclDecodeOpacity( rAttribs, XML_opacity, 1.0 ); |
440 | 5.11k | mrTypeModel.maFillModel.moColor2 = rAttribs.getString( XML_color2 ); |
441 | 5.11k | mrTypeModel.maFillModel.moOpacity2 = lclDecodeOpacity( rAttribs, XML_opacity2, 1.0 ); |
442 | 5.11k | mrTypeModel.maFillModel.moType = rAttribs.getToken( XML_type ); |
443 | 5.11k | mrTypeModel.maFillModel.moAngle = rAttribs.getInteger( XML_angle ); |
444 | 5.11k | mrTypeModel.maFillModel.moFocus = lclDecodePercent( rAttribs, XML_focus, 0.0 ); |
445 | 5.11k | mrTypeModel.maFillModel.moFocusPos = lclDecodePercentPair( rAttribs, XML_focusposition ); |
446 | 5.11k | mrTypeModel.maFillModel.moFocusSize = lclDecodePercentPair( rAttribs, XML_focussize ); |
447 | 5.11k | mrTypeModel.maFillModel.moBitmapPath = decodeFragmentPath( rAttribs, bHasORelId ? O_TOKEN(relid) : R_TOKEN(id) ); |
448 | 5.11k | mrTypeModel.maFillModel.moRotate = lclDecodeBool( rAttribs, XML_rotate ); |
449 | 5.11k | break; |
450 | 0 | } |
451 | 425 | case VML_TOKEN( imagedata ): |
452 | 425 | { |
453 | | // shapes in docx use r:id for the relationship id |
454 | | // in xlsx it they use o:relid |
455 | 425 | bool bHasORelId = rAttribs.hasAttribute( O_TOKEN( relid ) ); |
456 | 425 | mrTypeModel.moGraphicPath = decodeFragmentPath( rAttribs, bHasORelId ? O_TOKEN( relid ) : R_TOKEN( id ) ); |
457 | 425 | mrTypeModel.moGraphicTitle = rAttribs.getString( O_TOKEN( title ) ); |
458 | | |
459 | | // Get crop attributes. |
460 | 425 | mrTypeModel.moCropBottom = rAttribs.getString(XML_cropbottom); |
461 | 425 | mrTypeModel.moCropLeft = rAttribs.getString(XML_cropleft); |
462 | 425 | mrTypeModel.moCropRight = rAttribs.getString(XML_cropright); |
463 | 425 | mrTypeModel.moCropTop = rAttribs.getString(XML_croptop); |
464 | | |
465 | | // Gain / contrast. |
466 | 425 | std::optional<OUString> oGain = rAttribs.getString(XML_gain); |
467 | 425 | sal_Int32 nGain = 0x10000; |
468 | 425 | if (oGain.has_value() && oGain.value().endsWith("f")) |
469 | 0 | { |
470 | 0 | nGain = oGain.value().toInt32(); |
471 | 0 | } |
472 | 425 | if (nGain < 0x10000) |
473 | 0 | { |
474 | 0 | nGain *= 101; // 100 + 1 to round |
475 | 0 | nGain /= 0x10000; |
476 | 0 | nGain -= 100; |
477 | 0 | } |
478 | 425 | mrTypeModel.mnGain = nGain; |
479 | | |
480 | | // Blacklevel / brightness. |
481 | 425 | std::optional<OUString> oBlacklevel = rAttribs.getString(XML_blacklevel); |
482 | 425 | sal_Int16 nBlacklevel = 0; |
483 | 425 | if (oBlacklevel.has_value() && oBlacklevel.value().endsWith("f")) |
484 | 0 | { |
485 | 0 | nBlacklevel = oBlacklevel.value().toInt32(); |
486 | 0 | } |
487 | 425 | if (nBlacklevel != 0) |
488 | 0 | { |
489 | 0 | nBlacklevel /= 327; |
490 | 0 | } |
491 | 425 | mrTypeModel.mnBlacklevel = nBlacklevel; |
492 | 425 | } |
493 | 425 | break; |
494 | 184 | case NMSP_vmlWord | XML_wrap: |
495 | 184 | mrTypeModel.moWrapAnchorX = rAttribs.getString(XML_anchorx); |
496 | 184 | mrTypeModel.moWrapAnchorY = rAttribs.getString(XML_anchory); |
497 | 184 | mrTypeModel.moWrapType = rAttribs.getString(XML_type); |
498 | 184 | mrTypeModel.moWrapSide = rAttribs.getString(XML_side); |
499 | 184 | break; |
500 | 4.95k | case VML_TOKEN( shadow ): |
501 | 4.95k | { |
502 | 4.95k | mrTypeModel.maShadowModel.mbHasShadow = true; |
503 | 4.95k | mrTypeModel.maShadowModel.moShadowOn = lclDecodeBool(rAttribs, XML_on).value_or(false); |
504 | 4.95k | assignIfUsed(mrTypeModel.maShadowModel.moColor, rAttribs.getString(XML_color)); |
505 | 4.95k | assignIfUsed(mrTypeModel.maShadowModel.moOffset, rAttribs.getString(XML_offset)); |
506 | 4.95k | mrTypeModel.maShadowModel.moOpacity = lclDecodePercent(rAttribs, XML_opacity, 1.0); |
507 | 4.95k | } |
508 | 4.95k | break; |
509 | 20 | case VML_TOKEN( textpath ): |
510 | 20 | assignIfUsed(mrTypeModel.maTextpathModel.moString, rAttribs.getString(XML_string)); |
511 | 20 | assignIfUsed(mrTypeModel.maTextpathModel.moStyle, rAttribs.getString(XML_style)); |
512 | 20 | assignIfUsed(mrTypeModel.maTextpathModel.moTrim, lclDecodeBool(rAttribs, XML_trim)); |
513 | 20 | break; |
514 | 19.5k | } |
515 | 19.5k | return nullptr; |
516 | 19.5k | } |
517 | | |
518 | | std::optional< OUString > ShapeTypeContext::decodeFragmentPath( const AttributeList& rAttribs, sal_Int32 nToken ) const |
519 | 5.54k | { |
520 | 5.54k | std::optional< OUString > oFragmentPath; |
521 | 5.54k | std::optional< OUString > oRelId = rAttribs.getString( nToken ); |
522 | 5.54k | if( oRelId.has_value() ) |
523 | 429 | oFragmentPath = getFragmentPathFromRelId( oRelId.value() ); |
524 | 5.54k | return oFragmentPath; |
525 | 5.54k | } |
526 | | |
527 | | void ShapeTypeContext::setStyle( std::u16string_view rStyle ) |
528 | 8.02k | { |
529 | 8.02k | sal_Int32 nIndex = 0; |
530 | 66.0k | while( nIndex >= 0 ) |
531 | 58.0k | { |
532 | 58.0k | std::u16string_view aName, aValue; |
533 | 58.0k | if( ConversionHelper::separatePair( aName, aValue, o3tl::getToken(rStyle, 0, ';', nIndex ), ':' ) ) |
534 | 57.2k | { |
535 | 57.2k | if( aName == u"position" ) mrTypeModel.maPosition = aValue; |
536 | 50.4k | else if( aName == u"z-index" ) mrTypeModel.maZIndex = aValue; |
537 | 44.7k | else if( aName == u"left" ) mrTypeModel.maLeft = aValue; |
538 | 43.9k | else if( aName == u"top" ) mrTypeModel.maTop = aValue; |
539 | 43.4k | else if( aName == u"width" ) mrTypeModel.maWidth = aValue; |
540 | 36.4k | else if( aName == u"height" ) mrTypeModel.maHeight = aValue; |
541 | 29.5k | else if( aName == u"margin-left" ) mrTypeModel.maMarginLeft = aValue; |
542 | 23.5k | else if( aName == u"margin-top" ) mrTypeModel.maMarginTop = aValue; |
543 | 17.6k | else if( aName == u"mso-position-vertical-relative" ) mrTypeModel.maPositionVerticalRelative = aValue; |
544 | 17.1k | else if( aName == u"mso-position-horizontal-relative" ) mrTypeModel.maPositionHorizontalRelative = aValue; |
545 | 16.6k | else if( aName == u"mso-position-horizontal" ) mrTypeModel.maPositionHorizontal = aValue; |
546 | 16.5k | else if( aName == u"mso-position-vertical" ) mrTypeModel.maPositionVertical = aValue; |
547 | 16.3k | else if( aName == u"mso-width-percent" ) mrTypeModel.maWidthPercent = aValue; |
548 | 16.2k | else if( aName == u"mso-width-relative" ) mrTypeModel.maWidthRelative = aValue; |
549 | 15.8k | else if( aName == u"mso-height-percent" ) mrTypeModel.maHeightPercent = aValue; |
550 | 15.8k | else if( aName == u"mso-height-relative" ) mrTypeModel.maHeightRelative = aValue; |
551 | 15.4k | else if( aName == u"mso-fit-shape-to-text" ) mrTypeModel.mbAutoHeight = true; |
552 | 15.4k | else if( aName == u"rotation" ) mrTypeModel.maRotation = aValue; |
553 | 15.2k | else if( aName == u"flip" ) mrTypeModel.maFlip = aValue; |
554 | 15.0k | else if( aName == u"visibility" ) |
555 | 5.20k | mrTypeModel.mbVisible = aValue != u"hidden"; |
556 | 9.82k | else if( aName == u"mso-wrap-style" ) mrTypeModel.maWrapStyle = aValue; |
557 | 5.56k | else if ( aName == u"v-text-anchor" ) mrTypeModel.maVTextAnchor = aValue; |
558 | 1.32k | else if ( aName == u"mso-wrap-distance-left" ) mrTypeModel.maWrapDistanceLeft = aValue; |
559 | 1.05k | else if ( aName == u"mso-wrap-distance-right" ) mrTypeModel.maWrapDistanceRight = aValue; |
560 | 787 | else if ( aName == u"mso-wrap-distance-top" ) mrTypeModel.maWrapDistanceTop = aValue; |
561 | 519 | else if ( aName == u"mso-wrap-distance-bottom" ) mrTypeModel.maWrapDistanceBottom = aValue; |
562 | 57.2k | } |
563 | 58.0k | } |
564 | 8.02k | } |
565 | | |
566 | | ShapeContext::ShapeContext(ContextHandler2Helper const& rParent, |
567 | | const std::shared_ptr<ShapeBase>& pShape, const AttributeList& rAttribs) |
568 | 7.22k | : ShapeTypeContext(rParent, pShape, rAttribs) |
569 | 7.22k | , mrShape(*pShape) |
570 | 7.22k | , mrShapeModel(pShape->getShapeModel()) |
571 | 7.22k | { |
572 | | // collect shape specific attributes |
573 | 7.22k | mrShapeModel.maType = rAttribs.getXString( XML_type, OUString() ); |
574 | | // polyline path |
575 | 7.22k | setPoints( rAttribs.getStringDefaulted( XML_points) ); |
576 | | // line start and end positions |
577 | 7.22k | setFrom(rAttribs.getStringDefaulted(XML_from)); |
578 | 7.22k | setTo(rAttribs.getStringDefaulted(XML_to)); |
579 | 7.22k | setControl1(rAttribs.getStringDefaulted(XML_control1)); |
580 | 7.22k | setControl2(rAttribs.getStringDefaulted(XML_control2)); |
581 | 7.22k | setVmlPath(rAttribs.getStringDefaulted(XML_path)); |
582 | 7.22k | setHyperlink(rAttribs.getStringDefaulted(XML_href)); |
583 | 7.22k | } |
584 | | |
585 | | ContextHandlerRef ShapeContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) |
586 | 28.1k | { |
587 | | // Excel specific shape client data |
588 | 28.1k | if( isRootElement() ) switch( nElement ) |
589 | 28.1k | { |
590 | 5.80k | case VML_TOKEN( textbox ): |
591 | 5.80k | { |
592 | | // Calculate the shape type: map both <rect> and <v:shape> with a textbox shape type to |
593 | | // a TextShape. |
594 | 5.80k | sal_Int32 nShapeType = 0; |
595 | 5.80k | if (ShapeContainer* pShapeContainer = mrShape.getContainer()) |
596 | 5.80k | { |
597 | 5.80k | OUString aType = mrShapeModel.maType; |
598 | 5.80k | if (!aType.isEmpty() && aType[0] == '#') |
599 | 5.17k | { |
600 | 5.17k | aType = aType.copy(1); |
601 | 5.17k | } |
602 | 5.80k | if (const ShapeType* pShapeType = pShapeContainer->getShapeTypeById(aType)) |
603 | 5.16k | { |
604 | 5.16k | nShapeType = pShapeType->getTypeModel().moShapeType.value(); |
605 | 5.16k | } |
606 | 5.80k | } |
607 | 5.80k | mrShapeModel.mbInGroup = (getParentElement() == VML_TOKEN(group)); |
608 | | |
609 | | // FIXME: the shape with textbox should be used for the next cases |
610 | 5.80k | if (getCurrentElement() == VML_TOKEN(rect) || nShapeType == ESCHER_ShpInst_TextBox) |
611 | 5.33k | { |
612 | 5.33k | if (mrShapeModel.mbInGroup) |
613 | | // FIXME: without this a text will be added into the group-shape instead of its |
614 | | // parent shape |
615 | 23 | { |
616 | 23 | if (mrShape.isTextBox()) |
617 | 0 | dynamic_cast<SimpleShape&>(mrShape).setService( |
618 | 0 | u"com.sun.star.drawing.CustomShape"_ustr); |
619 | 23 | else |
620 | 23 | dynamic_cast<SimpleShape&>(mrShape).setService( |
621 | 23 | u"com.sun.star.drawing.TextShape"_ustr); |
622 | 23 | } |
623 | 5.31k | else |
624 | | // FIXME: without this we does not handle some properties like shadow |
625 | 5.31k | dynamic_cast<SimpleShape&>(mrShape).setService(u"com.sun.star.text.TextFrame"_ustr); |
626 | 5.33k | } |
627 | 5.80k | return new TextBoxContext( *this, mrShapeModel.createTextBox(mrShape.getTypeModel()), rAttribs, |
628 | 5.80k | mrShape.getDrawing().getFilter().getGraphicHelper()); |
629 | 0 | } |
630 | 4.77k | case VMLX_TOKEN( ClientData ): |
631 | | // tdf#41466 ActiveX control shapes with a textbox are transformed into a frame |
632 | | // (see unit test testActiveXOptionButtonGroup) |
633 | 4.77k | dynamic_cast<SimpleShape&>(mrShape).setService(u"com.sun.star.text.TextFrame"_ustr); |
634 | 4.77k | return new ClientDataContext( *this, mrShapeModel.createClientData(), rAttribs ); |
635 | 0 | case VMLPPT_TOKEN( textdata ): |
636 | | // Force RectangleShape, this is ugly :( |
637 | | // and is there because of the lines above which change it to TextFrame |
638 | 0 | dynamic_cast< SimpleShape& >( mrShape ).setService( |
639 | 0 | u"com.sun.star.drawing.RectangleShape"_ustr); |
640 | 0 | mrShapeModel.maLegacyDiagramPath = getFragmentPathFromRelId(rAttribs.getStringDefaulted(XML_id)); |
641 | 0 | break; |
642 | 3 | case O_TOKEN( signatureline ): |
643 | 3 | mrShapeModel.mbIsSignatureLine = true; |
644 | 3 | mrShapeModel.maSignatureId = rAttribs.getStringDefaulted(XML_id); |
645 | 3 | mrShapeModel.maSignatureLineSuggestedSignerName |
646 | 3 | = rAttribs.getStringDefaulted(O_TOKEN(suggestedsigner)); |
647 | 3 | mrShapeModel.maSignatureLineSuggestedSignerTitle |
648 | 3 | = rAttribs.getStringDefaulted(O_TOKEN(suggestedsigner2)); |
649 | 3 | mrShapeModel.maSignatureLineSuggestedSignerEmail |
650 | 3 | = rAttribs.getStringDefaulted(O_TOKEN(suggestedsigneremail)); |
651 | 3 | mrShapeModel.maSignatureLineSigningInstructions |
652 | 3 | = rAttribs.getStringDefaulted(XML_signinginstructions); |
653 | | // we used to save this with an "o:" prefix, which is incorrect, so to support older |
654 | | // data, try the older way if the correct way is empty. |
655 | 3 | if (mrShapeModel.maSignatureLineSigningInstructions.isEmpty()) |
656 | 3 | mrShapeModel.maSignatureLineSigningInstructions |
657 | 3 | = rAttribs.getStringDefaulted(O_TOKEN(signinginstructions)); |
658 | 3 | mrShapeModel.mbSignatureLineShowSignDate = ConversionHelper::decodeBool( |
659 | 3 | rAttribs.getString(XML_showsigndate, u"t"_ustr)); // default is true |
660 | 3 | mrShapeModel.mbSignatureLineCanAddComment = ConversionHelper::decodeBool( |
661 | 3 | rAttribs.getString(XML_allowcomments, u"f"_ustr)); // default is false |
662 | 3 | break; |
663 | 30 | case O_TOKEN( lock ): |
664 | | // TODO |
665 | 30 | break; |
666 | 28.1k | } |
667 | | // handle remaining stuff in base class |
668 | 17.5k | return ShapeTypeContext::onCreateContext( nElement, rAttribs ); |
669 | 28.1k | } |
670 | | void ShapeContext::setWriterShape() |
671 | 5 | { |
672 | 5 | mrShape.setTextBox(true); |
673 | 5 | } |
674 | | |
675 | | void ShapeContext::setPoints(std::u16string_view rPoints) |
676 | 7.22k | { |
677 | 7.22k | mrShapeModel.maPoints.clear(); |
678 | 7.22k | sal_Int32 nIndex = 0; |
679 | | |
680 | 14.4k | while (nIndex >= 0) |
681 | 7.22k | { |
682 | 7.22k | sal_Int32 nX = ConversionHelper::decodeMeasureToTwip( |
683 | 7.22k | mrShape.getDrawing().getFilter().getGraphicHelper(), o3tl::getToken(rPoints, 0, ',', nIndex), |
684 | 7.22k | 0, true, true); |
685 | 7.22k | sal_Int32 nY = ConversionHelper::decodeMeasureToTwip( |
686 | 7.22k | mrShape.getDrawing().getFilter().getGraphicHelper(), o3tl::getToken(rPoints, 0, ',', nIndex), |
687 | 7.22k | 0, false, true); |
688 | 7.22k | mrShapeModel.maPoints.emplace_back(nX, nY); |
689 | 7.22k | } |
690 | | // VML polyline has no size in its style attribute. Word writes the size to attribute |
691 | | // coordsize with values in twip but without unit. For others we get size from points. |
692 | 7.22k | if (!mrShape.getTypeModel().maWidth.isEmpty() || !mrShape.getTypeModel().maHeight.isEmpty()) |
693 | 6.92k | return; |
694 | | |
695 | 305 | if (mrShape.getTypeModel().moCoordSize.has_value()) |
696 | 0 | { |
697 | 0 | double fWidth = mrShape.getTypeModel().moCoordSize.value().first; |
698 | 0 | fWidth = o3tl::convert(fWidth, o3tl::Length::twip, o3tl::Length::pt); |
699 | 0 | double fHeight = mrShape.getTypeModel().moCoordSize.value().second; |
700 | 0 | fHeight = o3tl::convert(fHeight, o3tl::Length::twip, o3tl::Length::pt); |
701 | 0 | mrShape.getTypeModel().maWidth = OUString::number(fWidth) + "pt"; |
702 | 0 | mrShape.getTypeModel().maHeight = OUString::number(fHeight) + "pt"; |
703 | 0 | } |
704 | 305 | else if (mrShapeModel.maPoints.size()) |
705 | 305 | { |
706 | 305 | double fMinX = mrShapeModel.maPoints[0].X; |
707 | 305 | double fMaxX = mrShapeModel.maPoints[0].X; |
708 | 305 | double fMinY = mrShapeModel.maPoints[0].Y; |
709 | 305 | double fMaxY = mrShapeModel.maPoints[0].Y; |
710 | 305 | for (const auto& rPoint : mrShapeModel.maPoints) |
711 | 305 | { |
712 | 305 | if (rPoint.X < fMinX) |
713 | 0 | fMinX = rPoint.X; |
714 | 305 | else if (rPoint.X > fMaxX) |
715 | 0 | fMaxX = rPoint.X; |
716 | 305 | if (rPoint.Y < fMinY) |
717 | 0 | fMinY = rPoint.Y; |
718 | 305 | else if (rPoint.Y > fMaxY) |
719 | 0 | fMaxY = rPoint.Y; |
720 | 305 | } |
721 | 305 | mrShape.getTypeModel().maWidth |
722 | 305 | = OUString::number( |
723 | 305 | o3tl::convert(fMaxX - fMinX, o3tl::Length::twip, o3tl::Length::pt)) |
724 | 305 | + "pt"; |
725 | 305 | mrShape.getTypeModel().maHeight |
726 | 305 | = OUString::number( |
727 | 305 | o3tl::convert(fMaxY - fMinY, o3tl::Length::twip, o3tl::Length::pt)) |
728 | 305 | + "pt"; |
729 | | // Set moCoordSize, otherwise default (1000,1000) is used. |
730 | 305 | mrShape.getTypeModel().moCoordSize = |
731 | 305 | Int32Pair(basegfx::fround(fMaxX - fMinX), basegfx::fround(fMaxY - fMinY)); |
732 | 305 | } |
733 | 305 | } |
734 | | |
735 | | void ShapeContext::setFrom( const OUString& rPoints ) |
736 | 7.22k | { |
737 | 7.22k | if (!rPoints.isEmpty()) |
738 | 271 | mrShapeModel.maFrom = rPoints; |
739 | 7.22k | } |
740 | | |
741 | | void ShapeContext::setTo( const OUString& rPoints ) |
742 | 7.22k | { |
743 | 7.22k | if (!rPoints.isEmpty()) |
744 | 271 | mrShapeModel.maTo = rPoints; |
745 | 7.22k | } |
746 | | |
747 | | void ShapeContext::setControl1( const OUString& rPoints ) |
748 | 7.22k | { |
749 | 7.22k | if (!rPoints.isEmpty()) |
750 | 0 | mrShapeModel.maControl1 = rPoints; |
751 | 7.22k | } |
752 | | |
753 | | void ShapeContext::setControl2( const OUString& rPoints ) |
754 | 7.22k | { |
755 | 7.22k | if (!rPoints.isEmpty()) |
756 | 0 | mrShapeModel.maControl2 = rPoints; |
757 | 7.22k | } |
758 | | void ShapeContext::setVmlPath( const OUString& rPath ) |
759 | 7.22k | { |
760 | 7.22k | if (!rPath.isEmpty()) |
761 | 462 | mrShapeModel.maVmlPath = rPath; |
762 | 7.22k | } |
763 | | |
764 | | void ShapeContext::setHyperlink( const OUString& rHyperlink ) |
765 | 7.22k | { |
766 | 7.22k | if (!rHyperlink.isEmpty()) |
767 | 5 | mrShapeModel.maHyperlink = rHyperlink; |
768 | 7.22k | } |
769 | | |
770 | | GroupShapeContext::GroupShapeContext(ContextHandler2Helper const& rParent, |
771 | | const std::shared_ptr<GroupShape>& pShape, |
772 | | const AttributeList& rAttribs) |
773 | 110 | : ShapeContext(rParent, pShape, rAttribs) |
774 | 110 | , mrShapes(pShape->getChildren()) |
775 | 110 | { |
776 | 110 | } |
777 | | |
778 | | ContextHandlerRef GroupShapeContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) |
779 | 690 | { |
780 | | // try to create a context of an embedded shape |
781 | 690 | ContextHandlerRef xContext = createShapeContext( *this, mrShapes, nElement, rAttribs ); |
782 | | // handle remaining stuff of this shape in base class |
783 | 690 | return xContext ? xContext : ShapeContext::onCreateContext( nElement, rAttribs ); |
784 | 690 | } |
785 | | |
786 | | RectangleShapeContext::RectangleShapeContext(ContextHandler2Helper const& rParent, |
787 | | const AttributeList& rAttribs, |
788 | | const std::shared_ptr<RectangleShape>& pShape) |
789 | 562 | : ShapeContext(rParent, pShape, rAttribs) |
790 | 562 | { |
791 | 562 | } |
792 | | |
793 | | ContextHandlerRef RectangleShapeContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) |
794 | 673 | { |
795 | | // The parent class's context is fine |
796 | 673 | return ShapeContext::onCreateContext( nElement, rAttribs ); |
797 | 673 | } |
798 | | |
799 | | ControlShapeContext::ControlShapeContext( ::oox::core::ContextHandler2Helper const & rParent, ShapeContainer& rShapes, const AttributeList& rAttribs ) |
800 | 0 | : ShapeContextBase (rParent) |
801 | 0 | { |
802 | 0 | ::oox::vml::ControlInfo aInfo; |
803 | 0 | aInfo.maShapeId = rAttribs.getXString( W_TOKEN( shapeid ), OUString() ); |
804 | 0 | aInfo.maFragmentPath = getFragmentPathFromRelId(rAttribs.getStringDefaulted( R_TOKEN(id))); |
805 | 0 | aInfo.maName = rAttribs.getStringDefaulted( W_TOKEN( name )); |
806 | 0 | aInfo.mbTextContentShape = true; |
807 | 0 | rShapes.getDrawing().registerControl(aInfo); |
808 | 0 | } |
809 | | |
810 | | } // namespace oox |
811 | | |
812 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |