LibreOffice Module cppcanvas (master) 1
textaction.cxx
Go to the documentation of this file.
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
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
34
35#include <tools/gen.hxx>
36#include <utility>
37#include <vcl/canvastools.hxx>
38#include <vcl/virdev.hxx>
39
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
51using namespace ::com::sun::star;
52
53namespace 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 {
62 tools::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 tools::modifyClip( o_rRenderState,
71 rState,
72 rCanvas,
73 rStartPoint,
74 nullptr,
75 &rState.fontRotation );
76
77 basegfx::B2DHomMatrix aLocalTransformation(basegfx::utils::createRotateB2DHomMatrix(rState.fontRotation));
78 aLocalTransformation.translate( rStartPoint.getX(),
79 rStartPoint.getY() );
80 ::canvas::tools::appendToRenderState( o_rRenderState,
81 aLocalTransformation );
82
83 o_rRenderState.DeviceColor = rState.textColor;
84 }
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 {
92 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 ::canvas::tools::prependToRenderState( o_rRenderState,
102 rTextTransform );
103 }
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 {
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 if( !o_rFont.is() )
115 {
116 // Use completely default FontRequest
117 const rendering::FontRequest aFontRequest;
118
119 geometry::Matrix2D aFontMatrix;
120 ::canvas::tools::setIdentityMatrix2D( aFontMatrix );
121
122 o_rFont = rCanvas->getUNOCanvas()->createFont(
123 aFontRequest,
124 uno::Sequence< beans::PropertyValue >(),
125 aFontMatrix );
126 }
127
128 init( o_rRenderState,
129 rStartPoint,
130 rState,
131 rCanvas );
132 }
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 {
141 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 ::canvas::tools::prependToRenderState( o_rRenderState,
151 rTextTransform );
152 }
153
154 void initLayoutWidth(double& rLayoutWidth, const uno::Sequence<double>& rOffsets)
155 {
156 ENSURE_OR_THROW(rOffsets.hasElements(),
157 "::cppcanvas::internal::initLayoutWidth(): zero-length array" );
158 rLayoutWidth = *(std::max_element(rOffsets.begin(), rOffsets.end()));
159 }
160
161 uno::Sequence< double > setupDXArray( KernArraySpan rCharWidths,
162 sal_Int32 nLen,
163 const OutDevState& rState )
164 {
165 // convert character widths from logical units
166 uno::Sequence< double > aCharWidthSeq( nLen );
167 double* pOutputWidths( aCharWidthSeq.getArray() );
168
169 // #143885# maintain (nearly) full precision of DX
170 // array, by circumventing integer-based
171 // OutDev-mapping
172 const double nScale( rState.mapModeTransform.get(0,0) );
173 for( int i = 0; i < nLen; ++i )
174 {
175 // TODO(F2): use correct scale direction
176 *pOutputWidths++ = rCharWidths[i] * nScale;
177 }
178
179 return aCharWidthSeq;
180 }
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 {
188 // no external DX array given, create one from given
189 // string
190 KernArray aCharWidths;
191
192 rVDev.GetTextArray( rText, &aCharWidths, nStartPos, nLen );
193
194 return setupDXArray( aCharWidths, nLen, rState );
195 }
196
197 ::basegfx::B2DPoint adaptStartPoint( const ::basegfx::B2DPoint& rStartPoint,
198 const OutDevState& rState,
199 const uno::Sequence< double >& rOffsets )
200 {
201 ::basegfx::B2DPoint aLocalPoint( rStartPoint );
202
203 if( rState.textAlignment )
204 {
205 // text origin is right, not left. Modify start point
206 // accordingly, because XCanvas::drawTextLayout()
207 // always aligns left!
208
209 const double nOffset( rOffsets[ rOffsets.getLength()-1 ] );
210
211 // correct start point for rotated text: rotate around
212 // former start point
213 aLocalPoint.setX( aLocalPoint.getX() + cos( rState.fontRotation )*nOffset );
214 aLocalPoint.setY( aLocalPoint.getY() + sin( rState.fontRotation )*nOffset );
215 }
216
217 return aLocalPoint;
218 }
219
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 {
237 ENSURE_OR_THROW( rOffsets.hasElements(),
238 "::cppcanvas::internal::initArrayAction(): zero-length DX array" );
239
240 const ::basegfx::B2DPoint aLocalStartPoint(
241 adaptStartPoint( rStartPoint, rState, rOffsets ) );
242
243 uno::Reference< rendering::XCanvasFont > xFont( rState.xFont );
244
245 if( pTextTransform )
246 init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas, *pTextTransform );
247 else
248 init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas );
249
250 o_rTextLayout = xFont->createTextLayout(
251 rendering::StringContext( rText, nStartPos, nLen ),
252 rState.textDirection,
253 0 );
254
255 ENSURE_OR_THROW( o_rTextLayout.is(),
256 "::cppcanvas::internal::initArrayAction(): Invalid font" );
257
258 o_rTextLayout->applyLogicalAdvancements( rOffsets );
259 o_rTextLayout->applyKashidaPositions( rKashidas );
260
261 }
262
263 double getLineWidth( ::VirtualDevice const & rVDev,
264 const OutDevState& rState,
265 const rendering::StringContext& rStringContext )
266 {
267 // TODO(F2): use correct scale direction
268 const ::basegfx::B2DSize aSize( rVDev.GetTextWidth( rStringContext.Text,
269 static_cast<sal_uInt16>(rStringContext.StartPosition),
270 static_cast<sal_uInt16>(rStringContext.Length) ),
271 0 );
272
273 return (rState.mapModeTransform * aSize).getWidth();
274 }
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 {
284 ENSURE_OR_THROW( rSubset.mnSubsetEnd > rSubset.mnSubsetBegin,
285 "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
286
287 uno::Sequence< double > aOrigOffsets( rOrigTextLayout->queryLogicalAdvancements() );
288 const double* pOffsets( aOrigOffsets.getConstArray() );
289
290 ENSURE_OR_THROW( aOrigOffsets.getLength() >= rSubset.mnSubsetEnd,
291 "::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 const double nMinPos( rSubset.mnSubsetBegin <= 0 ? 0 :
300 *(std::min_element( pOffsets+rSubset.mnSubsetBegin-1,
301 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 const double nMaxPos(
309 *(std::max_element( pOffsets + (rSubset.mnSubsetBegin <= 0 ?
310 0 : rSubset.mnSubsetBegin-1),
311 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 const double nOffset = rOrigTextLayout->getMainTextDirection()
321 ? 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 if (nOffset > 0.0)
333 {
334 ::basegfx::B2DHomMatrix aTranslation;
335 if( rOrigTextLayout->getFont()->getFontRequest().FontDescription.IsVertical == css::util::TriState_YES )
336 {
337 // vertical text -> offset in y direction
338 aTranslation.translate(0.0, nOffset);
339 }
340 else
341 {
342 // horizontal text -> offset in x direction
343 aTranslation.translate(nOffset, 0.0);
344 }
345
346 ::canvas::tools::appendToRenderState( io_rRenderState,
347 aTranslation );
348 }
349
350
351 // reduce DX array to given substring
352
353
354 const sal_Int32 nNewElements( rSubset.mnSubsetEnd - rSubset.mnSubsetBegin );
355 uno::Sequence< double > aAdaptedOffsets( nNewElements );
356 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 std::transform( pOffsets + rSubset.mnSubsetBegin,
362 pOffsets + rSubset.mnSubsetEnd,
363 pAdaptedOffsets,
364 [nMinPos](double aPos) { return aPos - nMinPos; } );
365
366 o_rMinPos = nMinPos;
367 o_rMaxPos = nMaxPos;
368
369 return aAdaptedOffsets;
370 }
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 {
377 // create temporary new text layout with subset string
378
379
380 const sal_Int32 nNewStartPos( rOrigContext.StartPosition + std::min(
381 rSubset.mnSubsetBegin, rOrigContext.Length-1 ) );
382 const sal_Int32 nNewLength( std::max(
383 std::min(
384 rSubset.mnSubsetEnd - rSubset.mnSubsetBegin,
385 rOrigContext.Length ),
386 sal_Int32( 0 ) ) );
387
388 const rendering::StringContext aContext( rOrigContext.Text,
389 nNewStartPos,
390 nNewLength );
391
392 uno::Reference< rendering::XTextLayout > xTextLayout(
393 rOrigTextLayout->getFont()->createTextLayout( aContext,
394 rOrigTextLayout->getMainTextDirection(),
395 0 ),
396 uno::UNO_SET_THROW );
397
398 return xTextLayout;
399 }
400
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 {
428 ::canvas::tools::prependToRenderState(io_rRenderState, rTransformation);
429
430 if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
431 {
432 // empty range, empty layout
433 io_rTextLayout.clear();
434
435 return;
436 }
437
438 ENSURE_OR_THROW( io_rTextLayout.is(),
439 "createSubsetLayout(): Invalid input layout" );
440
441 const rendering::StringContext& rOrigContext( io_rTextLayout->getText() );
442
443 if( rSubset.mnSubsetBegin == 0 &&
444 rSubset.mnSubsetEnd == rOrigContext.Length )
445 {
446 // full range, no need for subsetting
447 return;
448 }
449
450 uno::Reference< rendering::XTextLayout > xTextLayout(
451 createSubsetLayout( rOrigContext, rSubset, io_rTextLayout ) );
452
453 if( xTextLayout.is() )
454 {
455 xTextLayout->applyLogicalAdvancements(
456 calcSubsetOffsets( io_rRenderState,
457 o_rMinPos,
458 o_rMaxPos,
459 io_rTextLayout,
460 nLayoutWidth,
461 rSubset ) );
462 uno::Sequence< sal_Bool > aOrigKashidaPositions(io_rTextLayout->queryKashidaPositions());
463 uno::Sequence< sal_Bool > aKashidaPositions(aOrigKashidaPositions.getArray() + rSubset.mnSubsetBegin,
464 rSubset.mnSubsetEnd - rSubset.mnSubsetBegin);
465 xTextLayout->applyKashidaPositions(aKashidaPositions);
466 }
467
468 io_rTextLayout = xTextLayout;
469 }
470
471
477 class TextRenderer
478 {
479 public:
480 virtual ~TextRenderer() {}
481
483 virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const = 0;
484 };
485
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 {
502 ::Color aEmptyColor( COL_AUTO );
503 uno::Reference<rendering::XColorSpace> xColorSpace(
504 xCanvas->getDevice()->getDeviceColorSpace() );
505
506 // draw shadow text, if enabled
507 if( rShadowColor != aEmptyColor )
508 {
509 rendering::RenderState aShadowState( rRenderState );
510 ::basegfx::B2DHomMatrix aTranslate;
511
512 aTranslate.translate(rShadowOffset.getWidth(),
513 rShadowOffset.getHeight());
514
515 ::canvas::tools::appendToRenderState(aShadowState, aTranslate);
516
517 aShadowState.DeviceColor =
519 xColorSpace );
520
521 rRenderer( aShadowState, rTextFillColor, false );
522 }
523
524 // draw relief text, if enabled
525 if( rReliefColor != aEmptyColor )
526 {
527 rendering::RenderState aReliefState( rRenderState );
528 ::basegfx::B2DHomMatrix aTranslate;
529
530 aTranslate.translate(rReliefOffset.getWidth(),
531 rReliefOffset.getHeight());
532
533 ::canvas::tools::appendToRenderState(aReliefState, aTranslate);
534
535 aReliefState.DeviceColor =
537 xColorSpace );
538
539 rRenderer( aReliefState, rTextFillColor, false );
540 }
541
542 // draw normal text
543 rRenderer( rRenderState, rTextFillColor, true );
544
545 return true;
546 }
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 {
556 ::basegfx::B2DRange aBounds( rTextBounds );
557
558 // add extends of text lines
559 aBounds.expand( rLineBounds );
560
561 // TODO(Q3): Provide this functionality at the B2DRange
562 ::basegfx::B2DRange aTotalBounds( aBounds );
563 aTotalBounds.expand(
564 ::basegfx::B2DRange( aBounds.getMinX() + rReliefOffset.getWidth(),
565 aBounds.getMinY() + rReliefOffset.getHeight(),
566 aBounds.getMaxX() + rReliefOffset.getWidth(),
567 aBounds.getMaxY() + rReliefOffset.getHeight() ) );
568 aTotalBounds.expand(
569 ::basegfx::B2DRange( aBounds.getMinX() + rShadowOffset.getWidth(),
570 aBounds.getMinY() + rShadowOffset.getHeight(),
571 aBounds.getMaxX() + rShadowOffset.getWidth(),
572 aBounds.getMaxY() + rShadowOffset.getHeight() ) );
573
574 return tools::calcDevicePixelBounds( aTotalBounds,
575 rViewState,
576 rRenderState );
577 }
578
579 void initEffectLinePolyPolygon( ::basegfx::B2DSize& o_rOverallSize,
580 uno::Reference< rendering::XPolyPolygon2D >& o_rTextLines,
581 const CanvasSharedPtr& rCanvas,
582 double nLineWidth,
583 const tools::TextLineInfo& rLineInfo )
584 {
585 const ::basegfx::B2DPolyPolygon aPoly(
586 tools::createTextLinesPolyPolygon( 0.0, nLineWidth,
587 rLineInfo ) );
588 auto aRange = basegfx::utils::getRange( aPoly ).getRange();
589 o_rOverallSize = basegfx::B2DSize(aRange.getX(), aRange.getY());
590
591 o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
592 rCanvas->getUNOCanvas()->getDevice(),
593 aPoly );
594 }
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;
639 rendering::RenderState maState;
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 mxFont( rState.xFont ),
650 maStringContext( rString, nStartPos, nLen ),
651 mpCanvas( rCanvas ),
652 maTextDirection( rState.textDirection )
653 {
655 rStartPoint,
656 rState, rCanvas );
657
659 "::cppcanvas::internal::TextAction(): Invalid font" );
660 }
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 mxFont( rState.xFont ),
670 maStringContext( rString, nStartPos, nLen ),
671 mpCanvas( rCanvas ),
672 maTextDirection( rState.textDirection )
673 {
675 rStartPoint,
676 rState, rCanvas, rTextTransform );
677
679 "::cppcanvas::internal::TextAction(): Invalid font" );
680 }
681
682 bool TextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
683 {
684 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextAction::render()" );
685 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextAction: 0x" << std::hex << this );
686
687 rendering::RenderState aLocalState( maState );
688 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
689
690 mpCanvas->getUNOCanvas()->drawText( maStringContext, mxFont,
691 mpCanvas->getViewState(), aLocalState, maTextDirection );
692
693 return true;
694 }
695
696 bool TextAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
697 const Subset& /*rSubset*/ ) const
698 {
699 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 return render( rTransformation );
706 }
707
708 ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
709 {
710 // create XTextLayout, to have the
711 // XTextLayout::queryTextBounds() method available
712 uno::Reference< rendering::XTextLayout > xTextLayout(
713 mxFont->createTextLayout(
716 0 ) );
717
718 rendering::RenderState aLocalState( maState );
719 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
720
721 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
722 xTextLayout->queryTextBounds() ),
723 mpCanvas->getViewState(),
724 aLocalState );
725 }
726
727 ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
728 const Subset& /*rSubset*/ ) const
729 {
730 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 return getBounds( rTransformation );
737 }
738
739 sal_Int32 TextAction::getActionCount() const
740 {
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 return 1;
746 }
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:
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;
811 rendering::RenderState maState;
812 const tools::TextLineInfo maTextLineInfo;
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;
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 mxFont( rState.xFont ),
836 maStringContext( rText, nStartPos, nLen ),
837 mpCanvas( rCanvas ),
838 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
839 maReliefOffset( rReliefOffset ),
840 maReliefColor( rReliefColor ),
841 maShadowOffset( rShadowOffset ),
842 maShadowColor( rShadowColor ),
843 maTextFillColor( rTextFillColor ),
844 maTextDirection( rState.textDirection )
845 {
846 const double nLineWidth(getLineWidth( rVDev, rState, maStringContext ));
847 initEffectLinePolyPolygon( maLinesOverallSize,
849 rCanvas,
852
854 rStartPoint,
855 rState, rCanvas );
856
857 ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
858 "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
859 }
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 mxFont( rState.xFont ),
875 maStringContext( rText, nStartPos, nLen ),
876 mpCanvas( rCanvas ),
877 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
878 maReliefOffset( rReliefOffset ),
879 maReliefColor( rReliefColor ),
880 maShadowOffset( rShadowOffset ),
881 maShadowColor( rShadowColor ),
882 maTextFillColor( rTextFillColor ),
883 maTextDirection( rState.textDirection )
884 {
885 const double nLineWidth( getLineWidth( rVDev, rState, maStringContext ) );
886 initEffectLinePolyPolygon( maLinesOverallSize,
888 rCanvas,
889 nLineWidth,
891
893 rStartPoint,
894 rState, rCanvas, rTextTransform );
895
896 ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
897 "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
898 }
899
900 bool EffectTextAction::operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool /*bNormalText*/ ) const
901 {
902 const rendering::ViewState& rViewState( mpCanvas->getViewState() );
903 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
904
905 //rhbz#1589029 non-transparent text fill background support
906 if (rTextFillColor != COL_AUTO)
907 {
908 rendering::RenderState aLocalState( rRenderState );
909 aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
910 rTextFillColor, rCanvas->getDevice()->getDeviceColorSpace());
911 auto xTextBounds = queryTextBounds(rCanvas);
912 // background of text
913 rCanvas->fillPolyPolygon(xTextBounds, rViewState, aLocalState);
914 }
915
916 // under/over lines
917 rCanvas->fillPolyPolygon( mxTextLines,
918 rViewState,
919 rRenderState );
920
921 rCanvas->drawText( maStringContext, mxFont,
922 rViewState,
923 rRenderState,
925
926 return true;
927 }
928
929 bool EffectTextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
930 {
931 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextAction::render()" );
932 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextAction: 0x" << std::hex << this );
933
934 rendering::RenderState aLocalState( maState );
935 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
936
937 return renderEffectText( *this,
938 aLocalState,
939 mpCanvas->getUNOCanvas(),
945 }
946
947 bool EffectTextAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
948 const Subset& /*rSubset*/ ) const
949 {
950 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 return render( rTransformation );
957 }
958
959 geometry::RealRectangle2D EffectTextAction::queryTextBounds() const
960 {
961 // create XTextLayout, to have the
962 // XTextLayout::queryTextBounds() method available
963 uno::Reference< rendering::XTextLayout > xTextLayout(
964 mxFont->createTextLayout(
967 0 ) );
968
969 return xTextLayout->queryTextBounds();
970 }
971
972 css::uno::Reference<css::rendering::XPolyPolygon2D> EffectTextAction::queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const
973 {
974 auto aTextBounds = queryTextBounds();
975 auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
976 auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
977 return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(rCanvas->getDevice(), aTextBoundsPoly);
978 }
979
980 ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
981 {
982 rendering::RenderState aLocalState( maState );
983 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
984
985 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
986 queryTextBounds() ),
992 aLocalState,
993 mpCanvas->getViewState() );
994 }
995
996 ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
997 const Subset& /*rSubset*/ ) const
998 {
999 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 return getBounds( rTransformation );
1006 }
1007
1008 sal_Int32 EffectTextAction::getActionCount() const
1009 {
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 return 1;
1015 }
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;
1063 rendering::RenderState maState;
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 mpCanvas( rCanvas )
1076 {
1077 initLayoutWidth(mnLayoutWidth, rOffsets);
1078
1079 initArrayAction( maState,
1081 rStartPoint,
1082 rString,
1083 nStartPos,
1084 nLen,
1085 rOffsets,
1086 rKashidas,
1087 rCanvas,
1088 rState, nullptr );
1089 }
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 mpCanvas( rCanvas )
1101 {
1102 initLayoutWidth(mnLayoutWidth, rOffsets);
1103
1104 initArrayAction( maState,
1106 rStartPoint,
1107 rString,
1108 nStartPos,
1109 nLen,
1110 rOffsets,
1111 rKashidas,
1112 rCanvas,
1113 rState,
1114 &rTextTransform );
1115 }
1116
1117 bool TextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1118 {
1119 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction::render()" );
1120 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction: 0x" << std::hex << this );
1121
1122 rendering::RenderState aLocalState( maState );
1123 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1124
1125 mpCanvas->getUNOCanvas()->drawTextLayout( mxTextLayout,
1126 mpCanvas->getViewState(),
1127 aLocalState );
1128
1129 return true;
1130 }
1131
1132 bool TextArrayAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1133 const Subset& rSubset ) const
1134 {
1135 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction::renderSubset()" );
1136 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction: 0x" << std::hex << this );
1137
1138 rendering::RenderState aLocalState( maState );
1139 uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1140
1141 double nDummy0, nDummy1;
1142 createSubsetLayout( xTextLayout,
1144 aLocalState,
1145 nDummy0,
1146 nDummy1,
1147 rTransformation,
1148 rSubset );
1149
1150 if( !xTextLayout.is() )
1151 return true; // empty layout, render nothing
1152
1153 mpCanvas->getUNOCanvas()->drawTextLayout( xTextLayout,
1154 mpCanvas->getViewState(),
1155 aLocalState );
1156
1157 return true;
1158 }
1159
1160 ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1161 {
1162 rendering::RenderState aLocalState( maState );
1163 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1164
1165 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1166 mxTextLayout->queryTextBounds() ),
1167 mpCanvas->getViewState(),
1168 aLocalState );
1169 }
1170
1171 ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1172 const Subset& rSubset ) const
1173 {
1174 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction::getBounds( subset )" );
1175 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction: 0x" << std::hex << this );
1176
1177 rendering::RenderState aLocalState( maState );
1178 uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1179
1180 double nDummy0, nDummy1;
1181 createSubsetLayout( xTextLayout,
1183 aLocalState,
1184 nDummy0,
1185 nDummy1,
1186 rTransformation,
1187 rSubset );
1188
1189 if( !xTextLayout.is() )
1190 return ::basegfx::B2DRange(); // empty layout, empty bounds
1191
1192 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1193 xTextLayout->queryTextBounds() ),
1194 mpCanvas->getViewState(),
1195 aLocalState );
1196 }
1197
1198 sal_Int32 TextArrayAction::getActionCount() const
1199 {
1200 const rendering::StringContext& rOrigContext( mxTextLayout->getText() );
1201
1202 return rOrigContext.Length;
1203 }
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;
1269 rendering::RenderState maState;
1270 const tools::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 mpCanvas( rCanvas ),
1295 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1296 maTextLinesHelper(mpCanvas, rState),
1297 maReliefOffset( rReliefOffset ),
1298 maReliefColor( rReliefColor ),
1299 maShadowOffset( rShadowOffset ),
1300 maShadowColor( rShadowColor ),
1301 maTextFillColor( rTextFillColor )
1302 {
1303 initLayoutWidth(mnLayoutWidth, rOffsets);
1304
1306
1307 initArrayAction( maState,
1309 rStartPoint,
1310 rText,
1311 nStartPos,
1312 nLen,
1313 rOffsets,
1314 rKashidas,
1315 rCanvas,
1316 rState, nullptr );
1317 }
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 mpCanvas( rCanvas ),
1335 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1336 maTextLinesHelper(mpCanvas, rState),
1337 maReliefOffset( rReliefOffset ),
1338 maReliefColor( rReliefColor ),
1339 maShadowOffset( rShadowOffset ),
1340 maShadowColor( rShadowColor ),
1341 maTextFillColor( rTextFillColor )
1342 {
1343 initLayoutWidth(mnLayoutWidth, rOffsets);
1344
1346
1347 initArrayAction( maState,
1349 rStartPoint,
1350 rText,
1351 nStartPos,
1352 nLen,
1353 rOffsets,
1354 rKashidas,
1355 rCanvas,
1356 rState,
1357 &rTextTransform );
1358 }
1359
1360 css::uno::Reference<css::rendering::XPolyPolygon2D> EffectTextArrayAction::queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const
1361 {
1362 const geometry::RealRectangle2D aTextBounds(mxTextLayout->queryTextBounds());
1363 auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
1364 auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
1365 return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(rCanvas->getDevice(), aTextBoundsPoly);
1366 }
1367
1368 bool EffectTextArrayAction::operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText) const
1369 {
1370 const rendering::ViewState& rViewState( mpCanvas->getViewState() );
1371 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
1372
1373 //rhbz#1589029 non-transparent text fill background support
1374 if (rTextFillColor != COL_AUTO)
1375 {
1376 rendering::RenderState aLocalState(rRenderState);
1377 aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
1378 rTextFillColor, rCanvas->getDevice()->getDeviceColorSpace());
1379 auto xTextBounds = queryTextBounds(rCanvas);
1380 // background of text
1381 rCanvas->fillPolyPolygon(xTextBounds, rViewState, aLocalState);
1382 }
1383
1384 // under/over lines
1385 maTextLinesHelper.render(rRenderState, bNormalText);
1386
1387 rCanvas->drawTextLayout( mxTextLayout,
1388 rViewState,
1389 rRenderState );
1390
1391 return true;
1392 }
1393
1394 bool EffectTextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1395 {
1396 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::render()" );
1397 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1398
1399 rendering::RenderState aLocalState( maState );
1400 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1401
1402 return renderEffectText( *this,
1403 aLocalState,
1404 mpCanvas->getUNOCanvas(),
1410 }
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 mrCanvas( rCanvas ),
1420 mrTextLayout( rTextLayout ),
1421 mrTextLinesHelper( rTextLinesHelper ),
1422 mrViewState( rViewState )
1423 {
1424 }
1425
1426 // TextRenderer interface
1427 virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor,bool bNormalText) const override
1428 {
1429 mrTextLinesHelper.render(rRenderState, bNormalText);
1430
1431 //rhbz#1589029 non-transparent text fill background support
1432 if (rTextFillColor != COL_AUTO)
1433 {
1434 rendering::RenderState aLocalState(rRenderState);
1435 aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
1436 rTextFillColor, mrCanvas->getDevice()->getDeviceColorSpace());
1437 auto xTextBounds = queryTextBounds();
1438 // background of text
1439 mrCanvas->fillPolyPolygon(xTextBounds, mrViewState, aLocalState);
1440 }
1441
1442 mrCanvas->drawTextLayout( mrTextLayout,
1444 rRenderState );
1445
1446 return true;
1447 }
1448
1449 private:
1450
1451 css::uno::Reference<css::rendering::XPolyPolygon2D> queryTextBounds() const
1452 {
1453 const geometry::RealRectangle2D aTextBounds(mrTextLayout->queryTextBounds());
1454 auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
1455 auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
1456 return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(mrCanvas->getDevice(), aTextBoundsPoly);
1457 }
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 {
1468 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::renderSubset()" );
1469 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1470
1471 rendering::RenderState aLocalState( maState );
1472 uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1473 const geometry::RealRectangle2D aTextBounds( mxTextLayout->queryTextBounds() );
1474
1475 double nMinPos(0.0);
1476 double nMaxPos(aTextBounds.X2 - aTextBounds.X1);
1477
1478 createSubsetLayout( xTextLayout,
1480 aLocalState,
1481 nMinPos,
1482 nMaxPos,
1483 rTransformation,
1484 rSubset );
1485
1486 if( !xTextLayout.is() )
1487 return true; // empty layout, render nothing
1488
1489
1490 // create and setup local line polygon
1491 // ===================================
1492
1493 uno::Reference< rendering::XCanvas > xCanvas( mpCanvas->getUNOCanvas() );
1494 const rendering::ViewState& rViewState( mpCanvas->getViewState() );
1495
1496 TextLinesHelper aHelper = maTextLinesHelper;
1497 aHelper.init(nMaxPos - nMinPos, maTextLineInfo);
1498
1499
1500 // render everything
1501 // =================
1502
1503 return renderEffectText(
1504 EffectTextArrayRenderHelper( xCanvas,
1505 xTextLayout,
1506 aHelper,
1507 rViewState ),
1508 aLocalState,
1509 xCanvas,
1515 }
1516
1517 ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1518 {
1519 rendering::RenderState aLocalState( maState );
1520 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1521
1522 ::basegfx::B2DSize aSize = maTextLinesHelper.getOverallSize();
1523
1524 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1525 mxTextLayout->queryTextBounds() ),
1526 basegfx::B2DRange(0, 0,
1527 aSize.getWidth(),
1528 aSize.getHeight()),
1531 aLocalState,
1532 mpCanvas->getViewState() );
1533 }
1534
1535 ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1536 const Subset& rSubset ) const
1537 {
1538 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::getBounds( subset )" );
1539 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1540
1541 rendering::RenderState aLocalState( maState );
1542 uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1543 const geometry::RealRectangle2D aTextBounds( mxTextLayout->queryTextBounds() );
1544
1545 double nMinPos(0.0);
1546 double nMaxPos(aTextBounds.X2 - aTextBounds.X1);
1547
1548 createSubsetLayout( xTextLayout,
1550 aLocalState,
1551 nMinPos,
1552 nMaxPos,
1553 rTransformation,
1554 rSubset );
1555
1556 if( !xTextLayout.is() )
1557 return ::basegfx::B2DRange(); // empty layout, empty bounds
1558
1559
1560 // create and setup local line polygon
1561 // ===================================
1562
1563 const ::basegfx::B2DPolyPolygon aPoly(
1565 0.0, nMaxPos - nMinPos,
1566 maTextLineInfo ) );
1567
1568 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1569 xTextLayout->queryTextBounds() ),
1570 ::basegfx::utils::getRange( aPoly ),
1573 aLocalState,
1574 mpCanvas->getViewState() );
1575 }
1576
1577 sal_Int32 EffectTextArrayAction::getActionCount() const
1578 {
1579 const rendering::StringContext& rOrigContext( mxTextLayout->getText() );
1580
1581 return rOrigContext.Length;
1582 }
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 ::basegfx::B2DRectangle& rOutlineBounds,
1596 uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
1597 const uno::Sequence< double >& rOffsets,
1598 VirtualDevice const & rVDev,
1599 const CanvasSharedPtr& rCanvas,
1600 const OutDevState& rState );
1601 OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1602 const ::basegfx::B2DSize& rReliefOffset,
1603 const ::Color& rReliefColor,
1604 const ::basegfx::B2DSize& rShadowOffset,
1605 const ::Color& rShadowColor,
1606 const ::basegfx::B2DRectangle& rOutlineBounds,
1607 uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
1608 const uno::Sequence< double >& rOffsets,
1609 VirtualDevice const & rVDev,
1610 const CanvasSharedPtr& rCanvas,
1611 const OutDevState& rState,
1612 const ::basegfx::B2DHomMatrix& rTextTransform );
1613
1614 OutlineAction(const OutlineAction&) = delete;
1615 const OutlineAction& operator=(const OutlineAction&) = delete;
1616
1617 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1618 virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1619 const Subset& rSubset ) const override;
1620
1621 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1622 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1623 const Subset& rSubset ) const override;
1624
1625 virtual sal_Int32 getActionCount() const override;
1626
1627 private:
1628 // TextRenderer interface
1629 virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const override;
1630
1631 // TODO(P2): This is potentially a real mass object
1632 // (every character might be a separate TextAction),
1633 // thus, make it as lightweight as possible. For
1634 // example, share common RenderState among several
1635 // TextActions, maybe using maOffsets for the
1636 // translation.
1637
1638 uno::Reference< rendering::XPolyPolygon2D > mxTextPoly;
1639
1640 const uno::Sequence< double > maOffsets;
1642 rendering::RenderState maState;
1644 const uno::Sequence< double > maFillColor;
1645 const tools::TextLineInfo maTextLineInfo;
1647 const ::basegfx::B2DRectangle maOutlineBounds;
1648 uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
1649 const ::basegfx::B2DSize maReliefOffset;
1650 const ::Color maReliefColor;
1651 const ::basegfx::B2DSize maShadowOffset;
1652 const ::Color maShadowColor;
1653 const ::Color maTextFillColor;
1654 };
1655
1656 double calcOutlineWidth( const OutDevState& rState,
1657 VirtualDevice const & rVDev )
1658 {
1659 const ::basegfx::B2DSize aFontSize( 0,
1660 rVDev.GetFont().GetFontHeight() / 64.0 );
1661
1662 const double nOutlineWidth(
1663 (rState.mapModeTransform * aFontSize).getHeight() );
1664
1665 return nOutlineWidth < 1.0 ? 1.0 : nOutlineWidth;
1666 }
1667
1668 OutlineAction::OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1669 const ::basegfx::B2DSize& rReliefOffset,
1670 const ::Color& rReliefColor,
1671 const ::basegfx::B2DSize& rShadowOffset,
1672 const ::Color& rShadowColor,
1673 const ::basegfx::B2DRectangle& rOutlineBounds,
1674 uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
1675 const uno::Sequence< double >& rOffsets,
1676 VirtualDevice const & rVDev,
1677 const CanvasSharedPtr& rCanvas,
1678 const OutDevState& rState ) :
1679 mxTextPoly(std::move( xTextPoly )),
1680 maOffsets( rOffsets ),
1681 mpCanvas( rCanvas ),
1682 mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
1685 COL_WHITE,
1686 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1687 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1688 maOutlineBounds( rOutlineBounds ),
1689 maReliefOffset( rReliefOffset ),
1690 maReliefColor( rReliefColor ),
1691 maShadowOffset( rShadowOffset ),
1692 maShadowColor( rShadowColor )
1693 {
1694 double nLayoutWidth = 0.0;
1695
1696 initLayoutWidth(nLayoutWidth, rOffsets);
1697
1698 initEffectLinePolyPolygon( maLinesOverallSize,
1700 rCanvas,
1701 nLayoutWidth,
1703
1704 init( maState,
1705 rStartPoint,
1706 rState,
1707 rCanvas );
1708 }
1709
1710 OutlineAction::OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1711 const ::basegfx::B2DSize& rReliefOffset,
1712 const ::Color& rReliefColor,
1713 const ::basegfx::B2DSize& rShadowOffset,
1714 const ::Color& rShadowColor,
1715 const ::basegfx::B2DRectangle& rOutlineBounds,
1716 uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
1717 const uno::Sequence< double >& rOffsets,
1718 VirtualDevice const & rVDev,
1719 const CanvasSharedPtr& rCanvas,
1720 const OutDevState& rState,
1721 const ::basegfx::B2DHomMatrix& rTextTransform ) :
1722 mxTextPoly(std::move( xTextPoly )),
1723 maOffsets( rOffsets ),
1724 mpCanvas( rCanvas ),
1725 mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
1728 COL_WHITE,
1729 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1730 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1731 maOutlineBounds( rOutlineBounds ),
1732 maReliefOffset( rReliefOffset ),
1733 maReliefColor( rReliefColor ),
1734 maShadowOffset( rShadowOffset ),
1735 maShadowColor( rShadowColor )
1736 {
1737 double nLayoutWidth = 0.0;
1738 initLayoutWidth(nLayoutWidth, rOffsets);
1739
1740 initEffectLinePolyPolygon( maLinesOverallSize,
1742 rCanvas,
1743 nLayoutWidth,
1745
1746 init( maState,
1747 rStartPoint,
1748 rState,
1749 rCanvas,
1750 rTextTransform );
1751 }
1752
1753 bool OutlineAction::operator()( const rendering::RenderState& rRenderState, const ::Color& /*rTextFillColor*/, bool /*bNormalText*/ ) const
1754 {
1755 const rendering::ViewState& rViewState( mpCanvas->getViewState() );
1756 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
1757
1758 rendering::StrokeAttributes aStrokeAttributes;
1759
1760 aStrokeAttributes.StrokeWidth = mnOutlineWidth;
1761 aStrokeAttributes.MiterLimit = 1.0;
1762 aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
1763 aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
1764 aStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
1765
1766 rendering::RenderState aLocalState( rRenderState );
1767 aLocalState.DeviceColor = maFillColor;
1768
1769 // TODO(P1): implement caching
1770
1771 // background of text
1772 rCanvas->fillPolyPolygon( mxTextPoly,
1773 rViewState,
1774 aLocalState );
1775
1776 // border line of text
1777 rCanvas->strokePolyPolygon( mxTextPoly,
1778 rViewState,
1779 rRenderState,
1780 aStrokeAttributes );
1781
1782 // underlines/strikethrough - background
1783 rCanvas->fillPolyPolygon( mxTextLines,
1784 rViewState,
1785 aLocalState );
1786 // underlines/strikethrough - border
1787 rCanvas->strokePolyPolygon( mxTextLines,
1788 rViewState,
1789 rRenderState,
1790 aStrokeAttributes );
1791
1792 return true;
1793 }
1794
1795 bool OutlineAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1796 {
1797 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::render()" );
1798 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1799
1800 rendering::RenderState aLocalState( maState );
1801 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1802
1803 return renderEffectText( *this,
1804 aLocalState,
1805 mpCanvas->getUNOCanvas(),
1811 }
1812
1813#if 0 // see #if'ed out use in OutlineAction::renderSubset below:
1814 class OutlineTextArrayRenderHelper : public TextRenderer
1815 {
1816 public:
1817 OutlineTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas,
1818 const uno::Reference< rendering::XPolyPolygon2D >& rTextPolygon,
1819 const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon,
1820 const rendering::ViewState& rViewState,
1821 double nOutlineWidth ) :
1824 ::COL_WHITE,
1825 rCanvas->getDevice()->getDeviceColorSpace() )),
1826 mnOutlineWidth( nOutlineWidth ),
1827 mrCanvas( rCanvas ),
1828 mrTextPolygon( rTextPolygon ),
1829 mrLinePolygon( rLinePolygon ),
1830 mrViewState( rViewState )
1831 {
1832 }
1833
1834 // TextRenderer interface
1835 virtual bool operator()( const rendering::RenderState& rRenderState ) const
1836 {
1837 rendering::StrokeAttributes aStrokeAttributes;
1838
1839 aStrokeAttributes.StrokeWidth = mnOutlineWidth;
1840 aStrokeAttributes.MiterLimit = 1.0;
1841 aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
1842 aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
1843 aStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
1844
1845 rendering::RenderState aLocalState( rRenderState );
1846 aLocalState.DeviceColor = maFillColor;
1847
1848 // TODO(P1): implement caching
1849
1850 // background of text
1851 mrCanvas->fillPolyPolygon( mrTextPolygon,
1853 aLocalState );
1854
1855 // border line of text
1856 mrCanvas->strokePolyPolygon( mrTextPolygon,
1858 rRenderState,
1859 aStrokeAttributes );
1860
1861 // underlines/strikethrough - background
1862 mrCanvas->fillPolyPolygon( mrLinePolygon,
1864 aLocalState );
1865 // underlines/strikethrough - border
1866 mrCanvas->strokePolyPolygon( mrLinePolygon,
1868 rRenderState,
1869 aStrokeAttributes );
1870
1871 return true;
1872 }
1873
1874 private:
1875 const uno::Sequence< double > maFillColor;
1876 double mnOutlineWidth;
1877 const uno::Reference< rendering::XCanvas >& mrCanvas;
1878 const uno::Reference< rendering::XPolyPolygon2D >& mrTextPolygon;
1879 const uno::Reference< rendering::XPolyPolygon2D >& mrLinePolygon;
1880 const rendering::ViewState& mrViewState;
1881 };
1882#endif
1883
1884 bool OutlineAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1885 const Subset& rSubset ) const
1886 {
1887 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::OutlineAction::renderSubset()" );
1888 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::OutlineAction: 0x" << std::hex << this );
1889
1890 if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
1891 return true; // empty range, render nothing
1892
1893#if 1
1894 // TODO(F3): Subsetting NYI for outline text!
1895 return render( rTransformation );
1896#else
1897 const rendering::StringContext rOrigContext( mxTextLayout->getText() );
1898
1899 if( rSubset.mnSubsetBegin == 0 &&
1900 rSubset.mnSubsetEnd == rOrigContext.Length )
1901 {
1902 // full range, no need for subsetting
1903 return render( rTransformation );
1904 }
1905
1906 rendering::RenderState aLocalState( maState );
1907 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1908
1909
1910 // create and setup local Text polygon
1911 // ===================================
1912
1913 uno::Reference< rendering::XPolyPolygon2D > xTextPolygon();
1914
1915 // TODO(P3): Provide an API method for that!
1916
1917 if( !xTextLayout.is() )
1918 return false;
1919
1920 // render everything
1921 // =================
1922
1923 return renderEffectText(
1924 OutlineTextArrayRenderHelper(
1925 xCanvas,
1927 xTextLayout,
1928 xTextLines,
1929 rViewState ),
1930 aLocalState,
1931 rViewState,
1932 xCanvas,
1937#endif
1938 }
1939
1940 ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1941 {
1942 rendering::RenderState aLocalState( maState );
1943 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1944
1945 return calcEffectTextBounds( maOutlineBounds,
1951 aLocalState,
1952 mpCanvas->getViewState() );
1953 }
1954
1955 ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1956 const Subset& /*rSubset*/ ) const
1957 {
1958 SAL_WARN( "cppcanvas.emf", "OutlineAction::getBounds(): Subset not yet supported by this object" );
1959
1960 return getBounds( rTransformation );
1961 }
1962
1963 sal_Int32 OutlineAction::getActionCount() const
1964 {
1965 // TODO(F3): Subsetting NYI for outline text!
1966 return maOffsets.getLength();
1967 }
1968
1969
1970 // Action factory methods
1971
1972
1979 std::shared_ptr<Action> createOutline( const ::basegfx::B2DPoint& rStartPoint,
1980 const ::basegfx::B2DSize& rReliefOffset,
1981 const ::Color& rReliefColor,
1982 const ::basegfx::B2DSize& rShadowOffset,
1983 const ::Color& rShadowColor,
1984 const OUString& rText,
1985 sal_Int32 nStartPos,
1986 sal_Int32 nLen,
1987 KernArraySpan pDXArray,
1988 o3tl::span<const sal_Bool> pKashidaArray,
1989 VirtualDevice& rVDev,
1990 const CanvasSharedPtr& rCanvas,
1991 const OutDevState& rState,
1992 const Renderer::Parameters& rParms )
1993 {
1994 // operate on raw DX array here (in logical coordinate
1995 // system), to have a higher resolution
1996 // PolyPolygon. That polygon is then converted to
1997 // device coordinate system.
1998
1999 // #i68512# Temporarily switch off font rotation
2000 // (which is already contained in the render state
2001 // transformation matrix - otherwise, glyph polygons
2002 // will be rotated twice)
2003 const vcl::Font aOrigFont( rVDev.GetFont() );
2004 vcl::Font aUnrotatedFont( aOrigFont );
2005 aUnrotatedFont.SetOrientation(0_deg10);
2006 rVDev.SetFont( aUnrotatedFont );
2007
2008 // TODO(F3): Don't understand parameter semantics of
2009 // GetTextOutlines()
2010 ::basegfx::B2DPolyPolygon aResultingPolyPolygon;
2011 PolyPolyVector aVCLPolyPolyVector;
2012 const bool bHaveOutlines( rVDev.GetTextOutlines( aVCLPolyPolyVector, rText,
2013 static_cast<sal_uInt16>(nStartPos),
2014 static_cast<sal_uInt16>(nStartPos),
2015 static_cast<sal_uInt16>(nLen),
2016 0, pDXArray, pKashidaArray ) );
2017 rVDev.SetFont(aOrigFont);
2018
2019 if( !bHaveOutlines )
2020 return std::shared_ptr<Action>();
2021
2022 // remove offsetting from mapmode transformation
2023 // (outline polygons must stay at origin, only need to
2024 // be scaled)
2025 ::basegfx::B2DHomMatrix aMapModeTransform(
2026 rState.mapModeTransform );
2027 aMapModeTransform.set(0,2, 0.0);
2028 aMapModeTransform.set(1,2, 0.0);
2029
2030 for( const auto& rVCLPolyPolygon : aVCLPolyPolyVector )
2031 {
2032 ::basegfx::B2DPolyPolygon aPolyPolygon = rVCLPolyPolygon.getB2DPolyPolygon();
2033 aPolyPolygon.transform( aMapModeTransform );
2034
2035 // append result to collecting polypoly
2036 for( sal_uInt32 i=0; i<aPolyPolygon.count(); ++i )
2037 {
2038 // #i47795# Ensure closed polygons (since
2039 // FreeType returns the glyph outlines
2040 // open)
2041 const ::basegfx::B2DPolygon& rPoly( aPolyPolygon.getB2DPolygon( i ) );
2042 const sal_uInt32 nCount( rPoly.count() );
2043 if( nCount<3 ||
2044 rPoly.isClosed() )
2045 {
2046 // polygon either degenerate, or
2047 // already closed.
2048 aResultingPolyPolygon.append( rPoly );
2049 }
2050 else
2051 {
2052 ::basegfx::B2DPolygon aPoly(rPoly);
2053 aPoly.setClosed(true);
2054
2055 aResultingPolyPolygon.append( aPoly );
2056 }
2057 }
2058 }
2059
2060 const uno::Sequence< double > aCharWidthSeq(
2061 !pDXArray.empty() ?
2062 setupDXArray( pDXArray, nLen, rState ) :
2063 setupDXArray( rText,
2064 nStartPos,
2065 nLen,
2066 rVDev,
2067 rState ));
2068 const uno::Reference< rendering::XPolyPolygon2D > xTextPoly(
2069 ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
2070 rCanvas->getUNOCanvas()->getDevice(),
2071 aResultingPolyPolygon ) );
2072
2073 if( rParms.maTextTransformation )
2074 {
2075 return std::make_shared<OutlineAction>(
2076 rStartPoint,
2077 rReliefOffset,
2078 rReliefColor,
2079 rShadowOffset,
2080 rShadowColor,
2081 ::basegfx::utils::getRange(aResultingPolyPolygon),
2082 xTextPoly,
2083 aCharWidthSeq,
2084 rVDev,
2085 rCanvas,
2086 rState,
2087 *rParms.maTextTransformation );
2088 }
2089 else
2090 {
2091 return std::make_shared<OutlineAction>(
2092 rStartPoint,
2093 rReliefOffset,
2094 rReliefColor,
2095 rShadowOffset,
2096 rShadowColor,
2097 ::basegfx::utils::getRange(aResultingPolyPolygon),
2098 xTextPoly,
2099 aCharWidthSeq,
2100 rVDev,
2101 rCanvas,
2102 rState );
2103 }
2104 }
2105
2106 } // namespace
2107
2108
2109 std::shared_ptr<Action> TextActionFactory::createTextAction( const ::Point& rStartPoint,
2110 const ::Size& rReliefOffset,
2111 const ::Color& rReliefColor,
2112 const ::Size& rShadowOffset,
2113 const ::Color& rShadowColor,
2114 const ::Color& rTextFillColor,
2115 const OUString& rText,
2116 sal_Int32 nStartPos,
2117 sal_Int32 nLen,
2118 KernArraySpan pDXArray,
2119 o3tl::span<const sal_Bool> pKashidaArray,
2120 VirtualDevice& rVDev,
2121 const CanvasSharedPtr& rCanvas,
2122 const OutDevState& rState,
2123 const Renderer::Parameters& rParms,
2124 bool bSubsettable )
2125 {
2126 const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2127 rVDev ) );
2128 // #143885# maintain (nearly) full precision positioning,
2129 // by circumventing integer-based OutDev-mapping
2130 const ::basegfx::B2DPoint aStartPoint(
2131 rState.mapModeTransform *
2132 ::basegfx::B2DPoint(rStartPoint.X() + aBaselineOffset.Width(),
2133 rStartPoint.Y() + aBaselineOffset.Height()) );
2134
2135 const ::basegfx::B2DSize aReliefOffset(
2136 rState.mapModeTransform * vcl::unotools::b2DSizeFromSize( rReliefOffset ) );
2137 const ::basegfx::B2DSize aShadowOffset(
2138 rState.mapModeTransform * vcl::unotools::b2DSizeFromSize( rShadowOffset ) );
2139
2140 if( rState.isTextOutlineModeSet )
2141 {
2142 return createOutline(
2143 aStartPoint,
2144 aReliefOffset,
2145 rReliefColor,
2146 aShadowOffset,
2147 rShadowColor,
2148 rText,
2149 nStartPos,
2150 nLen,
2151 pDXArray,
2152 pKashidaArray,
2153 rVDev,
2154 rCanvas,
2155 rState,
2156 rParms );
2157 }
2158
2159 // convert DX array to device coordinate system (and
2160 // create it in the first place, if pDXArray is NULL)
2161 const uno::Sequence< double > aCharWidths(
2162 !pDXArray.empty() ?
2163 setupDXArray( pDXArray, nLen, rState ) :
2164 setupDXArray( rText,
2165 nStartPos,
2166 nLen,
2167 rVDev,
2168 rState ));
2169
2170 const uno::Sequence< sal_Bool > aKashidas(pKashidaArray.data(), pKashidaArray.size());
2171
2172 // determine type of text action to create
2173 // =======================================
2174
2175 const ::Color aEmptyColor( COL_AUTO );
2176
2177 std::shared_ptr<Action> ret;
2178
2179 // no DX array, and no need to subset - no need to store
2180 // DX array, then.
2181 if( pDXArray.empty() && !bSubsettable )
2182 {
2183 // effects, or not?
2184 if( !rState.textOverlineStyle &&
2185 !rState.textUnderlineStyle &&
2186 !rState.textStrikeoutStyle &&
2187 rReliefColor == aEmptyColor &&
2188 rShadowColor == aEmptyColor &&
2189 rTextFillColor == aEmptyColor )
2190 {
2191 // nope
2192 if( rParms.maTextTransformation )
2193 {
2194 ret = std::make_shared<TextAction>(
2195 aStartPoint,
2196 rText,
2197 nStartPos,
2198 nLen,
2199 rCanvas,
2200 rState,
2201 *rParms.maTextTransformation );
2202 }
2203 else
2204 {
2205 ret = std::make_shared<TextAction>(
2206 aStartPoint,
2207 rText,
2208 nStartPos,
2209 nLen,
2210 rCanvas,
2211 rState );
2212 }
2213 }
2214 else
2215 {
2216 // at least one of the effects requested
2217 if( rParms.maTextTransformation )
2218 ret = std::make_shared<EffectTextAction>(
2219 aStartPoint,
2220 aReliefOffset,
2221 rReliefColor,
2222 aShadowOffset,
2223 rShadowColor,
2224 rTextFillColor,
2225 rText,
2226 nStartPos,
2227 nLen,
2228 rVDev,
2229 rCanvas,
2230 rState,
2231 *rParms.maTextTransformation );
2232 else
2233 ret = std::make_shared<EffectTextAction>(
2234 aStartPoint,
2235 aReliefOffset,
2236 rReliefColor,
2237 aShadowOffset,
2238 rShadowColor,
2239 rTextFillColor,
2240 rText,
2241 nStartPos,
2242 nLen,
2243 rVDev,
2244 rCanvas,
2245 rState );
2246 }
2247 }
2248 else
2249 {
2250 // DX array necessary - any effects?
2251 if( !rState.textOverlineStyle &&
2252 !rState.textUnderlineStyle &&
2253 !rState.textStrikeoutStyle &&
2254 rReliefColor == aEmptyColor &&
2255 rShadowColor == aEmptyColor &&
2256 rTextFillColor == aEmptyColor )
2257 {
2258 // nope
2259 if( rParms.maTextTransformation )
2260 ret = std::make_shared<TextArrayAction>(
2261 aStartPoint,
2262 rText,
2263 nStartPos,
2264 nLen,
2265 aCharWidths,
2266 aKashidas,
2267 rCanvas,
2268 rState,
2269 *rParms.maTextTransformation );
2270 else
2271 ret = std::make_shared<TextArrayAction>(
2272 aStartPoint,
2273 rText,
2274 nStartPos,
2275 nLen,
2276 aCharWidths,
2277 aKashidas,
2278 rCanvas,
2279 rState );
2280 }
2281 else
2282 {
2283 // at least one of the effects requested
2284 if( rParms.maTextTransformation )
2285 ret = std::make_shared<EffectTextArrayAction>(
2286 aStartPoint,
2287 aReliefOffset,
2288 rReliefColor,
2289 aShadowOffset,
2290 rShadowColor,
2291 rTextFillColor,
2292 rText,
2293 nStartPos,
2294 nLen,
2295 aCharWidths,
2296 aKashidas,
2297 rVDev,
2298 rCanvas,
2299 rState,
2300 *rParms.maTextTransformation );
2301 else
2302 ret = std::make_shared<EffectTextArrayAction>(
2303 aStartPoint,
2304 aReliefOffset,
2305 rReliefColor,
2306 aShadowOffset,
2307 rShadowColor,
2308 rTextFillColor,
2309 rText,
2310 nStartPos,
2311 nLen,
2312 aCharWidths,
2313 aKashidas,
2314 rVDev,
2315 rCanvas,
2316 rState );
2317 }
2318 }
2319 return ret;
2320 }
2321}
2322
2323/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_Int32 nLineWidth
FILE * init(int, char **)
bool empty() const
const vcl::Font & GetFont() const
void SetFont(const vcl::Font &rNewFont)
tools::Long GetTextWidth(const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, vcl::text::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
tools::Long GetTextArray(const OUString &rStr, KernArray *pDXAry, sal_Int32 nIndex=0, sal_Int32 nLen=-1, bool bCaret=false, vcl::text::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
bool GetTextOutlines(PolyPolyVector &, const OUString &rStr, sal_Int32 nBase=0, sal_Int32 nIndex=0, sal_Int32 nLen=-1, sal_uLong nLayoutWidth=0, KernArraySpan aDXArray=KernArraySpan(), o3tl::span< const sal_Bool > pKashidaArray={}) const
void translate(double fX, double fY)
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
void transform(const basegfx::B2DHomMatrix &rMatrix)
sal_uInt32 count() const
B2DVector getRange() const
TYPE getWidth() const
TYPE getHeight() const
constexpr size_type size() const noexcept
constexpr pointer data() const noexcept
tools::Long GetFontHeight() const
constexpr ::Color COL_WHITE(0xFF, 0xFF, 0xFF)
constexpr ::Color COL_AUTO(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
int nCount
#define ENSURE_OR_THROW(c, m)
Any aHelper
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
B2DHomMatrix createRotateB2DHomMatrix(double fRadiant)
B2DRange getRange(const B2DPolygon &rCandidate)
std::shared_ptr< Action > createTextAction(const ::Point &rStartPoint, const ::Size &rReliefOffset, const ::Color &rReliefColor, const ::Size &rShadowOffset, const ::Color &rShadowColor, const ::Color &rTextFillColor, const OUString &rText, sal_Int32 nStartPos, sal_Int32 nLen, KernArraySpan pDXArray, o3tl::span< const sal_Bool > pKashidaArray, VirtualDevice &rVDev, const CanvasSharedPtr &rCanvas, const OutDevState &rState, const Renderer::Parameters &rParms, bool bSubsettable)
Create text action, optionally shadow/relief effect.
bool modifyClip(rendering::RenderState &o_rRenderState, const struct ::cppcanvas::internal::OutDevState &rOutdevState, const CanvasSharedPtr &rCanvas, const ::basegfx::B2DPoint &rOffset, const ::basegfx::B2DVector *pScaling, const double *pRotation)
Definition: mtftools.cxx:112
void initRenderState(rendering::RenderState &renderState, const ::cppcanvas::internal::OutDevState &outdevState)
Definition: mtftools.cxx:42
::basegfx::B2DPolyPolygon createTextLinesPolyPolygon(const ::basegfx::B2DPoint &rStartPos, const double &rLineWidth, const TextLineInfo &rTextLineInfo)
Definition: mtftools.cxx:615
TextLineInfo createTextLineInfo(const ::VirtualDevice &rVDev, const ::cppcanvas::internal::OutDevState &rState)
Generate text underline/strikeout info struct from OutDev state.
Definition: mtftools.cxx:218
::Size getBaselineOffset(const ::cppcanvas::internal::OutDevState &outdevState, const VirtualDevice &rVDev)
Calc output offset relative to baseline.
Definition: mtftools.cxx:51
::basegfx::B2DRange calcDevicePixelBounds(const ::basegfx::B2DRange &rBounds, const rendering::ViewState &viewState, const rendering::RenderState &renderState)
Definition: mtftools.cxx:628
std::shared_ptr< Canvas > CanvasSharedPtr
int i
basegfx::B2DSize b2DSizeFromSize(const Size &rSize)
uno::Sequence< double > colorToDoubleSequence(const Color &rColor, const uno::Reference< rendering::XColorSpace > &xColorSpace)
css::uno::Sequence< double > VCL_DLLPUBLIC colorToDoubleSequence(const Color &rColor, const css::uno::Reference< css::rendering::XColorSpace > &xColorSpace)
std::vector< tools::PolyPolygon > PolyPolyVector
Parameters for the Renderer.
Definition: renderer.hxx:101
::std::optional< ::basegfx::B2DHomMatrix > maTextTransformation
Optionally transforms all text output actions with the given matrix (in addition to the overall canva...
Definition: renderer.hxx:122
::basegfx::B2DHomMatrix mapModeTransform
Definition: outdevstate.hxx:97
double mnOutlineWidth
const uno::Sequence< double > maFillColor
uno::Reference< rendering::XPolyPolygon2D > mxTextPoly
const uno::Reference< rendering::XCanvas > & mrCanvas
const tools::TextLineInfo maTextLineInfo
Definition: textaction.cxx:812
const uno::Reference< rendering::XTextLayout > & mrTextLayout
const rendering::ViewState & mrViewState
const CanvasSharedPtr mpCanvas
Definition: textaction.cxx:638
double mnLayoutWidth
const uno::Sequence< double > maOffsets
rendering::RenderState maState
Definition: textaction.cxx:639
uno::Reference< rendering::XPolyPolygon2D > mxTextLines
Definition: textaction.cxx:814
const sal_Int8 maTextDirection
Definition: textaction.cxx:640
uno::Reference< rendering::XCanvasFont > mxFont
Definition: textaction.cxx:636
const ::basegfx::B2DSize maReliefOffset
Definition: textaction.cxx:815
const ::basegfx::B2DSize maShadowOffset
Definition: textaction.cxx:817
const rendering::StringContext maStringContext
Definition: textaction.cxx:637
::basegfx::B2DSize maLinesOverallSize
Definition: textaction.cxx:813
const ::Color maTextFillColor
Definition: textaction.cxx:819
const ::Color maReliefColor
Definition: textaction.cxx:816
TextLinesHelper maTextLinesHelper
const ::basegfx::B2DRectangle maOutlineBounds
uno::Reference< rendering::XTextLayout > mxTextLayout
const ::Color maShadowColor
Definition: textaction.cxx:818
const TextLinesHelper & mrTextLinesHelper
signed char sal_Int8