/src/libreoffice/oox/source/export/vmlexport.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 <config_folders.h> |
21 | | #include <rtl/bootstrap.hxx> |
22 | | #include <svl/itemset.hxx> |
23 | | #include <oox/export/drawingml.hxx> |
24 | | #include <oox/export/vmlexport.hxx> |
25 | | #include <sax/fastattribs.hxx> |
26 | | |
27 | | #include <oox/token/tokens.hxx> |
28 | | |
29 | | #include <rtl/strbuf.hxx> |
30 | | #include <rtl/ustring.hxx> |
31 | | #include <sal/log.hxx> |
32 | | |
33 | | #include <tools/stream.hxx> |
34 | | #include <comphelper/sequenceashashmap.hxx> |
35 | | #include <svx/msdffdef.hxx> |
36 | | #include <svx/svdotext.hxx> |
37 | | #include <svx/svdograf.hxx> |
38 | | #include <svx/sdmetitm.hxx> |
39 | | #include <utility> |
40 | | #include <vcl/cvtgrf.hxx> |
41 | | #include <filter/msfilter/msdffimp.hxx> |
42 | | #include <filter/msfilter/util.hxx> |
43 | | #include <filter/msfilter/escherex.hxx> |
44 | | #include <o3tl/string_view.hxx> |
45 | | #include <drawingml/fontworkhelpers.hxx> |
46 | | |
47 | | #include <com/sun/star/beans/XPropertySet.hpp> |
48 | | #include <com/sun/star/beans/XPropertySetInfo.hpp> |
49 | | #include <com/sun/star/drawing/XShape.hpp> |
50 | | #include <com/sun/star/text/HoriOrientation.hpp> |
51 | | #include <com/sun/star/text/VertOrientation.hpp> |
52 | | #include <com/sun/star/text/RelOrientation.hpp> |
53 | | #include <com/sun/star/text/WritingMode2.hpp> |
54 | | #include <com/sun/star/text/XTextFrame.hpp> |
55 | | |
56 | | #include <cstdio> |
57 | | |
58 | | using namespace sax_fastparser; |
59 | | using namespace oox::vml; |
60 | | using namespace com::sun::star; |
61 | | |
62 | | const sal_Int32 Tag_Container = 44444; |
63 | | const sal_Int32 Tag_Commit = 44445; |
64 | | |
65 | | VMLExport::VMLExport( ::sax_fastparser::FSHelperPtr pSerializer, VMLTextExport* pTextExport ) |
66 | 0 | : EscherEx( std::make_shared<EscherExGlobal>(), nullptr, /*bOOXML=*/true ) |
67 | 0 | , m_pSerializer(std::move( pSerializer )) |
68 | 0 | , m_pTextExport( pTextExport ) |
69 | 0 | , m_eHOri( 0 ) |
70 | 0 | , m_eVOri( 0 ) |
71 | 0 | , m_eHRel( 0 ) |
72 | 0 | , m_eVRel( 0 ) |
73 | 0 | , m_bInline( false ) |
74 | 0 | , m_pSdrObject( nullptr ) |
75 | | , m_nShapeType( ESCHER_ShpInst_Nil ) |
76 | 0 | , m_nShapeFlags(ShapeFlag::NONE) |
77 | 0 | , m_ShapeStyle( 200 ) |
78 | 0 | , m_aShapeTypeWritten( ESCHER_ShpInst_COUNT ) |
79 | 0 | , m_bSkipwzName( false ) |
80 | 0 | , m_bUseHashMarkForType( false ) |
81 | 0 | , m_bOverrideShapeIdGeneration( false ) |
82 | 0 | , m_nShapeIDCounter( 0 ) |
83 | 0 | { |
84 | 0 | mnGroupLevel = 1; |
85 | 0 | } |
86 | | |
87 | | void VMLExport::SetFS( const ::sax_fastparser::FSHelperPtr& pSerializer ) |
88 | 0 | { |
89 | 0 | m_pSerializer = pSerializer; |
90 | 0 | } |
91 | | |
92 | | VMLExport::~VMLExport() |
93 | 0 | { |
94 | 0 | } |
95 | | |
96 | | void VMLExport::OpenContainer( sal_uInt16 nEscherContainer, int nRecInstance ) |
97 | 0 | { |
98 | 0 | EscherEx::OpenContainer( nEscherContainer, nRecInstance ); |
99 | |
|
100 | 0 | if ( nEscherContainer != ESCHER_SpContainer ) |
101 | 0 | return; |
102 | | |
103 | | // opening a shape container |
104 | 0 | SAL_WARN_IF(m_nShapeType != ESCHER_ShpInst_Nil, "oox.vml", "opening shape inside of a shape!"); |
105 | 0 | m_nShapeType = ESCHER_ShpInst_Nil; |
106 | 0 | m_ShapePath.clear(); |
107 | 0 | m_pShapeAttrList = FastSerializerHelper::createAttrList(); |
108 | |
|
109 | 0 | m_ShapeStyle.setLength(0); |
110 | 0 | m_ShapeStyle.ensureCapacity(200); |
111 | | |
112 | | // postpone the output so that we are able to write even the elements |
113 | | // that we learn inside Commit() |
114 | 0 | m_pSerializer->mark(Tag_Container); |
115 | 0 | } |
116 | | |
117 | | void VMLExport::CloseContainer() |
118 | 0 | { |
119 | 0 | if ( mRecTypes.back() == ESCHER_SpContainer ) |
120 | 0 | { |
121 | | // write the shape now when we have all the info |
122 | 0 | sal_Int32 nShapeElement = StartShape(); |
123 | |
|
124 | 0 | m_pSerializer->mergeTopMarks(Tag_Container); |
125 | |
|
126 | 0 | EndShape( nShapeElement ); |
127 | | |
128 | | // cleanup |
129 | 0 | m_nShapeType = ESCHER_ShpInst_Nil; |
130 | 0 | m_pShapeAttrList = nullptr; |
131 | 0 | m_ShapePath.clear(); |
132 | 0 | } |
133 | |
|
134 | 0 | EscherEx::CloseContainer(); |
135 | 0 | } |
136 | | |
137 | | sal_uInt32 VMLExport::EnterGroup( const OUString& rShapeName, const tools::Rectangle* pRect ) |
138 | 0 | { |
139 | 0 | sal_uInt32 nShapeId = GenerateShapeId(); |
140 | |
|
141 | 0 | OStringBuffer aStyle( 200 ); |
142 | 0 | rtl::Reference<FastAttributeList> pAttrList = FastSerializerHelper::createAttrList(); |
143 | |
|
144 | 0 | pAttrList->add( XML_id, ShapeIdString( nShapeId ) ); |
145 | |
|
146 | 0 | if ( rShapeName.getLength() ) |
147 | 0 | pAttrList->add( XML_alt, rShapeName ); |
148 | |
|
149 | 0 | bool rbAbsolutePos = true; |
150 | | //editAs |
151 | 0 | OUString rEditAs = EscherEx::GetEditAs(); |
152 | 0 | if (!rEditAs.isEmpty()) |
153 | 0 | { |
154 | 0 | pAttrList->add(XML_editas, rEditAs); |
155 | 0 | rbAbsolutePos = false; |
156 | 0 | } |
157 | | |
158 | | // style |
159 | 0 | if ( pRect ) |
160 | 0 | AddRectangleDimensions( aStyle, *pRect, rbAbsolutePos ); |
161 | |
|
162 | 0 | if ( !aStyle.isEmpty() ) |
163 | 0 | pAttrList->add( XML_style, aStyle ); |
164 | | |
165 | | // coordorigin/coordsize |
166 | 0 | if ( pRect && ( mnGroupLevel == 1 ) ) |
167 | 0 | { |
168 | 0 | pAttrList->add( XML_coordorigin, |
169 | 0 | OString::number( pRect->Left() ) + "," + OString::number( pRect->Top() ) ); |
170 | |
|
171 | 0 | pAttrList->add( XML_coordsize, |
172 | 0 | OString::number( pRect->Right() - pRect->Left() ) + "," + |
173 | 0 | OString::number( pRect->Bottom() - pRect->Top() ) ); |
174 | 0 | } |
175 | |
|
176 | 0 | m_pSerializer->startElementNS( XML_v, XML_group, pAttrList ); |
177 | |
|
178 | 0 | mnGroupLevel++; |
179 | 0 | return nShapeId; |
180 | 0 | } |
181 | | |
182 | | void VMLExport::LeaveGroup() |
183 | 0 | { |
184 | 0 | --mnGroupLevel; |
185 | 0 | m_pSerializer->endElementNS( XML_v, XML_group ); |
186 | 0 | } |
187 | | |
188 | | void VMLExport::AddShape( sal_uInt32 nShapeType, ShapeFlag nShapeFlags, sal_uInt32 nShapeId ) |
189 | 0 | { |
190 | 0 | m_nShapeType = nShapeType; |
191 | 0 | m_nShapeFlags = nShapeFlags; |
192 | |
|
193 | 0 | m_sShapeId = ShapeIdString( nShapeId ); |
194 | 0 | if (m_sShapeId.startsWith("_x0000_")) |
195 | 0 | { |
196 | | // xml_id must be set elsewhere. The id is critical for matching VBA macros etc, |
197 | | // and the spid is critical to link to the shape number elsewhere. |
198 | 0 | m_pShapeAttrList->addNS( XML_o, XML_spid, m_sShapeId ); |
199 | 0 | } |
200 | 0 | else if (IsWaterMarkShape(m_pSdrObject->GetName())) |
201 | 0 | { |
202 | | // Shape is a watermark object - keep the original shape's name |
203 | | // because Microsoft detects if it is a watermark by the actual name |
204 | 0 | m_pShapeAttrList->add( XML_id, m_pSdrObject->GetName() ); |
205 | | // also ('o:spid') |
206 | 0 | m_pShapeAttrList->addNS( XML_o, XML_spid, m_sShapeId ); |
207 | 0 | } |
208 | 0 | else |
209 | 0 | { |
210 | 0 | m_pShapeAttrList->add(XML_id, m_sShapeId); |
211 | 0 | } |
212 | 0 | } |
213 | | |
214 | | bool VMLExport::IsWaterMarkShape(std::u16string_view rStr) |
215 | 0 | { |
216 | 0 | if (rStr.empty() ) return false; |
217 | | |
218 | 0 | return o3tl::starts_with(rStr, u"PowerPlusWaterMarkObject") || o3tl::starts_with(rStr, u"WordPictureWatermark"); |
219 | 0 | } |
220 | | |
221 | | void VMLExport::OverrideShapeIDGen(bool bOverrideShapeIdGen, const OString& sShapeIDPrefix) |
222 | 0 | { |
223 | 0 | m_bOverrideShapeIdGeneration = bOverrideShapeIdGen; |
224 | 0 | if(bOverrideShapeIdGen) |
225 | 0 | { |
226 | 0 | assert(!sShapeIDPrefix.isEmpty()); |
227 | 0 | m_sShapeIDPrefix = sShapeIDPrefix; |
228 | 0 | } |
229 | 0 | else |
230 | 0 | m_sShapeIDPrefix.clear(); |
231 | 0 | } |
232 | | |
233 | | static void impl_AddArrowHead( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue ) |
234 | 0 | { |
235 | 0 | if ( !pAttrList ) |
236 | 0 | return; |
237 | | |
238 | 0 | const char *pArrowHead = nullptr; |
239 | 0 | switch ( nValue ) |
240 | 0 | { |
241 | 0 | case ESCHER_LineNoEnd: pArrowHead = "none"; break; |
242 | 0 | case ESCHER_LineArrowEnd: pArrowHead = "block"; break; |
243 | 0 | case ESCHER_LineArrowStealthEnd: pArrowHead = "classic"; break; |
244 | 0 | case ESCHER_LineArrowDiamondEnd: pArrowHead = "diamond"; break; |
245 | 0 | case ESCHER_LineArrowOvalEnd: pArrowHead = "oval"; break; |
246 | 0 | case ESCHER_LineArrowOpenEnd: pArrowHead = "open"; break; |
247 | 0 | } |
248 | | |
249 | 0 | if ( pArrowHead ) |
250 | 0 | pAttrList->add( nElement, pArrowHead ); |
251 | 0 | } |
252 | | |
253 | | static void impl_AddArrowLength( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue ) |
254 | 0 | { |
255 | 0 | if ( !pAttrList ) |
256 | 0 | return; |
257 | | |
258 | 0 | const char *pArrowLength = nullptr; |
259 | 0 | switch ( nValue ) |
260 | 0 | { |
261 | 0 | case ESCHER_LineShortArrow: pArrowLength = "short"; break; |
262 | 0 | case ESCHER_LineMediumLenArrow: pArrowLength = "medium"; break; |
263 | 0 | case ESCHER_LineLongArrow: pArrowLength = "long"; break; |
264 | 0 | } |
265 | | |
266 | 0 | if ( pArrowLength ) |
267 | 0 | pAttrList->add( nElement, pArrowLength ); |
268 | 0 | } |
269 | | |
270 | | static void impl_AddArrowWidth( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue ) |
271 | 0 | { |
272 | 0 | if ( !pAttrList ) |
273 | 0 | return; |
274 | | |
275 | 0 | const char *pArrowWidth = nullptr; |
276 | 0 | switch ( nValue ) |
277 | 0 | { |
278 | 0 | case ESCHER_LineNarrowArrow: pArrowWidth = "narrow"; break; |
279 | 0 | case ESCHER_LineMediumWidthArrow: pArrowWidth = "medium"; break; |
280 | 0 | case ESCHER_LineWideArrow: pArrowWidth = "wide"; break; |
281 | 0 | } |
282 | | |
283 | 0 | if ( pArrowWidth ) |
284 | 0 | pAttrList->add( nElement, pArrowWidth ); |
285 | 0 | } |
286 | | |
287 | | static void impl_AddBool( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, bool bValue ) |
288 | 0 | { |
289 | 0 | if ( !pAttrList ) |
290 | 0 | return; |
291 | | |
292 | 0 | pAttrList->add( nElement, bValue? "t": "f" ); |
293 | 0 | } |
294 | | |
295 | | static void impl_AddColor( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nColor ) |
296 | 0 | { |
297 | 0 | SAL_WARN_IF( nColor & 0xFF000000 , "oox.vml" , "TODO: this is not a RGB value!"); |
298 | | |
299 | 0 | if ( !pAttrList || ( nColor & 0xFF000000 ) ) |
300 | 0 | return; |
301 | | |
302 | 0 | nColor = ( ( nColor & 0xFF ) << 16 ) + ( nColor & 0xFF00 ) + ( ( nColor & 0xFF0000 ) >> 16 ); |
303 | |
|
304 | 0 | const char *pColor = nullptr; |
305 | 0 | char pRgbColor[10]; |
306 | 0 | switch ( nColor ) |
307 | 0 | { |
308 | 0 | case 0x000000: pColor = "black"; break; |
309 | 0 | case 0xC0C0C0: pColor = "silver"; break; |
310 | 0 | case 0x808080: pColor = "gray"; break; |
311 | 0 | case 0xFFFFFF: pColor = "white"; break; |
312 | 0 | case 0x800000: pColor = "maroon"; break; |
313 | 0 | case 0xFF0000: pColor = "red"; break; |
314 | 0 | case 0x800080: pColor = "purple"; break; |
315 | 0 | case 0xFF00FF: pColor = "fuchsia"; break; |
316 | 0 | case 0x008000: pColor = "green"; break; |
317 | 0 | case 0x00FF00: pColor = "lime"; break; |
318 | 0 | case 0x808000: pColor = "olive"; break; |
319 | 0 | case 0xFFFF00: pColor = "yellow"; break; |
320 | 0 | case 0x000080: pColor = "navy"; break; |
321 | 0 | case 0x0000FF: pColor = "blue"; break; |
322 | 0 | case 0x008080: pColor = "teal"; break; |
323 | 0 | case 0x00FFFF: pColor = "aqua"; break; |
324 | 0 | default: |
325 | 0 | { |
326 | 0 | snprintf( pRgbColor, sizeof( pRgbColor ), "#%06x", static_cast< unsigned int >( nColor ) ); // not too handy to use OString::valueOf() here :-( |
327 | 0 | pColor = pRgbColor; |
328 | 0 | } |
329 | 0 | break; |
330 | 0 | } |
331 | | |
332 | 0 | pAttrList->add( nElement, pColor ); |
333 | 0 | } |
334 | | |
335 | | static void impl_AddInt( sax_fastparser::FastAttributeList *pAttrList, sal_Int32 nElement, sal_uInt32 nValue ) |
336 | 0 | { |
337 | 0 | if ( !pAttrList ) |
338 | 0 | return; |
339 | | |
340 | 0 | pAttrList->add( nElement, OString::number( nValue ) ); |
341 | 0 | } |
342 | | |
343 | | static sal_uInt16 impl_GetUInt16( const sal_uInt8* &pVal ) |
344 | 0 | { |
345 | 0 | sal_uInt16 nRet = *pVal++; |
346 | 0 | nRet += ( *pVal++ ) << 8; |
347 | 0 | return nRet; |
348 | 0 | } |
349 | | |
350 | | static sal_Int32 impl_GetPointComponent( const sal_uInt8* &pVal, sal_uInt16 nPointSize ) |
351 | 0 | { |
352 | 0 | sal_Int32 nRet = 0; |
353 | 0 | if ( ( nPointSize == 0xfff0 ) || ( nPointSize == 4 ) ) |
354 | 0 | { |
355 | 0 | sal_uInt16 nUnsigned = *pVal++; |
356 | 0 | nUnsigned += ( *pVal++ ) << 8; |
357 | |
|
358 | 0 | nRet = sal_Int16( nUnsigned ); |
359 | 0 | } |
360 | 0 | else if ( nPointSize == 8 ) |
361 | 0 | { |
362 | 0 | sal_uInt32 nUnsigned = *pVal++; |
363 | 0 | nUnsigned += ( *pVal++ ) << 8; |
364 | 0 | nUnsigned += ( *pVal++ ) << 16; |
365 | 0 | nUnsigned += ( *pVal++ ) << 24; |
366 | |
|
367 | 0 | nRet = nUnsigned; |
368 | 0 | } |
369 | |
|
370 | 0 | return nRet; |
371 | 0 | } |
372 | | |
373 | | void VMLExport::AddSdrObjectVMLObject( const SdrObject& rObj) |
374 | 0 | { |
375 | 0 | m_pSdrObject = &rObj; |
376 | 0 | } |
377 | | void VMLExport::Commit( EscherPropertyContainer& rProps, const tools::Rectangle& rRect ) |
378 | 0 | { |
379 | 0 | if ( m_nShapeType == ESCHER_ShpInst_Nil ) |
380 | 0 | return; |
381 | | |
382 | | // postpone the output of the embedded elements so that they are written |
383 | | // inside the shapes |
384 | 0 | m_pSerializer->mark(Tag_Commit); |
385 | | |
386 | | // dimensions |
387 | 0 | if ( m_nShapeType == ESCHER_ShpInst_Line ) |
388 | 0 | AddLineDimensions( rRect ); |
389 | 0 | else |
390 | 0 | { |
391 | 0 | if ( IsWaterMarkShape( m_pSdrObject->GetName() ) ) |
392 | 0 | { |
393 | | // Watermark need some padding to be compatible with MSO |
394 | 0 | tools::Long nPaddingY = 0; |
395 | 0 | const SfxItemSet& rSet = m_pSdrObject->GetMergedItemSet(); |
396 | 0 | if ( const SdrMetricItem* pItem = rSet.GetItem( SDRATTR_TEXT_UPPERDIST ) ) |
397 | 0 | nPaddingY += pItem->GetValue(); |
398 | |
|
399 | 0 | tools::Rectangle aRect( rRect ); |
400 | 0 | aRect.setHeight( aRect.getOpenHeight() + nPaddingY ); |
401 | 0 | AddRectangleDimensions( m_ShapeStyle, aRect ); |
402 | 0 | } |
403 | 0 | else |
404 | 0 | AddRectangleDimensions( m_ShapeStyle, rRect ); |
405 | 0 | } |
406 | | |
407 | | // properties |
408 | | // The numbers of defines ESCHER_Prop_foo and DFF_Prop_foo correspond to the PIDs in |
409 | | // 'Microsoft Office Drawing 97-2007 Binary Format Specification'. |
410 | | // The property values are set by EscherPropertyContainer::CreateCustomShapeProperties() method. |
411 | 0 | bool bAlreadyWritten[ 0xFFF ] = {}; |
412 | 0 | const EscherProperties &rOpts = rProps.GetOpts(); |
413 | 0 | for (auto const& opt : rOpts) |
414 | 0 | { |
415 | 0 | sal_uInt16 nId = ( opt.nPropId & 0x0FFF ); |
416 | |
|
417 | 0 | if ( bAlreadyWritten[ nId ] ) |
418 | 0 | continue; |
419 | | |
420 | 0 | switch ( nId ) |
421 | 0 | { |
422 | 0 | case ESCHER_Prop_WrapText: // 133 |
423 | 0 | { |
424 | 0 | const char *pWrapType = nullptr; |
425 | 0 | switch ( opt.nPropValue ) |
426 | 0 | { |
427 | 0 | case ESCHER_WrapSquare: |
428 | 0 | case ESCHER_WrapByPoints: pWrapType = "square"; break; // these two are equivalent according to the docu |
429 | 0 | case ESCHER_WrapNone: pWrapType = "none"; break; |
430 | 0 | case ESCHER_WrapTopBottom: |
431 | 0 | case ESCHER_WrapThrough: |
432 | 0 | break; // last two are *undefined* in MS-ODRAW, don't exist in VML |
433 | 0 | } |
434 | 0 | if ( pWrapType ) |
435 | 0 | { |
436 | 0 | m_ShapeStyle.append(";mso-wrap-style:"); |
437 | 0 | m_ShapeStyle.append(pWrapType); |
438 | 0 | } |
439 | 0 | } |
440 | 0 | bAlreadyWritten[ ESCHER_Prop_WrapText ] = true; |
441 | 0 | break; |
442 | | |
443 | 0 | case ESCHER_Prop_AnchorText: // 135 |
444 | 0 | { |
445 | 0 | char const* pValue(nullptr); |
446 | 0 | switch (opt.nPropValue) |
447 | 0 | { |
448 | 0 | case ESCHER_AnchorTop: |
449 | 0 | pValue = "top"; |
450 | 0 | break; |
451 | 0 | case ESCHER_AnchorMiddle: |
452 | 0 | pValue = "middle"; |
453 | 0 | break; |
454 | 0 | case ESCHER_AnchorBottom: |
455 | 0 | pValue = "bottom"; |
456 | 0 | break; |
457 | 0 | case ESCHER_AnchorTopCentered: |
458 | 0 | pValue = "top-center"; |
459 | 0 | break; |
460 | 0 | case ESCHER_AnchorMiddleCentered: |
461 | 0 | pValue = "middle-center"; |
462 | 0 | break; |
463 | 0 | case ESCHER_AnchorBottomCentered: |
464 | 0 | pValue = "bottom-center"; |
465 | 0 | break; |
466 | 0 | case ESCHER_AnchorTopBaseline: |
467 | 0 | pValue = "top-baseline"; |
468 | 0 | break; |
469 | 0 | case ESCHER_AnchorBottomBaseline: |
470 | 0 | pValue = "bottom-baseline"; |
471 | 0 | break; |
472 | 0 | case ESCHER_AnchorTopCenteredBaseline: |
473 | 0 | pValue = "top-center-baseline"; |
474 | 0 | break; |
475 | 0 | case ESCHER_AnchorBottomCenteredBaseline: |
476 | 0 | pValue = "bottom-center-baseline"; |
477 | 0 | break; |
478 | 0 | } |
479 | 0 | m_ShapeStyle.append(";v-text-anchor:"); |
480 | 0 | m_ShapeStyle.append(pValue); |
481 | 0 | } |
482 | 0 | break; |
483 | | |
484 | 0 | case ESCHER_Prop_txflTextFlow: // 136 |
485 | 0 | { |
486 | | // at least "bottom-to-top" only has an effect when it's on the v:textbox element, not on v:shape |
487 | 0 | assert(m_TextboxStyle.isEmpty()); |
488 | 0 | switch (opt.nPropValue) |
489 | 0 | { |
490 | 0 | case ESCHER_txflHorzN: |
491 | 0 | m_TextboxStyle.append("layout-flow:horizontal"); |
492 | 0 | break; |
493 | 0 | case ESCHER_txflTtoBA: |
494 | 0 | m_TextboxStyle.append("layout-flow:vertical"); |
495 | 0 | break; |
496 | 0 | case ESCHER_txflBtoT: |
497 | 0 | m_TextboxStyle.append("mso-layout-flow-alt:bottom-to-top"); |
498 | 0 | break; |
499 | 0 | default: |
500 | 0 | assert(false); // unimplemented in escher export |
501 | 0 | break; |
502 | 0 | } |
503 | 0 | } |
504 | 0 | break; |
505 | | |
506 | | // coordorigin |
507 | 0 | case ESCHER_Prop_geoLeft: // 320 |
508 | 0 | case ESCHER_Prop_geoTop: // 321 |
509 | 0 | { |
510 | 0 | sal_uInt32 nLeft = 0, nTop = 0; |
511 | |
|
512 | 0 | if ( nId == ESCHER_Prop_geoLeft ) |
513 | 0 | { |
514 | 0 | nLeft = opt.nPropValue; |
515 | 0 | rProps.GetOpt( ESCHER_Prop_geoTop, nTop ); |
516 | 0 | } |
517 | 0 | else |
518 | 0 | { |
519 | 0 | nTop = opt.nPropValue; |
520 | 0 | rProps.GetOpt( ESCHER_Prop_geoLeft, nLeft ); |
521 | 0 | } |
522 | 0 | if(nTop!=0 && nLeft!=0) |
523 | 0 | m_pShapeAttrList->add( XML_coordorigin, |
524 | 0 | OString::number( nLeft ) + "," + OString::number( nTop ) ); |
525 | 0 | } |
526 | 0 | bAlreadyWritten[ ESCHER_Prop_geoLeft ] = true; |
527 | 0 | bAlreadyWritten[ ESCHER_Prop_geoTop ] = true; |
528 | 0 | break; |
529 | | |
530 | | // coordsize |
531 | 0 | case ESCHER_Prop_geoRight: // 322 |
532 | 0 | case ESCHER_Prop_geoBottom: // 323 |
533 | 0 | { |
534 | 0 | sal_uInt32 nLeft = 0, nRight = 0, nTop = 0, nBottom = 0; |
535 | 0 | rProps.GetOpt( ESCHER_Prop_geoLeft, nLeft ); |
536 | 0 | rProps.GetOpt( ESCHER_Prop_geoTop, nTop ); |
537 | |
|
538 | 0 | if ( nId == ESCHER_Prop_geoRight ) |
539 | 0 | { |
540 | 0 | nRight = opt.nPropValue; |
541 | 0 | rProps.GetOpt( ESCHER_Prop_geoBottom, nBottom ); |
542 | 0 | } |
543 | 0 | else |
544 | 0 | { |
545 | 0 | nBottom = opt.nPropValue; |
546 | 0 | rProps.GetOpt( ESCHER_Prop_geoRight, nRight ); |
547 | 0 | } |
548 | |
|
549 | 0 | if(nBottom!=0 && nRight!=0 ) |
550 | 0 | m_pShapeAttrList->add( XML_coordsize, |
551 | 0 | OString::number( nRight - nLeft ) + "," + OString::number( nBottom - nTop ) ); |
552 | 0 | } |
553 | 0 | bAlreadyWritten[ ESCHER_Prop_geoRight ] = true; |
554 | 0 | bAlreadyWritten[ ESCHER_Prop_geoBottom ] = true; |
555 | 0 | break; |
556 | | |
557 | 0 | case ESCHER_Prop_pVertices: // 325 |
558 | 0 | case ESCHER_Prop_pSegmentInfo: // 326 |
559 | 0 | { |
560 | 0 | EscherPropSortStruct aVertices; |
561 | 0 | EscherPropSortStruct aSegments; |
562 | |
|
563 | 0 | if ( rProps.GetOpt( ESCHER_Prop_pVertices, aVertices ) && |
564 | 0 | rProps.GetOpt( ESCHER_Prop_pSegmentInfo, aSegments ) ) |
565 | 0 | { |
566 | 0 | const sal_uInt8 *pVerticesIt = aVertices.nProp.data() + 6; |
567 | 0 | const sal_uInt8 *pSegmentIt = aSegments.nProp.data(); |
568 | 0 | OStringBuffer aPath( 512 ); |
569 | |
|
570 | 0 | sal_uInt16 nPointSize = aVertices.nProp[4] + ( aVertices.nProp[5] << 8 ); |
571 | | |
572 | | // number of segments |
573 | 0 | sal_uInt16 nSegments = impl_GetUInt16( pSegmentIt ); |
574 | 0 | pSegmentIt += 4; |
575 | |
|
576 | 0 | for ( ; nSegments; --nSegments ) |
577 | 0 | { |
578 | 0 | sal_uInt16 nSeg = impl_GetUInt16( pSegmentIt ); |
579 | | |
580 | | // The segment type is stored in the upper 3 bits |
581 | | // and segment count is stored in the lower 13 |
582 | | // bits. |
583 | 0 | unsigned char nSegmentType = (nSeg & 0xE000) >> 13; |
584 | 0 | unsigned short nSegmentCount = nSeg & 0x03FF; |
585 | |
|
586 | 0 | switch (nSegmentType) |
587 | 0 | { |
588 | 0 | case msopathMoveTo: |
589 | 0 | { |
590 | 0 | sal_Int32 nX = impl_GetPointComponent( pVerticesIt, nPointSize ); |
591 | 0 | sal_Int32 nY = impl_GetPointComponent( pVerticesIt, nPointSize ); |
592 | 0 | if (nX >= 0 && nY >= 0 ) |
593 | 0 | aPath.append( "m" + OString::number( nX ) + "," + OString::number( nY ) ); |
594 | 0 | break; |
595 | 0 | } |
596 | 0 | case msopathClientEscape: |
597 | 0 | break; |
598 | 0 | case msopathEscape: |
599 | 0 | { |
600 | | // If the segment type is msopathEscape, the lower 13 bits are |
601 | | // divided in a 5 bit escape code and 8 bit |
602 | | // vertex count (not segment count!) |
603 | 0 | unsigned char nEscapeCode = (nSegmentCount & 0x1F00) >> 8; |
604 | 0 | unsigned char nVertexCount = nSegmentCount & 0x00FF; |
605 | 0 | pVerticesIt += nVertexCount; |
606 | |
|
607 | 0 | switch (nEscapeCode) |
608 | 0 | { |
609 | 0 | case 0xa: // nofill |
610 | 0 | aPath.append( "nf" ); |
611 | 0 | break; |
612 | 0 | case 0xb: // nostroke |
613 | 0 | aPath.append( "ns" ); |
614 | 0 | break; |
615 | 0 | } |
616 | | |
617 | 0 | break; |
618 | 0 | } |
619 | 0 | case msopathLineTo: |
620 | 0 | for (unsigned short i = 0; i < nSegmentCount; ++i) |
621 | 0 | { |
622 | 0 | sal_Int32 nX = impl_GetPointComponent( pVerticesIt, nPointSize ); |
623 | 0 | sal_Int32 nY = impl_GetPointComponent( pVerticesIt, nPointSize ); |
624 | 0 | aPath.append( "l" + OString::number( nX ) + "," + OString::number( nY ) ); |
625 | 0 | } |
626 | 0 | break; |
627 | 0 | case msopathCurveTo: |
628 | 0 | for (unsigned short i = 0; i < nSegmentCount; ++i) |
629 | 0 | { |
630 | 0 | sal_Int32 nX1 = impl_GetPointComponent( pVerticesIt, nPointSize ); |
631 | 0 | sal_Int32 nY1 = impl_GetPointComponent( pVerticesIt, nPointSize ); |
632 | 0 | sal_Int32 nX2 = impl_GetPointComponent( pVerticesIt, nPointSize ); |
633 | 0 | sal_Int32 nY2 = impl_GetPointComponent( pVerticesIt, nPointSize ); |
634 | 0 | sal_Int32 nX3 = impl_GetPointComponent( pVerticesIt, nPointSize ); |
635 | 0 | sal_Int32 nY3 = impl_GetPointComponent( pVerticesIt, nPointSize ); |
636 | 0 | aPath.append( "c" + OString::number( nX1 ) + "," + OString::number( nY1 ) + "," + |
637 | 0 | OString::number( nX2 ) + "," + OString::number( nY2 ) + "," + |
638 | 0 | OString::number( nX3 ) + "," + OString::number( nY3 ) ); |
639 | 0 | } |
640 | 0 | break; |
641 | 0 | case msopathClose: |
642 | 0 | aPath.append( "x" ); |
643 | 0 | break; |
644 | 0 | case msopathEnd: |
645 | 0 | aPath.append( "e" ); |
646 | 0 | break; |
647 | 0 | default: |
648 | 0 | SAL_WARN("oox", "Totally b0rked"); |
649 | 0 | break; |
650 | 0 | case msopathInvalid: |
651 | 0 | SAL_WARN("oox", "Invalid - should never be found"); |
652 | 0 | break; |
653 | 0 | } |
654 | 0 | } |
655 | | // We set the path attribute in StartShape() because only then do we know what shape we are writing. |
656 | 0 | OString pathString = aPath.makeStringAndClear(); |
657 | 0 | if ( !pathString.isEmpty() && pathString != "xe" ) |
658 | 0 | m_ShapePath = std::move(pathString); |
659 | 0 | } |
660 | 0 | else |
661 | 0 | SAL_WARN("oox.vml", "unhandled shape path, missing either pVertices or pSegmentInfo."); |
662 | 0 | } |
663 | 0 | bAlreadyWritten[ ESCHER_Prop_pVertices ] = true; |
664 | 0 | bAlreadyWritten[ ESCHER_Prop_pSegmentInfo ] = true; |
665 | 0 | break; |
666 | | |
667 | 0 | case ESCHER_Prop_fillType: // 384 |
668 | 0 | case ESCHER_Prop_fillColor: // 385 |
669 | 0 | case ESCHER_Prop_fillBackColor: // 387 |
670 | 0 | case ESCHER_Prop_fillBlip: // 390 |
671 | 0 | case ESCHER_Prop_fNoFillHitTest: // 447 |
672 | 0 | case ESCHER_Prop_fillOpacity: // 386 |
673 | 0 | { |
674 | 0 | sal_uInt32 nValue; |
675 | 0 | rtl::Reference<sax_fastparser::FastAttributeList> pAttrList |
676 | 0 | = FastSerializerHelper::createAttrList(); |
677 | |
|
678 | 0 | bool imageData = false; |
679 | 0 | EscherPropSortStruct aStruct; |
680 | 0 | const SdrGrafObj* pSdrGrafObj = dynamic_cast<const SdrGrafObj*>(m_pSdrObject); |
681 | |
|
682 | 0 | if (pSdrGrafObj && pSdrGrafObj->isSignatureLine() && m_pTextExport) |
683 | 0 | { |
684 | 0 | rtl::Reference<sax_fastparser::FastAttributeList> pAttrListSignatureLine |
685 | 0 | = FastSerializerHelper::createAttrList(); |
686 | 0 | pAttrListSignatureLine->add(XML_issignatureline, "t"); |
687 | 0 | if (!pSdrGrafObj->getSignatureLineId().isEmpty()) |
688 | 0 | { |
689 | 0 | pAttrListSignatureLine->add( |
690 | 0 | XML_id, pSdrGrafObj->getSignatureLineId()); |
691 | 0 | } |
692 | 0 | if (!pSdrGrafObj->getSignatureLineSuggestedSignerName().isEmpty()) |
693 | 0 | { |
694 | 0 | pAttrListSignatureLine->add( |
695 | 0 | FSNS(XML_o, XML_suggestedsigner), |
696 | 0 | pSdrGrafObj->getSignatureLineSuggestedSignerName()); |
697 | 0 | } |
698 | 0 | if (!pSdrGrafObj->getSignatureLineSuggestedSignerTitle().isEmpty()) |
699 | 0 | { |
700 | 0 | pAttrListSignatureLine->add( |
701 | 0 | FSNS(XML_o, XML_suggestedsigner2), |
702 | 0 | pSdrGrafObj->getSignatureLineSuggestedSignerTitle()); |
703 | 0 | } |
704 | 0 | if (!pSdrGrafObj->getSignatureLineSuggestedSignerEmail().isEmpty()) |
705 | 0 | { |
706 | 0 | pAttrListSignatureLine->add( |
707 | 0 | FSNS(XML_o, XML_suggestedsigneremail), |
708 | 0 | pSdrGrafObj->getSignatureLineSuggestedSignerEmail()); |
709 | 0 | } |
710 | 0 | if (!pSdrGrafObj->getSignatureLineSigningInstructions().isEmpty()) |
711 | 0 | { |
712 | 0 | pAttrListSignatureLine->add(XML_signinginstructionsset, "t"); |
713 | 0 | pAttrListSignatureLine->add( |
714 | 0 | FSNS(XML_o, XML_signinginstructions), |
715 | 0 | pSdrGrafObj->getSignatureLineSigningInstructions()); |
716 | 0 | } |
717 | 0 | pAttrListSignatureLine->add( |
718 | 0 | XML_showsigndate, |
719 | 0 | pSdrGrafObj->isSignatureLineShowSignDate() ? "t" : "f"); |
720 | 0 | pAttrListSignatureLine->add( |
721 | 0 | XML_allowcomments, |
722 | 0 | pSdrGrafObj->isSignatureLineCanAddComment() ? "t" : "f"); |
723 | |
|
724 | 0 | m_pSerializer->singleElementNS( |
725 | 0 | XML_o, XML_signatureline, |
726 | 0 | pAttrListSignatureLine); |
727 | | |
728 | | // Get signature line graphic |
729 | 0 | const uno::Reference<graphic::XGraphic>& xGraphic |
730 | 0 | = pSdrGrafObj->getSignatureLineUnsignedGraphic(); |
731 | 0 | Graphic aGraphic(xGraphic); |
732 | 0 | OUString aImageId = m_pTextExport->GetDrawingML().writeGraphicToStorage(aGraphic, false); |
733 | 0 | pAttrList->add(FSNS(XML_r, XML_id), aImageId); |
734 | 0 | imageData = true; |
735 | 0 | } |
736 | 0 | else if (rProps.GetOpt(ESCHER_Prop_fillBlip, aStruct) && m_pTextExport) |
737 | 0 | { |
738 | 0 | SvMemoryStream aStream; |
739 | | // The first bytes are WW8-specific, we're only interested in the PNG |
740 | 0 | int nHeaderSize = 25; |
741 | 0 | aStream.WriteBytes(aStruct.nProp.data() + nHeaderSize, |
742 | 0 | aStruct.nProp.size() - nHeaderSize); |
743 | 0 | aStream.Seek(0); |
744 | 0 | Graphic aGraphic; |
745 | 0 | GraphicConverter::Import(aStream, aGraphic); |
746 | 0 | OUString aImageId = m_pTextExport->GetDrawingML().writeGraphicToStorage(aGraphic, false); |
747 | 0 | if (!aImageId.isEmpty()) |
748 | 0 | { |
749 | 0 | pAttrList->add(FSNS(XML_r, XML_id), aImageId); |
750 | 0 | imageData = true; |
751 | 0 | } |
752 | 0 | } |
753 | |
|
754 | 0 | if (rProps.GetOpt(ESCHER_Prop_fNoFillHitTest, nValue)) |
755 | 0 | impl_AddBool(pAttrList.get(), FSNS(XML_o, XML_detectmouseclick), nValue != 0); |
756 | |
|
757 | 0 | if (imageData && ((pSdrGrafObj && pSdrGrafObj->isSignatureLine()) |
758 | 0 | || m_nShapeType == ESCHER_ShpInst_PictureFrame)) |
759 | 0 | m_pSerializer->singleElementNS( XML_v, XML_imagedata, pAttrList ); |
760 | 0 | else |
761 | 0 | { |
762 | 0 | if ( rProps.GetOpt( ESCHER_Prop_fillType, nValue ) ) |
763 | 0 | { |
764 | 0 | const char *pFillType = nullptr; |
765 | 0 | switch ( nValue ) |
766 | 0 | { |
767 | 0 | case ESCHER_FillSolid: pFillType = "solid"; break; |
768 | | // TODO case ESCHER_FillPattern: pFillType = ""; break; |
769 | 0 | case ESCHER_FillTexture: pFillType = "tile"; break; |
770 | 0 | case ESCHER_FillPicture: pFillType = "frame"; break; |
771 | | // TODO case ESCHER_FillShade: pFillType = ""; break; |
772 | | // TODO case ESCHER_FillShadeCenter: pFillType = ""; break; |
773 | | // TODO case ESCHER_FillShadeShape: pFillType = ""; break; |
774 | | // TODO case ESCHER_FillShadeScale: pFillType = ""; break; |
775 | | // TODO case ESCHER_FillShadeTitle: pFillType = ""; break; |
776 | | // TODO case ESCHER_FillBackground: pFillType = ""; break; |
777 | 0 | default: |
778 | 0 | SAL_INFO("oox.vml", "Unhandled fill type: " << nValue); |
779 | 0 | break; |
780 | 0 | } |
781 | 0 | if ( pFillType ) |
782 | 0 | pAttrList->add( XML_type, pFillType ); |
783 | 0 | } |
784 | 0 | else if (!rProps.GetOpt(ESCHER_Prop_fillColor, nValue)) |
785 | 0 | pAttrList->add( XML_on, "false" ); |
786 | | |
787 | 0 | if ( rProps.GetOpt( ESCHER_Prop_fillColor, nValue ) ) |
788 | 0 | impl_AddColor( m_pShapeAttrList.get(), XML_fillcolor, nValue ); |
789 | |
|
790 | 0 | if ( rProps.GetOpt( ESCHER_Prop_fillBackColor, nValue ) ) |
791 | 0 | impl_AddColor( pAttrList.get(), XML_color2, nValue ); |
792 | |
|
793 | 0 | if (rProps.GetOpt(ESCHER_Prop_fillOpacity, nValue)) |
794 | | // Partly undo the transformation at the end of EscherPropertyContainer::CreateFillProperties(): VML opacity is 0..1. |
795 | 0 | pAttrList->add(XML_opacity, OString::number(double((nValue * 100) >> 16) / 100)); |
796 | 0 | m_pSerializer->singleElementNS( XML_v, XML_fill, pAttrList ); |
797 | |
|
798 | 0 | } |
799 | 0 | } |
800 | 0 | bAlreadyWritten[ ESCHER_Prop_fillType ] = true; |
801 | 0 | bAlreadyWritten[ ESCHER_Prop_fillColor ] = true; |
802 | 0 | bAlreadyWritten[ ESCHER_Prop_fillBackColor ] = true; |
803 | 0 | bAlreadyWritten[ ESCHER_Prop_fillBlip ] = true; |
804 | 0 | bAlreadyWritten[ ESCHER_Prop_fNoFillHitTest ] = true; |
805 | 0 | bAlreadyWritten[ ESCHER_Prop_fillOpacity ] = true; |
806 | 0 | break; |
807 | | |
808 | 0 | case ESCHER_Prop_lineColor: // 448 |
809 | 0 | case ESCHER_Prop_lineWidth: // 459 |
810 | 0 | case ESCHER_Prop_lineDashing: // 462 |
811 | 0 | case ESCHER_Prop_lineStartArrowhead: // 464 |
812 | 0 | case ESCHER_Prop_lineEndArrowhead: // 465 |
813 | 0 | case ESCHER_Prop_lineStartArrowWidth: // 466 |
814 | 0 | case ESCHER_Prop_lineStartArrowLength: // 467 |
815 | 0 | case ESCHER_Prop_lineEndArrowWidth: // 468 |
816 | 0 | case ESCHER_Prop_lineEndArrowLength: // 469 |
817 | 0 | case ESCHER_Prop_lineJoinStyle: // 470 |
818 | 0 | case ESCHER_Prop_lineEndCapStyle: // 471 |
819 | 0 | { |
820 | 0 | sal_uInt32 nValue; |
821 | 0 | rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = FastSerializerHelper::createAttrList(); |
822 | |
|
823 | 0 | if ( rProps.GetOpt( ESCHER_Prop_lineColor, nValue ) ) |
824 | 0 | impl_AddColor( pAttrList.get(), XML_color, nValue ); |
825 | |
|
826 | 0 | if ( rProps.GetOpt( ESCHER_Prop_lineWidth, nValue ) ) |
827 | 0 | impl_AddInt( pAttrList.get(), XML_weight, nValue ); |
828 | |
|
829 | 0 | if ( rProps.GetOpt( ESCHER_Prop_lineDashing, nValue ) ) |
830 | 0 | { |
831 | 0 | const char *pDashStyle = nullptr; |
832 | 0 | switch ( nValue ) |
833 | 0 | { |
834 | 0 | case ESCHER_LineSolid: pDashStyle = "solid"; break; |
835 | 0 | case ESCHER_LineDashSys: pDashStyle = "shortdash"; break; |
836 | 0 | case ESCHER_LineDotSys: pDashStyle = "shortdot"; break; |
837 | 0 | case ESCHER_LineDashDotSys: pDashStyle = "shortdashdot"; break; |
838 | 0 | case ESCHER_LineDashDotDotSys: pDashStyle = "shortdashdotdot"; break; |
839 | 0 | case ESCHER_LineDotGEL: pDashStyle = "dot"; break; |
840 | 0 | case ESCHER_LineDashGEL: pDashStyle = "dash"; break; |
841 | 0 | case ESCHER_LineLongDashGEL: pDashStyle = "longdash"; break; |
842 | 0 | case ESCHER_LineDashDotGEL: pDashStyle = "dashdot"; break; |
843 | 0 | case ESCHER_LineLongDashDotGEL: pDashStyle = "longdashdot"; break; |
844 | 0 | case ESCHER_LineLongDashDotDotGEL: pDashStyle = "longdashdotdot"; break; |
845 | 0 | } |
846 | 0 | if ( pDashStyle ) |
847 | 0 | pAttrList->add( XML_dashstyle, pDashStyle ); |
848 | 0 | } |
849 | | |
850 | 0 | if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowhead, nValue ) ) |
851 | 0 | impl_AddArrowHead( pAttrList.get(), XML_startarrow, nValue ); |
852 | |
|
853 | 0 | if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowhead, nValue ) ) |
854 | 0 | impl_AddArrowHead( pAttrList.get(), XML_endarrow, nValue ); |
855 | |
|
856 | 0 | if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowWidth, nValue ) ) |
857 | 0 | impl_AddArrowWidth( pAttrList.get(), XML_startarrowwidth, nValue ); |
858 | |
|
859 | 0 | if ( rProps.GetOpt( ESCHER_Prop_lineStartArrowLength, nValue ) ) |
860 | 0 | impl_AddArrowLength( pAttrList.get(), XML_startarrowlength, nValue ); |
861 | |
|
862 | 0 | if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowWidth, nValue ) ) |
863 | 0 | impl_AddArrowWidth( pAttrList.get(), XML_endarrowwidth, nValue ); |
864 | |
|
865 | 0 | if ( rProps.GetOpt( ESCHER_Prop_lineEndArrowLength, nValue ) ) |
866 | 0 | impl_AddArrowLength( pAttrList.get(), XML_endarrowlength, nValue ); |
867 | |
|
868 | 0 | if ( rProps.GetOpt( ESCHER_Prop_lineJoinStyle, nValue ) ) |
869 | 0 | { |
870 | 0 | const char *pJoinStyle = nullptr; |
871 | 0 | switch ( nValue ) |
872 | 0 | { |
873 | 0 | case ESCHER_LineJoinBevel: pJoinStyle = "bevel"; break; |
874 | 0 | case ESCHER_LineJoinMiter: pJoinStyle = "miter"; break; |
875 | 0 | case ESCHER_LineJoinRound: pJoinStyle = "round"; break; |
876 | 0 | } |
877 | 0 | if ( pJoinStyle ) |
878 | 0 | pAttrList->add( XML_joinstyle, pJoinStyle ); |
879 | 0 | } |
880 | | |
881 | 0 | if ( rProps.GetOpt( ESCHER_Prop_lineEndCapStyle, nValue ) ) |
882 | 0 | { |
883 | 0 | const char *pEndCap = nullptr; |
884 | 0 | switch ( nValue ) |
885 | 0 | { |
886 | 0 | case ESCHER_LineEndCapRound: pEndCap = "round"; break; |
887 | 0 | case ESCHER_LineEndCapSquare: pEndCap = "square"; break; |
888 | 0 | case ESCHER_LineEndCapFlat: pEndCap = "flat"; break; |
889 | 0 | } |
890 | 0 | if ( pEndCap ) |
891 | 0 | pAttrList->add( XML_endcap, pEndCap ); |
892 | 0 | } |
893 | | |
894 | 0 | m_pSerializer->singleElementNS( XML_v, XML_stroke, pAttrList ); |
895 | 0 | } |
896 | 0 | bAlreadyWritten[ ESCHER_Prop_lineColor ] = true; |
897 | 0 | bAlreadyWritten[ ESCHER_Prop_lineWidth ] = true; |
898 | 0 | bAlreadyWritten[ ESCHER_Prop_lineDashing ] = true; |
899 | 0 | bAlreadyWritten[ ESCHER_Prop_lineStartArrowhead ] = true; |
900 | 0 | bAlreadyWritten[ ESCHER_Prop_lineEndArrowhead ] = true; |
901 | 0 | bAlreadyWritten[ ESCHER_Prop_lineStartArrowWidth ] = true; |
902 | 0 | bAlreadyWritten[ ESCHER_Prop_lineStartArrowLength ] = true; |
903 | 0 | bAlreadyWritten[ ESCHER_Prop_lineEndArrowWidth ] = true; |
904 | 0 | bAlreadyWritten[ ESCHER_Prop_lineEndArrowLength ] = true; |
905 | 0 | bAlreadyWritten[ ESCHER_Prop_lineJoinStyle ] = true; |
906 | 0 | bAlreadyWritten[ ESCHER_Prop_lineEndCapStyle ] = true; |
907 | 0 | break; |
908 | | |
909 | 0 | case ESCHER_Prop_fHidden: |
910 | 0 | if ( !opt.nPropValue ) |
911 | 0 | m_ShapeStyle.append( ";visibility:hidden" ); |
912 | 0 | break; |
913 | 0 | case ESCHER_Prop_shadowColor: |
914 | 0 | case ESCHER_Prop_fshadowObscured: |
915 | 0 | { |
916 | 0 | sal_uInt32 nValue = 0; |
917 | 0 | bool bShadow = false; |
918 | 0 | bool bObscured = false; |
919 | 0 | if ( rProps.GetOpt( ESCHER_Prop_fshadowObscured, nValue ) ) |
920 | 0 | { |
921 | 0 | bShadow = (( nValue & 0x20002 ) == 0x20002 ); |
922 | 0 | bObscured = (( nValue & 0x10001 ) == 0x10001 ); |
923 | 0 | } |
924 | 0 | if ( bShadow ) |
925 | 0 | { |
926 | 0 | rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = FastSerializerHelper::createAttrList(); |
927 | 0 | impl_AddBool( pAttrList.get(), XML_on, bShadow ); |
928 | 0 | impl_AddBool( pAttrList.get(), XML_obscured, bObscured ); |
929 | |
|
930 | 0 | if ( rProps.GetOpt( ESCHER_Prop_shadowColor, nValue ) ) |
931 | 0 | impl_AddColor( pAttrList.get(), XML_color, nValue ); |
932 | |
|
933 | 0 | m_pSerializer->singleElementNS( XML_v, XML_shadow, pAttrList ); |
934 | 0 | bAlreadyWritten[ ESCHER_Prop_fshadowObscured ] = true; |
935 | 0 | bAlreadyWritten[ ESCHER_Prop_shadowColor ] = true; |
936 | 0 | } |
937 | 0 | } |
938 | 0 | break; |
939 | 0 | case ESCHER_Prop_gtextUNICODE: |
940 | 0 | case ESCHER_Prop_gtextFont: |
941 | 0 | { |
942 | 0 | EscherPropSortStruct aUnicode; |
943 | 0 | if (rProps.GetOpt(ESCHER_Prop_gtextUNICODE, aUnicode)) |
944 | 0 | { |
945 | 0 | SvMemoryStream aStream; |
946 | |
|
947 | 0 | if(!opt.nProp.empty()) |
948 | 0 | { |
949 | 0 | aStream.WriteBytes(opt.nProp.data(), opt.nProp.size()); |
950 | 0 | } |
951 | |
|
952 | 0 | aStream.Seek(0); |
953 | 0 | OUString aTextPathString = SvxMSDffManager::MSDFFReadZString(aStream, opt.nProp.size(), true); |
954 | 0 | aStream.Seek(0); |
955 | |
|
956 | 0 | m_pSerializer->singleElementNS(XML_v, XML_path, XML_textpathok, "t"); |
957 | |
|
958 | 0 | rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = FastSerializerHelper::createAttrList(); |
959 | 0 | pAttrList->add(XML_on, "t"); |
960 | 0 | pAttrList->add(XML_fitshape, "t"); |
961 | 0 | pAttrList->add(XML_string, aTextPathString); |
962 | 0 | EscherPropSortStruct aFont; |
963 | 0 | OUString aStyle; |
964 | 0 | if (rProps.GetOpt(ESCHER_Prop_gtextFont, aFont)) |
965 | 0 | { |
966 | 0 | aStream.WriteBytes(aFont.nProp.data(), aFont.nProp.size()); |
967 | 0 | aStream.Seek(0); |
968 | 0 | OUString aTextPathFont = SvxMSDffManager::MSDFFReadZString(aStream, aFont.nProp.size(), true); |
969 | 0 | aStyle += "font-family:\"" + aTextPathFont + "\""; |
970 | 0 | } |
971 | 0 | sal_uInt32 nSize; |
972 | 0 | if (rProps.GetOpt(ESCHER_Prop_gtextSize, nSize)) |
973 | 0 | { |
974 | 0 | float nSizeF = static_cast<sal_Int32>(nSize) / 65536.0; |
975 | 0 | OUString aSize = OUString::number(nSizeF); |
976 | 0 | aStyle += ";font-size:" + aSize + "pt"; |
977 | 0 | } |
978 | |
|
979 | 0 | sal_uInt32 nGtextFlags; |
980 | 0 | if (rProps.GetOpt(DFF_Prop_gtextFStrikethrough /*255*/, nGtextFlags)) |
981 | 0 | { |
982 | | // The property is in fact a collection of flags. Two bytes contain the |
983 | | // fUsegtextF* flags and the other two bytes at same place the associated |
984 | | // On/Off flags. See '2.3.22.10 Geometry Text Boolean Properties' section |
985 | | // in [MS-ODRAW]. |
986 | 0 | if ((nGtextFlags & 0x00200020) == 0x00200020) // DFF_Prop_gtextFBold = 250 |
987 | 0 | aStyle += ";font-weight:bold"; |
988 | 0 | if ((nGtextFlags & 0x00100010) == 0x00100010) // DFF_Prop_gtextFItalic = 251 |
989 | 0 | aStyle += ";font-style:italic"; |
990 | 0 | if ((nGtextFlags & 0x00800080) == 0x00800080) // no DFF, PID gtextFNormalize = 248 |
991 | 0 | aStyle += ";v-same-letter-heights:t"; |
992 | | |
993 | | // The value 'Fontwork character spacing' in LO is bound to field 'Scaling' |
994 | | // not to 'Spacing' in character properties. In fact the characters are |
995 | | // rendered with changed distance and width. The method in escherex.cxx has |
996 | | // put a rounded value of 'CharScaleWidth' API property to |
997 | | // DFF_Prop_gtextSpacing (=196) as integer part of 16.16 fixed point format. |
998 | | // fUsegtextFTight and gtextFTight (244) of MS binary format are not used. |
999 | 0 | sal_uInt32 nGtextSpacing; |
1000 | 0 | if (rProps.GetOpt(DFF_Prop_gtextSpacing, nGtextSpacing)) |
1001 | 0 | aStyle += ";v-text-spacing:" + OUString::number(nGtextSpacing) + "f"; |
1002 | 0 | } |
1003 | |
|
1004 | 0 | if (!aStyle.isEmpty()) |
1005 | 0 | pAttrList->add(XML_style, aStyle); |
1006 | | |
1007 | | // tdf#153260. LO renders all Fontwork shapes as if trim="t" is set. Default |
1008 | | // value is "f". So always write out "t", otherwise import will reduce the |
1009 | | // shape height as workaround for "f". |
1010 | 0 | pAttrList->add(XML_trim, "t"); |
1011 | |
|
1012 | 0 | m_pSerializer->singleElementNS(XML_v, XML_textpath, pAttrList); |
1013 | 0 | } |
1014 | |
|
1015 | 0 | bAlreadyWritten[ESCHER_Prop_gtextUNICODE] = true; |
1016 | 0 | bAlreadyWritten[ESCHER_Prop_gtextFont] = true; |
1017 | 0 | } |
1018 | 0 | break; |
1019 | 0 | case DFF_Prop_adjustValue: |
1020 | 0 | case DFF_Prop_adjust2Value: |
1021 | 0 | { |
1022 | | // FIXME: tdf#153296: The currently exported markup for <v:shapetype> is based on |
1023 | | // OOXML presets and unusable in regard to handles. Fontwork shapes use dedicated |
1024 | | // own markup, see FontworkHelpers::GetVMLFontworkShapetypeMarkup. |
1025 | | // Thus this is restricted to preset Fontwork shapes. Such have maximal two |
1026 | | // adjustment values. |
1027 | 0 | if ((mso_sptTextSimple <= m_nShapeType && m_nShapeType <= mso_sptTextOnRing) |
1028 | 0 | || (mso_sptTextPlainText <= m_nShapeType && m_nShapeType <= mso_sptTextCanDown)) |
1029 | 0 | { |
1030 | 0 | sal_uInt32 nValue; |
1031 | 0 | OString sAdj; |
1032 | 0 | if (rProps.GetOpt(DFF_Prop_adjustValue, nValue)) |
1033 | 0 | { |
1034 | 0 | sAdj = OString::number(static_cast<sal_Int32>(nValue)); |
1035 | 0 | if (rProps.GetOpt(DFF_Prop_adjust2Value, nValue)) |
1036 | 0 | sAdj += "," + OString::number(static_cast<sal_Int32>(nValue)); |
1037 | 0 | } |
1038 | 0 | if (!sAdj.isEmpty()) |
1039 | 0 | m_pShapeAttrList->add(XML_adj, sAdj); |
1040 | 0 | bAlreadyWritten[DFF_Prop_adjustValue] = true; |
1041 | 0 | bAlreadyWritten[DFF_Prop_adjust2Value] = true; |
1042 | 0 | } |
1043 | 0 | } |
1044 | 0 | break; |
1045 | 0 | case ESCHER_Prop_Rotation: |
1046 | 0 | { |
1047 | | // The higher half of the variable contains the angle. |
1048 | 0 | m_ShapeStyle.append(";rotation:" + OString::number(double(opt.nPropValue >> 16))); |
1049 | 0 | bAlreadyWritten[ESCHER_Prop_Rotation] = true; |
1050 | 0 | } |
1051 | 0 | break; |
1052 | 0 | case ESCHER_Prop_fNoLineDrawDash: |
1053 | 0 | { |
1054 | | // See DffPropertyReader::ApplyLineAttributes(). |
1055 | 0 | impl_AddBool( m_pShapeAttrList.get(), XML_stroked, (opt.nPropValue & 8) != 0 ); |
1056 | 0 | bAlreadyWritten[ESCHER_Prop_fNoLineDrawDash] = true; |
1057 | 0 | } |
1058 | 0 | break; |
1059 | 0 | case ESCHER_Prop_wzName: |
1060 | 0 | { |
1061 | 0 | SvMemoryStream aStream; |
1062 | |
|
1063 | 0 | if(!opt.nProp.empty()) |
1064 | 0 | { |
1065 | 0 | aStream.WriteBytes(opt.nProp.data(), opt.nProp.size()); |
1066 | 0 | } |
1067 | |
|
1068 | 0 | aStream.Seek(0); |
1069 | 0 | OUString idStr = SvxMSDffManager::MSDFFReadZString(aStream, opt.nProp.size(), true); |
1070 | 0 | aStream.Seek(0); |
1071 | 0 | if (!IsWaterMarkShape(m_pSdrObject->GetName()) && !m_bSkipwzName) |
1072 | 0 | m_pShapeAttrList->add(XML_ID, idStr); |
1073 | | |
1074 | | // note that XML_ID is different from XML_id (although it looks like a LO |
1075 | | // implementation distinction without valid justification to me). |
1076 | 0 | bAlreadyWritten[ESCHER_Prop_wzName] = true; |
1077 | 0 | } |
1078 | 0 | break; |
1079 | 0 | default: |
1080 | | #if OSL_DEBUG_LEVEL > 0 |
1081 | | const size_t opt_nProp_size(opt.nProp.size()); |
1082 | | SAL_WARN( "oox.vml", "TODO VMLExport::Commit(), unimplemented id: " << nId |
1083 | | << ", value: " << opt.nPropValue |
1084 | | << ", data: [" << opt_nProp_size << "]"); |
1085 | | if ( opt.nProp.size() ) |
1086 | | { |
1087 | | const sal_uInt8 *pIt = opt.nProp.data(); |
1088 | | OStringBuffer buf( " ( " ); |
1089 | | for ( int nCount = opt.nProp.size(); nCount; --nCount ) |
1090 | | { |
1091 | | buf.append( OString::number(static_cast<sal_Int32>(*pIt), 16) + " "); |
1092 | | ++pIt; |
1093 | | } |
1094 | | buf.append( ")" ); |
1095 | | SAL_WARN("oox.vml", std::string_view(buf)); |
1096 | | } |
1097 | | #endif |
1098 | 0 | break; |
1099 | 0 | } |
1100 | 0 | } |
1101 | | |
1102 | 0 | m_pSerializer->mergeTopMarks(Tag_Commit, sax_fastparser::MergeMarks::POSTPONE ); |
1103 | 0 | } |
1104 | | |
1105 | | OString VMLExport::ShapeIdString( sal_uInt32 nId ) |
1106 | 0 | { |
1107 | 0 | if(m_bOverrideShapeIdGeneration) |
1108 | 0 | return m_sShapeIDPrefix + OString::number( nId ); |
1109 | 0 | else |
1110 | 0 | return "shape_" + OString::number( nId ); |
1111 | 0 | } |
1112 | | |
1113 | | void VMLExport::AddFlipXY( ) |
1114 | 0 | { |
1115 | 0 | if (m_nShapeFlags & (ShapeFlag::FlipH | ShapeFlag::FlipV)) |
1116 | 0 | { |
1117 | 0 | m_ShapeStyle.append( ";flip:" ); |
1118 | |
|
1119 | 0 | if (m_nShapeFlags & ShapeFlag::FlipH) |
1120 | 0 | m_ShapeStyle.append( "x" ); |
1121 | |
|
1122 | 0 | if (m_nShapeFlags & ShapeFlag::FlipV) |
1123 | 0 | m_ShapeStyle.append( "y" ); |
1124 | 0 | } |
1125 | 0 | } |
1126 | | |
1127 | | void VMLExport::AddLineDimensions( const tools::Rectangle& rRectangle ) |
1128 | 0 | { |
1129 | | // style |
1130 | 0 | if (!m_ShapeStyle.isEmpty()) |
1131 | 0 | m_ShapeStyle.append( ";" ); |
1132 | |
|
1133 | 0 | m_ShapeStyle.append( "position:absolute" ); |
1134 | |
|
1135 | 0 | AddFlipXY(); |
1136 | | |
1137 | | // the actual dimensions |
1138 | 0 | OString aLeft, aTop, aRight, aBottom; |
1139 | |
|
1140 | 0 | if ( mnGroupLevel == 1 ) |
1141 | 0 | { |
1142 | 0 | static constexpr OString aPt( "pt"_ostr ); |
1143 | 0 | aLeft = OString::number( double( rRectangle.Left() ) / 20 ) + aPt; |
1144 | 0 | aTop = OString::number( double( rRectangle.Top() ) / 20 ) + aPt; |
1145 | 0 | aRight = OString::number( double( rRectangle.Right() ) / 20 ) + aPt; |
1146 | 0 | aBottom = OString::number( double( rRectangle.Bottom() ) / 20 ) + aPt; |
1147 | 0 | } |
1148 | 0 | else |
1149 | 0 | { |
1150 | 0 | aLeft = OString::number( rRectangle.Left() ); |
1151 | 0 | aTop = OString::number( rRectangle.Top() ); |
1152 | 0 | aRight = OString::number( rRectangle.Right() ); |
1153 | 0 | aBottom = OString::number( rRectangle.Bottom() ); |
1154 | 0 | } |
1155 | |
|
1156 | 0 | m_pShapeAttrList->add( XML_from, aLeft + "," + aTop ); |
1157 | |
|
1158 | 0 | m_pShapeAttrList->add( XML_to, aRight + "," + aBottom ); |
1159 | 0 | } |
1160 | | |
1161 | | void VMLExport::AddRectangleDimensions( OStringBuffer& rBuffer, const tools::Rectangle& rRectangle, bool rbAbsolutePos) |
1162 | 0 | { |
1163 | 0 | if ( !rBuffer.isEmpty() ) |
1164 | 0 | rBuffer.append( ";" ); |
1165 | |
|
1166 | 0 | if (rbAbsolutePos && !m_bInline) |
1167 | 0 | { |
1168 | 0 | rBuffer.append( "position:absolute;" ); |
1169 | 0 | } |
1170 | |
|
1171 | 0 | if(m_bInline) |
1172 | 0 | { |
1173 | 0 | rBuffer.append( "width:" + OString::number( double( rRectangle.Right() - rRectangle.Left() ) / 20 ) + |
1174 | 0 | "pt;height:" + OString::number( double( rRectangle.Bottom() - rRectangle.Top() ) / 20 ) + |
1175 | 0 | "pt" ); |
1176 | 0 | } |
1177 | 0 | else if ( mnGroupLevel == 1 ) |
1178 | 0 | { |
1179 | 0 | rBuffer.append( "margin-left:" + OString::number( double( rRectangle.Left() ) / 20 ) + |
1180 | 0 | "pt;margin-top:" + OString::number( double( rRectangle.Top() ) / 20 ) + |
1181 | 0 | "pt;width:" + OString::number( double( rRectangle.Right() - rRectangle.Left() ) / 20 ) + |
1182 | 0 | "pt;height:" + OString::number( double( rRectangle.Bottom() - rRectangle.Top() ) / 20 ) + |
1183 | 0 | "pt" ); |
1184 | 0 | } |
1185 | 0 | else |
1186 | 0 | { |
1187 | 0 | rBuffer.append( "left:" + OString::number( rRectangle.Left() ) + |
1188 | 0 | ";top:" + OString::number( rRectangle.Top() ) + |
1189 | 0 | ";width:" + OString::number( rRectangle.Right() - rRectangle.Left() ) + |
1190 | 0 | ";height:" + OString::number( rRectangle.Bottom() - rRectangle.Top() ) ); |
1191 | 0 | } |
1192 | |
|
1193 | 0 | AddFlipXY(); |
1194 | 0 | } |
1195 | | |
1196 | | void VMLExport::AddShapeAttribute( sal_Int32 nAttribute, std::string_view rValue ) |
1197 | 0 | { |
1198 | 0 | m_pShapeAttrList->add( nAttribute, rValue ); |
1199 | 0 | } |
1200 | | |
1201 | | static std::vector<OString> lcl_getShapeTypes() |
1202 | 0 | { |
1203 | 0 | std::vector<OString> aRet; |
1204 | |
|
1205 | 0 | OUString aPath(u"$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/filter/vml-shape-types"_ustr); |
1206 | 0 | rtl::Bootstrap::expandMacros(aPath); |
1207 | 0 | SvFileStream aStream(aPath, StreamMode::READ); |
1208 | 0 | if (aStream.GetError() != ERRCODE_NONE) |
1209 | 0 | SAL_WARN("oox", "failed to open vml-shape-types"); |
1210 | 0 | OStringBuffer aLine; |
1211 | 0 | bool bNotDone = aStream.ReadLine(aLine); |
1212 | 0 | while (bNotDone) |
1213 | 0 | { |
1214 | | // Filter out comments. |
1215 | 0 | if (!o3tl::starts_with(aLine, "/")) |
1216 | 0 | aRet.push_back(OString(aLine)); |
1217 | 0 | bNotDone = aStream.ReadLine(aLine); |
1218 | 0 | } |
1219 | 0 | return aRet; |
1220 | 0 | } |
1221 | | |
1222 | | static bool lcl_isTextBox(const SdrObject* pSdrObject) |
1223 | 0 | { |
1224 | 0 | uno::Reference<beans::XPropertySet> xPropertySet(const_cast<SdrObject*>(pSdrObject)->getUnoShape(), uno::UNO_QUERY); |
1225 | 0 | if (!xPropertySet.is()) |
1226 | 0 | return false; |
1227 | 0 | uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo(); |
1228 | 0 | if (!xPropertySetInfo->hasPropertyByName(u"TextBox"_ustr)) |
1229 | 0 | return false; |
1230 | 0 | css::uno::Any aTextBox(xPropertySet->getPropertyValue(u"TextBox"_ustr)); |
1231 | 0 | if (!aTextBox.hasValue()) |
1232 | 0 | return false; |
1233 | 0 | return aTextBox.get<bool>(); |
1234 | 0 | } |
1235 | | |
1236 | | static OUString lcl_getAnchorIdFromGrabBag(const SdrObject* pSdrObject) |
1237 | 0 | { |
1238 | 0 | OUString aResult; |
1239 | |
|
1240 | 0 | uno::Reference<beans::XPropertySet> xShape(const_cast<SdrObject*>(pSdrObject)->getUnoShape(), uno::UNO_QUERY); |
1241 | 0 | if (xShape->getPropertySetInfo()->hasPropertyByName(u"InteropGrabBag"_ustr)) |
1242 | 0 | { |
1243 | 0 | comphelper::SequenceAsHashMap aInteropGrabBag(xShape->getPropertyValue(u"InteropGrabBag"_ustr)); |
1244 | 0 | auto it = aInteropGrabBag.find(u"AnchorId"_ustr); |
1245 | 0 | if (it != aInteropGrabBag.end()) |
1246 | 0 | it->second >>= aResult; |
1247 | 0 | } |
1248 | |
|
1249 | 0 | return aResult; |
1250 | 0 | } |
1251 | | |
1252 | | sal_uInt32 VMLExport::GenerateShapeId() |
1253 | 0 | { |
1254 | 0 | if(!m_bOverrideShapeIdGeneration) |
1255 | 0 | return EscherEx::GenerateShapeId(); |
1256 | 0 | else |
1257 | 0 | return m_nShapeIDCounter++; |
1258 | 0 | } |
1259 | | |
1260 | | OString VMLExport::GetVMLShapeTypeDefinition( |
1261 | | std::string_view sShapeID, const bool bIsPictureFrame ) |
1262 | 0 | { |
1263 | 0 | OString sShapeType; |
1264 | 0 | if ( !bIsPictureFrame ) |
1265 | | // We don't have a shape definition for host control in presetShapeDefinitions.xml |
1266 | | // So use a definition copied from DOCX file created with MSO |
1267 | 0 | sShapeType = OString::Concat("<v:shapetype id=\"_x0000_t") + sShapeID + |
1268 | 0 | "\" coordsize=\"21600,21600\" o:spt=\"" + sShapeID + |
1269 | 0 | "\" path=\"m,l,21600l21600,21600l21600,xe\">\n" |
1270 | 0 | "<v:stroke joinstyle=\"miter\"/>\n" |
1271 | 0 | "<v:path shadowok=\"f\" o:extrusionok=\"f\" strokeok=\"f\" fillok=\"f\" o:connecttype=\"rect\"/>\n" |
1272 | 0 | "<o:lock v:ext=\"edit\" shapetype=\"t\"/>\n" |
1273 | 0 | "</v:shapetype>"; |
1274 | 0 | else |
1275 | | // We don't have a shape definition for picture frame in presetShapeDefinitions.xml |
1276 | | // So use a definition copied from DOCX file created with MSO |
1277 | 0 | sShapeType = OString::Concat("<v:shapetype id=\"_x0000_t") + sShapeID + |
1278 | 0 | "\" coordsize=\"21600,21600\" o:spt=\"" + sShapeID + |
1279 | 0 | "\" o:preferrelative=\"t\" path=\"m@4@5l@4@11@9@11@9@5xe\" filled=\"f\" stroked=\"f\">\n" |
1280 | 0 | "<v:stroke joinstyle=\"miter\"/>\n" |
1281 | 0 | "<v:formulas>\n" |
1282 | 0 | "<v:f eqn=\"if lineDrawn pixelLineWidth 0\"/>\n" |
1283 | 0 | "<v:f eqn=\"sum @0 1 0\"/>\n" |
1284 | 0 | "<v:f eqn=\"sum 0 0 @1\"/>\n" |
1285 | 0 | "<v:f eqn=\"prod @2 1 2\"/>\n" |
1286 | 0 | "<v:f eqn=\"prod @3 21600 pixelWidth\"/>\n" |
1287 | 0 | "<v:f eqn=\"prod @3 21600 pixelHeight\"/>\n" |
1288 | 0 | "<v:f eqn=\"sum @0 0 1\"/>\n" |
1289 | 0 | "<v:f eqn=\"prod @6 1 2\"/>\n" |
1290 | 0 | "<v:f eqn=\"prod @7 21600 pixelWidth\"/>\n" |
1291 | 0 | "<v:f eqn=\"sum @8 21600 0\"/>\n" |
1292 | 0 | "<v:f eqn=\"prod @7 21600 pixelHeight\"/>\n" |
1293 | 0 | "<v:f eqn=\"sum @10 21600 0\"/>\n" |
1294 | 0 | "</v:formulas>\n" |
1295 | 0 | "<v:path o:extrusionok=\"f\" gradientshapeok=\"t\" o:connecttype=\"rect\"/>\n" |
1296 | 0 | "<o:lock v:ext=\"edit\" aspectratio=\"t\"/>\n" |
1297 | 0 | "</v:shapetype>"; |
1298 | 0 | return sShapeType; |
1299 | 0 | } |
1300 | | |
1301 | | sal_Int32 VMLExport::StartShape() |
1302 | 0 | { |
1303 | 0 | if ( m_nShapeType == ESCHER_ShpInst_Nil ) |
1304 | 0 | return -1; |
1305 | | |
1306 | | // some of the shapes have their own name ;-) |
1307 | 0 | sal_Int32 nShapeElement = -1; |
1308 | 0 | bool bReferToShapeType = false; |
1309 | 0 | switch ( m_nShapeType ) |
1310 | 0 | { |
1311 | 0 | case ESCHER_ShpInst_NotPrimitive: nShapeElement = XML_shape; break; |
1312 | 0 | case ESCHER_ShpInst_Rectangle: nShapeElement = XML_rect; break; |
1313 | 0 | case ESCHER_ShpInst_RoundRectangle: nShapeElement = XML_roundrect; break; |
1314 | 0 | case ESCHER_ShpInst_Ellipse: nShapeElement = XML_oval; break; |
1315 | 0 | case ESCHER_ShpInst_Arc: nShapeElement = XML_arc; break; |
1316 | 0 | case ESCHER_ShpInst_Line: nShapeElement = XML_line; break; |
1317 | 0 | case ESCHER_ShpInst_HostControl: |
1318 | 0 | { |
1319 | 0 | bReferToShapeType = true; |
1320 | 0 | nShapeElement = XML_shape; |
1321 | 0 | if ( !m_aShapeTypeWritten[ m_nShapeType ] ) |
1322 | 0 | { |
1323 | 0 | m_pSerializer->write(GetVMLShapeTypeDefinition(OString::number(m_nShapeType), false)); |
1324 | 0 | m_aShapeTypeWritten[ m_nShapeType ] = true; |
1325 | 0 | } |
1326 | 0 | break; |
1327 | 0 | } |
1328 | 0 | case ESCHER_ShpInst_PictureFrame: |
1329 | 0 | { |
1330 | 0 | bReferToShapeType = true; |
1331 | 0 | nShapeElement = XML_shape; |
1332 | 0 | if ( !m_aShapeTypeWritten[ m_nShapeType ] ) |
1333 | 0 | { |
1334 | 0 | m_pSerializer->write(GetVMLShapeTypeDefinition(OString::number(m_nShapeType), true)); |
1335 | 0 | m_aShapeTypeWritten[ m_nShapeType ] = true; |
1336 | 0 | } |
1337 | 0 | break; |
1338 | 0 | } |
1339 | 0 | default: |
1340 | 0 | nShapeElement = XML_shape; |
1341 | 0 | if (m_pSdrObject->IsTextPath()) |
1342 | 0 | { |
1343 | 0 | bReferToShapeType = m_aShapeTypeWritten[m_nShapeType]; |
1344 | 0 | if (!bReferToShapeType) |
1345 | 0 | { |
1346 | | // Does a predefined markup exist at all? |
1347 | 0 | OString sMarkup = FontworkHelpers::GetVMLFontworkShapetypeMarkup( |
1348 | 0 | static_cast<MSO_SPT>(m_nShapeType)); |
1349 | 0 | if (!sMarkup.isEmpty()) |
1350 | 0 | { |
1351 | 0 | m_pSerializer->write(sMarkup); |
1352 | 0 | m_aShapeTypeWritten[m_nShapeType] = true; |
1353 | 0 | bReferToShapeType = true; |
1354 | 0 | } |
1355 | 0 | } |
1356 | | // ToDo: The case bReferToShapeType==false happens for 'non-primitive' shapes for |
1357 | | // example. We need to get the geometry from CustomShapeGeometry in these cases. |
1358 | 0 | } |
1359 | 0 | else if ( m_nShapeType < ESCHER_ShpInst_COUNT ) |
1360 | 0 | { |
1361 | | // a predefined shape? |
1362 | 0 | static std::vector<OString> aShapeTypes = lcl_getShapeTypes(); |
1363 | 0 | SAL_WARN_IF(m_nShapeType >= aShapeTypes.size(), "oox.vml", "Unknown shape type!"); |
1364 | 0 | if (m_nShapeType < aShapeTypes.size() && aShapeTypes[m_nShapeType] != "NULL") |
1365 | 0 | { |
1366 | 0 | bReferToShapeType = true; |
1367 | 0 | if ( !m_aShapeTypeWritten[ m_nShapeType ] ) |
1368 | 0 | { |
1369 | 0 | m_pSerializer->write(aShapeTypes[m_nShapeType]); |
1370 | 0 | m_aShapeTypeWritten[ m_nShapeType ] = true; |
1371 | 0 | } |
1372 | 0 | } |
1373 | 0 | else |
1374 | 0 | { |
1375 | | // rectangle is probably the best fallback... |
1376 | 0 | nShapeElement = XML_rect; |
1377 | 0 | } |
1378 | 0 | } |
1379 | 0 | break; |
1380 | 0 | } |
1381 | | |
1382 | | // anchoring |
1383 | 0 | switch (m_eHOri) |
1384 | 0 | { |
1385 | 0 | case text::HoriOrientation::LEFT: |
1386 | 0 | m_ShapeStyle.append(";mso-position-horizontal:left"); |
1387 | 0 | break; |
1388 | 0 | case text::HoriOrientation::CENTER: |
1389 | 0 | m_ShapeStyle.append(";mso-position-horizontal:center"); |
1390 | 0 | break; |
1391 | 0 | case text::HoriOrientation::RIGHT: |
1392 | 0 | m_ShapeStyle.append(";mso-position-horizontal:right"); |
1393 | 0 | break; |
1394 | 0 | case text::HoriOrientation::INSIDE: |
1395 | 0 | m_ShapeStyle.append(";mso-position-horizontal:inside"); |
1396 | 0 | break; |
1397 | 0 | case text::HoriOrientation::OUTSIDE: |
1398 | 0 | m_ShapeStyle.append(";mso-position-horizontal:outside"); |
1399 | 0 | break; |
1400 | 0 | default: |
1401 | 0 | case text::HoriOrientation::NONE: |
1402 | 0 | break; |
1403 | 0 | } |
1404 | 0 | switch (m_eHRel) |
1405 | 0 | { |
1406 | 0 | case text::RelOrientation::PAGE_PRINT_AREA: |
1407 | 0 | m_ShapeStyle.append(";mso-position-horizontal-relative:margin"); |
1408 | 0 | break; |
1409 | 0 | case text::RelOrientation::PAGE_FRAME: |
1410 | 0 | case text::RelOrientation::PAGE_LEFT: |
1411 | 0 | case text::RelOrientation::PAGE_RIGHT: |
1412 | 0 | m_ShapeStyle.append(";mso-position-horizontal-relative:page"); |
1413 | 0 | break; |
1414 | 0 | case text::RelOrientation::CHAR: |
1415 | 0 | m_ShapeStyle.append(";mso-position-horizontal-relative:char"); |
1416 | 0 | break; |
1417 | 0 | default: |
1418 | 0 | break; |
1419 | 0 | } |
1420 | | |
1421 | 0 | switch (m_eVOri) |
1422 | 0 | { |
1423 | 0 | case text::VertOrientation::TOP: |
1424 | 0 | case text::VertOrientation::LINE_TOP: |
1425 | 0 | case text::VertOrientation::CHAR_TOP: |
1426 | 0 | m_ShapeStyle.append(";mso-position-vertical:top"); |
1427 | 0 | break; |
1428 | 0 | case text::VertOrientation::CENTER: |
1429 | 0 | case text::VertOrientation::LINE_CENTER: |
1430 | 0 | m_ShapeStyle.append(";mso-position-vertical:center"); |
1431 | 0 | break; |
1432 | 0 | case text::VertOrientation::BOTTOM: |
1433 | 0 | case text::VertOrientation::LINE_BOTTOM: |
1434 | 0 | case text::VertOrientation::CHAR_BOTTOM: |
1435 | 0 | m_ShapeStyle.append(";mso-position-vertical:bottom"); |
1436 | 0 | break; |
1437 | 0 | default: |
1438 | 0 | case text::VertOrientation::NONE: |
1439 | 0 | break; |
1440 | 0 | } |
1441 | 0 | switch (m_eVRel) |
1442 | 0 | { |
1443 | 0 | case text::RelOrientation::PAGE_PRINT_AREA: |
1444 | 0 | m_ShapeStyle.append(";mso-position-vertical-relative:margin"); |
1445 | 0 | break; |
1446 | 0 | case text::RelOrientation::PAGE_FRAME: |
1447 | 0 | m_ShapeStyle.append(";mso-position-vertical-relative:page"); |
1448 | 0 | break; |
1449 | 0 | default: |
1450 | 0 | break; |
1451 | 0 | } |
1452 | | |
1453 | 0 | if (!m_pSdrObject->getHyperlink().isEmpty()) |
1454 | 0 | m_pShapeAttrList->add( |
1455 | 0 | XML_href, m_pSdrObject->getHyperlink()); |
1456 | |
|
1457 | 0 | m_pShapeAttrList->addNS(XML_o, XML_allowincell, m_IsFollowingTextFlow ? "t" : "f"); |
1458 | | |
1459 | | // add style |
1460 | 0 | m_pShapeAttrList->add( XML_style, m_ShapeStyle.makeStringAndClear() ); |
1461 | |
|
1462 | 0 | OUString sAnchorId = lcl_getAnchorIdFromGrabBag(m_pSdrObject); |
1463 | 0 | if (!sAnchorId.isEmpty()) |
1464 | 0 | m_pShapeAttrList->addNS(XML_wp14, XML_anchorId, sAnchorId); |
1465 | |
|
1466 | 0 | if ( nShapeElement >= 0 && !m_pShapeAttrList->hasAttribute( XML_type ) && bReferToShapeType ) |
1467 | 0 | { |
1468 | 0 | OString sType; |
1469 | 0 | if (m_bUseHashMarkForType) |
1470 | 0 | sType = "#"_ostr; |
1471 | 0 | m_pShapeAttrList->add( XML_type, sType + |
1472 | 0 | "_x0000_t" + OString::number( m_nShapeType ) ); |
1473 | 0 | } |
1474 | | |
1475 | | // path attribute only valid for v:shape |
1476 | 0 | if ( nShapeElement == XML_shape && !m_ShapePath.isEmpty()) |
1477 | 0 | m_pShapeAttrList->add( XML_path, m_ShapePath ); |
1478 | | |
1479 | | // allow legacy id (which in form controls and textboxes |
1480 | | // by definition seems to have this otherwise illegal name). |
1481 | 0 | m_pSerializer->setAllowXEscape(!m_sShapeIDPrefix.startsWith("_x0000_")); |
1482 | | |
1483 | | // start of the shape |
1484 | 0 | m_pSerializer->startElementNS( XML_v, nShapeElement, m_pShapeAttrList ); |
1485 | 0 | m_pSerializer->setAllowXEscape(true); |
1486 | |
|
1487 | 0 | OString const textboxStyle(m_TextboxStyle.makeStringAndClear()); |
1488 | | |
1489 | | // now check if we have some editeng text (not associated textbox) and we have a text exporter registered |
1490 | 0 | const SdrTextObj* pTxtObj = DynCastSdrTextObj( m_pSdrObject ); |
1491 | 0 | if (pTxtObj && m_pTextExport && !m_pSdrObject->IsTextPath() |
1492 | 0 | && !IsWaterMarkShape(m_pSdrObject->GetName()) && !lcl_isTextBox(m_pSdrObject)) |
1493 | 0 | { |
1494 | 0 | std::optional<OutlinerParaObject> pParaObj; |
1495 | | |
1496 | | /* |
1497 | | #i13885# |
1498 | | When the object is actively being edited, that text is not set into |
1499 | | the objects normal text object, but lives in a separate object. |
1500 | | */ |
1501 | 0 | if (pTxtObj->IsTextEditActive()) |
1502 | 0 | { |
1503 | 0 | pParaObj = pTxtObj->CreateEditOutlinerParaObject(); |
1504 | 0 | } |
1505 | 0 | else if (pTxtObj->GetOutlinerParaObject()) |
1506 | 0 | { |
1507 | 0 | pParaObj = *pTxtObj->GetOutlinerParaObject(); |
1508 | 0 | } |
1509 | |
|
1510 | 0 | if( pParaObj ) |
1511 | 0 | { |
1512 | 0 | rtl::Reference<sax_fastparser::FastAttributeList> pTextboxAttrList = FastSerializerHelper::createAttrList(); |
1513 | 0 | if (!textboxStyle.isEmpty()) |
1514 | 0 | { |
1515 | 0 | pTextboxAttrList->add(XML_style, textboxStyle); |
1516 | 0 | } |
1517 | | |
1518 | | // this is reached only in case some text is attached to the shape |
1519 | 0 | m_pSerializer->startElementNS(XML_v, XML_textbox, pTextboxAttrList); |
1520 | 0 | m_pTextExport->WriteOutliner(*pParaObj); |
1521 | 0 | m_pSerializer->endElementNS(XML_v, XML_textbox); |
1522 | 0 | } |
1523 | 0 | } |
1524 | |
|
1525 | 0 | return nShapeElement; |
1526 | 0 | } |
1527 | | |
1528 | | void VMLExport::EndShape( sal_Int32 nShapeElement ) |
1529 | 0 | { |
1530 | 0 | if ( nShapeElement < 0 ) |
1531 | 0 | return; |
1532 | | |
1533 | 0 | if (m_pTextExport && lcl_isTextBox(m_pSdrObject)) |
1534 | 0 | { |
1535 | 0 | uno::Reference<drawing::XShape> xShape {const_cast<SdrObject*>(m_pSdrObject)->getUnoShape(), uno::UNO_QUERY}; |
1536 | 0 | uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY); |
1537 | 0 | uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo(); |
1538 | 0 | bool bBottomToTop = false; |
1539 | 0 | if (xPropertySetInfo->hasPropertyByName(u"CustomShapeGeometry"_ustr)) |
1540 | 0 | { |
1541 | | // In this case a DrawingML DOCX was imported. |
1542 | 0 | auto aAny = xPropertySet->getPropertyValue(u"WritingMode"_ustr); |
1543 | 0 | sal_Int16 nWritingMode; |
1544 | 0 | if ((aAny >>= nWritingMode) && nWritingMode == text::WritingMode2::BT_LR) |
1545 | 0 | bBottomToTop = true; |
1546 | 0 | } |
1547 | 0 | else |
1548 | 0 | { |
1549 | | // In this case a pure VML DOCX was imported, so there is no CustomShapeGeometry. |
1550 | 0 | auto pTextExport = m_pTextExport->GetDrawingML().GetTextExport(); |
1551 | | // FIXME: somewhy pTextExport is always nullptr, we should find its reason |
1552 | 0 | if (pTextExport) |
1553 | 0 | { |
1554 | 0 | auto xTextFrame = pTextExport->GetUnoTextFrame(xShape); |
1555 | 0 | uno::Reference<beans::XPropertySet> xPropSet(xTextFrame, uno::UNO_QUERY); |
1556 | 0 | auto aAny = xPropSet->getPropertyValue(u"WritingMode"_ustr); |
1557 | 0 | sal_Int16 nWritingMode; |
1558 | 0 | if (aAny >>= nWritingMode) |
1559 | 0 | { |
1560 | 0 | switch (nWritingMode) |
1561 | 0 | { |
1562 | 0 | case text::WritingMode2::BT_LR: |
1563 | 0 | bBottomToTop = true; |
1564 | 0 | break; |
1565 | 0 | default: |
1566 | 0 | break; |
1567 | 0 | } |
1568 | 0 | } |
1569 | 0 | } |
1570 | 0 | } |
1571 | 0 | rtl::Reference<sax_fastparser::FastAttributeList> pTextboxAttrList = FastSerializerHelper::createAttrList(); |
1572 | 0 | if (bBottomToTop) |
1573 | 0 | pTextboxAttrList->add(XML_style, "mso-layout-flow-alt:bottom-to-top"); |
1574 | 0 | m_pSerializer->startElementNS(XML_v, XML_textbox, pTextboxAttrList); |
1575 | |
|
1576 | 0 | m_pTextExport->WriteVMLTextBox(uno::Reference<drawing::XShape>(xPropertySet, uno::UNO_QUERY_THROW)); |
1577 | |
|
1578 | 0 | m_pSerializer->endElementNS(XML_v, XML_textbox); |
1579 | 0 | } |
1580 | | |
1581 | 0 | if (m_pWrapAttrList) |
1582 | 0 | { |
1583 | 0 | m_pSerializer->singleElementNS(XML_w10, XML_wrap, m_pWrapAttrList); |
1584 | 0 | } |
1585 | | |
1586 | | // end of the shape |
1587 | 0 | m_pSerializer->endElementNS( XML_v, nShapeElement ); |
1588 | 0 | } |
1589 | | |
1590 | | OString const & VMLExport::AddSdrObject( const SdrObject& rObj, |
1591 | | bool const bIsFollowingTextFlow, |
1592 | | sal_Int16 eHOri, sal_Int16 eVOri, sal_Int16 eHRel, sal_Int16 eVRel, |
1593 | | FastAttributeList* pWrapAttrList, |
1594 | | const bool bOOxmlExport, sal_uInt32 nId) |
1595 | 0 | { |
1596 | 0 | m_pSdrObject = &rObj; |
1597 | 0 | m_eHOri = eHOri; |
1598 | 0 | m_eVOri = eVOri; |
1599 | 0 | m_eHRel = eHRel; |
1600 | 0 | m_eVRel = eVRel; |
1601 | 0 | m_pWrapAttrList = pWrapAttrList; |
1602 | 0 | m_bInline = false; |
1603 | 0 | m_IsFollowingTextFlow = bIsFollowingTextFlow; |
1604 | 0 | EscherEx::AddSdrObject(rObj, bOOxmlExport, nId); |
1605 | 0 | return m_sShapeId; |
1606 | 0 | } |
1607 | | |
1608 | | OString const & VMLExport::AddInlineSdrObject( const SdrObject& rObj, const bool bOOxmlExport ) |
1609 | 0 | { |
1610 | 0 | m_pSdrObject = &rObj; |
1611 | 0 | m_eHOri = -1; |
1612 | 0 | m_eVOri = -1; |
1613 | 0 | m_eHRel = -1; |
1614 | 0 | m_eVRel = -1; |
1615 | 0 | m_pWrapAttrList.clear(); |
1616 | 0 | m_bInline = true; |
1617 | 0 | m_IsFollowingTextFlow = true; |
1618 | 0 | EscherEx::AddSdrObject(rObj, bOOxmlExport); |
1619 | 0 | return m_sShapeId; |
1620 | 0 | } |
1621 | | |
1622 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |