1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <comphelper/diagnose_ex.hxx>
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>
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>
35 #include <tools/gen.hxx>
37 #include <vcl/canvastools.hxx>
38 #include <vcl/virdev.hxx>
40 #include <basegfx/utils/canvastools.hxx>
41 #include <canvas/canvastools.hxx>
43 #include <sal/log.hxx>
45 #include "textaction.hxx"
46 #include "textlineshelper.hxx"
47 #include <outdevstate.hxx>
48 #include "mtftools.hxx"
51 using namespace ::com::sun::star
;
53 namespace cppcanvas::internal
57 void init( rendering::RenderState
& o_rRenderState
,
58 const ::basegfx::B2DPoint
& rStartPoint
,
59 const OutDevState
& rState
,
60 const CanvasSharedPtr
& rCanvas
)
62 tools::initRenderState(o_rRenderState
,rState
);
64 // #i36950# Offset clip back to origin (as it's also moved
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
70 tools::modifyClip( o_rRenderState
,
75 &rState
.fontRotation
);
77 basegfx::B2DHomMatrix
aLocalTransformation(basegfx::utils::createRotateB2DHomMatrix(rState
.fontRotation
));
78 aLocalTransformation
.translate( rStartPoint
.getX(),
80 ::canvas::tools::appendToRenderState( o_rRenderState
,
81 aLocalTransformation
);
83 o_rRenderState
.DeviceColor
= rState
.textColor
;
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
)
92 init( o_rRenderState
, rStartPoint
, rState
, rCanvas
);
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)!
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
,
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
)
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
116 // Use completely default FontRequest
117 const rendering::FontRequest aFontRequest
;
119 geometry::Matrix2D aFontMatrix
;
120 ::canvas::tools::setIdentityMatrix2D( aFontMatrix
);
122 o_rFont
= rCanvas
->getUNOCanvas()->createFont(
124 uno::Sequence
< beans::PropertyValue
>(),
128 init( o_rRenderState
,
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
)
141 init( o_rRenderState
, o_rFont
, rStartPoint
, rState
, rCanvas
);
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)!
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
,
154 void initLayoutWidth(double& rLayoutWidth
, const uno::Sequence
<double>& rOffsets
)
156 ENSURE_OR_THROW(rOffsets
.hasElements(),
157 "::cppcanvas::internal::initLayoutWidth(): zero-length array" );
158 rLayoutWidth
= *(std::max_element(rOffsets
.begin(), rOffsets
.end()));
161 uno::Sequence
< double > setupDXArray( KernArraySpan rCharWidths
,
163 const OutDevState
& rState
)
165 // convert character widths from logical units
166 uno::Sequence
< double > aCharWidthSeq( nLen
);
167 double* pOutputWidths( aCharWidthSeq
.getArray() );
169 // #143885# maintain (nearly) full precision of DX
170 // array, by circumventing integer-based
172 const double nScale( rState
.mapModeTransform
.get(0,0) );
173 for( int i
= 0; i
< nLen
; ++i
)
175 // TODO(F2): use correct scale direction
176 *pOutputWidths
++ = rCharWidths
[i
] * nScale
;
179 return aCharWidthSeq
;
182 uno::Sequence
< double > setupDXArray( const OUString
& rText
,
185 VirtualDevice
const & rVDev
,
186 const OutDevState
& rState
)
188 // no external DX array given, create one from given
190 KernArray aCharWidths
;
192 rVDev
.GetTextArray( rText
, &aCharWidths
, nStartPos
, nLen
);
194 return setupDXArray( aCharWidths
, nLen
, rState
);
197 ::basegfx::B2DPoint
adaptStartPoint( const ::basegfx::B2DPoint
& rStartPoint
,
198 const OutDevState
& rState
,
199 const uno::Sequence
< double >& rOffsets
)
201 ::basegfx::B2DPoint
aLocalPoint( rStartPoint
);
203 if( rState
.textAlignment
)
205 // text origin is right, not left. Modify start point
206 // accordingly, because XCanvas::drawTextLayout()
207 // always aligns left!
209 const double nOffset( rOffsets
[ rOffsets
.getLength()-1 ] );
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
);
220 /** Perform common setup for array text actions
222 This method creates the XTextLayout object and
223 initializes it, e.g. with the logical advancements.
225 void initArrayAction( rendering::RenderState
& o_rRenderState
,
226 uno::Reference
< rendering::XTextLayout
>& o_rTextLayout
,
227 const ::basegfx::B2DPoint
& rStartPoint
,
228 const OUString
& rText
,
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
)
237 ENSURE_OR_THROW( rOffsets
.hasElements(),
238 "::cppcanvas::internal::initArrayAction(): zero-length DX array" );
240 const ::basegfx::B2DPoint
aLocalStartPoint(
241 adaptStartPoint( rStartPoint
, rState
, rOffsets
) );
243 uno::Reference
< rendering::XCanvasFont
> xFont( rState
.xFont
);
246 init( o_rRenderState
, xFont
, aLocalStartPoint
, rState
, rCanvas
, *pTextTransform
);
248 init( o_rRenderState
, xFont
, aLocalStartPoint
, rState
, rCanvas
);
250 o_rTextLayout
= xFont
->createTextLayout(
251 rendering::StringContext( rText
, nStartPos
, nLen
),
252 rState
.textDirection
,
255 ENSURE_OR_THROW( o_rTextLayout
.is(),
256 "::cppcanvas::internal::initArrayAction(): Invalid font" );
258 o_rTextLayout
->applyLogicalAdvancements( rOffsets
);
259 o_rTextLayout
->applyKashidaPositions( rKashidas
);
263 double getLineWidth( ::VirtualDevice
const & rVDev
,
264 const OutDevState
& rState
,
265 const rendering::StringContext
& rStringContext
)
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
) ),
273 return (rState
.mapModeTransform
* aSize
).getWidth();
276 uno::Sequence
< double >
277 calcSubsetOffsets( rendering::RenderState
& io_rRenderState
,
280 const uno::Reference
< rendering::XTextLayout
>& rOrigTextLayout
,
282 const ::cppcanvas::internal::Action::Subset
& rSubset
)
284 ENSURE_OR_THROW( rSubset
.mnSubsetEnd
> rSubset
.mnSubsetBegin
,
285 "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
287 uno::Sequence
< double > aOrigOffsets( rOrigTextLayout
->queryLogicalAdvancements() );
288 const double* pOffsets( aOrigOffsets
.getConstArray() );
290 ENSURE_OR_THROW( aOrigOffsets
.getLength() >= rSubset
.mnSubsetEnd
,
291 "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
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
299 const double nMinPos( rSubset
.mnSubsetBegin
<= 0 ? 0 :
300 *(std::min_element( pOffsets
+rSubset
.mnSubsetBegin
-1,
301 pOffsets
+rSubset
.mnSubsetEnd
)) );
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
308 const double nMaxPos(
309 *(std::max_element( pOffsets
+ (rSubset
.mnSubsetBegin
<= 0 ?
310 0 : rSubset
.mnSubsetBegin
-1),
311 pOffsets
+ rSubset
.mnSubsetEnd
)) );
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 ---|
320 const double nOffset
= rOrigTextLayout
->getMainTextDirection()
321 ? nLayoutWidth
- nMaxPos
: nMinPos
;
324 // adapt render state, to move text output to given offset
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
334 ::basegfx::B2DHomMatrix aTranslation
;
335 if( rOrigTextLayout
->getFont()->getFontRequest().FontDescription
.IsVertical
== css::util::TriState_YES
)
337 // vertical text -> offset in y direction
338 aTranslation
.translate(0.0, nOffset
);
342 // horizontal text -> offset in x direction
343 aTranslation
.translate(nOffset
, 0.0);
346 ::canvas::tools::appendToRenderState( io_rRenderState
,
351 // reduce DX array to given substring
354 const sal_Int32
nNewElements( rSubset
.mnSubsetEnd
- rSubset
.mnSubsetBegin
);
355 uno::Sequence
< double > aAdaptedOffsets( nNewElements
);
356 double* pAdaptedOffsets( aAdaptedOffsets
.getArray() );
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
,
364 [nMinPos
](double aPos
) { return aPos
- nMinPos
; } );
369 return aAdaptedOffsets
;
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
)
377 // create temporary new text layout with subset string
380 const sal_Int32
nNewStartPos( rOrigContext
.StartPosition
+ std::min(
381 rSubset
.mnSubsetBegin
, rOrigContext
.Length
-1 ) );
382 const sal_Int32
nNewLength( std::max(
384 rSubset
.mnSubsetEnd
- rSubset
.mnSubsetBegin
,
385 rOrigContext
.Length
),
388 const rendering::StringContext
aContext( rOrigContext
.Text
,
392 uno::Reference
< rendering::XTextLayout
> xTextLayout(
393 rOrigTextLayout
->getFont()->createTextLayout( aContext
,
394 rOrigTextLayout
->getMainTextDirection(),
396 uno::UNO_SET_THROW
);
401 /** Setup subset text layout
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.
408 @param io_rRenderState
409 Must contain original render state on input, will
410 contain shifted render state concatenated with
411 rTransformation on output.
413 @param rTransformation
414 Additional transformation, to be prepended to render
420 void createSubsetLayout( uno::Reference
< rendering::XTextLayout
>& io_rTextLayout
,
422 rendering::RenderState
& io_rRenderState
,
425 const ::basegfx::B2DHomMatrix
& rTransformation
,
426 const Action::Subset
& rSubset
)
428 ::canvas::tools::prependToRenderState(io_rRenderState
, rTransformation
);
430 if( rSubset
.mnSubsetBegin
== rSubset
.mnSubsetEnd
)
432 // empty range, empty layout
433 io_rTextLayout
.clear();
438 ENSURE_OR_THROW( io_rTextLayout
.is(),
439 "createSubsetLayout(): Invalid input layout" );
441 const rendering::StringContext
aOrigContext( io_rTextLayout
->getText() );
443 if( rSubset
.mnSubsetBegin
== 0 &&
444 rSubset
.mnSubsetEnd
== aOrigContext
.Length
)
446 // full range, no need for subsetting
450 uno::Reference
< rendering::XTextLayout
> xTextLayout(
451 createSubsetLayout( aOrigContext
, rSubset
, io_rTextLayout
) );
453 if( xTextLayout
.is() )
455 xTextLayout
->applyLogicalAdvancements(
456 calcSubsetOffsets( io_rRenderState
,
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
);
468 io_rTextLayout
= std::move(xTextLayout
);
472 /** Interface for renderEffectText functor below.
474 This is interface is used from the renderEffectText()
475 method below, to call the client implementation.
480 virtual ~TextRenderer() {}
482 /// Render text with given RenderState
483 virtual bool operator()( const rendering::RenderState
& rRenderState
, const ::Color
& rTextFillColor
, bool bNormalText
) const = 0;
486 /** Render effect text.
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)
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
)
502 ::Color
aEmptyColor( COL_AUTO
);
503 uno::Reference
<rendering::XColorSpace
> xColorSpace(
504 xCanvas
->getDevice()->getDeviceColorSpace() );
506 // draw shadow text, if enabled
507 if( rShadowColor
!= aEmptyColor
)
509 rendering::RenderState
aShadowState( rRenderState
);
510 ::basegfx::B2DHomMatrix aTranslate
;
512 aTranslate
.translate(rShadowOffset
.getWidth(),
513 rShadowOffset
.getHeight());
515 ::canvas::tools::appendToRenderState(aShadowState
, aTranslate
);
517 aShadowState
.DeviceColor
=
518 vcl::unotools::colorToDoubleSequence( rShadowColor
,
521 rRenderer( aShadowState
, rTextFillColor
, false );
524 // draw relief text, if enabled
525 if( rReliefColor
!= aEmptyColor
)
527 rendering::RenderState
aReliefState( rRenderState
);
528 ::basegfx::B2DHomMatrix aTranslate
;
530 aTranslate
.translate(rReliefOffset
.getWidth(),
531 rReliefOffset
.getHeight());
533 ::canvas::tools::appendToRenderState(aReliefState
, aTranslate
);
535 aReliefState
.DeviceColor
=
536 vcl::unotools::colorToDoubleSequence( rReliefColor
,
539 rRenderer( aReliefState
, rTextFillColor
, false );
543 rRenderer( rRenderState
, rTextFillColor
, true );
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
)
556 ::basegfx::B2DRange
aBounds( rTextBounds
);
558 // add extends of text lines
559 aBounds
.expand( rLineBounds
);
561 // TODO(Q3): Provide this functionality at the B2DRange
562 ::basegfx::B2DRange
aTotalBounds( aBounds
);
564 ::basegfx::B2DRange( aBounds
.getMinX() + rReliefOffset
.getWidth(),
565 aBounds
.getMinY() + rReliefOffset
.getHeight(),
566 aBounds
.getMaxX() + rReliefOffset
.getWidth(),
567 aBounds
.getMaxY() + rReliefOffset
.getHeight() ) );
569 ::basegfx::B2DRange( aBounds
.getMinX() + rShadowOffset
.getWidth(),
570 aBounds
.getMinY() + rShadowOffset
.getHeight(),
571 aBounds
.getMaxX() + rShadowOffset
.getWidth(),
572 aBounds
.getMaxY() + rShadowOffset
.getHeight() ) );
574 return tools::calcDevicePixelBounds( aTotalBounds
,
579 void initEffectLinePolyPolygon( ::basegfx::B2DSize
& o_rOverallSize
,
580 uno::Reference
< rendering::XPolyPolygon2D
>& o_rTextLines
,
581 const CanvasSharedPtr
& rCanvas
,
583 const tools::TextLineInfo
& rLineInfo
)
585 const ::basegfx::B2DPolyPolygon
aPoly(
586 tools::createTextLinesPolyPolygon( 0.0, nLineWidth
,
588 auto aRange
= basegfx::utils::getRange( aPoly
).getRange();
589 o_rOverallSize
= basegfx::B2DSize(aRange
.getX(), aRange
.getY());
591 o_rTextLines
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
592 rCanvas
->getUNOCanvas()->getDevice(),
597 class TextAction
: public Action
600 TextAction( const ::basegfx::B2DPoint
& rStartPoint
,
601 const OUString
& rString
,
604 const CanvasSharedPtr
& rCanvas
,
605 const OutDevState
& rState
);
607 TextAction( const ::basegfx::B2DPoint
& rStartPoint
,
608 const OUString
& rString
,
611 const CanvasSharedPtr
& rCanvas
,
612 const OutDevState
& rState
,
613 const ::basegfx::B2DHomMatrix
& rTextTransform
);
615 TextAction(const TextAction
&) = delete;
616 const TextAction
& operator=(const TextAction
&) = delete;
618 virtual bool render( const ::basegfx::B2DHomMatrix
& rTransformation
) const override
;
619 virtual bool renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
620 const Subset
& rSubset
) const override
;
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
;
626 virtual sal_Int32
getActionCount() const override
;
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
636 uno::Reference
< rendering::XCanvasFont
> mxFont
;
637 const rendering::StringContext maStringContext
;
638 const CanvasSharedPtr mpCanvas
;
639 rendering::RenderState maState
;
640 const sal_Int8 maTextDirection
;
643 TextAction::TextAction( const ::basegfx::B2DPoint
& rStartPoint
,
644 const OUString
& rString
,
647 const CanvasSharedPtr
& rCanvas
,
648 const OutDevState
& rState
) :
649 mxFont( rState
.xFont
),
650 maStringContext( rString
, nStartPos
, nLen
),
652 maTextDirection( rState
.textDirection
)
654 init( maState
, mxFont
,
658 ENSURE_OR_THROW( mxFont
.is(),
659 "::cppcanvas::internal::TextAction(): Invalid font" );
662 TextAction::TextAction( const ::basegfx::B2DPoint
& rStartPoint
,
663 const OUString
& rString
,
666 const CanvasSharedPtr
& rCanvas
,
667 const OutDevState
& rState
,
668 const ::basegfx::B2DHomMatrix
& rTextTransform
) :
669 mxFont( rState
.xFont
),
670 maStringContext( rString
, nStartPos
, nLen
),
672 maTextDirection( rState
.textDirection
)
674 init( maState
, mxFont
,
676 rState
, rCanvas
, rTextTransform
);
678 ENSURE_OR_THROW( mxFont
.is(),
679 "::cppcanvas::internal::TextAction(): Invalid font" );
682 bool TextAction::render( const ::basegfx::B2DHomMatrix
& rTransformation
) const
684 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextAction::render()" );
685 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextAction: 0x" << std::hex
<< this );
687 rendering::RenderState
aLocalState( maState
);
688 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
690 mpCanvas
->getUNOCanvas()->drawText( maStringContext
, mxFont
,
691 mpCanvas
->getViewState(), aLocalState
, maTextDirection
);
696 bool TextAction::renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
697 const Subset
& /*rSubset*/ ) const
699 SAL_WARN( "cppcanvas.emf", "TextAction::renderSubset(): Subset not supported by this object" );
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
);
708 ::basegfx::B2DRange
TextAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const
710 // create XTextLayout, to have the
711 // XTextLayout::queryTextBounds() method available
712 uno::Reference
< rendering::XTextLayout
> xTextLayout(
713 mxFont
->createTextLayout(
718 rendering::RenderState
aLocalState( maState
);
719 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
721 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
722 xTextLayout
->queryTextBounds() ),
723 mpCanvas
->getViewState(),
727 ::basegfx::B2DRange
TextAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
728 const Subset
& /*rSubset*/ ) const
730 SAL_WARN( "cppcanvas.emf", "TextAction::getBounds(): Subset not supported by this object" );
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
);
739 sal_Int32
TextAction::getActionCount() const
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
749 class EffectTextAction
:
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
,
763 VirtualDevice
const & rVDev
,
764 const CanvasSharedPtr
& rCanvas
,
765 const OutDevState
& rState
);
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
,
776 VirtualDevice
const & rVDev
,
777 const CanvasSharedPtr
& rCanvas
,
778 const OutDevState
& rState
,
779 const ::basegfx::B2DHomMatrix
& rTextTransform
);
781 EffectTextAction(const EffectTextAction
&) = delete;
782 const EffectTextAction
& operator=(const EffectTextAction
&) = delete;
784 virtual bool render( const ::basegfx::B2DHomMatrix
& rTransformation
) const override
;
785 virtual bool renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
786 const Subset
& rSubset
) const override
;
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
;
792 virtual sal_Int32
getActionCount() const override
;
795 /// Interface TextRenderer
796 virtual bool operator()( const rendering::RenderState
& rRenderState
, const ::Color
& rTextFillColor
, bool bNormalText
) const override
;
798 geometry::RealRectangle2D
queryTextBounds() const;
799 css::uno::Reference
<css::rendering::XPolyPolygon2D
> queryTextBounds(const uno::Reference
<rendering::XCanvas
>& rCanvas
) const;
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
808 uno::Reference
< rendering::XCanvasFont
> mxFont
;
809 const rendering::StringContext maStringContext
;
810 const CanvasSharedPtr mpCanvas
;
811 rendering::RenderState maState
;
812 const tools::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
;
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
,
832 VirtualDevice
const & rVDev
,
833 const CanvasSharedPtr
& rCanvas
,
834 const OutDevState
& rState
) :
835 mxFont( rState
.xFont
),
836 maStringContext( rText
, nStartPos
, nLen
),
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
)
846 const double nLineWidth(getLineWidth( rVDev
, rState
, maStringContext
));
847 initEffectLinePolyPolygon( maLinesOverallSize
,
853 init( maState
, mxFont
,
857 ENSURE_OR_THROW( mxFont
.is() && mxTextLines
.is(),
858 "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
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
,
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
),
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
)
885 const double nLineWidth( getLineWidth( rVDev
, rState
, maStringContext
) );
886 initEffectLinePolyPolygon( maLinesOverallSize
,
892 init( maState
, mxFont
,
894 rState
, rCanvas
, rTextTransform
);
896 ENSURE_OR_THROW( mxFont
.is() && mxTextLines
.is(),
897 "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
900 bool EffectTextAction::operator()( const rendering::RenderState
& rRenderState
, const ::Color
& rTextFillColor
, bool /*bNormalText*/ ) const
902 const rendering::ViewState
aViewState( mpCanvas
->getViewState() );
903 const uno::Reference
< rendering::XCanvas
> aCanvas( mpCanvas
->getUNOCanvas() );
905 //rhbz#1589029 non-transparent text fill background support
906 if (rTextFillColor
!= COL_AUTO
)
908 rendering::RenderState
aLocalState( rRenderState
);
909 aLocalState
.DeviceColor
= vcl::unotools::colorToDoubleSequence(
910 rTextFillColor
, aCanvas
->getDevice()->getDeviceColorSpace());
911 auto xTextBounds
= queryTextBounds(aCanvas
);
912 // background of text
913 aCanvas
->fillPolyPolygon(xTextBounds
, aViewState
, aLocalState
);
917 aCanvas
->fillPolyPolygon( mxTextLines
,
921 aCanvas
->drawText( maStringContext
, mxFont
,
929 bool EffectTextAction::render( const ::basegfx::B2DHomMatrix
& rTransformation
) const
931 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextAction::render()" );
932 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextAction: 0x" << std::hex
<< this );
934 rendering::RenderState
aLocalState( maState
);
935 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
937 return renderEffectText( *this,
939 mpCanvas
->getUNOCanvas(),
947 bool EffectTextAction::renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
948 const Subset
& /*rSubset*/ ) const
950 SAL_WARN( "cppcanvas.emf", "EffectTextAction::renderSubset(): Subset not supported by this object" );
952 // TODO(P1): Retrieve necessary font metric info for
953 // TextAction from XCanvas. Currently, the
954 // TextActionFactory does not generate this object for
956 return render( rTransformation
);
959 geometry::RealRectangle2D
EffectTextAction::queryTextBounds() const
961 // create XTextLayout, to have the
962 // XTextLayout::queryTextBounds() method available
963 uno::Reference
< rendering::XTextLayout
> xTextLayout(
964 mxFont
->createTextLayout(
969 return xTextLayout
->queryTextBounds();
972 css::uno::Reference
<css::rendering::XPolyPolygon2D
> EffectTextAction::queryTextBounds(const uno::Reference
<rendering::XCanvas
>& rCanvas
) const
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
);
980 ::basegfx::B2DRange
EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const
982 rendering::RenderState
aLocalState( maState
);
983 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
985 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
987 ::basegfx::B2DRange( 0,0,
988 maLinesOverallSize
.getWidth(),
989 maLinesOverallSize
.getHeight() ),
993 mpCanvas
->getViewState() );
996 ::basegfx::B2DRange
EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
997 const Subset
& /*rSubset*/ ) const
999 SAL_WARN( "cppcanvas.emf", "EffectTextAction::getBounds(): Subset not supported by this object" );
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
);
1008 sal_Int32
EffectTextAction::getActionCount() const
1010 // TODO(P1): Retrieve necessary font metric info for
1011 // TextAction from XCanvas. Currently, the
1012 // TextActionFactory does not generate this object for
1018 class TextArrayAction
: public Action
1021 TextArrayAction( const ::basegfx::B2DPoint
& rStartPoint
,
1022 const OUString
& rString
,
1023 sal_Int32 nStartPos
,
1025 const uno::Sequence
< double >& rOffsets
,
1026 const uno::Sequence
< sal_Bool
>& rKashidas
,
1027 const CanvasSharedPtr
& rCanvas
,
1028 const OutDevState
& rState
);
1030 TextArrayAction( const ::basegfx::B2DPoint
& rStartPoint
,
1031 const OUString
& rString
,
1032 sal_Int32 nStartPos
,
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
);
1040 TextArrayAction(const TextArrayAction
&) = delete;
1041 const TextArrayAction
& operator=(const TextArrayAction
&) = delete;
1043 virtual bool render( const ::basegfx::B2DHomMatrix
& rTransformation
) const override
;
1044 virtual bool renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
1045 const Subset
& rSubset
) const override
;
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
;
1051 virtual sal_Int32
getActionCount() const override
;
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
1061 uno::Reference
< rendering::XTextLayout
> mxTextLayout
;
1062 const CanvasSharedPtr mpCanvas
;
1063 rendering::RenderState maState
;
1064 double mnLayoutWidth
;
1067 TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint
& rStartPoint
,
1068 const OUString
& rString
,
1069 sal_Int32 nStartPos
,
1071 const uno::Sequence
< double >& rOffsets
,
1072 const uno::Sequence
< sal_Bool
>& rKashidas
,
1073 const CanvasSharedPtr
& rCanvas
,
1074 const OutDevState
& rState
) :
1077 initLayoutWidth(mnLayoutWidth
, rOffsets
);
1079 initArrayAction( maState
,
1091 TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint
& rStartPoint
,
1092 const OUString
& rString
,
1093 sal_Int32 nStartPos
,
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
) :
1102 initLayoutWidth(mnLayoutWidth
, rOffsets
);
1104 initArrayAction( maState
,
1117 bool TextArrayAction::render( const ::basegfx::B2DHomMatrix
& rTransformation
) const
1119 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction::render()" );
1120 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction: 0x" << std::hex
<< this );
1122 rendering::RenderState
aLocalState( maState
);
1123 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
1125 mpCanvas
->getUNOCanvas()->drawTextLayout( mxTextLayout
,
1126 mpCanvas
->getViewState(),
1132 bool TextArrayAction::renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
1133 const Subset
& rSubset
) const
1135 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction::renderSubset()" );
1136 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction: 0x" << std::hex
<< this );
1138 rendering::RenderState
aLocalState( maState
);
1139 uno::Reference
< rendering::XTextLayout
> xTextLayout( mxTextLayout
);
1141 double nDummy0
, nDummy1
;
1142 createSubsetLayout( xTextLayout
,
1150 if( !xTextLayout
.is() )
1151 return true; // empty layout, render nothing
1153 mpCanvas
->getUNOCanvas()->drawTextLayout( xTextLayout
,
1154 mpCanvas
->getViewState(),
1160 ::basegfx::B2DRange
TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const
1162 rendering::RenderState
aLocalState( maState
);
1163 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
1165 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1166 mxTextLayout
->queryTextBounds() ),
1167 mpCanvas
->getViewState(),
1171 ::basegfx::B2DRange
TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
1172 const Subset
& rSubset
) const
1174 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction::getBounds( subset )" );
1175 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextArrayAction: 0x" << std::hex
<< this );
1177 rendering::RenderState
aLocalState( maState
);
1178 uno::Reference
< rendering::XTextLayout
> xTextLayout( mxTextLayout
);
1180 double nDummy0
, nDummy1
;
1181 createSubsetLayout( xTextLayout
,
1189 if( !xTextLayout
.is() )
1190 return ::basegfx::B2DRange(); // empty layout, empty bounds
1192 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1193 xTextLayout
->queryTextBounds() ),
1194 mpCanvas
->getViewState(),
1198 sal_Int32
TextArrayAction::getActionCount() const
1200 const rendering::StringContext
aOrigContext( mxTextLayout
->getText() );
1202 return aOrigContext
.Length
;
1206 class EffectTextArrayAction
:
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
,
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
,
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
);
1241 EffectTextArrayAction(const EffectTextArrayAction
&) = delete;
1242 const EffectTextArrayAction
& operator=(const EffectTextArrayAction
&) = delete;
1244 virtual bool render( const ::basegfx::B2DHomMatrix
& rTransformation
) const override
;
1245 virtual bool renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
1246 const Subset
& rSubset
) const override
;
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
;
1252 virtual sal_Int32
getActionCount() const override
;
1255 // TextRenderer interface
1256 virtual bool operator()( const rendering::RenderState
& rRenderState
, const ::Color
& rTextFillColor
, bool bNormalText
) const override
;
1258 css::uno::Reference
<css::rendering::XPolyPolygon2D
> queryTextBounds(const uno::Reference
<rendering::XCanvas
>& rCanvas
) const;
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
1267 uno::Reference
< rendering::XTextLayout
> mxTextLayout
;
1268 const CanvasSharedPtr mpCanvas
;
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
;
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
,
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
)
1303 initLayoutWidth(mnLayoutWidth
, rOffsets
);
1305 maTextLinesHelper
.init(mnLayoutWidth
, maTextLineInfo
);
1307 initArrayAction( maState
,
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
,
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
)
1343 initLayoutWidth(mnLayoutWidth
, rOffsets
);
1345 maTextLinesHelper
.init(mnLayoutWidth
, maTextLineInfo
);
1347 initArrayAction( maState
,
1360 css::uno::Reference
<css::rendering::XPolyPolygon2D
> EffectTextArrayAction::queryTextBounds(const uno::Reference
<rendering::XCanvas
>& rCanvas
) const
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
);
1368 bool EffectTextArrayAction::operator()( const rendering::RenderState
& rRenderState
, const ::Color
& rTextFillColor
, bool bNormalText
) const
1370 const rendering::ViewState
aViewState( mpCanvas
->getViewState() );
1371 const uno::Reference
< rendering::XCanvas
> aCanvas( mpCanvas
->getUNOCanvas() );
1373 //rhbz#1589029 non-transparent text fill background support
1374 if (rTextFillColor
!= COL_AUTO
)
1376 rendering::RenderState
aLocalState(rRenderState
);
1377 aLocalState
.DeviceColor
= vcl::unotools::colorToDoubleSequence(
1378 rTextFillColor
, aCanvas
->getDevice()->getDeviceColorSpace());
1379 auto xTextBounds
= queryTextBounds(aCanvas
);
1380 // background of text
1381 aCanvas
->fillPolyPolygon(xTextBounds
, aViewState
, aLocalState
);
1385 maTextLinesHelper
.render(rRenderState
, bNormalText
);
1387 aCanvas
->drawTextLayout( mxTextLayout
,
1394 bool EffectTextArrayAction::render( const ::basegfx::B2DHomMatrix
& rTransformation
) const
1396 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::render()" );
1397 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex
<< this );
1399 rendering::RenderState
aLocalState( maState
);
1400 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
1402 return renderEffectText( *this,
1404 mpCanvas
->getUNOCanvas(),
1412 class EffectTextArrayRenderHelper
: public TextRenderer
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
)
1426 // TextRenderer interface
1427 virtual bool operator()( const rendering::RenderState
& rRenderState
, const ::Color
& rTextFillColor
,bool bNormalText
) const override
1429 mrTextLinesHelper
.render(rRenderState
, bNormalText
);
1431 //rhbz#1589029 non-transparent text fill background support
1432 if (rTextFillColor
!= COL_AUTO
)
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
);
1442 mrCanvas
->drawTextLayout( mrTextLayout
,
1451 css::uno::Reference
<css::rendering::XPolyPolygon2D
> queryTextBounds() const
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
);
1459 const uno::Reference
< rendering::XCanvas
>& mrCanvas
;
1460 const uno::Reference
< rendering::XTextLayout
>& mrTextLayout
;
1461 const TextLinesHelper
& mrTextLinesHelper
;
1462 const rendering::ViewState
& mrViewState
;
1465 bool EffectTextArrayAction::renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
1466 const Subset
& rSubset
) const
1468 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::renderSubset()" );
1469 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex
<< this );
1471 rendering::RenderState
aLocalState( maState
);
1472 uno::Reference
< rendering::XTextLayout
> xTextLayout( mxTextLayout
);
1473 const geometry::RealRectangle2D
aTextBounds( mxTextLayout
->queryTextBounds() );
1475 double nMinPos(0.0);
1476 double nMaxPos(aTextBounds
.X2
- aTextBounds
.X1
);
1478 createSubsetLayout( xTextLayout
,
1486 if( !xTextLayout
.is() )
1487 return true; // empty layout, render nothing
1490 // create and setup local line polygon
1491 // ===================================
1493 uno::Reference
< rendering::XCanvas
> xCanvas( mpCanvas
->getUNOCanvas() );
1494 const rendering::ViewState
aViewState( mpCanvas
->getViewState() );
1496 TextLinesHelper aHelper
= maTextLinesHelper
;
1497 aHelper
.init(nMaxPos
- nMinPos
, maTextLineInfo
);
1500 // render everything
1501 // =================
1503 return renderEffectText(
1504 EffectTextArrayRenderHelper( xCanvas
,
1517 ::basegfx::B2DRange
EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const
1519 rendering::RenderState
aLocalState( maState
);
1520 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
1522 ::basegfx::B2DSize aSize
= maTextLinesHelper
.getOverallSize();
1524 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1525 mxTextLayout
->queryTextBounds() ),
1526 basegfx::B2DRange(0, 0,
1532 mpCanvas
->getViewState() );
1535 ::basegfx::B2DRange
EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
1536 const Subset
& rSubset
) const
1538 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::getBounds( subset )" );
1539 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex
<< this );
1541 rendering::RenderState
aLocalState( maState
);
1542 uno::Reference
< rendering::XTextLayout
> xTextLayout( mxTextLayout
);
1543 const geometry::RealRectangle2D
aTextBounds( mxTextLayout
->queryTextBounds() );
1545 double nMinPos(0.0);
1546 double nMaxPos(aTextBounds
.X2
- aTextBounds
.X1
);
1548 createSubsetLayout( xTextLayout
,
1556 if( !xTextLayout
.is() )
1557 return ::basegfx::B2DRange(); // empty layout, empty bounds
1560 // create and setup local line polygon
1561 // ===================================
1563 const ::basegfx::B2DPolyPolygon
aPoly(
1564 tools::createTextLinesPolyPolygon(
1565 0.0, nMaxPos
- nMinPos
,
1568 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1569 xTextLayout
->queryTextBounds() ),
1570 ::basegfx::utils::getRange( aPoly
),
1574 mpCanvas
->getViewState() );
1577 sal_Int32
EffectTextArrayAction::getActionCount() const
1579 const rendering::StringContext
aOrigContext( mxTextLayout
->getText() );
1581 return aOrigContext
.Length
;
1585 class OutlineAction
:
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
);
1618 OutlineAction(const OutlineAction
&) = delete;
1619 const OutlineAction
& operator=(const OutlineAction
&) = delete;
1621 virtual bool render( const ::basegfx::B2DHomMatrix
& rTransformation
) const override
;
1622 virtual bool renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
1623 const Subset
& rSubset
) const override
;
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
;
1629 virtual sal_Int32
getActionCount() const override
;
1632 // TextRenderer interface
1633 virtual bool operator()( const rendering::RenderState
& rRenderState
, const ::Color
& rTextFillColor
, bool bNormalText
) const override
;
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
1642 uno::Reference
< rendering::XPolyPolygon2D
> mxTextPoly
;
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 tools::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
;
1662 double calcOutlineWidth( const OutDevState
& rState
,
1663 VirtualDevice
const & rVDev
)
1665 const ::basegfx::B2DSize
aFontSize( 0,
1666 rVDev
.GetFont().GetFontHeight() / 64.0 );
1668 const double nOutlineWidth(
1669 (rState
.mapModeTransform
* aFontSize
).getHeight() );
1671 return nOutlineWidth
< 1.0 ? 1.0 : nOutlineWidth
;
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 mxTextPoly(std::move( xTextPoly
)),
1688 maOffsets( rOffsets
),
1689 mpCanvas( rCanvas
),
1690 mnOutlineWidth( calcOutlineWidth(rState
,rVDev
) ),
1692 vcl::unotools::colorToDoubleSequence(
1694 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1695 mxBackgroundFillPoly(std::move( xFillPoly
)),
1696 maTextLineInfo( tools::createTextLineInfo( rVDev
, rState
) ),
1697 maOutlineBounds( rOutlineBounds
),
1698 maReliefOffset( rReliefOffset
),
1699 maReliefColor( rReliefColor
),
1700 maShadowOffset( rShadowOffset
),
1701 maShadowColor( rShadowColor
),
1702 maBackgroundFillColor( rFillColor
)
1704 double nLayoutWidth
= 0.0;
1706 initLayoutWidth(nLayoutWidth
, rOffsets
);
1708 initEffectLinePolyPolygon( maLinesOverallSize
,
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 mxTextPoly(std::move( xTextPoly
)),
1735 maOffsets( rOffsets
),
1736 mpCanvas( rCanvas
),
1737 mnOutlineWidth( calcOutlineWidth(rState
,rVDev
) ),
1739 vcl::unotools::colorToDoubleSequence(
1741 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1742 mxBackgroundFillPoly(std::move( xFillPoly
)),
1743 maTextLineInfo( tools::createTextLineInfo( rVDev
, rState
) ),
1744 maOutlineBounds( rOutlineBounds
),
1745 maReliefOffset( rReliefOffset
),
1746 maReliefColor( rReliefColor
),
1747 maShadowOffset( rShadowOffset
),
1748 maShadowColor( rShadowColor
),
1749 maBackgroundFillColor( rFillColor
)
1751 double nLayoutWidth
= 0.0;
1752 initLayoutWidth(nLayoutWidth
, rOffsets
);
1754 initEffectLinePolyPolygon( maLinesOverallSize
,
1767 bool OutlineAction::operator()( const rendering::RenderState
& rRenderState
, const ::Color
& /*rTextFillColor*/, bool /*bNormalText*/ ) const
1769 const rendering::ViewState
aViewState( mpCanvas
->getViewState() );
1770 const uno::Reference
< rendering::XCanvas
> xCanvas( mpCanvas
->getUNOCanvas() );
1772 if (mxBackgroundFillPoly
.is())
1774 rendering::RenderState
aLocalState( rRenderState
);
1775 aLocalState
.DeviceColor
= vcl::unotools::colorToDoubleSequence(
1776 maBackgroundFillColor
, xCanvas
->getDevice()->getDeviceColorSpace());
1777 xCanvas
->fillPolyPolygon(mxBackgroundFillPoly
, aViewState
, aLocalState
);
1780 rendering::StrokeAttributes aStrokeAttributes
;
1782 aStrokeAttributes
.StrokeWidth
= mnOutlineWidth
;
1783 aStrokeAttributes
.MiterLimit
= 1.0;
1784 aStrokeAttributes
.StartCapType
= rendering::PathCapType::BUTT
;
1785 aStrokeAttributes
.EndCapType
= rendering::PathCapType::BUTT
;
1786 aStrokeAttributes
.JoinType
= rendering::PathJoinType::MITER
;
1788 rendering::RenderState
aLocalState( rRenderState
);
1789 aLocalState
.DeviceColor
= maFillColor
;
1791 // TODO(P1): implement caching
1793 // background of text
1794 xCanvas
->fillPolyPolygon( mxTextPoly
,
1798 // border line of text
1799 xCanvas
->strokePolyPolygon( mxTextPoly
,
1802 aStrokeAttributes
);
1804 // underlines/strikethrough - background
1805 xCanvas
->fillPolyPolygon( mxTextLines
,
1808 // underlines/strikethrough - border
1809 xCanvas
->strokePolyPolygon( mxTextLines
,
1812 aStrokeAttributes
);
1817 bool OutlineAction::render( const ::basegfx::B2DHomMatrix
& rTransformation
) const
1819 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::render()" );
1820 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex
<< this );
1822 rendering::RenderState
aLocalState( maState
);
1823 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
1825 return renderEffectText( *this,
1827 mpCanvas
->getUNOCanvas(),
1835 #if 0 // see #if'ed out use in OutlineAction::renderSubset below:
1836 class OutlineTextArrayRenderHelper
: public TextRenderer
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
) :
1845 vcl::unotools::colorToDoubleSequence(
1847 rCanvas
->getDevice()->getDeviceColorSpace() )),
1848 mnOutlineWidth( nOutlineWidth
),
1849 mrCanvas( rCanvas
),
1850 mrTextPolygon( rTextPolygon
),
1851 mrLinePolygon( rLinePolygon
),
1852 mrViewState( rViewState
)
1856 // TextRenderer interface
1857 virtual bool operator()( const rendering::RenderState
& rRenderState
) const
1859 rendering::StrokeAttributes aStrokeAttributes
;
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
;
1867 rendering::RenderState
aLocalState( rRenderState
);
1868 aLocalState
.DeviceColor
= maFillColor
;
1870 // TODO(P1): implement caching
1872 // background of text
1873 mrCanvas
->fillPolyPolygon( mrTextPolygon
,
1877 // border line of text
1878 mrCanvas
->strokePolyPolygon( mrTextPolygon
,
1881 aStrokeAttributes
);
1883 // underlines/strikethrough - background
1884 mrCanvas
->fillPolyPolygon( mrLinePolygon
,
1887 // underlines/strikethrough - border
1888 mrCanvas
->strokePolyPolygon( mrLinePolygon
,
1891 aStrokeAttributes
);
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
;
1906 bool OutlineAction::renderSubset( const ::basegfx::B2DHomMatrix
& rTransformation
,
1907 const Subset
& rSubset
) const
1909 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::OutlineAction::renderSubset()" );
1910 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::OutlineAction: 0x" << std::hex
<< this );
1912 if( rSubset
.mnSubsetBegin
== rSubset
.mnSubsetEnd
)
1913 return true; // empty range, render nothing
1916 // TODO(F3): Subsetting NYI for outline text!
1917 return render( rTransformation
);
1919 const rendering::StringContext
rOrigContext( mxTextLayout
->getText() );
1921 if( rSubset
.mnSubsetBegin
== 0 &&
1922 rSubset
.mnSubsetEnd
== rOrigContext
.Length
)
1924 // full range, no need for subsetting
1925 return render( rTransformation
);
1928 rendering::RenderState
aLocalState( maState
);
1929 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
1932 // create and setup local Text polygon
1933 // ===================================
1935 uno::Reference
< rendering::XPolyPolygon2D
> xTextPolygon();
1937 // TODO(P3): Provide an API method for that!
1939 if( !xTextLayout
.is() )
1942 // render everything
1943 // =================
1945 return renderEffectText(
1946 OutlineTextArrayRenderHelper(
1962 ::basegfx::B2DRange
OutlineAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
) const
1964 rendering::RenderState
aLocalState( maState
);
1965 ::canvas::tools::prependToRenderState(aLocalState
, rTransformation
);
1967 return calcEffectTextBounds( maOutlineBounds
,
1968 ::basegfx::B2DRange(0, 0,
1969 maLinesOverallSize
.getWidth(),
1970 maLinesOverallSize
.getHeight()),
1974 mpCanvas
->getViewState() );
1977 ::basegfx::B2DRange
OutlineAction::getBounds( const ::basegfx::B2DHomMatrix
& rTransformation
,
1978 const Subset
& /*rSubset*/ ) const
1980 SAL_WARN( "cppcanvas.emf", "OutlineAction::getBounds(): Subset not yet supported by this object" );
1982 return getBounds( rTransformation
);
1985 sal_Int32
OutlineAction::getActionCount() const
1987 // TODO(F3): Subsetting NYI for outline text!
1988 return maOffsets
.getLength();
1992 // Action factory methods
1995 /** Create an outline action
1997 This method extracts the polygonal outline from the
1998 text, and creates a properly setup OutlineAction from
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
,
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
)
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.
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 const vcl::Font
aOrigFont( rVDev
.GetFont() );
2027 vcl::Font
aUnrotatedFont( aOrigFont
);
2028 aUnrotatedFont
.SetOrientation(0_deg10
);
2029 rVDev
.SetFont( aUnrotatedFont
);
2031 // TODO(F3): Don't understand parameter semantics of
2032 // GetTextOutlines()
2033 ::basegfx::B2DPolyPolygon aResultingPolyPolygon
;
2034 PolyPolyVector aVCLPolyPolyVector
;
2035 const bool bHaveOutlines( rVDev
.GetTextOutlines( aVCLPolyPolyVector
, rText
,
2036 static_cast<sal_uInt16
>(nStartPos
),
2037 static_cast<sal_uInt16
>(nStartPos
),
2038 static_cast<sal_uInt16
>(nLen
),
2039 0, pDXArray
, pKashidaArray
) );
2040 rVDev
.SetFont(aOrigFont
);
2042 if( !bHaveOutlines
)
2043 return std::shared_ptr
<Action
>();
2045 // remove offsetting from mapmode transformation
2046 // (outline polygons must stay at origin, only need to
2048 ::basegfx::B2DHomMatrix
aMapModeTransform(
2049 rState
.mapModeTransform
);
2050 aMapModeTransform
.set(0,2, 0.0);
2051 aMapModeTransform
.set(1,2, 0.0);
2053 for( const auto& rVCLPolyPolygon
: aVCLPolyPolyVector
)
2055 ::basegfx::B2DPolyPolygon aPolyPolygon
= rVCLPolyPolygon
.getB2DPolyPolygon();
2056 aPolyPolygon
.transform( aMapModeTransform
);
2058 // append result to collecting polypoly
2059 for( sal_uInt32 i
=0; i
<aPolyPolygon
.count(); ++i
)
2061 // #i47795# Ensure closed polygons (since
2062 // FreeType returns the glyph outlines
2064 const ::basegfx::B2DPolygon
& rPoly( aPolyPolygon
.getB2DPolygon( i
) );
2065 const sal_uInt32
nCount( rPoly
.count() );
2069 // polygon either degenerate, or
2071 aResultingPolyPolygon
.append( rPoly
);
2075 ::basegfx::B2DPolygon
aPoly(rPoly
);
2076 aPoly
.setClosed(true);
2078 aResultingPolyPolygon
.append( aPoly
);
2083 const uno::Sequence
< double > aCharWidthSeq(
2085 setupDXArray( pDXArray
, nLen
, rState
) :
2086 setupDXArray( rText
,
2091 const uno::Reference
< rendering::XPolyPolygon2D
> xTextPoly(
2092 ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
2093 rCanvas
->getUNOCanvas()->getDevice(),
2094 aResultingPolyPolygon
) );
2096 // create background color fill polygon?
2097 css::uno::Reference
<css::rendering::XPolyPolygon2D
> xTextBoundsPoly
;
2098 if (rTextFillColor
!= COL_AUTO
)
2100 rendering::StringContext
aStringContext( rText
, nStartPos
, nLen
);
2101 uno::Reference
< rendering::XTextLayout
> xTextLayout(
2102 rState
.xFont
->createTextLayout(
2104 rState
.textDirection
,
2107 auto aTextBounds
= xTextLayout
->queryTextBounds();
2108 auto aB2DBounds
= ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds
);
2109 auto aTextBoundsPoly
= ::basegfx::utils::createPolygonFromRect(aB2DBounds
);
2110 xTextBoundsPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolygon(
2111 rCanvas
->getUNOCanvas()->getDevice(),
2115 if( rParms
.maTextTransformation
)
2117 return std::make_shared
<OutlineAction
>(
2125 ::basegfx::utils::getRange(aResultingPolyPolygon
),
2131 *rParms
.maTextTransformation
);
2135 return std::make_shared
<OutlineAction
>(
2143 ::basegfx::utils::getRange(aResultingPolyPolygon
),
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
,
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
,
2172 const ::Size
aBaselineOffset( tools::getBaselineOffset( rState
,
2174 // #143885# maintain (nearly) full precision positioning,
2175 // by circumventing integer-based OutDev-mapping
2176 const ::basegfx::B2DPoint
aStartPoint(
2177 rState
.mapModeTransform
*
2178 ::basegfx::B2DPoint(rStartPoint
.X() + aBaselineOffset
.Width(),
2179 rStartPoint
.Y() + aBaselineOffset
.Height()) );
2181 const ::basegfx::B2DSize
aReliefOffset(
2182 rState
.mapModeTransform
* vcl::unotools::b2DSizeFromSize( rReliefOffset
) );
2183 const ::basegfx::B2DSize
aShadowOffset(
2184 rState
.mapModeTransform
* vcl::unotools::b2DSizeFromSize( rShadowOffset
) );
2186 if( rState
.isTextOutlineModeSet
)
2188 return createOutline(
2206 // convert DX array to device coordinate system (and
2207 // create it in the first place, if pDXArray is NULL)
2208 const uno::Sequence
< double > aCharWidths(
2210 setupDXArray( pDXArray
, nLen
, rState
) :
2211 setupDXArray( rText
,
2217 const uno::Sequence
< sal_Bool
> aKashidas(pKashidaArray
.data(), pKashidaArray
.size());
2219 // determine type of text action to create
2220 // =======================================
2222 const ::Color
aEmptyColor( COL_AUTO
);
2224 std::shared_ptr
<Action
> ret
;
2226 // no DX array, and no need to subset - no need to store
2228 if( pDXArray
.empty() && !bSubsettable
)
2231 if( !rState
.textOverlineStyle
&&
2232 !rState
.textUnderlineStyle
&&
2233 !rState
.textStrikeoutStyle
&&
2234 rReliefColor
== aEmptyColor
&&
2235 rShadowColor
== aEmptyColor
&&
2236 rTextFillColor
== aEmptyColor
)
2239 if( rParms
.maTextTransformation
)
2241 ret
= std::make_shared
<TextAction
>(
2248 *rParms
.maTextTransformation
);
2252 ret
= std::make_shared
<TextAction
>(
2263 // at least one of the effects requested
2264 if( rParms
.maTextTransformation
)
2265 ret
= std::make_shared
<EffectTextAction
>(
2278 *rParms
.maTextTransformation
);
2280 ret
= std::make_shared
<EffectTextAction
>(
2297 // DX array necessary - any effects?
2298 if( !rState
.textOverlineStyle
&&
2299 !rState
.textUnderlineStyle
&&
2300 !rState
.textStrikeoutStyle
&&
2301 rReliefColor
== aEmptyColor
&&
2302 rShadowColor
== aEmptyColor
&&
2303 rTextFillColor
== aEmptyColor
)
2306 if( rParms
.maTextTransformation
)
2307 ret
= std::make_shared
<TextArrayAction
>(
2316 *rParms
.maTextTransformation
);
2318 ret
= std::make_shared
<TextArrayAction
>(
2330 // at least one of the effects requested
2331 if( rParms
.maTextTransformation
)
2332 ret
= std::make_shared
<EffectTextArrayAction
>(
2347 *rParms
.maTextTransformation
);
2349 ret
= std::make_shared
<EffectTextArrayAction
>(
2370 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */