Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/cppcanvas/source/mtfrenderer/textaction.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
21
#include <comphelper/diagnose_ex.hxx>
22
23
#include <com/sun/star/rendering/PathCapType.hpp>
24
#include <com/sun/star/rendering/PathJoinType.hpp>
25
#include <com/sun/star/rendering/XCanvas.hpp>
26
#include <com/sun/star/rendering/XCanvasFont.hpp>
27
28
#include <basegfx/matrix/b2dhommatrix.hxx>
29
#include <basegfx/range/b2drectangle.hxx>
30
#include <basegfx/vector/b2dsize.hxx>
31
#include <basegfx/polygon/b2dpolypolygontools.hxx>
32
#include <basegfx/polygon/b2dpolygontools.hxx>
33
#include <basegfx/matrix/b2dhommatrixtools.hxx>
34
35
#include <tools/gen.hxx>
36
#include <utility>
37
#include <vcl/canvastools.hxx>
38
#include <vcl/virdev.hxx>
39
40
#include <basegfx/utils/canvastools.hxx>
41
#include <canvas/canvastools.hxx>
42
#include <memory>
43
#include <sal/log.hxx>
44
45
#include "textaction.hxx"
46
#include "textlineshelper.hxx"
47
#include <outdevstate.hxx>
48
#include "mtftools.hxx"
49
50
51
using namespace ::com::sun::star;
52
53
namespace cppcanvas::internal
54
{
55
        namespace
56
        {
57
            void init( rendering::RenderState&                  o_rRenderState,
58
                       const ::basegfx::B2DPoint&               rStartPoint,
59
                       const OutDevState&                       rState,
60
                       const CanvasSharedPtr&                   rCanvas      )
61
0
            {
62
0
                cppcanvastools::initRenderState(o_rRenderState,rState);
63
64
                // #i36950# Offset clip back to origin (as it's also moved
65
                // by rStartPoint)
66
                // #i53964# Also take VCL font rotation into account,
67
                // since this, opposed to the FontMatrix rotation
68
                // elsewhere, _does_ get incorporated into the render
69
                // state transform.
70
0
                cppcanvastools::modifyClip( o_rRenderState,
71
0
                                   rState,
72
0
                                   rCanvas,
73
0
                                   rStartPoint,
74
0
                                   nullptr,
75
0
                                   &rState.fontRotation );
76
77
0
                basegfx::B2DHomMatrix aLocalTransformation(basegfx::utils::createRotateB2DHomMatrix(rState.fontRotation));
78
0
                aLocalTransformation.translate( rStartPoint.getX(),
79
0
                                                rStartPoint.getY() );
80
0
                ::canvastools::appendToRenderState( o_rRenderState,
81
0
                                                      aLocalTransformation );
82
83
0
                o_rRenderState.DeviceColor = rState.textColor;
84
0
            }
85
86
            void init( rendering::RenderState&                  o_rRenderState,
87
                       const ::basegfx::B2DPoint&               rStartPoint,
88
                       const OutDevState&                       rState,
89
                       const CanvasSharedPtr&                   rCanvas,
90
                       const ::basegfx::B2DHomMatrix&           rTextTransform  )
91
0
            {
92
0
                init( o_rRenderState, rStartPoint, rState, rCanvas );
93
94
                // TODO(F2): Also inversely-transform clip with
95
                // rTextTransform (which is actually rather hard, as the
96
                // text transform is _prepended_ to the render state)!
97
98
                // prepend extra font transform to render state
99
                // (prepend it, because it's interpreted in the unit
100
                // rect coordinate space)
101
0
                ::canvastools::prependToRenderState( o_rRenderState,
102
0
                                                       rTextTransform );
103
0
            }
104
105
            void init( rendering::RenderState&                      o_rRenderState,
106
                       uno::Reference< rendering::XCanvasFont >&    o_rFont,
107
                       const ::basegfx::B2DPoint&                   rStartPoint,
108
                       const OutDevState&                           rState,
109
                       const CanvasSharedPtr&                       rCanvas      )
110
0
            {
111
                // ensure that o_rFont is valid. It is possible that
112
                // text actions are generated without previously
113
                // setting a font. Then, just take a default font
114
0
                if( !o_rFont.is() )
115
0
                {
116
                    // Use completely default FontRequest
117
0
                    const rendering::FontRequest aFontRequest;
118
119
0
                    geometry::Matrix2D aFontMatrix;
120
0
                    ::canvastools::setIdentityMatrix2D( aFontMatrix );
121
122
0
                    o_rFont = rCanvas->getUNOCanvas()->createFont(
123
0
                        aFontRequest,
124
0
                        uno::Sequence< beans::PropertyValue >(),
125
0
                        aFontMatrix );
126
0
                }
127
128
0
                init( o_rRenderState,
129
0
                      rStartPoint,
130
0
                      rState,
131
0
                      rCanvas );
132
0
            }
133
134
            void init( rendering::RenderState&                      o_rRenderState,
135
                       uno::Reference< rendering::XCanvasFont >&    o_rFont,
136
                       const ::basegfx::B2DPoint&                   rStartPoint,
137
                       const OutDevState&                           rState,
138
                       const CanvasSharedPtr&                       rCanvas,
139
                       const ::basegfx::B2DHomMatrix&               rTextTransform  )
140
0
            {
141
0
                init( o_rRenderState, o_rFont, rStartPoint, rState, rCanvas );
142
143
                // TODO(F2): Also inversely-transform clip with
144
                // rTextTransform (which is actually rather hard, as the
145
                // text transform is _prepended_ to the render state)!
146
147
                // prepend extra font transform to render state
148
                // (prepend it, because it's interpreted in the unit
149
                // rect coordinate space)
150
0
                ::canvastools::prependToRenderState( o_rRenderState,
151
0
                                                       rTextTransform );
152
0
            }
153
154
            void initLayoutWidth(double& rLayoutWidth, const uno::Sequence<double>& rOffsets)
155
0
            {
156
0
                ENSURE_OR_THROW(rOffsets.hasElements(),
157
0
                                  "::cppcanvas::internal::initLayoutWidth(): zero-length array" );
158
0
                rLayoutWidth = *(std::max_element(rOffsets.begin(), rOffsets.end()));
159
0
            }
160
161
            uno::Sequence< double > setupDXArray( KernArraySpan    rCharWidths,
162
                                                  sal_Int32          nLen,
163
                                                  const OutDevState& rState )
164
0
            {
165
                // convert character widths from logical units
166
0
                uno::Sequence< double > aCharWidthSeq( nLen );
167
0
                double*                 pOutputWidths( aCharWidthSeq.getArray() );
168
169
                // #143885# maintain (nearly) full precision of DX
170
                // array, by circumventing integer-based
171
                // OutDev-mapping
172
0
                const double nScale( rState.mapModeTransform.get(0,0) );
173
0
                for( int i = 0; i < nLen; ++i )
174
0
                {
175
                    // TODO(F2): use correct scale direction
176
0
                    *pOutputWidths++ = rCharWidths[i] * nScale;
177
0
                }
178
179
0
                return aCharWidthSeq;
180
0
            }
181
182
            uno::Sequence< double > setupDXArray( const OUString&    rText,
183
                                                  sal_Int32          nStartPos,
184
                                                  sal_Int32          nLen,
185
                                                  VirtualDevice const & rVDev,
186
                                                  const OutDevState& rState )
187
0
            {
188
                // no external DX array given, create one from given
189
                // string
190
0
                KernArray aCharWidths;
191
192
0
                rVDev.GetTextArray( rText, &aCharWidths, nStartPos, nLen );
193
194
0
                return setupDXArray( aCharWidths, nLen, rState );
195
0
            }
196
197
            ::basegfx::B2DPoint adaptStartPoint( const ::basegfx::B2DPoint&     rStartPoint,
198
                                                 const OutDevState&             rState,
199
                                                 const uno::Sequence< double >& rOffsets )
200
0
            {
201
0
                ::basegfx::B2DPoint aLocalPoint( rStartPoint );
202
203
0
                if( rState.textAlignment )
204
0
                {
205
                    // text origin is right, not left. Modify start point
206
                    // accordingly, because XCanvas::drawTextLayout()
207
                    // always aligns left!
208
209
0
                    const double nOffset( rOffsets[ rOffsets.getLength()-1 ] );
210
211
                    // correct start point for rotated text: rotate around
212
                    // former start point
213
0
                    aLocalPoint.setX( aLocalPoint.getX() + cos( rState.fontRotation )*nOffset );
214
0
                    aLocalPoint.setY( aLocalPoint.getY() + sin( rState.fontRotation )*nOffset );
215
0
                }
216
217
0
                return aLocalPoint;
218
0
            }
219
220
            /** Perform common setup for array text actions
221
222
                This method creates the XTextLayout object and
223
                initializes it, e.g. with the logical advancements.
224
             */
225
            void initArrayAction( rendering::RenderState&                   o_rRenderState,
226
                                  uno::Reference< rendering::XTextLayout >& o_rTextLayout,
227
                                  const ::basegfx::B2DPoint&                rStartPoint,
228
                                  const OUString&                    rText,
229
                                  sal_Int32                                 nStartPos,
230
                                  sal_Int32                                 nLen,
231
                                  const uno::Sequence< double >&            rOffsets,
232
                                  const uno::Sequence< sal_Bool >&          rKashidas,
233
                                  const CanvasSharedPtr&                    rCanvas,
234
                                  const OutDevState&                        rState,
235
                                  const ::basegfx::B2DHomMatrix*            pTextTransform )
236
0
            {
237
0
                ENSURE_OR_THROW( rOffsets.hasElements(),
238
0
                                  "::cppcanvas::internal::initArrayAction(): zero-length DX array" );
239
240
0
                const ::basegfx::B2DPoint aLocalStartPoint(
241
0
                    adaptStartPoint( rStartPoint, rState, rOffsets ) );
242
243
0
                uno::Reference< rendering::XCanvasFont > xFont( rState.xFont );
244
245
0
                if( pTextTransform )
246
0
                    init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas, *pTextTransform );
247
0
                else
248
0
                    init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas );
249
250
0
                o_rTextLayout = xFont->createTextLayout(
251
0
                    rendering::StringContext( rText, nStartPos, nLen ),
252
0
                    rState.textDirection,
253
0
                    0 );
254
255
0
                ENSURE_OR_THROW( o_rTextLayout.is(),
256
0
                                  "::cppcanvas::internal::initArrayAction(): Invalid font" );
257
258
0
                o_rTextLayout->applyLogicalAdvancements( rOffsets );
259
0
                o_rTextLayout->applyKashidaPositions( rKashidas );
260
261
0
            }
262
263
            double getLineWidth( ::VirtualDevice const &         rVDev,
264
                                 const OutDevState&              rState,
265
                                 const rendering::StringContext& rStringContext )
266
0
            {
267
                // TODO(F2): use correct scale direction
268
0
                const ::basegfx::B2DSize aSize( rVDev.GetTextWidth( rStringContext.Text,
269
0
                                                                    static_cast<sal_uInt16>(rStringContext.StartPosition),
270
0
                                                                    static_cast<sal_uInt16>(rStringContext.Length) ),
271
0
                                    0 );
272
273
0
                return (rState.mapModeTransform * aSize).getWidth();
274
0
            }
275
276
            uno::Sequence< double >
277
                calcSubsetOffsets( rendering::RenderState&                          io_rRenderState,
278
                                   double&                                          o_rMinPos,
279
                                   double&                                          o_rMaxPos,
280
                                   const uno::Reference< rendering::XTextLayout >&  rOrigTextLayout,
281
                                   double                                           nLayoutWidth,
282
                                   const ::cppcanvas::internal::Action::Subset&     rSubset )
283
0
            {
284
0
                ENSURE_OR_THROW( rSubset.mnSubsetEnd > rSubset.mnSubsetBegin,
285
0
                                  "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
286
287
0
                uno::Sequence< double > aOrigOffsets( rOrigTextLayout->queryLogicalAdvancements() );
288
0
                const double*           pOffsets( aOrigOffsets.getConstArray() );
289
290
0
                ENSURE_OR_THROW( aOrigOffsets.getLength() >= rSubset.mnSubsetEnd,
291
0
                                  "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
292
293
294
                // determine leftmost position in given subset range -
295
                // as the DX array contains the output positions
296
                // starting with the second character (the first is
297
                // assumed to have output position 0), correct begin
298
                // iterator.
299
0
                const double nMinPos( rSubset.mnSubsetBegin <= 0 ? 0 :
300
0
                                      *(std::min_element( pOffsets+rSubset.mnSubsetBegin-1,
301
0
                                                            pOffsets+rSubset.mnSubsetEnd )) );
302
303
                // determine rightmost position in given subset range
304
                // - as the DX array contains the output positions
305
                // starting with the second character (the first is
306
                // assumed to have output position 0), correct begin
307
                // iterator.
308
0
                const double nMaxPos(
309
0
                    *(std::max_element( pOffsets + (rSubset.mnSubsetBegin <= 0 ?
310
0
                                                      0 : rSubset.mnSubsetBegin-1),
311
0
                                          pOffsets + rSubset.mnSubsetEnd )) );
312
313
                // Logical advancements always increase in logical text order.
314
                // For RTL text, nMaxPos is the distance from the right edge to
315
                // the leftmost position in the subset, so we have to convert
316
                // it to the offset from the origin (i.e. left edge ).
317
                // LTR: |---- min --->|---- max --->|            |
318
                // RTL: |             |<--- max ----|<--- min ---|
319
                //      |<- nOffset ->|                          |
320
0
                const double nOffset = rOrigTextLayout->getMainTextDirection()
321
0
                    ? nLayoutWidth - nMaxPos : nMinPos;
322
323
324
                // adapt render state, to move text output to given offset
325
326
327
                // TODO(F1): Strictly speaking, we also have to adapt
328
                // the clip here, which normally should _not_ move
329
                // with the output offset. Neglected for now, as it
330
                // does not matter for drawing layer output
331
332
0
                if (nOffset > 0.0)
333
0
                {
334
0
                    ::basegfx::B2DHomMatrix aTranslation;
335
0
                    if( rOrigTextLayout->getFont()->getFontRequest().FontDescription.IsVertical == css::util::TriState_YES )
336
0
                    {
337
                        // vertical text -> offset in y direction
338
0
                        aTranslation.translate(0.0, nOffset);
339
0
                    }
340
0
                    else
341
0
                    {
342
                        // horizontal text -> offset in x direction
343
0
                        aTranslation.translate(nOffset, 0.0);
344
0
                    }
345
346
0
                    ::canvastools::appendToRenderState( io_rRenderState,
347
0
                                                          aTranslation );
348
0
                }
349
350
351
                // reduce DX array to given substring
352
353
354
0
                const sal_Int32         nNewElements( rSubset.mnSubsetEnd - rSubset.mnSubsetBegin );
355
0
                uno::Sequence< double > aAdaptedOffsets( nNewElements );
356
0
                double*                 pAdaptedOffsets( aAdaptedOffsets.getArray() );
357
358
                // move to new output position (subtract nMinPos,
359
                // which is the new '0' position), copy only the range
360
                // as given by rSubset.
361
0
                std::transform( pOffsets + rSubset.mnSubsetBegin,
362
0
                                  pOffsets + rSubset.mnSubsetEnd,
363
0
                                  pAdaptedOffsets,
364
0
                                  [nMinPos](double aPos) { return aPos - nMinPos; } );
365
366
0
                o_rMinPos = nMinPos;
367
0
                o_rMaxPos = nMaxPos;
368
369
0
                return aAdaptedOffsets;
370
0
            }
371
372
            uno::Reference< rendering::XTextLayout >
373
                createSubsetLayout( const rendering::StringContext&                 rOrigContext,
374
                                    const ::cppcanvas::internal::Action::Subset&    rSubset,
375
                                    const uno::Reference< rendering::XTextLayout >& rOrigTextLayout )
376
0
            {
377
                // create temporary new text layout with subset string
378
379
380
0
                const sal_Int32 nNewStartPos( rOrigContext.StartPosition + std::min(
381
0
                                                  rSubset.mnSubsetBegin, rOrigContext.Length-1 ) );
382
0
                const sal_Int32 nNewLength( std::max(
383
0
                                                std::min(
384
0
                                                    rSubset.mnSubsetEnd - rSubset.mnSubsetBegin,
385
0
                                                    rOrigContext.Length ),
386
0
                                                sal_Int32( 0 ) ) );
387
388
0
                const rendering::StringContext aContext( rOrigContext.Text,
389
0
                                                         nNewStartPos,
390
0
                                                         nNewLength );
391
392
0
                uno::Reference< rendering::XTextLayout > xTextLayout(
393
0
                    rOrigTextLayout->getFont()->createTextLayout( aContext,
394
0
                                                                  rOrigTextLayout->getMainTextDirection(),
395
0
                                                                  0 ),
396
0
                    uno::UNO_SET_THROW );
397
398
0
                return xTextLayout;
399
0
            }
400
401
            /** Setup subset text layout
402
403
                @param io_rTextLayout
404
                Must contain original (full set) text layout on input,
405
                will contain subsetted text layout (or empty
406
                reference, for empty subsets) on output.
407
408
                @param io_rRenderState
409
                Must contain original render state on input, will
410
                contain shifted render state concatenated with
411
                rTransformation on output.
412
413
                @param rTransformation
414
                Additional transformation, to be prepended to render
415
                state
416
417
                @param rSubset
418
                Subset to prepare
419
             */
420
            void createSubsetLayout( uno::Reference< rendering::XTextLayout >&  io_rTextLayout,
421
                                     double                                     nLayoutWidth,
422
                                     rendering::RenderState&                    io_rRenderState,
423
                                     double&                                    o_rMinPos,
424
                                     double&                                    o_rMaxPos,
425
                                     const ::basegfx::B2DHomMatrix&             rTransformation,
426
                                     const Action::Subset&                      rSubset )
427
0
            {
428
0
                ::canvastools::prependToRenderState(io_rRenderState, rTransformation);
429
430
0
                if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
431
0
                {
432
                     // empty range, empty layout
433
0
                    io_rTextLayout.clear();
434
435
0
                    return;
436
0
                }
437
438
0
                ENSURE_OR_THROW( io_rTextLayout.is(),
439
0
                                  "createSubsetLayout(): Invalid input layout" );
440
441
0
                const rendering::StringContext aOrigContext( io_rTextLayout->getText() );
442
443
0
                if( rSubset.mnSubsetBegin == 0 &&
444
0
                    rSubset.mnSubsetEnd == aOrigContext.Length )
445
0
                {
446
                    // full range, no need for subsetting
447
0
                    return;
448
0
                }
449
450
0
                uno::Reference< rendering::XTextLayout > xTextLayout(
451
0
                    createSubsetLayout( aOrigContext, rSubset, io_rTextLayout ) );
452
453
0
                if( xTextLayout.is() )
454
0
                {
455
0
                    xTextLayout->applyLogicalAdvancements(
456
0
                        calcSubsetOffsets( io_rRenderState,
457
0
                                           o_rMinPos,
458
0
                                           o_rMaxPos,
459
0
                                           io_rTextLayout,
460
0
                                           nLayoutWidth,
461
0
                                           rSubset ) );
462
0
                    uno::Sequence< sal_Bool > aOrigKashidaPositions(io_rTextLayout->queryKashidaPositions());
463
0
                    uno::Sequence< sal_Bool > aKashidaPositions(aOrigKashidaPositions.getArray() + rSubset.mnSubsetBegin,
464
0
                                                                rSubset.mnSubsetEnd - rSubset.mnSubsetBegin);
465
0
                    xTextLayout->applyKashidaPositions(aKashidaPositions);
466
0
                }
467
468
0
                io_rTextLayout = std::move(xTextLayout);
469
0
            }
470
471
472
            /** Interface for renderEffectText functor below.
473
474
                This is interface is used from the renderEffectText()
475
                method below, to call the client implementation.
476
             */
477
            class TextRenderer
478
            {
479
            public:
480
0
                virtual ~TextRenderer() {}
481
482
                /// Render text with given RenderState
483
                virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const = 0;
484
            };
485
486
            /** Render effect text.
487
488
                @param rRenderer
489
                Functor object, will be called to render the actual
490
                part of the text effect (the text itself and the means
491
                to render it are unknown to this method)
492
             */
493
            bool renderEffectText( const TextRenderer&                          rRenderer,
494
                                   const rendering::RenderState&                rRenderState,
495
                                   const uno::Reference< rendering::XCanvas >&  xCanvas,
496
                                   const ::Color&                               rShadowColor,
497
                                   const ::basegfx::B2DSize&                    rShadowOffset,
498
                                   const ::Color&                               rReliefColor,
499
                                   const ::basegfx::B2DSize&                    rReliefOffset,
500
                                   const ::Color&                               rTextFillColor )
501
0
            {
502
0
                ::Color aEmptyColor( COL_AUTO );
503
0
                uno::Reference<rendering::XColorSpace> xColorSpace(
504
0
                    xCanvas->getDevice()->getDeviceColorSpace() );
505
506
                // draw shadow text, if enabled
507
0
                if( rShadowColor != aEmptyColor )
508
0
                {
509
0
                    rendering::RenderState aShadowState( rRenderState );
510
0
                    ::basegfx::B2DHomMatrix aTranslate;
511
512
0
                    aTranslate.translate(rShadowOffset.getWidth(),
513
0
                                         rShadowOffset.getHeight());
514
515
0
                    ::canvastools::appendToRenderState(aShadowState, aTranslate);
516
517
0
                    aShadowState.DeviceColor =
518
0
                        vcl::unotools::colorToDoubleSequence( rShadowColor,
519
0
                                                                xColorSpace );
520
521
0
                    rRenderer( aShadowState, rTextFillColor, false );
522
0
                }
523
524
                // draw relief text, if enabled
525
0
                if( rReliefColor != aEmptyColor )
526
0
                {
527
0
                    rendering::RenderState aReliefState( rRenderState );
528
0
                    ::basegfx::B2DHomMatrix aTranslate;
529
530
0
                    aTranslate.translate(rReliefOffset.getWidth(),
531
0
                                         rReliefOffset.getHeight());
532
533
0
                    ::canvastools::appendToRenderState(aReliefState, aTranslate);
534
535
0
                    aReliefState.DeviceColor =
536
0
                        vcl::unotools::colorToDoubleSequence( rReliefColor,
537
0
                                                                xColorSpace );
538
539
0
                    rRenderer( aReliefState, rTextFillColor, false );
540
0
                }
541
542
                // draw normal text
543
0
                rRenderer( rRenderState, rTextFillColor, true );
544
545
0
                return true;
546
0
            }
547
548
549
            ::basegfx::B2DRange calcEffectTextBounds( const ::basegfx::B2DRange&    rTextBounds,
550
                                                      const ::basegfx::B2DRange&    rLineBounds,
551
                                                      const ::basegfx::B2DSize&     rReliefOffset,
552
                                                      const ::basegfx::B2DSize&     rShadowOffset,
553
                                                      const rendering::RenderState& rRenderState,
554
                                                      const rendering::ViewState&   rViewState )
555
0
            {
556
0
                ::basegfx::B2DRange aBounds( rTextBounds );
557
558
                // add extends of text lines
559
0
                aBounds.expand( rLineBounds );
560
561
                // TODO(Q3): Provide this functionality at the B2DRange
562
0
                ::basegfx::B2DRange aTotalBounds( aBounds );
563
0
                aTotalBounds.expand(
564
0
                    ::basegfx::B2DRange( aBounds.getMinX() + rReliefOffset.getWidth(),
565
0
                                         aBounds.getMinY() + rReliefOffset.getHeight(),
566
0
                                         aBounds.getMaxX() + rReliefOffset.getWidth(),
567
0
                                         aBounds.getMaxY() + rReliefOffset.getHeight() ) );
568
0
                aTotalBounds.expand(
569
0
                    ::basegfx::B2DRange( aBounds.getMinX() + rShadowOffset.getWidth(),
570
0
                                         aBounds.getMinY() + rShadowOffset.getHeight(),
571
0
                                         aBounds.getMaxX() + rShadowOffset.getWidth(),
572
0
                                         aBounds.getMaxY() + rShadowOffset.getHeight() ) );
573
574
0
                return cppcanvastools::calcDevicePixelBounds( aTotalBounds,
575
0
                                                     rViewState,
576
0
                                                     rRenderState );
577
0
            }
578
579
            void initEffectLinePolyPolygon( ::basegfx::B2DSize&                             o_rOverallSize,
580
                                            uno::Reference< rendering::XPolyPolygon2D >&    o_rTextLines,
581
                                            const CanvasSharedPtr&                          rCanvas,
582
                                            double                                          nLineWidth,
583
                                            const cppcanvastools::TextLineInfo&                      rLineInfo   )
584
0
            {
585
0
                const ::basegfx::B2DPolyPolygon aPoly(
586
0
                    cppcanvastools::createTextLinesPolyPolygon( 0.0, nLineWidth,
587
0
                                                       rLineInfo ) );
588
0
                auto aRange = aPoly.getB2DRange().getRange();
589
0
                o_rOverallSize = basegfx::B2DSize(aRange.getX(), aRange.getY());
590
591
0
                o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
592
0
                    rCanvas->getUNOCanvas()->getDevice(),
593
0
                    aPoly );
594
0
            }
595
596
597
            class TextAction : public Action
598
            {
599
            public:
600
                TextAction( const ::basegfx::B2DPoint&  rStartPoint,
601
                            const OUString&      rString,
602
                            sal_Int32                   nStartPos,
603
                            sal_Int32                   nLen,
604
                            const CanvasSharedPtr&      rCanvas,
605
                            const OutDevState&          rState );
606
607
                TextAction( const ::basegfx::B2DPoint&      rStartPoint,
608
                            const OUString&          rString,
609
                            sal_Int32                       nStartPos,
610
                            sal_Int32                       nLen,
611
                            const CanvasSharedPtr&          rCanvas,
612
                            const OutDevState&              rState,
613
                            const ::basegfx::B2DHomMatrix&  rTextTransform );
614
615
                TextAction(const TextAction&) = delete;
616
                const TextAction& operator=(const TextAction&) = delete;
617
618
                virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
619
                virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
620
                                           const Subset&                  rSubset ) const override;
621
622
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
623
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&   rTransformation,
624
                                                       const Subset&                    rSubset ) const override;
625
626
                virtual sal_Int32 getActionCount() const override;
627
628
            private:
629
                // TODO(P2): This is potentially a real mass object
630
                // (every character might be a separate TextAction),
631
                // thus, make it as lightweight as possible. For
632
                // example, share common RenderState among several
633
                // TextActions, maybe using maOffsets for the
634
                // translation.
635
636
                uno::Reference< rendering::XCanvasFont >    mxFont;
637
                const rendering::StringContext              maStringContext;
638
                const CanvasSharedPtr                       mpCanvas;
639
                rendering::RenderState                      maState;
640
                const sal_Int8                              maTextDirection;
641
            };
642
643
            TextAction::TextAction( const ::basegfx::B2DPoint&  rStartPoint,
644
                                    const OUString&      rString,
645
                                    sal_Int32                   nStartPos,
646
                                    sal_Int32                   nLen,
647
                                    const CanvasSharedPtr&      rCanvas,
648
                                    const OutDevState&          rState  ) :
649
0
                mxFont( rState.xFont ),
650
0
                maStringContext( rString, nStartPos, nLen ),
651
0
                mpCanvas( rCanvas ),
652
0
                maTextDirection( rState.textDirection )
653
0
            {
654
0
                init( maState, mxFont,
655
0
                      rStartPoint,
656
0
                      rState, rCanvas );
657
658
0
                ENSURE_OR_THROW( mxFont.is(),
659
0
                                  "::cppcanvas::internal::TextAction(): Invalid font" );
660
0
            }
661
662
            TextAction::TextAction( const ::basegfx::B2DPoint&      rStartPoint,
663
                                    const OUString&          rString,
664
                                    sal_Int32                       nStartPos,
665
                                    sal_Int32                       nLen,
666
                                    const CanvasSharedPtr&          rCanvas,
667
                                    const OutDevState&              rState,
668
                                    const ::basegfx::B2DHomMatrix&  rTextTransform ) :
669
0
                mxFont( rState.xFont ),
670
0
                maStringContext( rString, nStartPos, nLen ),
671
0
                mpCanvas( rCanvas ),
672
0
                maTextDirection( rState.textDirection )
673
0
            {
674
0
                init( maState, mxFont,
675
0
                      rStartPoint,
676
0
                      rState, rCanvas, rTextTransform );
677
678
0
                ENSURE_OR_THROW( mxFont.is(),
679
0
                                  "::cppcanvas::internal::TextAction(): Invalid font" );
680
0
            }
681
682
            bool TextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
683
0
            {
684
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextAction::render()" );
685
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextAction: 0x" << std::hex << this );
686
687
0
                rendering::RenderState aLocalState( maState );
688
0
                ::canvastools::prependToRenderState(aLocalState, rTransformation);
689
690
0
                mpCanvas->getUNOCanvas()->drawText( maStringContext, mxFont,
691
0
                                                    mpCanvas->getViewState(), aLocalState, maTextDirection );
692
693
0
                return true;
694
0
            }
695
696
            bool TextAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
697
                                           const Subset&                  /*rSubset*/ ) const
698
0
            {
699
0
                SAL_WARN( "cppcanvas.emf", "TextAction::renderSubset(): Subset not supported by this object" );
700
701
                // TODO(P1): Retrieve necessary font metric info for
702
                // TextAction from XCanvas. Currently, the
703
                // TextActionFactory does not generate this object for
704
                // _subsettable_ text
705
0
                return render( rTransformation );
706
0
            }
707
708
            ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
709
0
            {
710
                // create XTextLayout, to have the
711
                // XTextLayout::queryTextBounds() method available
712
0
                uno::Reference< rendering::XTextLayout > xTextLayout(
713
0
                    mxFont->createTextLayout(
714
0
                        maStringContext,
715
0
                        maTextDirection,
716
0
                        0 ) );
717
718
0
                rendering::RenderState aLocalState( maState );
719
0
                ::canvastools::prependToRenderState(aLocalState, rTransformation);
720
721
0
                return cppcanvastools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
722
0
                                                         xTextLayout->queryTextBounds() ),
723
0
                                                     mpCanvas->getViewState(),
724
0
                                                     aLocalState );
725
0
            }
726
727
            ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix&   rTransformation,
728
                                                       const Subset&                    /*rSubset*/ ) const
729
0
            {
730
0
                SAL_WARN( "cppcanvas.emf", "TextAction::getBounds(): Subset not supported by this object" );
731
732
                // TODO(P1): Retrieve necessary font metric info for
733
                // TextAction from XCanvas. Currently, the
734
                // TextActionFactory does not generate this object for
735
                // _subsettable_ text
736
0
                return getBounds( rTransformation );
737
0
            }
738
739
            sal_Int32 TextAction::getActionCount() const
740
0
            {
741
                // TODO(P1): Retrieve necessary font metric info for
742
                // TextAction from XCanvas. Currently, the
743
                // TextActionFactory does not generate this object for
744
                // _subsettable_ text
745
0
                return 1;
746
0
            }
747
748
749
            class EffectTextAction :
750
                public Action,
751
                public TextRenderer
752
            {
753
            public:
754
                EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
755
                                  const ::basegfx::B2DSize&  rReliefOffset,
756
                                  const ::Color&             rReliefColor,
757
                                  const ::basegfx::B2DSize&  rShadowOffset,
758
                                  const ::Color&             rShadowColor,
759
                                  const ::Color&             rTextFillColor,
760
                                  const OUString&     rText,
761
                                  sal_Int32                  nStartPos,
762
                                  sal_Int32                  nLen,
763
                                  VirtualDevice const &      rVDev,
764
                                  const CanvasSharedPtr&     rCanvas,
765
                                  const OutDevState&         rState );
766
767
                EffectTextAction( const ::basegfx::B2DPoint&        rStartPoint,
768
                                  const ::basegfx::B2DSize&         rReliefOffset,
769
                                  const ::Color&                    rReliefColor,
770
                                  const ::basegfx::B2DSize&         rShadowOffset,
771
                                  const ::Color&                    rShadowColor,
772
                                  const ::Color&                    rTextFillColor,
773
                                  const OUString&            rText,
774
                                  sal_Int32                         nStartPos,
775
                                  sal_Int32                         nLen,
776
                                  VirtualDevice const &             rVDev,
777
                                  const CanvasSharedPtr&            rCanvas,
778
                                  const OutDevState&                rState,
779
                                  const ::basegfx::B2DHomMatrix&    rTextTransform );
780
781
                EffectTextAction(const EffectTextAction&) = delete;
782
                const EffectTextAction& operator=(const EffectTextAction&) = delete;
783
784
                virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
785
                virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
786
                                           const Subset&                  rSubset ) const override;
787
788
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
789
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&   rTransformation,
790
                                                       const Subset&                    rSubset ) const override;
791
792
                virtual sal_Int32 getActionCount() const override;
793
794
            private:
795
                /// Interface TextRenderer
796
                virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const override;
797
798
                geometry::RealRectangle2D queryTextBounds() const;
799
                css::uno::Reference<css::rendering::XPolyPolygon2D> queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const;
800
801
                // TODO(P2): This is potentially a real mass object
802
                // (every character might be a separate TextAction),
803
                // thus, make it as lightweight as possible. For
804
                // example, share common RenderState among several
805
                // TextActions, maybe using maOffsets for the
806
                // translation.
807
808
                uno::Reference< rendering::XCanvasFont >    mxFont;
809
                const rendering::StringContext              maStringContext;
810
                const CanvasSharedPtr                       mpCanvas;
811
                rendering::RenderState                      maState;
812
                const cppcanvastools::TextLineInfo                   maTextLineInfo;
813
                ::basegfx::B2DSize                          maLinesOverallSize;
814
                uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
815
                const ::basegfx::B2DSize                    maReliefOffset;
816
                const ::Color                               maReliefColor;
817
                const ::basegfx::B2DSize                    maShadowOffset;
818
                const ::Color                               maShadowColor;
819
                const ::Color                               maTextFillColor;
820
                const sal_Int8                              maTextDirection;
821
            };
822
823
            EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
824
                                                const ::basegfx::B2DSize&  rReliefOffset,
825
                                                const ::Color&             rReliefColor,
826
                                                const ::basegfx::B2DSize&  rShadowOffset,
827
                                                const ::Color&             rShadowColor,
828
                                                const ::Color&             rTextFillColor,
829
                                                const OUString&     rText,
830
                                                sal_Int32                  nStartPos,
831
                                                sal_Int32                  nLen,
832
                                                VirtualDevice const &      rVDev,
833
                                                const CanvasSharedPtr&     rCanvas,
834
                                                const OutDevState&         rState ) :
835
0
                mxFont( rState.xFont ),
836
0
                maStringContext( rText, nStartPos, nLen ),
837
0
                mpCanvas( rCanvas ),
838
0
                maTextLineInfo( cppcanvastools::createTextLineInfo( rVDev, rState ) ),
839
0
                maReliefOffset( rReliefOffset ),
840
0
                maReliefColor( rReliefColor ),
841
0
                maShadowOffset( rShadowOffset ),
842
0
                maShadowColor( rShadowColor ),
843
0
                maTextFillColor( rTextFillColor ),
844
0
                maTextDirection( rState.textDirection )
845
0
            {
846
0
                const double nLineWidth(getLineWidth( rVDev, rState, maStringContext ));
847
0
                initEffectLinePolyPolygon( maLinesOverallSize,
848
0
                                           mxTextLines,
849
0
                                           rCanvas,
850
0
                                           nLineWidth,
851
0
                                           maTextLineInfo );
852
853
0
                init( maState, mxFont,
854
0
                      rStartPoint,
855
0
                      rState, rCanvas );
856
857
0
                ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
858
0
                                  "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
859
0
            }
860
861
            EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint&      rStartPoint,
862
                                                const ::basegfx::B2DSize&       rReliefOffset,
863
                                                const ::Color&                  rReliefColor,
864
                                                const ::basegfx::B2DSize&       rShadowOffset,
865
                                                const ::Color&                  rShadowColor,
866
                                                const ::Color&                  rTextFillColor,
867
                                                const OUString&          rText,
868
                                                sal_Int32                       nStartPos,
869
                                                sal_Int32                       nLen,
870
                                                VirtualDevice const &           rVDev,
871
                                                const CanvasSharedPtr&          rCanvas,
872
                                                const OutDevState&              rState,
873
                                                const ::basegfx::B2DHomMatrix&  rTextTransform ) :
874
0
                mxFont( rState.xFont ),
875
0
                maStringContext( rText, nStartPos, nLen ),
876
0
                mpCanvas( rCanvas ),
877
0
                maTextLineInfo( cppcanvastools::createTextLineInfo( rVDev, rState ) ),
878
0
                maReliefOffset( rReliefOffset ),
879
0
                maReliefColor( rReliefColor ),
880
0
                maShadowOffset( rShadowOffset ),
881
0
                maShadowColor( rShadowColor ),
882
0
                maTextFillColor( rTextFillColor ),
883
0
                maTextDirection( rState.textDirection )
884
0
            {
885
0
                const double nLineWidth( getLineWidth( rVDev, rState, maStringContext ) );
886
0
                initEffectLinePolyPolygon( maLinesOverallSize,
887
0
                                           mxTextLines,
888
0
                                           rCanvas,
889
0
                                           nLineWidth,
890
0
                                           maTextLineInfo );
891
892
0
                init( maState, mxFont,
893
0
                      rStartPoint,
894
0
                      rState, rCanvas, rTextTransform );
895
896
0
                ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
897
0
                                  "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
898
0
            }
899
900
            bool EffectTextAction::operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool /*bNormalText*/ ) const
901
0
            {
902
0
                const rendering::ViewState aViewState( mpCanvas->getViewState() );
903
0
                const uno::Reference< rendering::XCanvas > aCanvas( mpCanvas->getUNOCanvas() );
904
905
                //rhbz#1589029 non-transparent text fill background support
906
0
                if (rTextFillColor != COL_AUTO)
907
0
                {
908
0
                    rendering::RenderState aLocalState( rRenderState );
909
0
                    aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
910
0
                        rTextFillColor, aCanvas->getDevice()->getDeviceColorSpace());
911
0
                    auto xTextBounds = queryTextBounds(aCanvas);
912
                    // background of text
913
0
                    aCanvas->fillPolyPolygon(xTextBounds, aViewState, aLocalState);
914
0
                }
915
916
                // under/over lines
917
0
                aCanvas->fillPolyPolygon( mxTextLines,
918
0
                                          aViewState,
919
0
                                          rRenderState );
920
921
0
                aCanvas->drawText( maStringContext, mxFont,
922
0
                                   aViewState,
923
0
                                   rRenderState,
924
0
                                   maTextDirection );
925
926
0
                return true;
927
0
            }
928
929
            bool EffectTextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
930
0
            {
931
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextAction::render()" );
932
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextAction: 0x" << std::hex << this );
933
934
0
                rendering::RenderState aLocalState( maState );
935
0
                ::canvastools::prependToRenderState(aLocalState, rTransformation);
936
937
0
                return renderEffectText( *this,
938
0
                                         aLocalState,
939
0
                                         mpCanvas->getUNOCanvas(),
940
0
                                         maShadowColor,
941
0
                                         maShadowOffset,
942
0
                                         maReliefColor,
943
0
                                         maReliefOffset,
944
0
                                         maTextFillColor);
945
0
            }
946
947
            bool EffectTextAction::renderSubset( const ::basegfx::B2DHomMatrix&   rTransformation,
948
                                                 const Subset&                    /*rSubset*/ ) const
949
0
            {
950
0
                SAL_WARN( "cppcanvas.emf", "EffectTextAction::renderSubset(): Subset not supported by this object" );
951
952
                // TODO(P1): Retrieve necessary font metric info for
953
                // TextAction from XCanvas. Currently, the
954
                // TextActionFactory does not generate this object for
955
                // subsettable text
956
0
                return render( rTransformation );
957
0
            }
958
959
            geometry::RealRectangle2D EffectTextAction::queryTextBounds() const
960
0
            {
961
                // create XTextLayout, to have the
962
                // XTextLayout::queryTextBounds() method available
963
0
                uno::Reference< rendering::XTextLayout > xTextLayout(
964
0
                    mxFont->createTextLayout(
965
0
                        maStringContext,
966
0
                        maTextDirection,
967
0
                        0 ) );
968
969
0
                return xTextLayout->queryTextBounds();
970
0
            }
971
972
            css::uno::Reference<css::rendering::XPolyPolygon2D> EffectTextAction::queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const
973
0
            {
974
0
                auto aTextBounds = queryTextBounds();
975
0
                auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
976
0
                auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
977
0
                return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(rCanvas->getDevice(), aTextBoundsPoly);
978
0
            }
979
980
            ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
981
0
            {
982
0
                rendering::RenderState aLocalState( maState );
983
0
                ::canvastools::prependToRenderState(aLocalState, rTransformation);
984
985
0
                return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
986
0
                                                 queryTextBounds() ),
987
0
                                             ::basegfx::B2DRange( 0,0,
988
0
                                                                  maLinesOverallSize.getWidth(),
989
0
                                                                  maLinesOverallSize.getHeight() ),
990
0
                                             maReliefOffset,
991
0
                                             maShadowOffset,
992
0
                                             aLocalState,
993
0
                                             mpCanvas->getViewState() );
994
0
            }
995
996
            ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
997
                                                             const Subset&                  /*rSubset*/ ) const
998
0
            {
999
0
                SAL_WARN( "cppcanvas.emf", "EffectTextAction::getBounds(): Subset not supported by this object" );
1000
1001
                // TODO(P1): Retrieve necessary font metric info for
1002
                // TextAction from XCanvas. Currently, the
1003
                // TextActionFactory does not generate this object for
1004
                // _subsettable_ text
1005
0
                return getBounds( rTransformation );
1006
0
            }
1007
1008
            sal_Int32 EffectTextAction::getActionCount() const
1009
0
            {
1010
                // TODO(P1): Retrieve necessary font metric info for
1011
                // TextAction from XCanvas. Currently, the
1012
                // TextActionFactory does not generate this object for
1013
                // subsettable text
1014
0
                return 1;
1015
0
            }
1016
1017
1018
            class TextArrayAction : public Action
1019
            {
1020
            public:
1021
                TextArrayAction( const ::basegfx::B2DPoint&     rStartPoint,
1022
                                 const OUString&         rString,
1023
                                 sal_Int32                      nStartPos,
1024
                                 sal_Int32                      nLen,
1025
                                 const uno::Sequence< double >& rOffsets,
1026
                                 const uno::Sequence< sal_Bool >& rKashidas,
1027
                                 const CanvasSharedPtr&         rCanvas,
1028
                                 const OutDevState&             rState );
1029
1030
                TextArrayAction( const ::basegfx::B2DPoint&     rStartPoint,
1031
                                 const OUString&         rString,
1032
                                 sal_Int32                      nStartPos,
1033
                                 sal_Int32                      nLen,
1034
                                 const uno::Sequence< double >& rOffsets,
1035
                                 const uno::Sequence< sal_Bool >& rKashidas,
1036
                                 const CanvasSharedPtr&         rCanvas,
1037
                                 const OutDevState&             rState,
1038
                                 const ::basegfx::B2DHomMatrix& rTextTransform );
1039
1040
                TextArrayAction(const TextArrayAction&) = delete;
1041
                const TextArrayAction& operator=(const TextArrayAction&) = delete;
1042
1043
                virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1044
                virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1045
                                           const Subset&                  rSubset ) const override;
1046
1047
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1048
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&   rTransformation,
1049
                                                       const Subset&                    rSubset ) const override;
1050
1051
                virtual sal_Int32 getActionCount() const override;
1052
1053
            private:
1054
                // TODO(P2): This is potentially a real mass object
1055
                // (every character might be a separate TextAction),
1056
                // thus, make it as lightweight as possible. For
1057
                // example, share common RenderState among several
1058
                // TextActions, maybe using maOffsets for the
1059
                // translation.
1060
1061
                uno::Reference< rendering::XTextLayout >    mxTextLayout;
1062
                const CanvasSharedPtr                       mpCanvas;
1063
                rendering::RenderState                      maState;
1064
                double                                      mnLayoutWidth;
1065
            };
1066
1067
            TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint&        rStartPoint,
1068
                                              const OUString&            rString,
1069
                                              sal_Int32                         nStartPos,
1070
                                              sal_Int32                         nLen,
1071
                                              const uno::Sequence< double >&    rOffsets,
1072
                                              const uno::Sequence< sal_Bool >&  rKashidas,
1073
                                              const CanvasSharedPtr&            rCanvas,
1074
                                              const OutDevState&                rState ) :
1075
0
                mpCanvas( rCanvas )
1076
0
            {
1077
0
                initLayoutWidth(mnLayoutWidth, rOffsets);
1078
1079
0
                initArrayAction( maState,
1080
0
                                 mxTextLayout,
1081
0
                                 rStartPoint,
1082
0
                                 rString,
1083
0
                                 nStartPos,
1084
0
                                 nLen,
1085
0
                                 rOffsets,
1086
0
                                 rKashidas,
1087
0
                                 rCanvas,
1088
0
                                 rState, nullptr );
1089
0
            }
1090
1091
            TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint&        rStartPoint,
1092
                                              const OUString&            rString,
1093
                                              sal_Int32                         nStartPos,
1094
                                              sal_Int32                         nLen,
1095
                                              const uno::Sequence< double >&    rOffsets,
1096
                                              const uno::Sequence< sal_Bool >&  rKashidas,
1097
                                              const CanvasSharedPtr&            rCanvas,
1098
                                              const OutDevState&                rState,
1099
                                              const ::basegfx::B2DHomMatrix&    rTextTransform ) :
1100
0
                mpCanvas( rCanvas )
1101
0
            {
1102
0
                initLayoutWidth(mnLayoutWidth, rOffsets);
1103
1104
0
                initArrayAction( maState,
1105
0
                                 mxTextLayout,
1106
0
                                 rStartPoint,
1107
0
                                 rString,
1108
0
                                 nStartPos,
1109
0
                                 nLen,
1110
0
                                 rOffsets,
1111
0
                                 rKashidas,
1112
0
                                 rCanvas,
1113
0
                                 rState,
1114
0
                                 &rTextTransform );
1115
0
            }
1116
1117
            bool TextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1118
0
            {
1119
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction::render()" );
1120
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction: 0x" << std::hex << this );
1121
1122
0
                rendering::RenderState aLocalState( maState );
1123
0
                ::canvastools::prependToRenderState(aLocalState, rTransformation);
1124
1125
0
                mpCanvas->getUNOCanvas()->drawTextLayout( mxTextLayout,
1126
0
                                                          mpCanvas->getViewState(),
1127
0
                                                          aLocalState );
1128
1129
0
                return true;
1130
0
            }
1131
1132
            bool TextArrayAction::renderSubset( const ::basegfx::B2DHomMatrix&    rTransformation,
1133
                                                const Subset&                     rSubset ) const
1134
0
            {
1135
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction::renderSubset()" );
1136
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction: 0x" << std::hex << this );
1137
1138
0
                rendering::RenderState                      aLocalState( maState );
1139
0
                uno::Reference< rendering::XTextLayout >    xTextLayout( mxTextLayout );
1140
1141
0
                double nDummy0, nDummy1;
1142
0
                createSubsetLayout( xTextLayout,
1143
0
                                    mnLayoutWidth,
1144
0
                                    aLocalState,
1145
0
                                    nDummy0,
1146
0
                                    nDummy1,
1147
0
                                    rTransformation,
1148
0
                                    rSubset );
1149
1150
0
                if( !xTextLayout.is() )
1151
0
                    return true; // empty layout, render nothing
1152
1153
0
                mpCanvas->getUNOCanvas()->drawTextLayout( xTextLayout,
1154
0
                                                          mpCanvas->getViewState(),
1155
0
                                                          aLocalState );
1156
1157
0
                return true;
1158
0
            }
1159
1160
            ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1161
0
            {
1162
0
                rendering::RenderState aLocalState( maState );
1163
0
                ::canvastools::prependToRenderState(aLocalState, rTransformation);
1164
1165
0
                return cppcanvastools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1166
0
                                                         mxTextLayout->queryTextBounds() ),
1167
0
                                                     mpCanvas->getViewState(),
1168
0
                                                     aLocalState );
1169
0
            }
1170
1171
            ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix&  rTransformation,
1172
                                                            const Subset&                   rSubset ) const
1173
0
            {
1174
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction::getBounds( subset )" );
1175
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction: 0x" << std::hex << this );
1176
1177
0
                rendering::RenderState                      aLocalState( maState );
1178
0
                uno::Reference< rendering::XTextLayout >    xTextLayout( mxTextLayout );
1179
1180
0
                double nDummy0, nDummy1;
1181
0
                createSubsetLayout( xTextLayout,
1182
0
                                    mnLayoutWidth,
1183
0
                                    aLocalState,
1184
0
                                    nDummy0,
1185
0
                                    nDummy1,
1186
0
                                    rTransformation,
1187
0
                                    rSubset );
1188
1189
0
                if( !xTextLayout.is() )
1190
0
                    return ::basegfx::B2DRange(); // empty layout, empty bounds
1191
1192
0
                return cppcanvastools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1193
0
                                                         xTextLayout->queryTextBounds() ),
1194
0
                                                     mpCanvas->getViewState(),
1195
0
                                                     aLocalState );
1196
0
            }
1197
1198
            sal_Int32 TextArrayAction::getActionCount() const
1199
0
            {
1200
0
                const rendering::StringContext aOrigContext( mxTextLayout->getText() );
1201
1202
0
                return aOrigContext.Length;
1203
0
            }
1204
1205
1206
            class EffectTextArrayAction :
1207
                public Action,
1208
                public TextRenderer
1209
            {
1210
            public:
1211
                EffectTextArrayAction( const ::basegfx::B2DPoint&       rStartPoint,
1212
                                       const ::basegfx::B2DSize&        rReliefOffset,
1213
                                       const ::Color&                   rReliefColor,
1214
                                       const ::basegfx::B2DSize&        rShadowOffset,
1215
                                       const ::Color&                   rShadowColor,
1216
                                       const ::Color&                   rTextFillColor,
1217
                                       const OUString&           rText,
1218
                                       sal_Int32                        nStartPos,
1219
                                       sal_Int32                        nLen,
1220
                                       const uno::Sequence< double >&   rOffsets,
1221
                                       const uno::Sequence< sal_Bool >& rKashidas,
1222
                                       VirtualDevice const &            rVDev,
1223
                                       const CanvasSharedPtr&           rCanvas,
1224
                                       const OutDevState&               rState  );
1225
                EffectTextArrayAction( const ::basegfx::B2DPoint&       rStartPoint,
1226
                                       const ::basegfx::B2DSize&        rReliefOffset,
1227
                                       const ::Color&                   rReliefColor,
1228
                                       const ::basegfx::B2DSize&        rShadowOffset,
1229
                                       const ::Color&                   rShadowColor,
1230
                                       const ::Color&                   rTextFillColor,
1231
                                       const OUString&           rText,
1232
                                       sal_Int32                        nStartPos,
1233
                                       sal_Int32                        nLen,
1234
                                       const uno::Sequence< double >&   rOffsets,
1235
                                       const uno::Sequence< sal_Bool >& rKashidas,
1236
                                       VirtualDevice const &            rVDev,
1237
                                       const CanvasSharedPtr&           rCanvas,
1238
                                       const OutDevState&               rState,
1239
                                       const ::basegfx::B2DHomMatrix&   rTextTransform );
1240
1241
                EffectTextArrayAction(const EffectTextArrayAction&) = delete;
1242
                const EffectTextArrayAction& operator=(const EffectTextArrayAction&) = delete;
1243
1244
                virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1245
                virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1246
                                           const Subset&                  rSubset ) const override;
1247
1248
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1249
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&   rTransformation,
1250
                                                       const Subset&                    rSubset ) const override;
1251
1252
                virtual sal_Int32 getActionCount() const override;
1253
1254
            private:
1255
                // TextRenderer interface
1256
                virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const override;
1257
1258
                css::uno::Reference<css::rendering::XPolyPolygon2D> queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const;
1259
1260
                // TODO(P2): This is potentially a real mass object
1261
                // (every character might be a separate TextAction),
1262
                // thus, make it as lightweight as possible. For
1263
                // example, share common RenderState among several
1264
                // TextActions, maybe using maOffsets for the
1265
                // translation.
1266
1267
                uno::Reference< rendering::XTextLayout >        mxTextLayout;
1268
                const CanvasSharedPtr                           mpCanvas;
1269
                rendering::RenderState                          maState;
1270
                const cppcanvastools::TextLineInfo                       maTextLineInfo;
1271
                TextLinesHelper                                 maTextLinesHelper;
1272
                const ::basegfx::B2DSize                        maReliefOffset;
1273
                const ::Color                                   maReliefColor;
1274
                const ::basegfx::B2DSize                        maShadowOffset;
1275
                const ::Color                                   maShadowColor;
1276
                const ::Color                                   maTextFillColor;
1277
                double                                          mnLayoutWidth;
1278
            };
1279
1280
            EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint&        rStartPoint,
1281
                                                          const ::basegfx::B2DSize&         rReliefOffset,
1282
                                                          const ::Color&                    rReliefColor,
1283
                                                          const ::basegfx::B2DSize&         rShadowOffset,
1284
                                                          const ::Color&                    rShadowColor,
1285
                                                          const ::Color&                    rTextFillColor,
1286
                                                          const OUString&            rText,
1287
                                                          sal_Int32                         nStartPos,
1288
                                                          sal_Int32                         nLen,
1289
                                                          const uno::Sequence< double >&    rOffsets,
1290
                                                          const uno::Sequence< sal_Bool >&  rKashidas,
1291
                                                          VirtualDevice const &             rVDev,
1292
                                                          const CanvasSharedPtr&            rCanvas,
1293
                                                          const OutDevState&                rState  ) :
1294
0
                mpCanvas( rCanvas ),
1295
0
                maTextLineInfo( cppcanvastools::createTextLineInfo( rVDev, rState ) ),
1296
0
                maTextLinesHelper(mpCanvas, rState),
1297
0
                maReliefOffset( rReliefOffset ),
1298
0
                maReliefColor( rReliefColor ),
1299
0
                maShadowOffset( rShadowOffset ),
1300
0
                maShadowColor( rShadowColor ),
1301
0
                maTextFillColor( rTextFillColor )
1302
0
            {
1303
0
                initLayoutWidth(mnLayoutWidth, rOffsets);
1304
1305
0
                maTextLinesHelper.init(mnLayoutWidth, maTextLineInfo);
1306
1307
0
                initArrayAction( maState,
1308
0
                                 mxTextLayout,
1309
0
                                 rStartPoint,
1310
0
                                 rText,
1311
0
                                 nStartPos,
1312
0
                                 nLen,
1313
0
                                 rOffsets,
1314
0
                                 rKashidas,
1315
0
                                 rCanvas,
1316
0
                                 rState, nullptr );
1317
0
            }
1318
1319
            EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint&        rStartPoint,
1320
                                                          const ::basegfx::B2DSize&         rReliefOffset,
1321
                                                          const ::Color&                    rReliefColor,
1322
                                                          const ::basegfx::B2DSize&         rShadowOffset,
1323
                                                          const ::Color&                    rShadowColor,
1324
                                                          const ::Color&                    rTextFillColor,
1325
                                                          const OUString&            rText,
1326
                                                          sal_Int32                         nStartPos,
1327
                                                          sal_Int32                         nLen,
1328
                                                          const uno::Sequence< double >&    rOffsets,
1329
                                                          const uno::Sequence< sal_Bool >&  rKashidas,
1330
                                                          VirtualDevice const &             rVDev,
1331
                                                          const CanvasSharedPtr&            rCanvas,
1332
                                                          const OutDevState&                rState,
1333
                                                          const ::basegfx::B2DHomMatrix&    rTextTransform ) :
1334
0
                mpCanvas( rCanvas ),
1335
0
                maTextLineInfo( cppcanvastools::createTextLineInfo( rVDev, rState ) ),
1336
0
                maTextLinesHelper(mpCanvas, rState),
1337
0
                maReliefOffset( rReliefOffset ),
1338
0
                maReliefColor( rReliefColor ),
1339
0
                maShadowOffset( rShadowOffset ),
1340
0
                maShadowColor( rShadowColor ),
1341
0
                maTextFillColor( rTextFillColor )
1342
0
            {
1343
0
                initLayoutWidth(mnLayoutWidth, rOffsets);
1344
1345
0
                maTextLinesHelper.init(mnLayoutWidth, maTextLineInfo);
1346
1347
0
                initArrayAction( maState,
1348
0
                                 mxTextLayout,
1349
0
                                 rStartPoint,
1350
0
                                 rText,
1351
0
                                 nStartPos,
1352
0
                                 nLen,
1353
0
                                 rOffsets,
1354
0
                                 rKashidas,
1355
0
                                 rCanvas,
1356
0
                                 rState,
1357
0
                                 &rTextTransform );
1358
0
            }
1359
1360
            css::uno::Reference<css::rendering::XPolyPolygon2D> EffectTextArrayAction::queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const
1361
0
            {
1362
0
                const geometry::RealRectangle2D aTextBounds(mxTextLayout->queryTextBounds());
1363
0
                auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
1364
0
                auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
1365
0
                return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(rCanvas->getDevice(), aTextBoundsPoly);
1366
0
            }
1367
1368
            bool EffectTextArrayAction::operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText) const
1369
0
            {
1370
0
                const rendering::ViewState aViewState( mpCanvas->getViewState() );
1371
0
                const uno::Reference< rendering::XCanvas > aCanvas( mpCanvas->getUNOCanvas() );
1372
1373
                //rhbz#1589029 non-transparent text fill background support
1374
0
                if (rTextFillColor != COL_AUTO)
1375
0
                {
1376
0
                    rendering::RenderState aLocalState(rRenderState);
1377
0
                    aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
1378
0
                        rTextFillColor, aCanvas->getDevice()->getDeviceColorSpace());
1379
0
                    auto xTextBounds = queryTextBounds(aCanvas);
1380
                    // background of text
1381
0
                    aCanvas->fillPolyPolygon(xTextBounds, aViewState, aLocalState);
1382
0
                }
1383
1384
                // under/over lines
1385
0
                maTextLinesHelper.render(rRenderState, bNormalText);
1386
1387
0
                aCanvas->drawTextLayout( mxTextLayout,
1388
0
                                         aViewState,
1389
0
                                         rRenderState );
1390
1391
0
                return true;
1392
0
            }
1393
1394
            bool EffectTextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1395
0
            {
1396
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::render()" );
1397
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1398
1399
0
                rendering::RenderState aLocalState( maState );
1400
0
                ::canvastools::prependToRenderState(aLocalState, rTransformation);
1401
1402
0
                return renderEffectText( *this,
1403
0
                                         aLocalState,
1404
0
                                         mpCanvas->getUNOCanvas(),
1405
0
                                         maShadowColor,
1406
0
                                         maShadowOffset,
1407
0
                                         maReliefColor,
1408
0
                                         maReliefOffset,
1409
0
                                         maTextFillColor);
1410
0
            }
1411
1412
            class EffectTextArrayRenderHelper : public TextRenderer
1413
            {
1414
            public:
1415
                EffectTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >&        rCanvas,
1416
                                             const uno::Reference< rendering::XTextLayout >&    rTextLayout,
1417
                                             const TextLinesHelper&                             rTextLinesHelper,
1418
                                             const rendering::ViewState&                        rViewState ) :
1419
0
                    mrCanvas( rCanvas ),
1420
0
                    mrTextLayout( rTextLayout ),
1421
0
                    mrTextLinesHelper( rTextLinesHelper ),
1422
0
                    mrViewState( rViewState )
1423
0
                {
1424
0
                }
1425
1426
                // TextRenderer interface
1427
                virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor,bool bNormalText) const override
1428
0
                {
1429
0
                    mrTextLinesHelper.render(rRenderState, bNormalText);
1430
1431
                    //rhbz#1589029 non-transparent text fill background support
1432
0
                    if (rTextFillColor != COL_AUTO)
1433
0
                    {
1434
0
                        rendering::RenderState aLocalState(rRenderState);
1435
0
                        aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
1436
0
                            rTextFillColor, mrCanvas->getDevice()->getDeviceColorSpace());
1437
0
                        auto xTextBounds = queryTextBounds();
1438
                        // background of text
1439
0
                        mrCanvas->fillPolyPolygon(xTextBounds, mrViewState, aLocalState);
1440
0
                    }
1441
1442
0
                    mrCanvas->drawTextLayout( mrTextLayout,
1443
0
                                              mrViewState,
1444
0
                                              rRenderState );
1445
1446
0
                    return true;
1447
0
                }
1448
1449
            private:
1450
1451
                css::uno::Reference<css::rendering::XPolyPolygon2D> queryTextBounds() const
1452
0
                {
1453
0
                    const geometry::RealRectangle2D aTextBounds(mrTextLayout->queryTextBounds());
1454
0
                    auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
1455
0
                    auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
1456
0
                    return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(mrCanvas->getDevice(), aTextBoundsPoly);
1457
0
                }
1458
1459
                const uno::Reference< rendering::XCanvas >&         mrCanvas;
1460
                const uno::Reference< rendering::XTextLayout >&     mrTextLayout;
1461
                const TextLinesHelper&                              mrTextLinesHelper;
1462
                const rendering::ViewState&                         mrViewState;
1463
            };
1464
1465
            bool EffectTextArrayAction::renderSubset( const ::basegfx::B2DHomMatrix&  rTransformation,
1466
                                                      const Subset&                   rSubset ) const
1467
0
            {
1468
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::renderSubset()" );
1469
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1470
1471
0
                rendering::RenderState                   aLocalState( maState );
1472
0
                uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1473
0
                const geometry::RealRectangle2D          aTextBounds( mxTextLayout->queryTextBounds() );
1474
1475
0
                double nMinPos(0.0);
1476
0
                double nMaxPos(aTextBounds.X2 - aTextBounds.X1);
1477
1478
0
                createSubsetLayout( xTextLayout,
1479
0
                                    mnLayoutWidth,
1480
0
                                    aLocalState,
1481
0
                                    nMinPos,
1482
0
                                    nMaxPos,
1483
0
                                    rTransformation,
1484
0
                                    rSubset );
1485
1486
0
                if( !xTextLayout.is() )
1487
0
                    return true; // empty layout, render nothing
1488
1489
1490
                // create and setup local line polygon
1491
                // ===================================
1492
1493
0
                uno::Reference< rendering::XCanvas > xCanvas( mpCanvas->getUNOCanvas() );
1494
0
                const rendering::ViewState           aViewState( mpCanvas->getViewState() );
1495
1496
0
                TextLinesHelper aHelper = maTextLinesHelper;
1497
0
                aHelper.init(nMaxPos - nMinPos, maTextLineInfo);
1498
1499
1500
                // render everything
1501
                // =================
1502
1503
0
                return renderEffectText(
1504
0
                    EffectTextArrayRenderHelper( xCanvas,
1505
0
                                                 xTextLayout,
1506
0
                                                 aHelper,
1507
0
                                                 aViewState ),
1508
0
                    aLocalState,
1509
0
                    xCanvas,
1510
0
                    maShadowColor,
1511
0
                    maShadowOffset,
1512
0
                    maReliefColor,
1513
0
                    maReliefOffset,
1514
0
                    maTextFillColor);
1515
0
            }
1516
1517
            ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1518
0
            {
1519
0
                rendering::RenderState aLocalState( maState );
1520
0
                ::canvastools::prependToRenderState(aLocalState, rTransformation);
1521
1522
0
                ::basegfx::B2DSize aSize = maTextLinesHelper.getOverallSize();
1523
1524
0
                return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1525
0
                                                 mxTextLayout->queryTextBounds() ),
1526
0
                                             basegfx::B2DRange(0, 0,
1527
0
                                                               aSize.getWidth(),
1528
0
                                                               aSize.getHeight()),
1529
0
                                             maReliefOffset,
1530
0
                                             maShadowOffset,
1531
0
                                             aLocalState,
1532
0
                                             mpCanvas->getViewState() );
1533
0
            }
1534
1535
            ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix&    rTransformation,
1536
                                                                  const Subset&                     rSubset ) const
1537
0
            {
1538
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::getBounds( subset )" );
1539
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1540
1541
0
                rendering::RenderState                   aLocalState( maState );
1542
0
                uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1543
0
                const geometry::RealRectangle2D          aTextBounds( mxTextLayout->queryTextBounds() );
1544
1545
0
                double nMinPos(0.0);
1546
0
                double nMaxPos(aTextBounds.X2 - aTextBounds.X1);
1547
1548
0
                createSubsetLayout( xTextLayout,
1549
0
                                    mnLayoutWidth,
1550
0
                                    aLocalState,
1551
0
                                    nMinPos,
1552
0
                                    nMaxPos,
1553
0
                                    rTransformation,
1554
0
                                    rSubset );
1555
1556
0
                if( !xTextLayout.is() )
1557
0
                    return ::basegfx::B2DRange(); // empty layout, empty bounds
1558
1559
1560
                // create and setup local line polygon
1561
                // ===================================
1562
1563
0
                const ::basegfx::B2DPolyPolygon aPoly(
1564
0
                    cppcanvastools::createTextLinesPolyPolygon(
1565
0
                        0.0, nMaxPos - nMinPos,
1566
0
                        maTextLineInfo ) );
1567
1568
0
                return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1569
0
                                                 xTextLayout->queryTextBounds() ),
1570
0
                                             aPoly.getB2DRange(),
1571
0
                                             maReliefOffset,
1572
0
                                             maShadowOffset,
1573
0
                                             aLocalState,
1574
0
                                             mpCanvas->getViewState() );
1575
0
            }
1576
1577
            sal_Int32 EffectTextArrayAction::getActionCount() const
1578
0
            {
1579
0
                const rendering::StringContext aOrigContext( mxTextLayout->getText() );
1580
1581
0
                return aOrigContext.Length;
1582
0
            }
1583
1584
1585
            class OutlineAction :
1586
                public Action,
1587
                public TextRenderer
1588
            {
1589
            public:
1590
                OutlineAction( const ::basegfx::B2DPoint&                           rStartPoint,
1591
                               const ::basegfx::B2DSize&                            rReliefOffset,
1592
                               const ::Color&                                       rReliefColor,
1593
                               const ::basegfx::B2DSize&                            rShadowOffset,
1594
                               const ::Color&                                       rShadowColor,
1595
                               const ::Color&                                       rFillColor,
1596
                               uno::Reference< rendering::XPolyPolygon2D >          xFillPoly,
1597
                               const ::basegfx::B2DRectangle&                       rOutlineBounds,
1598
                               uno::Reference< rendering::XPolyPolygon2D >          xTextPoly,
1599
                               const uno::Sequence< double >&                       rOffsets,
1600
                               VirtualDevice const &                                rVDev,
1601
                               const CanvasSharedPtr&                               rCanvas,
1602
                               const OutDevState&                                   rState  );
1603
                OutlineAction( const ::basegfx::B2DPoint&                           rStartPoint,
1604
                               const ::basegfx::B2DSize&                            rReliefOffset,
1605
                               const ::Color&                                       rReliefColor,
1606
                               const ::basegfx::B2DSize&                            rShadowOffset,
1607
                               const ::Color&                                       rShadowColor,
1608
                               const ::Color&                                       rFillColor,
1609
                               uno::Reference< rendering::XPolyPolygon2D >          xFillPoly,
1610
                               const ::basegfx::B2DRectangle&                       rOutlineBounds,
1611
                               uno::Reference< rendering::XPolyPolygon2D >          xTextPoly,
1612
                               const uno::Sequence< double >&                       rOffsets,
1613
                               VirtualDevice const &                                rVDev,
1614
                               const CanvasSharedPtr&                               rCanvas,
1615
                               const OutDevState&                                   rState,
1616
                               const ::basegfx::B2DHomMatrix&                       rTextTransform );
1617
1618
                OutlineAction(const OutlineAction&) = delete;
1619
                const OutlineAction& operator=(const OutlineAction&) = delete;
1620
1621
                virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1622
                virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1623
                                           const Subset&                  rSubset ) const override;
1624
1625
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1626
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&   rTransformation,
1627
                                                       const Subset&                    rSubset ) const override;
1628
1629
                virtual sal_Int32 getActionCount() const override;
1630
1631
            private:
1632
                // TextRenderer interface
1633
                virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const override;
1634
1635
                // TODO(P2): This is potentially a real mass object
1636
                // (every character might be a separate TextAction),
1637
                // thus, make it as lightweight as possible. For
1638
                // example, share common RenderState among several
1639
                // TextActions, maybe using maOffsets for the
1640
                // translation.
1641
1642
                uno::Reference< rendering::XPolyPolygon2D >         mxTextPoly;
1643
1644
                const uno::Sequence< double >                       maOffsets;
1645
                const CanvasSharedPtr                               mpCanvas;
1646
                rendering::RenderState                              maState;
1647
                double                                              mnOutlineWidth;
1648
                const uno::Sequence< double >                       maFillColor;
1649
                uno::Reference< rendering::XPolyPolygon2D >         mxBackgroundFillPoly;
1650
                const cppcanvastools::TextLineInfo                           maTextLineInfo;
1651
                ::basegfx::B2DSize                                  maLinesOverallSize;
1652
                const ::basegfx::B2DRectangle                       maOutlineBounds;
1653
                uno::Reference< rendering::XPolyPolygon2D >         mxTextLines;
1654
                const ::basegfx::B2DSize                            maReliefOffset;
1655
                const ::Color                                       maReliefColor;
1656
                const ::basegfx::B2DSize                            maShadowOffset;
1657
                const ::Color                                       maShadowColor;
1658
                const ::Color                                       maTextFillColor;
1659
                const ::Color                                       maBackgroundFillColor;
1660
            };
1661
1662
            double calcOutlineWidth( const OutDevState& rState,
1663
                                     VirtualDevice const & rVDev )
1664
0
            {
1665
0
                const ::basegfx::B2DSize aFontSize( 0,
1666
0
                                                    rVDev.GetFont().GetFontHeight() / 64.0 );
1667
1668
0
                const double nOutlineWidth(
1669
0
                    (rState.mapModeTransform * aFontSize).getHeight() );
1670
1671
0
                return nOutlineWidth < 1.0 ? 1.0 : nOutlineWidth;
1672
0
            }
1673
1674
            OutlineAction::OutlineAction( const ::basegfx::B2DPoint&                            rStartPoint,
1675
                                          const ::basegfx::B2DSize&                             rReliefOffset,
1676
                                          const ::Color&                                        rReliefColor,
1677
                                          const ::basegfx::B2DSize&                             rShadowOffset,
1678
                                          const ::Color&                                        rShadowColor,
1679
                                          const ::Color&                                        rFillColor,
1680
                                          uno::Reference< rendering::XPolyPolygon2D >           xFillPoly,
1681
                                          const ::basegfx::B2DRectangle&                        rOutlineBounds,
1682
                                          uno::Reference< rendering::XPolyPolygon2D >           xTextPoly,
1683
                                          const uno::Sequence< double >&                        rOffsets,
1684
                                          VirtualDevice const &                                 rVDev,
1685
                                          const CanvasSharedPtr&                                rCanvas,
1686
                                          const OutDevState&                                    rState  ) :
1687
0
                mxTextPoly(std::move( xTextPoly )),
1688
0
                maOffsets( rOffsets ),
1689
0
                mpCanvas( rCanvas ),
1690
0
                mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
1691
                maFillColor(
1692
0
                    vcl::unotools::colorToDoubleSequence(
1693
0
                        COL_WHITE,
1694
0
                        rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1695
0
                mxBackgroundFillPoly(std::move( xFillPoly )),
1696
0
                maTextLineInfo( cppcanvastools::createTextLineInfo( rVDev, rState ) ),
1697
0
                maOutlineBounds( rOutlineBounds ),
1698
0
                maReliefOffset( rReliefOffset ),
1699
0
                maReliefColor( rReliefColor ),
1700
0
                maShadowOffset( rShadowOffset ),
1701
0
                maShadowColor( rShadowColor ),
1702
0
                maBackgroundFillColor( rFillColor )
1703
0
            {
1704
0
                double nLayoutWidth = 0.0;
1705
1706
0
                initLayoutWidth(nLayoutWidth, rOffsets);
1707
1708
0
                initEffectLinePolyPolygon( maLinesOverallSize,
1709
0
                                           mxTextLines,
1710
0
                                           rCanvas,
1711
0
                                           nLayoutWidth,
1712
0
                                           maTextLineInfo );
1713
1714
0
                init( maState,
1715
0
                      rStartPoint,
1716
0
                      rState,
1717
0
                      rCanvas );
1718
0
            }
1719
1720
            OutlineAction::OutlineAction( const ::basegfx::B2DPoint&                            rStartPoint,
1721
                                          const ::basegfx::B2DSize&                             rReliefOffset,
1722
                                          const ::Color&                                        rReliefColor,
1723
                                          const ::basegfx::B2DSize&                             rShadowOffset,
1724
                                          const ::Color&                                        rShadowColor,
1725
                                          const ::Color&                                        rFillColor,
1726
                                          uno::Reference< rendering::XPolyPolygon2D >           xFillPoly,
1727
                                          const ::basegfx::B2DRectangle&                        rOutlineBounds,
1728
                                          uno::Reference< rendering::XPolyPolygon2D >           xTextPoly,
1729
                                          const uno::Sequence< double >&                        rOffsets,
1730
                                          VirtualDevice const &                                 rVDev,
1731
                                          const CanvasSharedPtr&                                rCanvas,
1732
                                          const OutDevState&                                    rState,
1733
                                          const ::basegfx::B2DHomMatrix&                        rTextTransform ) :
1734
0
                mxTextPoly(std::move( xTextPoly )),
1735
0
                maOffsets( rOffsets ),
1736
0
                mpCanvas( rCanvas ),
1737
0
                mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
1738
                maFillColor(
1739
0
                    vcl::unotools::colorToDoubleSequence(
1740
0
                        COL_WHITE,
1741
0
                        rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1742
0
                mxBackgroundFillPoly(std::move( xFillPoly )),
1743
0
                maTextLineInfo( cppcanvastools::createTextLineInfo( rVDev, rState ) ),
1744
0
                maOutlineBounds( rOutlineBounds ),
1745
0
                maReliefOffset( rReliefOffset ),
1746
0
                maReliefColor( rReliefColor ),
1747
0
                maShadowOffset( rShadowOffset ),
1748
0
                maShadowColor( rShadowColor ),
1749
0
                maBackgroundFillColor( rFillColor )
1750
0
            {
1751
0
                double nLayoutWidth = 0.0;
1752
0
                initLayoutWidth(nLayoutWidth, rOffsets);
1753
1754
0
                initEffectLinePolyPolygon( maLinesOverallSize,
1755
0
                                           mxTextLines,
1756
0
                                           rCanvas,
1757
0
                                           nLayoutWidth,
1758
0
                                           maTextLineInfo );
1759
1760
0
                init( maState,
1761
0
                      rStartPoint,
1762
0
                      rState,
1763
0
                      rCanvas,
1764
0
                      rTextTransform );
1765
0
            }
1766
1767
            bool OutlineAction::operator()( const rendering::RenderState& rRenderState, const ::Color& /*rTextFillColor*/, bool /*bNormalText*/ ) const
1768
0
            {
1769
0
                const rendering::ViewState                  aViewState( mpCanvas->getViewState() );
1770
0
                const uno::Reference< rendering::XCanvas >  xCanvas( mpCanvas->getUNOCanvas() );
1771
1772
0
                if (mxBackgroundFillPoly.is())
1773
0
                {
1774
0
                    rendering::RenderState aLocalState( rRenderState );
1775
0
                    aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
1776
0
                        maBackgroundFillColor, xCanvas->getDevice()->getDeviceColorSpace());
1777
0
                    xCanvas->fillPolyPolygon(mxBackgroundFillPoly, aViewState, aLocalState);
1778
0
                }
1779
1780
0
                rendering::StrokeAttributes aStrokeAttributes;
1781
1782
0
                aStrokeAttributes.StrokeWidth  = mnOutlineWidth;
1783
0
                aStrokeAttributes.MiterLimit   = 1.0;
1784
0
                aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
1785
0
                aStrokeAttributes.EndCapType   = rendering::PathCapType::BUTT;
1786
0
                aStrokeAttributes.JoinType     = rendering::PathJoinType::MITER;
1787
1788
0
                rendering::RenderState aLocalState( rRenderState );
1789
0
                aLocalState.DeviceColor = maFillColor;
1790
1791
                // TODO(P1): implement caching
1792
1793
                // background of text
1794
0
                xCanvas->fillPolyPolygon( mxTextPoly,
1795
0
                                          aViewState,
1796
0
                                          aLocalState );
1797
1798
                // border line of text
1799
0
                xCanvas->strokePolyPolygon( mxTextPoly,
1800
0
                                            aViewState,
1801
0
                                            rRenderState,
1802
0
                                            aStrokeAttributes );
1803
1804
                // underlines/strikethrough - background
1805
0
                xCanvas->fillPolyPolygon( mxTextLines,
1806
0
                                          aViewState,
1807
0
                                          aLocalState );
1808
                // underlines/strikethrough - border
1809
0
                xCanvas->strokePolyPolygon( mxTextLines,
1810
0
                                            aViewState,
1811
0
                                            rRenderState,
1812
0
                                            aStrokeAttributes );
1813
1814
0
                return true;
1815
0
            }
1816
1817
            bool OutlineAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1818
0
            {
1819
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::render()" );
1820
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1821
1822
0
                rendering::RenderState aLocalState( maState );
1823
0
                ::canvastools::prependToRenderState(aLocalState, rTransformation);
1824
1825
0
                return renderEffectText( *this,
1826
0
                                         aLocalState,
1827
0
                                         mpCanvas->getUNOCanvas(),
1828
0
                                         maShadowColor,
1829
0
                                         maShadowOffset,
1830
0
                                         maReliefColor,
1831
0
                                         maReliefOffset,
1832
0
                                         maTextFillColor);
1833
0
            }
1834
1835
#if 0 // see #if'ed out use in OutlineAction::renderSubset below:
1836
            class OutlineTextArrayRenderHelper : public TextRenderer
1837
            {
1838
            public:
1839
                OutlineTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >&        rCanvas,
1840
                                              const uno::Reference< rendering::XPolyPolygon2D >& rTextPolygon,
1841
                                              const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon,
1842
                                              const rendering::ViewState&                        rViewState,
1843
                                              double                                             nOutlineWidth ) :
1844
                    maFillColor(
1845
                        vcl::unotools::colorToDoubleSequence(
1846
                            ::COL_WHITE,
1847
                            rCanvas->getDevice()->getDeviceColorSpace() )),
1848
                    mnOutlineWidth( nOutlineWidth ),
1849
                    mrCanvas( rCanvas ),
1850
                    mrTextPolygon( rTextPolygon ),
1851
                    mrLinePolygon( rLinePolygon ),
1852
                    mrViewState( rViewState )
1853
                {
1854
                }
1855
1856
                // TextRenderer interface
1857
                virtual bool operator()( const rendering::RenderState& rRenderState ) const
1858
                {
1859
                    rendering::StrokeAttributes aStrokeAttributes;
1860
1861
                    aStrokeAttributes.StrokeWidth  = mnOutlineWidth;
1862
                    aStrokeAttributes.MiterLimit   = 1.0;
1863
                    aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
1864
                    aStrokeAttributes.EndCapType   = rendering::PathCapType::BUTT;
1865
                    aStrokeAttributes.JoinType     = rendering::PathJoinType::MITER;
1866
1867
                    rendering::RenderState aLocalState( rRenderState );
1868
                    aLocalState.DeviceColor = maFillColor;
1869
1870
                    // TODO(P1): implement caching
1871
1872
                    // background of text
1873
                    mrCanvas->fillPolyPolygon( mrTextPolygon,
1874
                                               mrViewState,
1875
                                               aLocalState );
1876
1877
                    // border line of text
1878
                    mrCanvas->strokePolyPolygon( mrTextPolygon,
1879
                                                 mrViewState,
1880
                                                 rRenderState,
1881
                                                 aStrokeAttributes );
1882
1883
                    // underlines/strikethrough - background
1884
                    mrCanvas->fillPolyPolygon( mrLinePolygon,
1885
                                               mrViewState,
1886
                                               aLocalState );
1887
                    // underlines/strikethrough - border
1888
                    mrCanvas->strokePolyPolygon( mrLinePolygon,
1889
                                                 mrViewState,
1890
                                                 rRenderState,
1891
                                                 aStrokeAttributes );
1892
1893
                    return true;
1894
                }
1895
1896
            private:
1897
                const uno::Sequence< double >                       maFillColor;
1898
                double                                              mnOutlineWidth;
1899
                const uno::Reference< rendering::XCanvas >&         mrCanvas;
1900
                const uno::Reference< rendering::XPolyPolygon2D >&  mrTextPolygon;
1901
                const uno::Reference< rendering::XPolyPolygon2D >&  mrLinePolygon;
1902
                const rendering::ViewState&                         mrViewState;
1903
            };
1904
#endif
1905
1906
            bool OutlineAction::renderSubset( const ::basegfx::B2DHomMatrix&  rTransformation,
1907
                                              const Subset&                   rSubset ) const
1908
0
            {
1909
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::OutlineAction::renderSubset()" );
1910
0
                SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::OutlineAction: 0x" << std::hex << this );
1911
1912
0
                if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
1913
0
                    return true; // empty range, render nothing
1914
1915
0
#if 1
1916
                // TODO(F3): Subsetting NYI for outline text!
1917
0
                return render( rTransformation );
1918
#else
1919
                const rendering::StringContext rOrigContext( mxTextLayout->getText() );
1920
1921
                if( rSubset.mnSubsetBegin == 0 &&
1922
                    rSubset.mnSubsetEnd == rOrigContext.Length )
1923
                {
1924
                    // full range, no need for subsetting
1925
                    return render( rTransformation );
1926
                }
1927
1928
                rendering::RenderState aLocalState( maState );
1929
                ::canvastools::prependToRenderState(aLocalState, rTransformation);
1930
1931
1932
                // create and setup local Text polygon
1933
                // ===================================
1934
1935
                uno::Reference< rendering::XPolyPolygon2D > xTextPolygon();
1936
1937
                // TODO(P3): Provide an API method for that!
1938
1939
                if( !xTextLayout.is() )
1940
                    return false;
1941
1942
                // render everything
1943
                // =================
1944
1945
                return renderEffectText(
1946
                    OutlineTextArrayRenderHelper(
1947
                        xCanvas,
1948
                        mnOutlineWidth,
1949
                        xTextLayout,
1950
                        xTextLines,
1951
                        rViewState ),
1952
                    aLocalState,
1953
                    rViewState,
1954
                    xCanvas,
1955
                    maShadowColor,
1956
                    maShadowOffset,
1957
                    maReliefColor,
1958
                    maReliefOffset );
1959
#endif
1960
0
            }
1961
1962
            ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1963
0
            {
1964
0
                rendering::RenderState aLocalState( maState );
1965
0
                ::canvastools::prependToRenderState(aLocalState, rTransformation);
1966
1967
0
                return calcEffectTextBounds( maOutlineBounds,
1968
0
                                             ::basegfx::B2DRange(0, 0,
1969
0
                                                                 maLinesOverallSize.getWidth(),
1970
0
                                                                 maLinesOverallSize.getHeight()),
1971
0
                                             maReliefOffset,
1972
0
                                             maShadowOffset,
1973
0
                                             aLocalState,
1974
0
                                             mpCanvas->getViewState() );
1975
0
            }
1976
1977
            ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix&    rTransformation,
1978
                                                          const Subset&                     /*rSubset*/ ) const
1979
0
            {
1980
0
                SAL_WARN( "cppcanvas.emf", "OutlineAction::getBounds(): Subset not yet supported by this object" );
1981
1982
0
                return getBounds( rTransformation );
1983
0
            }
1984
1985
            sal_Int32 OutlineAction::getActionCount() const
1986
0
            {
1987
                // TODO(F3): Subsetting NYI for outline text!
1988
0
                return maOffsets.getLength();
1989
0
            }
1990
1991
1992
            // Action factory methods
1993
1994
1995
            /** Create an outline action
1996
1997
                This method extracts the polygonal outline from the
1998
                text, and creates a properly setup OutlineAction from
1999
                it.
2000
             */
2001
            std::shared_ptr<Action> createOutline( const ::basegfx::B2DPoint&       rStartPoint,
2002
                                                   const ::basegfx::B2DSize&        rReliefOffset,
2003
                                                   const ::Color&                   rReliefColor,
2004
                                                   const ::basegfx::B2DSize&        rShadowOffset,
2005
                                                   const ::Color&                   rShadowColor,
2006
                                                   const ::Color&                   rTextFillColor,
2007
                                                   const OUString&                  rText,
2008
                                                   sal_Int32                        nStartPos,
2009
                                                   sal_Int32                        nLen,
2010
                                                   KernArraySpan                    pDXArray,
2011
                                                   std::span<const sal_Bool>        pKashidaArray,
2012
                                                   VirtualDevice&                   rVDev,
2013
                                                   const CanvasSharedPtr&           rCanvas,
2014
                                                   const OutDevState&               rState,
2015
                                                   const Renderer::Parameters&      rParms  )
2016
0
            {
2017
                // operate on raw DX array here (in logical coordinate
2018
                // system), to have a higher resolution
2019
                // PolyPolygon. That polygon is then converted to
2020
                // device coordinate system.
2021
2022
                // #i68512# Temporarily switch off font rotation
2023
                // (which is already contained in the render state
2024
                // transformation matrix - otherwise, glyph polygons
2025
                // will be rotated twice)
2026
0
                const vcl::Font aOrigFont( rVDev.GetFont() );
2027
0
                vcl::Font       aUnrotatedFont( aOrigFont );
2028
0
                aUnrotatedFont.SetOrientation(0_deg10);
2029
0
                rVDev.SetFont( aUnrotatedFont );
2030
2031
                // TODO(F3): Don't understand parameter semantics of
2032
                // GetTextOutlines()
2033
0
                ::basegfx::B2DPolyPolygon aResultingPolyPolygon;
2034
0
                PolyPolyVector aVCLPolyPolyVector;
2035
0
                const bool bHaveOutlines( rVDev.GetTextOutlines( aVCLPolyPolyVector, rText,
2036
0
                                                                 static_cast<sal_uInt16>(nStartPos),
2037
0
                                                                 static_cast<sal_uInt16>(nStartPos),
2038
0
                                                                 static_cast<sal_uInt16>(nLen),
2039
0
                                                                 0, pDXArray, pKashidaArray ) );
2040
0
                rVDev.SetFont(aOrigFont);
2041
2042
0
                if( !bHaveOutlines )
2043
0
                    return std::shared_ptr<Action>();
2044
2045
                // remove offsetting from mapmode transformation
2046
                // (outline polygons must stay at origin, only need to
2047
                // be scaled)
2048
0
                ::basegfx::B2DHomMatrix aMapModeTransform(
2049
0
                    rState.mapModeTransform );
2050
0
                aMapModeTransform.set(0,2, 0.0);
2051
0
                aMapModeTransform.set(1,2, 0.0);
2052
2053
0
                for( const auto& rVCLPolyPolygon : aVCLPolyPolyVector )
2054
0
                {
2055
0
                    ::basegfx::B2DPolyPolygon aPolyPolygon = rVCLPolyPolygon.getB2DPolyPolygon();
2056
0
                    aPolyPolygon.transform( aMapModeTransform );
2057
2058
                    // append result to collecting polypoly
2059
0
                    for( sal_uInt32 i=0; i<aPolyPolygon.count(); ++i )
2060
0
                    {
2061
                        // #i47795# Ensure closed polygons (since
2062
                        // FreeType returns the glyph outlines
2063
                        // open)
2064
0
                        const ::basegfx::B2DPolygon& rPoly( aPolyPolygon.getB2DPolygon( i ) );
2065
0
                        const sal_uInt32 nCount( rPoly.count() );
2066
0
                        if( nCount<3 ||
2067
0
                            rPoly.isClosed() )
2068
0
                        {
2069
                            // polygon either degenerate, or
2070
                            // already closed.
2071
0
                            aResultingPolyPolygon.append( rPoly );
2072
0
                        }
2073
0
                        else
2074
0
                        {
2075
0
                            ::basegfx::B2DPolygon aPoly(rPoly);
2076
0
                            aPoly.setClosed(true);
2077
2078
0
                            aResultingPolyPolygon.append( aPoly );
2079
0
                        }
2080
0
                    }
2081
0
                }
2082
2083
0
                const uno::Sequence< double > aCharWidthSeq(
2084
0
                    !pDXArray.empty() ?
2085
0
                    setupDXArray( pDXArray, nLen, rState ) :
2086
0
                    setupDXArray( rText,
2087
0
                                  nStartPos,
2088
0
                                  nLen,
2089
0
                                  rVDev,
2090
0
                                  rState ));
2091
0
                const uno::Reference< rendering::XPolyPolygon2D > xTextPoly(
2092
0
                    ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
2093
0
                        rCanvas->getUNOCanvas()->getDevice(),
2094
0
                        aResultingPolyPolygon ) );
2095
2096
                // create background color fill polygon?
2097
0
                css::uno::Reference<css::rendering::XPolyPolygon2D> xTextBoundsPoly;
2098
0
                if (rTextFillColor != COL_AUTO)
2099
0
                {
2100
0
                    rendering::StringContext aStringContext( rText, nStartPos, nLen );
2101
0
                    uno::Reference< rendering::XTextLayout > xTextLayout(
2102
0
                        rState.xFont->createTextLayout(
2103
0
                            aStringContext,
2104
0
                            rState.textDirection,
2105
0
                            0 ) );
2106
2107
0
                    auto aTextBounds = xTextLayout->queryTextBounds();
2108
0
                    auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
2109
0
                    auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
2110
0
                    xTextBoundsPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolygon(
2111
0
                        rCanvas->getUNOCanvas()->getDevice(),
2112
0
                        aTextBoundsPoly);
2113
0
                }
2114
2115
0
                if( rParms.maTextTransformation )
2116
0
                {
2117
0
                    return std::make_shared<OutlineAction>(
2118
0
                            rStartPoint,
2119
0
                            rReliefOffset,
2120
0
                            rReliefColor,
2121
0
                            rShadowOffset,
2122
0
                            rShadowColor,
2123
0
                            rTextFillColor,
2124
0
                            xTextBoundsPoly,
2125
0
                            aResultingPolyPolygon.getB2DRange(),
2126
0
                            xTextPoly,
2127
0
                            aCharWidthSeq,
2128
0
                            rVDev,
2129
0
                            rCanvas,
2130
0
                            rState,
2131
0
                            *rParms.maTextTransformation );
2132
0
                }
2133
0
                else
2134
0
                {
2135
0
                    return std::make_shared<OutlineAction>(
2136
0
                            rStartPoint,
2137
0
                            rReliefOffset,
2138
0
                            rReliefColor,
2139
0
                            rShadowOffset,
2140
0
                            rShadowColor,
2141
0
                            rTextFillColor,
2142
0
                            xTextBoundsPoly,
2143
0
                            aResultingPolyPolygon.getB2DRange(),
2144
0
                            xTextPoly,
2145
0
                            aCharWidthSeq,
2146
0
                            rVDev,
2147
0
                            rCanvas,
2148
0
                            rState  );
2149
0
                }
2150
0
            }
2151
2152
        } // namespace
2153
2154
2155
        std::shared_ptr<Action> TextActionFactory::createTextAction( const ::Point&                 rStartPoint,
2156
                                                             const ::Size&                  rReliefOffset,
2157
                                                             const ::Color&                 rReliefColor,
2158
                                                             const ::Size&                  rShadowOffset,
2159
                                                             const ::Color&                 rShadowColor,
2160
                                                             const ::Color&                 rTextFillColor,
2161
                                                             const OUString&                rText,
2162
                                                             sal_Int32                      nStartPos,
2163
                                                             sal_Int32                      nLen,
2164
                                                             KernArraySpan                  pDXArray,
2165
                                                             std::span<const sal_Bool>     pKashidaArray,
2166
                                                             VirtualDevice&                 rVDev,
2167
                                                             const CanvasSharedPtr&         rCanvas,
2168
                                                             const OutDevState&             rState,
2169
                                                             const Renderer::Parameters&    rParms,
2170
                                                             bool                           bSubsettable    )
2171
0
        {
2172
0
            const ::Size  aBaselineOffset( cppcanvastools::getBaselineOffset( rState,
2173
0
                                                                     rVDev ) );
2174
            // #143885# maintain (nearly) full precision positioning,
2175
            // by circumventing integer-based OutDev-mapping
2176
0
            const ::basegfx::B2DPoint aStartPoint(
2177
0
                rState.mapModeTransform *
2178
0
                ::basegfx::B2DPoint(rStartPoint.X() + aBaselineOffset.Width(),
2179
0
                                    rStartPoint.Y() + aBaselineOffset.Height()) );
2180
2181
0
            const ::basegfx::B2DSize aReliefOffset(
2182
0
                rState.mapModeTransform * vcl::unotools::b2DSizeFromSize( rReliefOffset ) );
2183
0
            const ::basegfx::B2DSize aShadowOffset(
2184
0
                rState.mapModeTransform * vcl::unotools::b2DSizeFromSize( rShadowOffset ) );
2185
2186
0
            if( rState.isTextOutlineModeSet )
2187
0
            {
2188
0
                return createOutline(
2189
0
                            aStartPoint,
2190
0
                            aReliefOffset,
2191
0
                            rReliefColor,
2192
0
                            aShadowOffset,
2193
0
                            rShadowColor,
2194
0
                            rTextFillColor,
2195
0
                            rText,
2196
0
                            nStartPos,
2197
0
                            nLen,
2198
0
                            pDXArray,
2199
0
                            pKashidaArray,
2200
0
                            rVDev,
2201
0
                            rCanvas,
2202
0
                            rState,
2203
0
                            rParms );
2204
0
            }
2205
2206
            // convert DX array to device coordinate system (and
2207
            // create it in the first place, if pDXArray is NULL)
2208
0
            const uno::Sequence< double > aCharWidths(
2209
0
                !pDXArray.empty() ?
2210
0
                setupDXArray( pDXArray, nLen, rState ) :
2211
0
                setupDXArray( rText,
2212
0
                              nStartPos,
2213
0
                              nLen,
2214
0
                              rVDev,
2215
0
                              rState ));
2216
2217
0
            const uno::Sequence< sal_Bool > aKashidas(pKashidaArray.data(), pKashidaArray.size());
2218
2219
            // determine type of text action to create
2220
            // =======================================
2221
2222
0
            const ::Color aEmptyColor( COL_AUTO );
2223
2224
0
            std::shared_ptr<Action> ret;
2225
2226
            // no DX array, and no need to subset - no need to store
2227
            // DX array, then.
2228
0
            if( pDXArray.empty() && !bSubsettable )
2229
0
            {
2230
                // effects, or not?
2231
0
                if( !rState.textOverlineStyle &&
2232
0
                    !rState.textUnderlineStyle &&
2233
0
                    !rState.textStrikeoutStyle &&
2234
0
                    rReliefColor == aEmptyColor &&
2235
0
                    rShadowColor == aEmptyColor &&
2236
0
                    rTextFillColor == aEmptyColor )
2237
0
                {
2238
                    // nope
2239
0
                    if( rParms.maTextTransformation )
2240
0
                    {
2241
0
                        ret = std::make_shared<TextAction>(
2242
0
                                                    aStartPoint,
2243
0
                                                    rText,
2244
0
                                                    nStartPos,
2245
0
                                                    nLen,
2246
0
                                                    rCanvas,
2247
0
                                                    rState,
2248
0
                                                    *rParms.maTextTransformation );
2249
0
                    }
2250
0
                    else
2251
0
                    {
2252
0
                        ret = std::make_shared<TextAction>(
2253
0
                                                    aStartPoint,
2254
0
                                                    rText,
2255
0
                                                    nStartPos,
2256
0
                                                    nLen,
2257
0
                                                    rCanvas,
2258
0
                                                    rState );
2259
0
                    }
2260
0
                }
2261
0
                else
2262
0
                {
2263
                    // at least one of the effects requested
2264
0
                    if( rParms.maTextTransformation )
2265
0
                        ret = std::make_shared<EffectTextAction>(
2266
0
                                                    aStartPoint,
2267
0
                                                    aReliefOffset,
2268
0
                                                    rReliefColor,
2269
0
                                                    aShadowOffset,
2270
0
                                                    rShadowColor,
2271
0
                                                    rTextFillColor,
2272
0
                                                    rText,
2273
0
                                                    nStartPos,
2274
0
                                                    nLen,
2275
0
                                                    rVDev,
2276
0
                                                    rCanvas,
2277
0
                                                    rState,
2278
0
                                                    *rParms.maTextTransformation );
2279
0
                    else
2280
0
                        ret = std::make_shared<EffectTextAction>(
2281
0
                                                    aStartPoint,
2282
0
                                                    aReliefOffset,
2283
0
                                                    rReliefColor,
2284
0
                                                    aShadowOffset,
2285
0
                                                    rShadowColor,
2286
0
                                                    rTextFillColor,
2287
0
                                                    rText,
2288
0
                                                    nStartPos,
2289
0
                                                    nLen,
2290
0
                                                    rVDev,
2291
0
                                                    rCanvas,
2292
0
                                                    rState );
2293
0
                }
2294
0
            }
2295
0
            else
2296
0
            {
2297
                // DX array necessary - any effects?
2298
0
                if( !rState.textOverlineStyle &&
2299
0
                    !rState.textUnderlineStyle &&
2300
0
                    !rState.textStrikeoutStyle &&
2301
0
                    rReliefColor == aEmptyColor &&
2302
0
                    rShadowColor == aEmptyColor &&
2303
0
                    rTextFillColor == aEmptyColor )
2304
0
                {
2305
                    // nope
2306
0
                    if( rParms.maTextTransformation )
2307
0
                        ret = std::make_shared<TextArrayAction>(
2308
0
                                                    aStartPoint,
2309
0
                                                    rText,
2310
0
                                                    nStartPos,
2311
0
                                                    nLen,
2312
0
                                                    aCharWidths,
2313
0
                                                    aKashidas,
2314
0
                                                    rCanvas,
2315
0
                                                    rState,
2316
0
                                                    *rParms.maTextTransformation );
2317
0
                    else
2318
0
                        ret = std::make_shared<TextArrayAction>(
2319
0
                                                    aStartPoint,
2320
0
                                                    rText,
2321
0
                                                    nStartPos,
2322
0
                                                    nLen,
2323
0
                                                    aCharWidths,
2324
0
                                                    aKashidas,
2325
0
                                                    rCanvas,
2326
0
                                                    rState );
2327
0
                }
2328
0
                else
2329
0
                {
2330
                    // at least one of the effects requested
2331
0
                    if( rParms.maTextTransformation )
2332
0
                        ret = std::make_shared<EffectTextArrayAction>(
2333
0
                                                    aStartPoint,
2334
0
                                                    aReliefOffset,
2335
0
                                                    rReliefColor,
2336
0
                                                    aShadowOffset,
2337
0
                                                    rShadowColor,
2338
0
                                                    rTextFillColor,
2339
0
                                                    rText,
2340
0
                                                    nStartPos,
2341
0
                                                    nLen,
2342
0
                                                    aCharWidths,
2343
0
                                                    aKashidas,
2344
0
                                                    rVDev,
2345
0
                                                    rCanvas,
2346
0
                                                    rState,
2347
0
                                                    *rParms.maTextTransformation );
2348
0
                    else
2349
0
                        ret = std::make_shared<EffectTextArrayAction>(
2350
0
                                                    aStartPoint,
2351
0
                                                    aReliefOffset,
2352
0
                                                    rReliefColor,
2353
0
                                                    aShadowOffset,
2354
0
                                                    rShadowColor,
2355
0
                                                    rTextFillColor,
2356
0
                                                    rText,
2357
0
                                                    nStartPos,
2358
0
                                                    nLen,
2359
0
                                                    aCharWidths,
2360
0
                                                    aKashidas,
2361
0
                                                    rVDev,
2362
0
                                                    rCanvas,
2363
0
                                                    rState );
2364
0
                }
2365
0
            }
2366
0
            return ret;
2367
0
        }
2368
}
2369
2370
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */