Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */