Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/oox/source/drawingml/textparagraphpropertiescontext.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 <drawingml/textparagraphpropertiescontext.hxx>
21
22
#include <com/sun/star/text/WritingMode2.hpp>
23
#include <com/sun/star/style/ParagraphAdjust.hpp>
24
#include <com/sun/star/xml/sax/SAXException.hpp>
25
#include <com/sun/star/graphic/XGraphic.hpp>
26
#include <com/sun/star/awt/Size.hpp>
27
#include <com/sun/star/uno/Reference.hxx>
28
29
#include <sal/log.hxx>
30
#include <comphelper/diagnose_ex.hxx>
31
#include <tools/UnitConversion.hxx>
32
33
#include <drawingml/colorchoicecontext.hxx>
34
#include <drawingml/misccontexts.hxx>
35
#include <drawingml/textcharacterpropertiescontext.hxx>
36
#include <drawingml/fillproperties.hxx>
37
#include <oox/helper/attributelist.hxx>
38
#include "textspacingcontext.hxx"
39
#include "texttabstoplistcontext.hxx"
40
#include <oox/token/namespaces.hxx>
41
#include <oox/token/properties.hxx>
42
#include <oox/token/tokens.hxx>
43
44
using namespace ::oox::core;
45
using namespace ::com::sun::star::uno;
46
using namespace ::com::sun::star::xml::sax;
47
using namespace ::com::sun::star::style;
48
using namespace ::com::sun::star::text;
49
using namespace ::com::sun::star::graphic;
50
51
namespace oox::drawingml {
52
namespace {
53
54
double  lclGetGraphicAspectRatio( const Reference< XGraphic >& rxGraphic )
55
0
{
56
0
    double fRatio = 1.0;
57
0
    Reference< css::beans::XPropertySet > xGraphicPropertySet( rxGraphic, UNO_QUERY_THROW );
58
0
    css::awt::Size aSizeHmm( 0, 0 );
59
0
    xGraphicPropertySet->getPropertyValue( u"Size100thMM"_ustr ) >>= aSizeHmm;
60
61
0
    if( aSizeHmm.Width > 0 && aSizeHmm.Height > 0)
62
0
        return double(aSizeHmm.Width)/double(aSizeHmm.Height);
63
0
    else
64
0
    {
65
0
        css::awt::Size aSourceSizePixel( 0, 0 );
66
0
        xGraphicPropertySet->getPropertyValue( u"SizePixel"_ustr ) >>= aSourceSizePixel;
67
68
0
        if( aSourceSizePixel.Width > 0 && aSourceSizePixel.Height > 0 )
69
0
            return double(aSourceSizePixel.Width)/double(aSourceSizePixel.Height);
70
0
    }
71
0
    return fRatio;
72
0
}
73
74
} //namespace
75
76
// CT_TextParagraphProperties
77
TextParagraphPropertiesContext::TextParagraphPropertiesContext( ContextHandler2Helper const & rParent,
78
                                                                const AttributeList& rAttribs,
79
                                                                TextParagraphProperties& rTextParagraphProperties,
80
                                                                uint16_t* pListNumberingMask)
81
296k
: ContextHandler2( rParent )
82
296k
, mrTextParagraphProperties( rTextParagraphProperties )
83
296k
, mrBulletList( rTextParagraphProperties.getBulletList() )
84
296k
, mpListNumberingMask( pListNumberingMask )
85
296k
, mbHaveNum( false )
86
296k
{
87
296k
    OUString sValue;
88
89
296k
    PropertyMap& rPropertyMap( mrTextParagraphProperties.getTextParagraphPropertyMap() );
90
91
    // ST_TextAlignType
92
296k
    if ( rAttribs.hasAttribute( XML_algn ) )
93
160k
    {
94
160k
        mrTextParagraphProperties.getParaAdjust() = GetParaAdjust( rAttribs.getToken( XML_algn, XML_l ) );
95
160k
    }
96
    // TODO see to do the same with RubyAdjust
97
98
    // ST_Coordinate32
99
296k
    if ( rAttribs.hasAttribute(XML_defTabSz))
100
106k
    {
101
106k
        sValue = rAttribs.getStringDefaulted(XML_defTabSz);
102
106k
        if(!sValue.isEmpty())
103
106k
        {
104
106k
            mrTextParagraphProperties.getDefaultTabSize() = GetCoordinate(sValue);
105
106k
        }
106
106k
    }
107
108
//  bool bEaLineBrk = rAttribs.getBool( XML_eaLnBrk, true );
109
296k
    if ( rAttribs.hasAttribute( XML_latinLnBrk ) )
110
105k
    {
111
105k
        bool bLatinLineBrk = rAttribs.getBool( XML_latinLnBrk, true );
112
105k
        rPropertyMap.setProperty( PROP_ParaIsHyphenation, bLatinLineBrk);
113
105k
    }
114
    // TODO see what to do with Asian hyphenation
115
116
    // ST_TextFontAlignType
117
    // TODO
118
//  sal_Int32 nFontAlign = rAttribs.getToken( XML_fontAlgn, XML_base );
119
120
296k
    if ( rAttribs.hasAttribute( XML_hangingPunct ) )
121
120k
    {
122
120k
        bool bHangingPunct = rAttribs.getBool( XML_hangingPunct, false );
123
120k
        rPropertyMap.setProperty( PROP_ParaIsHangingPunctuation, bHangingPunct);
124
120k
    }
125
126
  // ST_Coordinate
127
296k
    if ( rAttribs.hasAttribute( XML_indent ) )
128
81.5k
    {
129
81.5k
        sValue = rAttribs.getStringDefaulted( XML_indent );
130
81.5k
        mrTextParagraphProperties.getFirstLineIndentation() = std::optional< sal_Int32 >( sValue.isEmpty() ? 0 : GetCoordinate( sValue ) );
131
81.5k
    }
132
133
  // ST_TextIndentLevelType
134
    // -1 is an invalid value and denote the lack of level
135
296k
    sal_Int32 nLevel = rAttribs.getInteger( XML_lvl, 0 );
136
296k
    if( nLevel > 8 || nLevel < 0 )
137
0
    {
138
0
        nLevel = 0;
139
0
    }
140
141
296k
    mrTextParagraphProperties.setLevel( static_cast< sal_Int16 >( nLevel ) );
142
143
296k
    char name[] = "Outline X";
144
296k
    name[8] = static_cast<char>( '1' + nLevel );
145
296k
    const OUString sStyleNameValue( OUString::createFromAscii( name ) );
146
296k
    mrBulletList.setStyleName( sStyleNameValue );
147
148
    // ST_TextMargin
149
    // ParaLeftMargin
150
296k
    if ( rAttribs.hasAttribute( XML_marL ) )
151
148k
    {
152
148k
        sValue = rAttribs.getStringDefaulted( XML_marL );
153
148k
        mrTextParagraphProperties.getParaLeftMargin() = std::optional< sal_Int32 >( sValue.isEmpty() ? 0 : GetCoordinate( sValue ) );
154
148k
    }
155
156
    // ParaRightMargin
157
296k
    if ( rAttribs.hasAttribute( XML_marR ) )
158
1.75k
    {
159
1.75k
        sValue = rAttribs.getStringDefaulted( XML_marR );
160
1.75k
        sal_Int32 nMarR  = sValue.isEmpty() ? 0 : GetCoordinate( sValue ) ;
161
1.75k
        rPropertyMap.setProperty( PROP_ParaRightMargin, nMarR);
162
1.75k
    }
163
164
296k
    if ( rAttribs.hasAttribute( XML_rtl ) )
165
122k
    {
166
122k
        bool bRtl = rAttribs.getBool( XML_rtl, false );
167
122k
        rPropertyMap.setProperty( PROP_WritingMode, ( bRtl ? WritingMode2::RL_TB : WritingMode2::LR_TB ));
168
122k
    }
169
296k
}
170
171
TextParagraphPropertiesContext::~TextParagraphPropertiesContext()
172
296k
{
173
296k
    PropertyMap& rPropertyMap( mrTextParagraphProperties.getTextParagraphPropertyMap() );
174
296k
    if ( mrTextParagraphProperties.getLineSpacing().bHasValue )
175
47.3k
        rPropertyMap.setProperty( PROP_ParaLineSpacing, mrTextParagraphProperties.getLineSpacing().toLineSpacing());
176
248k
    else
177
248k
        rPropertyMap.setProperty( PROP_ParaLineSpacing, css::style::LineSpacing( css::style::LineSpacingMode::PROP, 100 ));
178
179
296k
    ::std::vector< TabStop >::size_type nTabCount = maTabList.size();
180
296k
    if( nTabCount != 0 )
181
216
    {
182
216
        Sequence< TabStop > aSeq( nTabCount );
183
216
        TabStop * aArray = aSeq.getArray();
184
216
        OSL_ENSURE( aArray != nullptr, "sequence array is NULL" );
185
216
        ::std::copy( maTabList.begin(), maTabList.end(), aArray );
186
216
        rPropertyMap.setProperty( PROP_ParaTabStops, aSeq);
187
216
    }
188
189
296k
    if (mxBlipProps && mxBlipProps->mxFillGraphic.is())
190
0
    {
191
0
        mrBulletList.setGraphic( mxBlipProps->mxFillGraphic );
192
0
        mrBulletList.setBulletAspectRatio( lclGetGraphicAspectRatio(mxBlipProps->mxFillGraphic) );
193
0
    }
194
195
296k
    if( !mbHaveNum )
196
296k
        markListUnnumbered();
197
296k
    if( mrBulletList.is() )
198
118k
        rPropertyMap.setProperty( PROP_IsNumbering, true);
199
296k
    sal_Int16 nLevel = mrTextParagraphProperties.getLevel();
200
296k
    rPropertyMap.setProperty( PROP_NumberingLevel, nLevel);
201
296k
    rPropertyMap.setProperty( PROP_NumberingIsNumber, true);
202
203
296k
    if( mrTextParagraphProperties.getParaAdjust() )
204
160k
        rPropertyMap.setProperty( PROP_ParaAdjust, *mrTextParagraphProperties.getParaAdjust());
205
296k
}
206
207
bool TextParagraphPropertiesContext::markListNumbered()
208
50
{
209
50
    sal_Int16 nLevel = mrTextParagraphProperties.getLevel();
210
211
    // We only track list state in some situations
212
50
    if (mpListNumberingMask == nullptr)
213
0
    {
214
0
        return false;
215
0
    }
216
217
50
    uint16_t nOldMask = *mpListNumberingMask;
218
    // The bit that represents this level  (..0001000)
219
50
    uint16_t nOurBit = static_cast<uint16_t>(1) << nLevel;
220
221
50
    uint16_t nTmp = nOldMask;
222
    // Clears all bits at our level and lower (..0000xxx)
223
50
    nTmp &= nOurBit - 1;
224
    // and put our bit in (..0001xxx)
225
50
    nTmp |= nOurBit;
226
227
50
    *mpListNumberingMask = nTmp;
228
229
50
    return (nOldMask & nOurBit) == 0;
230
50
}
231
232
void TextParagraphPropertiesContext::markListUnnumbered()
233
296k
{
234
296k
    sal_Int16 nLevel = mrTextParagraphProperties.getLevel();
235
236
    // We only track list state in some situations
237
296k
    if (mpListNumberingMask == nullptr)
238
186k
    {
239
186k
        return;
240
186k
    }
241
242
109k
    uint16_t nOldMask = *mpListNumberingMask;
243
    // The bit that represents this level  (..0001000)
244
109k
    uint16_t nOurBit = static_cast<uint16_t>(1) << nLevel;
245
246
109k
    uint16_t nTmp = nOldMask;
247
    // Clears all bits at our level and lower (..0000xxx)
248
109k
    nTmp &= nOurBit - 1;
249
250
109k
    *mpListNumberingMask = nTmp;
251
109k
}
252
253
ContextHandlerRef TextParagraphPropertiesContext::onCreateContext( sal_Int32 aElementToken, const AttributeList& rAttribs )
254
569k
{
255
569k
    switch( aElementToken )
256
569k
    {
257
47.3k
        case A_TOKEN( lnSpc ):          // CT_TextSpacing
258
47.3k
            return new TextSpacingContext( *this, mrTextParagraphProperties.getLineSpacing() );
259
68.2k
        case A_TOKEN( spcBef ):         // CT_TextSpacing
260
68.2k
            return new TextSpacingContext( *this, mrTextParagraphProperties.getParaTopMargin() );
261
19.7k
        case A_TOKEN( spcAft ):         // CT_TextSpacing
262
19.7k
            return new TextSpacingContext( *this, mrTextParagraphProperties.getParaBottomMargin() );
263
        // EG_TextBulletColor
264
1.51k
        case A_TOKEN( buClrTx ):        // CT_TextBulletColorFollowText ???
265
1.51k
            mrBulletList.mbBulletColorFollowText <<= true;
266
1.51k
            break;
267
7.14k
        case A_TOKEN( buClr ):          // CT_Color
268
7.14k
            return new ColorContext(*this, mrBulletList.maBulletColorPtr.get());
269
        // EG_TextBulletSize
270
1.51k
        case A_TOKEN( buSzTx ):         // CT_TextBulletSizeFollowText
271
1.51k
            mrBulletList.mbBulletSizeFollowText <<= true;
272
1.51k
            break;
273
31.4k
        case A_TOKEN( buSzPct ):        // CT_TextBulletSizePercent
274
31.4k
            mrBulletList.setBulletSize( std::lround( GetPercent( rAttribs.getStringDefaulted( XML_val ) ) / 1000.f ) );
275
31.4k
            break;
276
0
        case A_TOKEN( buSzPts ):        // CT_TextBulletSizePoint
277
0
            mrBulletList.setBulletSize(0);
278
0
            mrBulletList.setFontSize( static_cast<sal_Int16>(GetTextSize( rAttribs.getStringDefaulted( XML_val ) ) ) );
279
0
            break;
280
281
        // EG_TextBulletTypeface
282
1.55k
        case A_TOKEN( buFontTx ):       // CT_TextBulletTypefaceFollowText
283
1.55k
            mrBulletList.mbBulletFontFollowText <<= true;
284
1.55k
            break;
285
82.3k
        case A_TOKEN( buFont ):         // CT_TextFont
286
82.3k
            mrBulletList.maBulletFont.setAttributes( rAttribs );
287
82.3k
            break;
288
289
        // EG_TextBullet
290
35.9k
        case A_TOKEN( buNone ):         // CT_TextNoBullet
291
35.9k
            mrBulletList.setNone();
292
35.9k
            break;
293
50
        case A_TOKEN( buAutoNum ):      // CT_TextAutonumberBullet
294
50
        {
295
50
            bool bStartingNumList = markListNumbered();
296
50
            mbHaveNum = true;
297
298
50
            try {
299
50
                sal_Int32 nType = rAttribs.getToken( XML_type, 0 );
300
50
                sal_Int32 nStartAt = rAttribs.getInteger( XML_startAt, -1 );
301
50
                if( nStartAt >= 0 || bStartingNumList )
302
50
                {
303
50
                    mrTextParagraphProperties.setRestartNumbering(true);
304
50
                }
305
306
50
                if( nStartAt > 32767 )
307
0
                {
308
0
                    nStartAt = 32767;
309
0
                }
310
50
                else if( nStartAt < 1 )
311
0
                {
312
0
                    nStartAt = 1;
313
0
                }
314
50
                mrBulletList.setStartAt( nStartAt );
315
50
                mrBulletList.setType( nType );
316
50
            }
317
50
            catch(SAXException& /* e */ )
318
50
            {
319
0
                TOOLS_WARN_EXCEPTION("oox", "OOX: SAXException in XML_buAutoNum");
320
0
            }
321
50
            break;
322
50
        }
323
82.7k
        case A_TOKEN( buChar ):         // CT_TextCharBullet
324
82.7k
            try {
325
326
82.7k
                mrBulletList.setBulletChar( rAttribs.getStringDefaulted( XML_char ) );
327
82.7k
                mrBulletList.setSuffixNone();
328
82.7k
            }
329
82.7k
            catch(SAXException& /* e */)
330
82.7k
            {
331
0
                TOOLS_WARN_EXCEPTION("oox", "OOX: SAXException in XML_buChar");
332
0
            }
333
82.7k
            break;
334
0
        case A_TOKEN( buBlip ):         // CT_TextBlipBullet
335
0
            {
336
0
                mxBlipProps = std::make_shared<BlipFillProperties>();
337
0
                return new BlipFillContext(*this, rAttribs, *mxBlipProps, nullptr);
338
82.7k
            }
339
2.30k
        case A_TOKEN( tabLst ):         // CT_TextTabStopList
340
2.30k
            return new TextTabStopListContext( *this, maTabList );
341
187k
        case A_TOKEN( defRPr ):         // CT_TextCharacterProperties
342
187k
            return new TextCharacterPropertiesContext( *this, rAttribs, mrTextParagraphProperties.getTextCharacterProperties() );
343
0
        case W_TOKEN( jc ):
344
0
            {
345
0
                std::optional< OUString > oParaAdjust = rAttribs.getString( W_TOKEN(val) );
346
0
                if( oParaAdjust.has_value() && !oParaAdjust.value().isEmpty() )
347
0
                {
348
0
                    const OUString& sParaAdjust = oParaAdjust.value();
349
0
                    if( sParaAdjust == "left" )
350
0
                        mrTextParagraphProperties.setParaAdjust(ParagraphAdjust_LEFT);
351
0
                    else if ( sParaAdjust == "right" )
352
0
                        mrTextParagraphProperties.setParaAdjust(ParagraphAdjust_RIGHT);
353
0
                    else if ( sParaAdjust == "center" )
354
0
                        mrTextParagraphProperties.setParaAdjust(ParagraphAdjust_CENTER);
355
0
                    else if ( sParaAdjust == "both" )
356
0
                        mrTextParagraphProperties.setParaAdjust(ParagraphAdjust_BLOCK);
357
0
                }
358
0
            }
359
0
            break;
360
0
        case W_TOKEN( spacing ):
361
0
            {
362
                // Spacing before
363
0
                if( !rAttribs.getBool(W_TOKEN(beforeAutospacing), false) )
364
0
                {
365
0
                    std::optional<sal_Int32> oBefore = rAttribs.getInteger(W_TOKEN(before));
366
0
                    if (oBefore.has_value())
367
0
                    {
368
0
                        TextSpacing& rSpacing = mrTextParagraphProperties.getParaTopMargin();
369
0
                        rSpacing.nUnit = TextSpacing::Unit::Points;
370
0
                        rSpacing.nValue = convertTwipToMm100(oBefore.value());
371
0
                        rSpacing.bHasValue = true;
372
0
                    }
373
0
                    else
374
0
                    {
375
0
                        std::optional<sal_Int32> oBeforeLines = rAttribs.getInteger(W_TOKEN(beforeLines));
376
0
                        if (oBeforeLines.has_value())
377
0
                        {
378
0
                            TextSpacing& rSpacing = mrTextParagraphProperties.getParaTopMargin();
379
0
                            rSpacing.nUnit = TextSpacing::Unit::Percent;
380
0
                            rSpacing.nValue = oBeforeLines.value() * MAX_PERCENT / 100;
381
0
                            rSpacing.bHasValue = true;
382
0
                        }
383
0
                    }
384
0
                }
385
386
                // Spacing after
387
0
                if( !rAttribs.getBool(W_TOKEN(afterAutospacing), false) )
388
0
                {
389
0
                    std::optional<sal_Int32> oAfter = rAttribs.getInteger(W_TOKEN(after));
390
0
                    if (oAfter.has_value())
391
0
                    {
392
0
                        TextSpacing& rSpacing = mrTextParagraphProperties.getParaBottomMargin();
393
0
                        rSpacing.nUnit = TextSpacing::Unit::Points;
394
0
                        rSpacing.nValue = convertTwipToMm100(oAfter.value());
395
0
                        rSpacing.bHasValue = true;
396
0
                    }
397
0
                    else
398
0
                    {
399
0
                        std::optional<sal_Int32> oAfterLines = rAttribs.getInteger(W_TOKEN(afterLines));
400
0
                        if (oAfterLines.has_value())
401
0
                        {
402
0
                            TextSpacing& rSpacing = mrTextParagraphProperties.getParaBottomMargin();
403
0
                            rSpacing.nUnit = TextSpacing::Unit::Percent;
404
0
                            rSpacing.nValue = oAfterLines.value() * MAX_PERCENT / 100;
405
0
                            rSpacing.bHasValue = true;
406
0
                        }
407
0
                    }
408
0
                }
409
410
                // Line spacing
411
0
                std::optional<OUString> oLineRule = rAttribs.getString(W_TOKEN(lineRule));
412
0
                std::optional<sal_Int32> oLineSpacing = rAttribs.getInteger(W_TOKEN(line));
413
0
                if (oLineSpacing.has_value())
414
0
                {
415
0
                    TextSpacing& rLineSpacing = mrTextParagraphProperties.getLineSpacing();
416
0
                    if( !oLineRule.has_value() || oLineRule.value() == "auto" )
417
0
                    {
418
0
                        rLineSpacing.nUnit = TextSpacing::Unit::Percent;
419
0
                        rLineSpacing.nValue = oLineSpacing.value() * MAX_PERCENT / 240;
420
0
                    }
421
0
                    else
422
0
                    {
423
0
                        rLineSpacing.nUnit = TextSpacing::Unit::Points;
424
0
                        rLineSpacing.nValue = convertTwipToMm100(oLineSpacing.value());
425
0
                    }
426
0
                    rLineSpacing.bHasValue = true;
427
0
                }
428
0
            }
429
0
            break;
430
0
        default:
431
0
            SAL_WARN("oox", "TextParagraphPropertiesContext::onCreateContext: unhandled element: " << getBaseToken(aElementToken));
432
569k
    }
433
237k
    return this;
434
569k
}
435
436
}
437
438
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */