Bump version to 21.06.18.1
[LibreOffice.git] / cppcanvas / source / mtfrenderer / textaction.cxx
blob96cf8e9ee5f4ebfef2c53d9abec080f84bc24364
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <tools/diagnose_ex.h>
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>
36 #include <vcl/canvastools.hxx>
37 #include <vcl/virdev.hxx>
39 #include <basegfx/utils/canvastools.hxx>
40 #include <canvas/canvastools.hxx>
41 #include <memory>
42 #include <sal/log.hxx>
44 #include "textaction.hxx"
45 #include "textlineshelper.hxx"
46 #include <outdevstate.hxx>
47 #include "mtftools.hxx"
50 using namespace ::com::sun::star;
52 namespace cppcanvas::internal
54 namespace
56 void init( rendering::RenderState& o_rRenderState,
57 const ::basegfx::B2DPoint& rStartPoint,
58 const OutDevState& rState,
59 const CanvasSharedPtr& rCanvas )
61 tools::initRenderState(o_rRenderState,rState);
63 // #i36950# Offset clip back to origin (as it's also moved
64 // by rStartPoint)
65 // #i53964# Also take VCL font rotation into account,
66 // since this, opposed to the FontMatrix rotation
67 // elsewhere, _does_ get incorporated into the render
68 // state transform.
69 tools::modifyClip( o_rRenderState,
70 rState,
71 rCanvas,
72 rStartPoint,
73 nullptr,
74 &rState.fontRotation );
76 basegfx::B2DHomMatrix aLocalTransformation(basegfx::utils::createRotateB2DHomMatrix(rState.fontRotation));
77 aLocalTransformation.translate( rStartPoint.getX(),
78 rStartPoint.getY() );
79 ::canvas::tools::appendToRenderState( o_rRenderState,
80 aLocalTransformation );
82 o_rRenderState.DeviceColor = rState.textColor;
85 void init( rendering::RenderState& o_rRenderState,
86 const ::basegfx::B2DPoint& rStartPoint,
87 const OutDevState& rState,
88 const CanvasSharedPtr& rCanvas,
89 const ::basegfx::B2DHomMatrix& rTextTransform )
91 init( o_rRenderState, rStartPoint, rState, rCanvas );
93 // TODO(F2): Also inversely-transform clip with
94 // rTextTransform (which is actually rather hard, as the
95 // text transform is _prepended_ to the render state)!
97 // prepend extra font transform to render state
98 // (prepend it, because it's interpreted in the unit
99 // rect coordinate space)
100 ::canvas::tools::prependToRenderState( o_rRenderState,
101 rTextTransform );
104 void init( rendering::RenderState& o_rRenderState,
105 uno::Reference< rendering::XCanvasFont >& o_rFont,
106 const ::basegfx::B2DPoint& rStartPoint,
107 const OutDevState& rState,
108 const CanvasSharedPtr& rCanvas )
110 // ensure that o_rFont is valid. It is possible that
111 // text actions are generated without previously
112 // setting a font. Then, just take a default font
113 if( !o_rFont.is() )
115 // Use completely default FontRequest
116 const rendering::FontRequest aFontRequest;
118 geometry::Matrix2D aFontMatrix;
119 ::canvas::tools::setIdentityMatrix2D( aFontMatrix );
121 o_rFont = rCanvas->getUNOCanvas()->createFont(
122 aFontRequest,
123 uno::Sequence< beans::PropertyValue >(),
124 aFontMatrix );
127 init( o_rRenderState,
128 rStartPoint,
129 rState,
130 rCanvas );
133 void init( rendering::RenderState& o_rRenderState,
134 uno::Reference< rendering::XCanvasFont >& o_rFont,
135 const ::basegfx::B2DPoint& rStartPoint,
136 const OutDevState& rState,
137 const CanvasSharedPtr& rCanvas,
138 const ::basegfx::B2DHomMatrix& rTextTransform )
140 init( o_rRenderState, o_rFont, rStartPoint, rState, rCanvas );
142 // TODO(F2): Also inversely-transform clip with
143 // rTextTransform (which is actually rather hard, as the
144 // text transform is _prepended_ to the render state)!
146 // prepend extra font transform to render state
147 // (prepend it, because it's interpreted in the unit
148 // rect coordinate space)
149 ::canvas::tools::prependToRenderState( o_rRenderState,
150 rTextTransform );
153 void initLayoutWidth(double& rLayoutWidth, const uno::Sequence<double>& rOffsets)
155 ENSURE_OR_THROW(rOffsets.hasElements(),
156 "::cppcanvas::internal::initLayoutWidth(): zero-length array" );
157 rLayoutWidth = *(std::max_element(rOffsets.begin(), rOffsets.end()));
160 uno::Sequence< double > setupDXArray( const ::tools::Long* pCharWidths,
161 sal_Int32 nLen,
162 const OutDevState& rState )
164 // convert character widths from logical units
165 uno::Sequence< double > aCharWidthSeq( nLen );
166 double* pOutputWidths( aCharWidthSeq.getArray() );
168 // #143885# maintain (nearly) full precision of DX
169 // array, by circumventing integer-based
170 // OutDev-mapping
171 const double nScale( rState.mapModeTransform.get(0,0) );
172 for( int i = 0; i < nLen; ++i )
174 // TODO(F2): use correct scale direction
175 *pOutputWidths++ = *pCharWidths++ * nScale;
178 return aCharWidthSeq;
181 uno::Sequence< double > setupDXArray( const OUString& rText,
182 sal_Int32 nStartPos,
183 sal_Int32 nLen,
184 VirtualDevice const & rVDev,
185 const OutDevState& rState )
187 // no external DX array given, create one from given
188 // string
189 std::unique_ptr< ::tools::Long []> pCharWidths( new ::tools::Long[nLen] );
191 rVDev.GetTextArray( rText, pCharWidths.get(),
192 nStartPos, nLen );
194 return setupDXArray( pCharWidths.get(), 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 );
217 return aLocalPoint;
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,
229 sal_Int32 nStartPos,
230 sal_Int32 nLen,
231 const uno::Sequence< double >& rOffsets,
232 const CanvasSharedPtr& rCanvas,
233 const OutDevState& rState,
234 const ::basegfx::B2DHomMatrix* pTextTransform )
236 ENSURE_OR_THROW( rOffsets.hasElements(),
237 "::cppcanvas::internal::initArrayAction(): zero-length DX array" );
239 const ::basegfx::B2DPoint aLocalStartPoint(
240 adaptStartPoint( rStartPoint, rState, rOffsets ) );
242 uno::Reference< rendering::XCanvasFont > xFont( rState.xFont );
244 if( pTextTransform )
245 init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas, *pTextTransform );
246 else
247 init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas );
249 o_rTextLayout = xFont->createTextLayout(
250 rendering::StringContext( rText, nStartPos, nLen ),
251 rState.textDirection,
252 0 );
254 ENSURE_OR_THROW( o_rTextLayout.is(),
255 "::cppcanvas::internal::initArrayAction(): Invalid font" );
257 o_rTextLayout->applyLogicalAdvancements( rOffsets );
261 double getLineWidth( ::VirtualDevice const & rVDev,
262 const OutDevState& rState,
263 const rendering::StringContext& rStringContext )
265 // TODO(F2): use correct scale direction
266 const ::basegfx::B2DSize aSize( rVDev.GetTextWidth( rStringContext.Text,
267 static_cast<sal_uInt16>(rStringContext.StartPosition),
268 static_cast<sal_uInt16>(rStringContext.Length) ),
269 0 );
271 return (rState.mapModeTransform * aSize).getX();
274 uno::Sequence< double >
275 calcSubsetOffsets( rendering::RenderState& io_rRenderState,
276 double& o_rMinPos,
277 double& o_rMaxPos,
278 const uno::Reference< rendering::XTextLayout >& rOrigTextLayout,
279 double nLayoutWidth,
280 const ::cppcanvas::internal::Action::Subset& rSubset )
282 ENSURE_OR_THROW( rSubset.mnSubsetEnd > rSubset.mnSubsetBegin,
283 "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
285 uno::Sequence< double > aOrigOffsets( rOrigTextLayout->queryLogicalAdvancements() );
286 const double* pOffsets( aOrigOffsets.getConstArray() );
288 ENSURE_OR_THROW( aOrigOffsets.getLength() >= rSubset.mnSubsetEnd,
289 "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
292 // determine leftmost position in given subset range -
293 // as the DX array contains the output positions
294 // starting with the second character (the first is
295 // assumed to have output position 0), correct begin
296 // iterator.
297 const double nMinPos( rSubset.mnSubsetBegin <= 0 ? 0 :
298 *(std::min_element( pOffsets+rSubset.mnSubsetBegin-1,
299 pOffsets+rSubset.mnSubsetEnd )) );
301 // determine rightmost position in given subset range
302 // - as the DX array contains the output positions
303 // starting with the second character (the first is
304 // assumed to have output position 0), correct begin
305 // iterator.
306 const double nMaxPos(
307 *(std::max_element( pOffsets + (rSubset.mnSubsetBegin <= 0 ?
308 0 : rSubset.mnSubsetBegin-1),
309 pOffsets + rSubset.mnSubsetEnd )) );
311 // Logical advancements always increase in logical text order.
312 // For RTL text, nMaxPos is the distance from the right edge to
313 // the leftmost position in the subset, so we have to convert
314 // it to the offset from the origin (i.e. left edge ).
315 // LTR: |---- min --->|---- max --->| |
316 // RTL: | |<--- max ----|<--- min ---|
317 // |<- nOffset ->| |
318 const double nOffset = rOrigTextLayout->getMainTextDirection()
319 ? nLayoutWidth - nMaxPos : nMinPos;
322 // adapt render state, to move text output to given offset
325 // TODO(F1): Strictly speaking, we also have to adapt
326 // the clip here, which normally should _not_ move
327 // with the output offset. Neglected for now, as it
328 // does not matter for drawing layer output
330 if (nOffset > 0.0)
332 ::basegfx::B2DHomMatrix aTranslation;
333 if( rOrigTextLayout->getFont()->getFontRequest().FontDescription.IsVertical == css::util::TriState_YES )
335 // vertical text -> offset in y direction
336 aTranslation.translate(0.0, nOffset);
338 else
340 // horizontal text -> offset in x direction
341 aTranslation.translate(nOffset, 0.0);
344 ::canvas::tools::appendToRenderState( io_rRenderState,
345 aTranslation );
349 // reduce DX array to given substring
352 const sal_Int32 nNewElements( rSubset.mnSubsetEnd - rSubset.mnSubsetBegin );
353 uno::Sequence< double > aAdaptedOffsets( nNewElements );
354 double* pAdaptedOffsets( aAdaptedOffsets.getArray() );
356 // move to new output position (subtract nMinPos,
357 // which is the new '0' position), copy only the range
358 // as given by rSubset.
359 std::transform( pOffsets + rSubset.mnSubsetBegin,
360 pOffsets + rSubset.mnSubsetEnd,
361 pAdaptedOffsets,
362 [nMinPos](double aPos) { return aPos - nMinPos; } );
364 o_rMinPos = nMinPos;
365 o_rMaxPos = nMaxPos;
367 return aAdaptedOffsets;
370 uno::Reference< rendering::XTextLayout >
371 createSubsetLayout( const rendering::StringContext& rOrigContext,
372 const ::cppcanvas::internal::Action::Subset& rSubset,
373 const uno::Reference< rendering::XTextLayout >& rOrigTextLayout )
375 // create temporary new text layout with subset string
378 const sal_Int32 nNewStartPos( rOrigContext.StartPosition + std::min(
379 rSubset.mnSubsetBegin, rOrigContext.Length-1 ) );
380 const sal_Int32 nNewLength( std::max(
381 std::min(
382 rSubset.mnSubsetEnd - rSubset.mnSubsetBegin,
383 rOrigContext.Length ),
384 sal_Int32( 0 ) ) );
386 const rendering::StringContext aContext( rOrigContext.Text,
387 nNewStartPos,
388 nNewLength );
390 uno::Reference< rendering::XTextLayout > xTextLayout(
391 rOrigTextLayout->getFont()->createTextLayout( aContext,
392 rOrigTextLayout->getMainTextDirection(),
393 0 ),
394 uno::UNO_SET_THROW );
396 return xTextLayout;
399 /** Setup subset text layout
401 @param io_rTextLayout
402 Must contain original (full set) text layout on input,
403 will contain subsetted text layout (or empty
404 reference, for empty subsets) on output.
406 @param io_rRenderState
407 Must contain original render state on input, will
408 contain shifted render state concatenated with
409 rTransformation on output.
411 @param rTransformation
412 Additional transformation, to be prepended to render
413 state
415 @param rSubset
416 Subset to prepare
418 void createSubsetLayout( uno::Reference< rendering::XTextLayout >& io_rTextLayout,
419 double nLayoutWidth,
420 rendering::RenderState& io_rRenderState,
421 double& o_rMinPos,
422 double& o_rMaxPos,
423 const ::basegfx::B2DHomMatrix& rTransformation,
424 const Action::Subset& rSubset )
426 ::canvas::tools::prependToRenderState(io_rRenderState, rTransformation);
428 if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
430 // empty range, empty layout
431 io_rTextLayout.clear();
433 return;
436 ENSURE_OR_THROW( io_rTextLayout.is(),
437 "createSubsetLayout(): Invalid input layout" );
439 const rendering::StringContext& rOrigContext( io_rTextLayout->getText() );
441 if( rSubset.mnSubsetBegin == 0 &&
442 rSubset.mnSubsetEnd == rOrigContext.Length )
444 // full range, no need for subsetting
445 return;
448 uno::Reference< rendering::XTextLayout > xTextLayout(
449 createSubsetLayout( rOrigContext, rSubset, io_rTextLayout ) );
451 if( xTextLayout.is() )
453 xTextLayout->applyLogicalAdvancements(
454 calcSubsetOffsets( io_rRenderState,
455 o_rMinPos,
456 o_rMaxPos,
457 io_rTextLayout,
458 nLayoutWidth,
459 rSubset ) );
462 io_rTextLayout = xTextLayout;
466 /** Interface for renderEffectText functor below.
468 This is interface is used from the renderEffectText()
469 method below, to call the client implementation.
471 class TextRenderer
473 public:
474 virtual ~TextRenderer() {}
476 /// Render text with given RenderState
477 virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const = 0;
480 /** Render effect text.
482 @param rRenderer
483 Functor object, will be called to render the actual
484 part of the text effect (the text itself and the means
485 to render it are unknown to this method)
487 bool renderEffectText( const TextRenderer& rRenderer,
488 const rendering::RenderState& rRenderState,
489 const uno::Reference< rendering::XCanvas >& xCanvas,
490 const ::Color& rShadowColor,
491 const ::basegfx::B2DSize& rShadowOffset,
492 const ::Color& rReliefColor,
493 const ::basegfx::B2DSize& rReliefOffset,
494 const ::Color& rTextFillColor )
496 ::Color aEmptyColor( COL_AUTO );
497 uno::Reference<rendering::XColorSpace> xColorSpace(
498 xCanvas->getDevice()->getDeviceColorSpace() );
500 // draw shadow text, if enabled
501 if( rShadowColor != aEmptyColor )
503 rendering::RenderState aShadowState( rRenderState );
504 ::basegfx::B2DHomMatrix aTranslate;
506 aTranslate.translate( rShadowOffset.getX(),
507 rShadowOffset.getY() );
509 ::canvas::tools::appendToRenderState(aShadowState, aTranslate);
511 aShadowState.DeviceColor =
512 vcl::unotools::colorToDoubleSequence( rShadowColor,
513 xColorSpace );
515 rRenderer( aShadowState, rTextFillColor, false );
518 // draw relief text, if enabled
519 if( rReliefColor != aEmptyColor )
521 rendering::RenderState aReliefState( rRenderState );
522 ::basegfx::B2DHomMatrix aTranslate;
524 aTranslate.translate( rReliefOffset.getX(),
525 rReliefOffset.getY() );
527 ::canvas::tools::appendToRenderState(aReliefState, aTranslate);
529 aReliefState.DeviceColor =
530 vcl::unotools::colorToDoubleSequence( rReliefColor,
531 xColorSpace );
533 rRenderer( aReliefState, rTextFillColor, false );
536 // draw normal text
537 rRenderer( rRenderState, rTextFillColor, true );
539 return true;
543 ::basegfx::B2DRange calcEffectTextBounds( const ::basegfx::B2DRange& rTextBounds,
544 const ::basegfx::B2DRange& rLineBounds,
545 const ::basegfx::B2DSize& rReliefOffset,
546 const ::basegfx::B2DSize& rShadowOffset,
547 const rendering::RenderState& rRenderState,
548 const rendering::ViewState& rViewState )
550 ::basegfx::B2DRange aBounds( rTextBounds );
552 // add extends of text lines
553 aBounds.expand( rLineBounds );
555 // TODO(Q3): Provide this functionality at the B2DRange
556 ::basegfx::B2DRange aTotalBounds( aBounds );
557 aTotalBounds.expand(
558 ::basegfx::B2DRange( aBounds.getMinX() + rReliefOffset.getX(),
559 aBounds.getMinY() + rReliefOffset.getY(),
560 aBounds.getMaxX() + rReliefOffset.getX(),
561 aBounds.getMaxY() + rReliefOffset.getY() ) );
562 aTotalBounds.expand(
563 ::basegfx::B2DRange( aBounds.getMinX() + rShadowOffset.getX(),
564 aBounds.getMinY() + rShadowOffset.getY(),
565 aBounds.getMaxX() + rShadowOffset.getX(),
566 aBounds.getMaxY() + rShadowOffset.getY() ) );
568 return tools::calcDevicePixelBounds( aTotalBounds,
569 rViewState,
570 rRenderState );
573 void initEffectLinePolyPolygon( ::basegfx::B2DSize& o_rOverallSize,
574 uno::Reference< rendering::XPolyPolygon2D >& o_rTextLines,
575 const CanvasSharedPtr& rCanvas,
576 double nLineWidth,
577 const tools::TextLineInfo& rLineInfo )
579 const ::basegfx::B2DPolyPolygon aPoly(
580 tools::createTextLinesPolyPolygon( 0.0, nLineWidth,
581 rLineInfo ) );
583 o_rOverallSize = ::basegfx::utils::getRange( aPoly ).getRange();
585 o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
586 rCanvas->getUNOCanvas()->getDevice(),
587 aPoly );
591 class TextAction : public Action
593 public:
594 TextAction( const ::basegfx::B2DPoint& rStartPoint,
595 const OUString& rString,
596 sal_Int32 nStartPos,
597 sal_Int32 nLen,
598 const CanvasSharedPtr& rCanvas,
599 const OutDevState& rState );
601 TextAction( const ::basegfx::B2DPoint& rStartPoint,
602 const OUString& rString,
603 sal_Int32 nStartPos,
604 sal_Int32 nLen,
605 const CanvasSharedPtr& rCanvas,
606 const OutDevState& rState,
607 const ::basegfx::B2DHomMatrix& rTextTransform );
609 TextAction(const TextAction&) = delete;
610 const TextAction& operator=(const TextAction&) = delete;
612 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
613 virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
614 const Subset& rSubset ) const override;
616 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
617 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
618 const Subset& rSubset ) const override;
620 virtual sal_Int32 getActionCount() const override;
622 private:
623 // TODO(P2): This is potentially a real mass object
624 // (every character might be a separate TextAction),
625 // thus, make it as lightweight as possible. For
626 // example, share common RenderState among several
627 // TextActions, maybe using maOffsets for the
628 // translation.
630 uno::Reference< rendering::XCanvasFont > mxFont;
631 const rendering::StringContext maStringContext;
632 const CanvasSharedPtr mpCanvas;
633 rendering::RenderState maState;
634 const sal_Int8 maTextDirection;
637 TextAction::TextAction( const ::basegfx::B2DPoint& rStartPoint,
638 const OUString& rString,
639 sal_Int32 nStartPos,
640 sal_Int32 nLen,
641 const CanvasSharedPtr& rCanvas,
642 const OutDevState& rState ) :
643 mxFont( rState.xFont ),
644 maStringContext( rString, nStartPos, nLen ),
645 mpCanvas( rCanvas ),
646 maState(),
647 maTextDirection( rState.textDirection )
649 init( maState, mxFont,
650 rStartPoint,
651 rState, rCanvas );
653 ENSURE_OR_THROW( mxFont.is(),
654 "::cppcanvas::internal::TextAction(): Invalid font" );
657 TextAction::TextAction( const ::basegfx::B2DPoint& rStartPoint,
658 const OUString& rString,
659 sal_Int32 nStartPos,
660 sal_Int32 nLen,
661 const CanvasSharedPtr& rCanvas,
662 const OutDevState& rState,
663 const ::basegfx::B2DHomMatrix& rTextTransform ) :
664 mxFont( rState.xFont ),
665 maStringContext( rString, nStartPos, nLen ),
666 mpCanvas( rCanvas ),
667 maState(),
668 maTextDirection( rState.textDirection )
670 init( maState, mxFont,
671 rStartPoint,
672 rState, rCanvas, rTextTransform );
674 ENSURE_OR_THROW( mxFont.is(),
675 "::cppcanvas::internal::TextAction(): Invalid font" );
678 bool TextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
680 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextAction::render()" );
681 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::TextAction: 0x" << std::hex << this );
683 rendering::RenderState aLocalState( maState );
684 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
686 mpCanvas->getUNOCanvas()->drawText( maStringContext, mxFont,
687 mpCanvas->getViewState(), aLocalState, maTextDirection );
689 return true;
692 bool TextAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
693 const Subset& /*rSubset*/ ) const
695 SAL_WARN( "cppcanvas.emf", "TextAction::renderSubset(): Subset not supported by this object" );
697 // TODO(P1): Retrieve necessary font metric info for
698 // TextAction from XCanvas. Currently, the
699 // TextActionFactory does not generate this object for
700 // _subsettable_ text
701 return render( rTransformation );
704 ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
706 // create XTextLayout, to have the
707 // XTextLayout::queryTextBounds() method available
708 uno::Reference< rendering::XTextLayout > xTextLayout(
709 mxFont->createTextLayout(
710 maStringContext,
711 maTextDirection,
712 0 ) );
714 rendering::RenderState aLocalState( maState );
715 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
717 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
718 xTextLayout->queryTextBounds() ),
719 mpCanvas->getViewState(),
720 aLocalState );
723 ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
724 const Subset& /*rSubset*/ ) const
726 SAL_WARN( "cppcanvas.emf", "TextAction::getBounds(): Subset not supported by this object" );
728 // TODO(P1): Retrieve necessary font metric info for
729 // TextAction from XCanvas. Currently, the
730 // TextActionFactory does not generate this object for
731 // _subsettable_ text
732 return getBounds( rTransformation );
735 sal_Int32 TextAction::getActionCount() const
737 // TODO(P1): Retrieve necessary font metric info for
738 // TextAction from XCanvas. Currently, the
739 // TextActionFactory does not generate this object for
740 // _subsettable_ text
741 return 1;
745 class EffectTextAction :
746 public Action,
747 public TextRenderer
749 public:
750 EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
751 const ::basegfx::B2DSize& rReliefOffset,
752 const ::Color& rReliefColor,
753 const ::basegfx::B2DSize& rShadowOffset,
754 const ::Color& rShadowColor,
755 const ::Color& rTextFillColor,
756 const OUString& rText,
757 sal_Int32 nStartPos,
758 sal_Int32 nLen,
759 VirtualDevice const & rVDev,
760 const CanvasSharedPtr& rCanvas,
761 const OutDevState& rState );
763 EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
764 const ::basegfx::B2DSize& rReliefOffset,
765 const ::Color& rReliefColor,
766 const ::basegfx::B2DSize& rShadowOffset,
767 const ::Color& rShadowColor,
768 const ::Color& rTextFillColor,
769 const OUString& rText,
770 sal_Int32 nStartPos,
771 sal_Int32 nLen,
772 VirtualDevice const & rVDev,
773 const CanvasSharedPtr& rCanvas,
774 const OutDevState& rState,
775 const ::basegfx::B2DHomMatrix& rTextTransform );
777 EffectTextAction(const EffectTextAction&) = delete;
778 const EffectTextAction& operator=(const EffectTextAction&) = delete;
780 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
781 virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
782 const Subset& rSubset ) const override;
784 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
785 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
786 const Subset& rSubset ) const override;
788 virtual sal_Int32 getActionCount() const override;
790 private:
791 /// Interface TextRenderer
792 virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const override;
794 geometry::RealRectangle2D queryTextBounds() const;
795 css::uno::Reference<css::rendering::XPolyPolygon2D> queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const;
797 // TODO(P2): This is potentially a real mass object
798 // (every character might be a separate TextAction),
799 // thus, make it as lightweight as possible. For
800 // example, share common RenderState among several
801 // TextActions, maybe using maOffsets for the
802 // translation.
804 uno::Reference< rendering::XCanvasFont > mxFont;
805 const rendering::StringContext maStringContext;
806 const CanvasSharedPtr mpCanvas;
807 rendering::RenderState maState;
808 const tools::TextLineInfo maTextLineInfo;
809 ::basegfx::B2DSize maLinesOverallSize;
810 uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
811 const ::basegfx::B2DSize maReliefOffset;
812 const ::Color maReliefColor;
813 const ::basegfx::B2DSize maShadowOffset;
814 const ::Color maShadowColor;
815 const ::Color maTextFillColor;
816 const sal_Int8 maTextDirection;
819 EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
820 const ::basegfx::B2DSize& rReliefOffset,
821 const ::Color& rReliefColor,
822 const ::basegfx::B2DSize& rShadowOffset,
823 const ::Color& rShadowColor,
824 const ::Color& rTextFillColor,
825 const OUString& rText,
826 sal_Int32 nStartPos,
827 sal_Int32 nLen,
828 VirtualDevice const & rVDev,
829 const CanvasSharedPtr& rCanvas,
830 const OutDevState& rState ) :
831 mxFont( rState.xFont ),
832 maStringContext( rText, nStartPos, nLen ),
833 mpCanvas( rCanvas ),
834 maState(),
835 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
836 maLinesOverallSize(),
837 mxTextLines(),
838 maReliefOffset( rReliefOffset ),
839 maReliefColor( rReliefColor ),
840 maShadowOffset( rShadowOffset ),
841 maShadowColor( rShadowColor ),
842 maTextFillColor( rTextFillColor ),
843 maTextDirection( rState.textDirection )
845 const double nLineWidth(getLineWidth( rVDev, rState, maStringContext ));
846 initEffectLinePolyPolygon( maLinesOverallSize,
847 mxTextLines,
848 rCanvas,
849 nLineWidth,
850 maTextLineInfo );
852 init( maState, mxFont,
853 rStartPoint,
854 rState, rCanvas );
856 ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
857 "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
860 EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
861 const ::basegfx::B2DSize& rReliefOffset,
862 const ::Color& rReliefColor,
863 const ::basegfx::B2DSize& rShadowOffset,
864 const ::Color& rShadowColor,
865 const ::Color& rTextFillColor,
866 const OUString& rText,
867 sal_Int32 nStartPos,
868 sal_Int32 nLen,
869 VirtualDevice const & rVDev,
870 const CanvasSharedPtr& rCanvas,
871 const OutDevState& rState,
872 const ::basegfx::B2DHomMatrix& rTextTransform ) :
873 mxFont( rState.xFont ),
874 maStringContext( rText, nStartPos, nLen ),
875 mpCanvas( rCanvas ),
876 maState(),
877 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
878 maLinesOverallSize(),
879 mxTextLines(),
880 maReliefOffset( rReliefOffset ),
881 maReliefColor( rReliefColor ),
882 maShadowOffset( rShadowOffset ),
883 maShadowColor( rShadowColor ),
884 maTextFillColor( rTextFillColor ),
885 maTextDirection( rState.textDirection )
887 const double nLineWidth( getLineWidth( rVDev, rState, maStringContext ) );
888 initEffectLinePolyPolygon( maLinesOverallSize,
889 mxTextLines,
890 rCanvas,
891 nLineWidth,
892 maTextLineInfo );
894 init( maState, mxFont,
895 rStartPoint,
896 rState, rCanvas, rTextTransform );
898 ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
899 "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
902 bool EffectTextAction::operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool /*bNormalText*/ ) const
904 const rendering::ViewState& rViewState( mpCanvas->getViewState() );
905 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
907 //rhbz#1589029 non-transparent text fill background support
908 if (rTextFillColor != COL_AUTO)
910 rendering::RenderState aLocalState( rRenderState );
911 aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
912 rTextFillColor, rCanvas->getDevice()->getDeviceColorSpace());
913 auto xTextBounds = queryTextBounds(rCanvas);
914 // background of text
915 rCanvas->fillPolyPolygon(xTextBounds, rViewState, aLocalState);
918 // under/over lines
919 rCanvas->fillPolyPolygon( mxTextLines,
920 rViewState,
921 rRenderState );
923 rCanvas->drawText( maStringContext, mxFont,
924 rViewState,
925 rRenderState,
926 maTextDirection );
928 return true;
931 bool EffectTextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
933 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextAction::render()" );
934 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextAction: 0x" << std::hex << this );
936 rendering::RenderState aLocalState( maState );
937 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
939 return renderEffectText( *this,
940 aLocalState,
941 mpCanvas->getUNOCanvas(),
942 maShadowColor,
943 maShadowOffset,
944 maReliefColor,
945 maReliefOffset,
946 maTextFillColor);
949 bool EffectTextAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
950 const Subset& /*rSubset*/ ) const
952 SAL_WARN( "cppcanvas.emf", "EffectTextAction::renderSubset(): Subset not supported by this object" );
954 // TODO(P1): Retrieve necessary font metric info for
955 // TextAction from XCanvas. Currently, the
956 // TextActionFactory does not generate this object for
957 // subsettable text
958 return render( rTransformation );
961 geometry::RealRectangle2D EffectTextAction::queryTextBounds() const
963 // create XTextLayout, to have the
964 // XTextLayout::queryTextBounds() method available
965 uno::Reference< rendering::XTextLayout > xTextLayout(
966 mxFont->createTextLayout(
967 maStringContext,
968 maTextDirection,
969 0 ) );
971 return xTextLayout->queryTextBounds();
974 css::uno::Reference<css::rendering::XPolyPolygon2D> EffectTextAction::queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const
976 auto aTextBounds = queryTextBounds();
977 auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
978 auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
979 return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(rCanvas->getDevice(), aTextBoundsPoly);
982 ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
984 rendering::RenderState aLocalState( maState );
985 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
987 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
988 queryTextBounds() ),
989 ::basegfx::B2DRange( 0,0,
990 maLinesOverallSize.getX(),
991 maLinesOverallSize.getY() ),
992 maReliefOffset,
993 maShadowOffset,
994 aLocalState,
995 mpCanvas->getViewState() );
998 ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
999 const Subset& /*rSubset*/ ) const
1001 SAL_WARN( "cppcanvas.emf", "EffectTextAction::getBounds(): Subset not supported by this object" );
1003 // TODO(P1): Retrieve necessary font metric info for
1004 // TextAction from XCanvas. Currently, the
1005 // TextActionFactory does not generate this object for
1006 // _subsettable_ text
1007 return getBounds( rTransformation );
1010 sal_Int32 EffectTextAction::getActionCount() const
1012 // TODO(P1): Retrieve necessary font metric info for
1013 // TextAction from XCanvas. Currently, the
1014 // TextActionFactory does not generate this object for
1015 // subsettable text
1016 return 1;
1020 class TextArrayAction : public Action
1022 public:
1023 TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1024 const OUString& rString,
1025 sal_Int32 nStartPos,
1026 sal_Int32 nLen,
1027 const uno::Sequence< double >& rOffsets,
1028 const CanvasSharedPtr& rCanvas,
1029 const OutDevState& rState );
1031 TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1032 const OUString& rString,
1033 sal_Int32 nStartPos,
1034 sal_Int32 nLen,
1035 const uno::Sequence< double >& rOffsets,
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;
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.
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,
1070 sal_Int32 nLen,
1071 const uno::Sequence< double >& rOffsets,
1072 const CanvasSharedPtr& rCanvas,
1073 const OutDevState& rState ) :
1074 mxTextLayout(),
1075 mpCanvas( rCanvas ),
1076 maState()
1078 initLayoutWidth(mnLayoutWidth, rOffsets);
1080 initArrayAction( maState,
1081 mxTextLayout,
1082 rStartPoint,
1083 rString,
1084 nStartPos,
1085 nLen,
1086 rOffsets,
1087 rCanvas,
1088 rState, nullptr );
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 CanvasSharedPtr& rCanvas,
1097 const OutDevState& rState,
1098 const ::basegfx::B2DHomMatrix& rTextTransform ) :
1099 mxTextLayout(),
1100 mpCanvas( rCanvas ),
1101 maState()
1103 initLayoutWidth(mnLayoutWidth, rOffsets);
1105 initArrayAction( maState,
1106 mxTextLayout,
1107 rStartPoint,
1108 rString,
1109 nStartPos,
1110 nLen,
1111 rOffsets,
1112 rCanvas,
1113 rState,
1114 &rTextTransform );
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(),
1127 aLocalState );
1129 return true;
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,
1143 mnLayoutWidth,
1144 aLocalState,
1145 nDummy0,
1146 nDummy1,
1147 rTransformation,
1148 rSubset );
1150 if( !xTextLayout.is() )
1151 return true; // empty layout, render nothing
1153 mpCanvas->getUNOCanvas()->drawTextLayout( xTextLayout,
1154 mpCanvas->getViewState(),
1155 aLocalState );
1157 return true;
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(),
1168 aLocalState );
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,
1182 mnLayoutWidth,
1183 aLocalState,
1184 nDummy0,
1185 nDummy1,
1186 rTransformation,
1187 rSubset );
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(),
1195 aLocalState );
1198 sal_Int32 TextArrayAction::getActionCount() const
1200 const rendering::StringContext& rOrigContext( mxTextLayout->getText() );
1202 return rOrigContext.Length;
1206 class EffectTextArrayAction :
1207 public Action,
1208 public TextRenderer
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 VirtualDevice const & rVDev,
1222 const CanvasSharedPtr& rCanvas,
1223 const OutDevState& rState );
1224 EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1225 const ::basegfx::B2DSize& rReliefOffset,
1226 const ::Color& rReliefColor,
1227 const ::basegfx::B2DSize& rShadowOffset,
1228 const ::Color& rShadowColor,
1229 const ::Color& rTextFillColor,
1230 const OUString& rText,
1231 sal_Int32 nStartPos,
1232 sal_Int32 nLen,
1233 const uno::Sequence< double >& rOffsets,
1234 VirtualDevice const & rVDev,
1235 const CanvasSharedPtr& rCanvas,
1236 const OutDevState& rState,
1237 const ::basegfx::B2DHomMatrix& rTextTransform );
1239 EffectTextArrayAction(const EffectTextArrayAction&) = delete;
1240 const EffectTextArrayAction& operator=(const EffectTextArrayAction&) = delete;
1242 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1243 virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1244 const Subset& rSubset ) const override;
1246 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1247 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1248 const Subset& rSubset ) const override;
1250 virtual sal_Int32 getActionCount() const override;
1252 private:
1253 // TextRenderer interface
1254 virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const override;
1256 css::uno::Reference<css::rendering::XPolyPolygon2D> queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const;
1258 // TODO(P2): This is potentially a real mass object
1259 // (every character might be a separate TextAction),
1260 // thus, make it as lightweight as possible. For
1261 // example, share common RenderState among several
1262 // TextActions, maybe using maOffsets for the
1263 // translation.
1265 uno::Reference< rendering::XTextLayout > mxTextLayout;
1266 const CanvasSharedPtr mpCanvas;
1267 rendering::RenderState maState;
1268 const tools::TextLineInfo maTextLineInfo;
1269 TextLinesHelper maTextLinesHelper;
1270 const ::basegfx::B2DSize maReliefOffset;
1271 const ::Color maReliefColor;
1272 const ::basegfx::B2DSize maShadowOffset;
1273 const ::Color maShadowColor;
1274 const ::Color maTextFillColor;
1275 double mnLayoutWidth;
1278 EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1279 const ::basegfx::B2DSize& rReliefOffset,
1280 const ::Color& rReliefColor,
1281 const ::basegfx::B2DSize& rShadowOffset,
1282 const ::Color& rShadowColor,
1283 const ::Color& rTextFillColor,
1284 const OUString& rText,
1285 sal_Int32 nStartPos,
1286 sal_Int32 nLen,
1287 const uno::Sequence< double >& rOffsets,
1288 VirtualDevice const & rVDev,
1289 const CanvasSharedPtr& rCanvas,
1290 const OutDevState& rState ) :
1291 mxTextLayout(),
1292 mpCanvas( rCanvas ),
1293 maState(),
1294 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1295 maTextLinesHelper(mpCanvas, rState),
1296 maReliefOffset( rReliefOffset ),
1297 maReliefColor( rReliefColor ),
1298 maShadowOffset( rShadowOffset ),
1299 maShadowColor( rShadowColor ),
1300 maTextFillColor( rTextFillColor )
1302 initLayoutWidth(mnLayoutWidth, rOffsets);
1304 maTextLinesHelper.init(mnLayoutWidth, maTextLineInfo);
1306 initArrayAction( maState,
1307 mxTextLayout,
1308 rStartPoint,
1309 rText,
1310 nStartPos,
1311 nLen,
1312 rOffsets,
1313 rCanvas,
1314 rState, nullptr );
1317 EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1318 const ::basegfx::B2DSize& rReliefOffset,
1319 const ::Color& rReliefColor,
1320 const ::basegfx::B2DSize& rShadowOffset,
1321 const ::Color& rShadowColor,
1322 const ::Color& rTextFillColor,
1323 const OUString& rText,
1324 sal_Int32 nStartPos,
1325 sal_Int32 nLen,
1326 const uno::Sequence< double >& rOffsets,
1327 VirtualDevice const & rVDev,
1328 const CanvasSharedPtr& rCanvas,
1329 const OutDevState& rState,
1330 const ::basegfx::B2DHomMatrix& rTextTransform ) :
1331 mxTextLayout(),
1332 mpCanvas( rCanvas ),
1333 maState(),
1334 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1335 maTextLinesHelper(mpCanvas, rState),
1336 maReliefOffset( rReliefOffset ),
1337 maReliefColor( rReliefColor ),
1338 maShadowOffset( rShadowOffset ),
1339 maShadowColor( rShadowColor ),
1340 maTextFillColor( rTextFillColor )
1342 initLayoutWidth(mnLayoutWidth, rOffsets);
1344 maTextLinesHelper.init(mnLayoutWidth, maTextLineInfo);
1346 initArrayAction( maState,
1347 mxTextLayout,
1348 rStartPoint,
1349 rText,
1350 nStartPos,
1351 nLen,
1352 rOffsets,
1353 rCanvas,
1354 rState,
1355 &rTextTransform );
1358 css::uno::Reference<css::rendering::XPolyPolygon2D> EffectTextArrayAction::queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const
1360 const geometry::RealRectangle2D aTextBounds(mxTextLayout->queryTextBounds());
1361 auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
1362 auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
1363 return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(rCanvas->getDevice(), aTextBoundsPoly);
1366 bool EffectTextArrayAction::operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText) const
1368 const rendering::ViewState& rViewState( mpCanvas->getViewState() );
1369 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
1371 //rhbz#1589029 non-transparent text fill background support
1372 if (rTextFillColor != COL_AUTO)
1374 rendering::RenderState aLocalState(rRenderState);
1375 aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
1376 rTextFillColor, rCanvas->getDevice()->getDeviceColorSpace());
1377 auto xTextBounds = queryTextBounds(rCanvas);
1378 // background of text
1379 rCanvas->fillPolyPolygon(xTextBounds, rViewState, aLocalState);
1382 // under/over lines
1383 maTextLinesHelper.render(rRenderState, bNormalText);
1385 rCanvas->drawTextLayout( mxTextLayout,
1386 rViewState,
1387 rRenderState );
1389 return true;
1392 bool EffectTextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1394 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::render()" );
1395 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1397 rendering::RenderState aLocalState( maState );
1398 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1400 return renderEffectText( *this,
1401 aLocalState,
1402 mpCanvas->getUNOCanvas(),
1403 maShadowColor,
1404 maShadowOffset,
1405 maReliefColor,
1406 maReliefOffset,
1407 maTextFillColor);
1410 class EffectTextArrayRenderHelper : public TextRenderer
1412 public:
1413 EffectTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas,
1414 const uno::Reference< rendering::XTextLayout >& rTextLayout,
1415 const TextLinesHelper& rTextLinesHelper,
1416 const rendering::ViewState& rViewState ) :
1417 mrCanvas( rCanvas ),
1418 mrTextLayout( rTextLayout ),
1419 mrTextLinesHelper( rTextLinesHelper ),
1420 mrViewState( rViewState )
1424 // TextRenderer interface
1425 virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor,bool bNormalText) const override
1427 mrTextLinesHelper.render(rRenderState, bNormalText);
1429 //rhbz#1589029 non-transparent text fill background support
1430 if (rTextFillColor != COL_AUTO)
1432 rendering::RenderState aLocalState(rRenderState);
1433 aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
1434 rTextFillColor, mrCanvas->getDevice()->getDeviceColorSpace());
1435 auto xTextBounds = queryTextBounds();
1436 // background of text
1437 mrCanvas->fillPolyPolygon(xTextBounds, mrViewState, aLocalState);
1440 mrCanvas->drawTextLayout( mrTextLayout,
1441 mrViewState,
1442 rRenderState );
1444 return true;
1447 private:
1449 css::uno::Reference<css::rendering::XPolyPolygon2D> queryTextBounds() const
1451 const geometry::RealRectangle2D aTextBounds(mrTextLayout->queryTextBounds());
1452 auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
1453 auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
1454 return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(mrCanvas->getDevice(), aTextBoundsPoly);
1457 const uno::Reference< rendering::XCanvas >& mrCanvas;
1458 const uno::Reference< rendering::XTextLayout >& mrTextLayout;
1459 const TextLinesHelper& mrTextLinesHelper;
1460 const rendering::ViewState& mrViewState;
1463 bool EffectTextArrayAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1464 const Subset& rSubset ) const
1466 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::renderSubset()" );
1467 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1469 rendering::RenderState aLocalState( maState );
1470 uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1471 const geometry::RealRectangle2D aTextBounds( mxTextLayout->queryTextBounds() );
1473 double nMinPos(0.0);
1474 double nMaxPos(aTextBounds.X2 - aTextBounds.X1);
1476 createSubsetLayout( xTextLayout,
1477 mnLayoutWidth,
1478 aLocalState,
1479 nMinPos,
1480 nMaxPos,
1481 rTransformation,
1482 rSubset );
1484 if( !xTextLayout.is() )
1485 return true; // empty layout, render nothing
1488 // create and setup local line polygon
1489 // ===================================
1491 uno::Reference< rendering::XCanvas > xCanvas( mpCanvas->getUNOCanvas() );
1492 const rendering::ViewState& rViewState( mpCanvas->getViewState() );
1494 TextLinesHelper aHelper = maTextLinesHelper;
1495 aHelper.init(nMaxPos - nMinPos, maTextLineInfo);
1498 // render everything
1499 // =================
1501 return renderEffectText(
1502 EffectTextArrayRenderHelper( xCanvas,
1503 xTextLayout,
1504 aHelper,
1505 rViewState ),
1506 aLocalState,
1507 xCanvas,
1508 maShadowColor,
1509 maShadowOffset,
1510 maReliefColor,
1511 maReliefOffset,
1512 maTextFillColor);
1515 ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1517 rendering::RenderState aLocalState( maState );
1518 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1520 ::basegfx::B2DSize aSize = maTextLinesHelper.getOverallSize();
1522 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1523 mxTextLayout->queryTextBounds() ),
1524 ::basegfx::B2DRange( 0,0,
1525 aSize.getX(),
1526 aSize.getY() ),
1527 maReliefOffset,
1528 maShadowOffset,
1529 aLocalState,
1530 mpCanvas->getViewState() );
1533 ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1534 const Subset& rSubset ) const
1536 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::getBounds( subset )" );
1537 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1539 rendering::RenderState aLocalState( maState );
1540 uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1541 const geometry::RealRectangle2D aTextBounds( mxTextLayout->queryTextBounds() );
1543 double nMinPos(0.0);
1544 double nMaxPos(aTextBounds.X2 - aTextBounds.X1);
1546 createSubsetLayout( xTextLayout,
1547 mnLayoutWidth,
1548 aLocalState,
1549 nMinPos,
1550 nMaxPos,
1551 rTransformation,
1552 rSubset );
1554 if( !xTextLayout.is() )
1555 return ::basegfx::B2DRange(); // empty layout, empty bounds
1558 // create and setup local line polygon
1559 // ===================================
1561 const ::basegfx::B2DPolyPolygon aPoly(
1562 tools::createTextLinesPolyPolygon(
1563 0.0, nMaxPos - nMinPos,
1564 maTextLineInfo ) );
1566 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1567 xTextLayout->queryTextBounds() ),
1568 ::basegfx::utils::getRange( aPoly ),
1569 maReliefOffset,
1570 maShadowOffset,
1571 aLocalState,
1572 mpCanvas->getViewState() );
1575 sal_Int32 EffectTextArrayAction::getActionCount() const
1577 const rendering::StringContext& rOrigContext( mxTextLayout->getText() );
1579 return rOrigContext.Length;
1583 class OutlineAction :
1584 public Action,
1585 public TextRenderer
1587 public:
1588 OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1589 const ::basegfx::B2DSize& rReliefOffset,
1590 const ::Color& rReliefColor,
1591 const ::basegfx::B2DSize& rShadowOffset,
1592 const ::Color& rShadowColor,
1593 const ::basegfx::B2DRectangle& rOutlineBounds,
1594 const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly,
1595 const uno::Sequence< double >& rOffsets,
1596 VirtualDevice const & rVDev,
1597 const CanvasSharedPtr& rCanvas,
1598 const OutDevState& rState );
1599 OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1600 const ::basegfx::B2DSize& rReliefOffset,
1601 const ::Color& rReliefColor,
1602 const ::basegfx::B2DSize& rShadowOffset,
1603 const ::Color& rShadowColor,
1604 const ::basegfx::B2DRectangle& rOutlineBounds,
1605 const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly,
1606 const uno::Sequence< double >& rOffsets,
1607 VirtualDevice const & rVDev,
1608 const CanvasSharedPtr& rCanvas,
1609 const OutDevState& rState,
1610 const ::basegfx::B2DHomMatrix& rTextTransform );
1612 OutlineAction(const OutlineAction&) = delete;
1613 const OutlineAction& operator=(const OutlineAction&) = delete;
1615 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1616 virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1617 const Subset& rSubset ) const override;
1619 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1620 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1621 const Subset& rSubset ) const override;
1623 virtual sal_Int32 getActionCount() const override;
1625 private:
1626 // TextRenderer interface
1627 virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const override;
1629 // TODO(P2): This is potentially a real mass object
1630 // (every character might be a separate TextAction),
1631 // thus, make it as lightweight as possible. For
1632 // example, share common RenderState among several
1633 // TextActions, maybe using maOffsets for the
1634 // translation.
1636 uno::Reference< rendering::XPolyPolygon2D > mxTextPoly;
1638 const uno::Sequence< double > maOffsets;
1639 const CanvasSharedPtr mpCanvas;
1640 rendering::RenderState maState;
1641 double mnOutlineWidth;
1642 const uno::Sequence< double > maFillColor;
1643 const tools::TextLineInfo maTextLineInfo;
1644 ::basegfx::B2DSize maLinesOverallSize;
1645 const ::basegfx::B2DRectangle maOutlineBounds;
1646 uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
1647 const ::basegfx::B2DSize maReliefOffset;
1648 const ::Color maReliefColor;
1649 const ::basegfx::B2DSize maShadowOffset;
1650 const ::Color maShadowColor;
1651 const ::Color maTextFillColor;
1654 double calcOutlineWidth( const OutDevState& rState,
1655 VirtualDevice const & rVDev )
1657 const ::basegfx::B2DSize aFontSize( 0,
1658 rVDev.GetFont().GetFontHeight() / 64.0 );
1660 const double nOutlineWidth(
1661 (rState.mapModeTransform * aFontSize).getY() );
1663 return nOutlineWidth < 1.0 ? 1.0 : nOutlineWidth;
1666 OutlineAction::OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1667 const ::basegfx::B2DSize& rReliefOffset,
1668 const ::Color& rReliefColor,
1669 const ::basegfx::B2DSize& rShadowOffset,
1670 const ::Color& rShadowColor,
1671 const ::basegfx::B2DRectangle& rOutlineBounds,
1672 const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly,
1673 const uno::Sequence< double >& rOffsets,
1674 VirtualDevice const & rVDev,
1675 const CanvasSharedPtr& rCanvas,
1676 const OutDevState& rState ) :
1677 mxTextPoly( rTextPoly ),
1678 maOffsets( rOffsets ),
1679 mpCanvas( rCanvas ),
1680 maState(),
1681 mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
1682 maFillColor(
1683 vcl::unotools::colorToDoubleSequence(
1684 COL_WHITE,
1685 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1686 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1687 maLinesOverallSize(),
1688 maOutlineBounds( rOutlineBounds ),
1689 mxTextLines(),
1690 maReliefOffset( rReliefOffset ),
1691 maReliefColor( rReliefColor ),
1692 maShadowOffset( rShadowOffset ),
1693 maShadowColor( rShadowColor )
1695 double nLayoutWidth = 0.0;
1697 initLayoutWidth(nLayoutWidth, rOffsets);
1699 initEffectLinePolyPolygon( maLinesOverallSize,
1700 mxTextLines,
1701 rCanvas,
1702 nLayoutWidth,
1703 maTextLineInfo );
1705 init( maState,
1706 rStartPoint,
1707 rState,
1708 rCanvas );
1711 OutlineAction::OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1712 const ::basegfx::B2DSize& rReliefOffset,
1713 const ::Color& rReliefColor,
1714 const ::basegfx::B2DSize& rShadowOffset,
1715 const ::Color& rShadowColor,
1716 const ::basegfx::B2DRectangle& rOutlineBounds,
1717 const uno::Reference< rendering::XPolyPolygon2D >& rTextPoly,
1718 const uno::Sequence< double >& rOffsets,
1719 VirtualDevice const & rVDev,
1720 const CanvasSharedPtr& rCanvas,
1721 const OutDevState& rState,
1722 const ::basegfx::B2DHomMatrix& rTextTransform ) :
1723 mxTextPoly( rTextPoly ),
1724 maOffsets( rOffsets ),
1725 mpCanvas( rCanvas ),
1726 maState(),
1727 mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
1728 maFillColor(
1729 vcl::unotools::colorToDoubleSequence(
1730 COL_WHITE,
1731 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1732 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1733 maLinesOverallSize(),
1734 maOutlineBounds( rOutlineBounds ),
1735 mxTextLines(),
1736 maReliefOffset( rReliefOffset ),
1737 maReliefColor( rReliefColor ),
1738 maShadowOffset( rShadowOffset ),
1739 maShadowColor( rShadowColor )
1741 double nLayoutWidth = 0.0;
1742 initLayoutWidth(nLayoutWidth, rOffsets);
1744 initEffectLinePolyPolygon( maLinesOverallSize,
1745 mxTextLines,
1746 rCanvas,
1747 nLayoutWidth,
1748 maTextLineInfo );
1750 init( maState,
1751 rStartPoint,
1752 rState,
1753 rCanvas,
1754 rTextTransform );
1757 bool OutlineAction::operator()( const rendering::RenderState& rRenderState, const ::Color& /*rTextFillColor*/, bool /*bNormalText*/ ) const
1759 const rendering::ViewState& rViewState( mpCanvas->getViewState() );
1760 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
1762 rendering::StrokeAttributes aStrokeAttributes;
1764 aStrokeAttributes.StrokeWidth = mnOutlineWidth;
1765 aStrokeAttributes.MiterLimit = 1.0;
1766 aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
1767 aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
1768 aStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
1770 rendering::RenderState aLocalState( rRenderState );
1771 aLocalState.DeviceColor = maFillColor;
1773 // TODO(P1): implement caching
1775 // background of text
1776 rCanvas->fillPolyPolygon( mxTextPoly,
1777 rViewState,
1778 aLocalState );
1780 // border line of text
1781 rCanvas->strokePolyPolygon( mxTextPoly,
1782 rViewState,
1783 rRenderState,
1784 aStrokeAttributes );
1786 // underlines/strikethrough - background
1787 rCanvas->fillPolyPolygon( mxTextLines,
1788 rViewState,
1789 aLocalState );
1790 // underlines/strikethrough - border
1791 rCanvas->strokePolyPolygon( mxTextLines,
1792 rViewState,
1793 rRenderState,
1794 aStrokeAttributes );
1796 return true;
1799 bool OutlineAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1801 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::render()" );
1802 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1804 rendering::RenderState aLocalState( maState );
1805 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1807 return renderEffectText( *this,
1808 aLocalState,
1809 mpCanvas->getUNOCanvas(),
1810 maShadowColor,
1811 maShadowOffset,
1812 maReliefColor,
1813 maReliefOffset,
1814 maTextFillColor);
1817 #if 0 // see #if'ed out use in OutlineAction::renderSubset below:
1818 class OutlineTextArrayRenderHelper : public TextRenderer
1820 public:
1821 OutlineTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas,
1822 const uno::Reference< rendering::XPolyPolygon2D >& rTextPolygon,
1823 const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon,
1824 const rendering::ViewState& rViewState,
1825 double nOutlineWidth ) :
1826 maFillColor(
1827 vcl::unotools::colorToDoubleSequence(
1828 ::COL_WHITE,
1829 rCanvas->getDevice()->getDeviceColorSpace() )),
1830 mnOutlineWidth( nOutlineWidth ),
1831 mrCanvas( rCanvas ),
1832 mrTextPolygon( rTextPolygon ),
1833 mrLinePolygon( rLinePolygon ),
1834 mrViewState( rViewState )
1838 // TextRenderer interface
1839 virtual bool operator()( const rendering::RenderState& rRenderState ) const
1841 rendering::StrokeAttributes aStrokeAttributes;
1843 aStrokeAttributes.StrokeWidth = mnOutlineWidth;
1844 aStrokeAttributes.MiterLimit = 1.0;
1845 aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
1846 aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
1847 aStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
1849 rendering::RenderState aLocalState( rRenderState );
1850 aLocalState.DeviceColor = maFillColor;
1852 // TODO(P1): implement caching
1854 // background of text
1855 mrCanvas->fillPolyPolygon( mrTextPolygon,
1856 mrViewState,
1857 aLocalState );
1859 // border line of text
1860 mrCanvas->strokePolyPolygon( mrTextPolygon,
1861 mrViewState,
1862 rRenderState,
1863 aStrokeAttributes );
1865 // underlines/strikethrough - background
1866 mrCanvas->fillPolyPolygon( mrLinePolygon,
1867 mrViewState,
1868 aLocalState );
1869 // underlines/strikethrough - border
1870 mrCanvas->strokePolyPolygon( mrLinePolygon,
1871 mrViewState,
1872 rRenderState,
1873 aStrokeAttributes );
1875 return true;
1878 private:
1879 const uno::Sequence< double > maFillColor;
1880 double mnOutlineWidth;
1881 const uno::Reference< rendering::XCanvas >& mrCanvas;
1882 const uno::Reference< rendering::XPolyPolygon2D >& mrTextPolygon;
1883 const uno::Reference< rendering::XPolyPolygon2D >& mrLinePolygon;
1884 const rendering::ViewState& mrViewState;
1886 #endif
1888 bool OutlineAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1889 const Subset& rSubset ) const
1891 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::OutlineAction::renderSubset()" );
1892 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::OutlineAction: 0x" << std::hex << this );
1894 if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
1895 return true; // empty range, render nothing
1897 #if 1
1898 // TODO(F3): Subsetting NYI for outline text!
1899 return render( rTransformation );
1900 #else
1901 const rendering::StringContext rOrigContext( mxTextLayout->getText() );
1903 if( rSubset.mnSubsetBegin == 0 &&
1904 rSubset.mnSubsetEnd == rOrigContext.Length )
1906 // full range, no need for subsetting
1907 return render( rTransformation );
1910 rendering::RenderState aLocalState( maState );
1911 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1914 // create and setup local Text polygon
1915 // ===================================
1917 uno::Reference< rendering::XPolyPolygon2D > xTextPolygon();
1919 // TODO(P3): Provide an API method for that!
1921 if( !xTextLayout.is() )
1922 return false;
1924 // render everything
1925 // =================
1927 return renderEffectText(
1928 OutlineTextArrayRenderHelper(
1929 xCanvas,
1930 mnOutlineWidth,
1931 xTextLayout,
1932 xTextLines,
1933 rViewState ),
1934 aLocalState,
1935 rViewState,
1936 xCanvas,
1937 maShadowColor,
1938 maShadowOffset,
1939 maReliefColor,
1940 maReliefOffset );
1941 #endif
1944 ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1946 rendering::RenderState aLocalState( maState );
1947 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1949 return calcEffectTextBounds( maOutlineBounds,
1950 ::basegfx::B2DRange( 0,0,
1951 maLinesOverallSize.getX(),
1952 maLinesOverallSize.getY() ),
1953 maReliefOffset,
1954 maShadowOffset,
1955 aLocalState,
1956 mpCanvas->getViewState() );
1959 ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1960 const Subset& /*rSubset*/ ) const
1962 SAL_WARN( "cppcanvas.emf", "OutlineAction::getBounds(): Subset not yet supported by this object" );
1964 return getBounds( rTransformation );
1967 sal_Int32 OutlineAction::getActionCount() const
1969 // TODO(F3): Subsetting NYI for outline text!
1970 return maOffsets.getLength();
1974 // Action factory methods
1977 /** Create an outline action
1979 This method extracts the polygonal outline from the
1980 text, and creates a properly setup OutlineAction from
1983 std::shared_ptr<Action> createOutline( const ::basegfx::B2DPoint& rStartPoint,
1984 const ::basegfx::B2DSize& rReliefOffset,
1985 const ::Color& rReliefColor,
1986 const ::basegfx::B2DSize& rShadowOffset,
1987 const ::Color& rShadowColor,
1988 const OUString& rText,
1989 sal_Int32 nStartPos,
1990 sal_Int32 nLen,
1991 const ::tools::Long* pDXArray,
1992 VirtualDevice& rVDev,
1993 const CanvasSharedPtr& rCanvas,
1994 const OutDevState& rState,
1995 const Renderer::Parameters& rParms )
1997 // operate on raw DX array here (in logical coordinate
1998 // system), to have a higher resolution
1999 // PolyPolygon. That polygon is then converted to
2000 // device coordinate system.
2002 // #i68512# Temporarily switch off font rotation
2003 // (which is already contained in the render state
2004 // transformation matrix - otherwise, glyph polygons
2005 // will be rotated twice)
2006 const vcl::Font aOrigFont( rVDev.GetFont() );
2007 vcl::Font aUnrotatedFont( aOrigFont );
2008 aUnrotatedFont.SetOrientation(Degree10(0));
2009 rVDev.SetFont( aUnrotatedFont );
2011 // TODO(F3): Don't understand parameter semantics of
2012 // GetTextOutlines()
2013 ::basegfx::B2DPolyPolygon aResultingPolyPolygon;
2014 PolyPolyVector aVCLPolyPolyVector;
2015 const bool bHaveOutlines( rVDev.GetTextOutlines( aVCLPolyPolyVector, rText,
2016 static_cast<sal_uInt16>(nStartPos),
2017 static_cast<sal_uInt16>(nStartPos),
2018 static_cast<sal_uInt16>(nLen),
2019 0, pDXArray ) );
2020 rVDev.SetFont(aOrigFont);
2022 if( !bHaveOutlines )
2023 return std::shared_ptr<Action>();
2025 // remove offsetting from mapmode transformation
2026 // (outline polygons must stay at origin, only need to
2027 // be scaled)
2028 ::basegfx::B2DHomMatrix aMapModeTransform(
2029 rState.mapModeTransform );
2030 aMapModeTransform.set(0,2, 0.0);
2031 aMapModeTransform.set(1,2, 0.0);
2033 for( const auto& rVCLPolyPolygon : aVCLPolyPolyVector )
2035 ::basegfx::B2DPolyPolygon aPolyPolygon = rVCLPolyPolygon.getB2DPolyPolygon();
2036 aPolyPolygon.transform( aMapModeTransform );
2038 // append result to collecting polypoly
2039 for( sal_uInt32 i=0; i<aPolyPolygon.count(); ++i )
2041 // #i47795# Ensure closed polygons (since
2042 // FreeType returns the glyph outlines
2043 // open)
2044 const ::basegfx::B2DPolygon& rPoly( aPolyPolygon.getB2DPolygon( i ) );
2045 const sal_uInt32 nCount( rPoly.count() );
2046 if( nCount<3 ||
2047 rPoly.isClosed() )
2049 // polygon either degenerate, or
2050 // already closed.
2051 aResultingPolyPolygon.append( rPoly );
2053 else
2055 ::basegfx::B2DPolygon aPoly(rPoly);
2056 aPoly.setClosed(true);
2058 aResultingPolyPolygon.append( aPoly );
2063 const uno::Sequence< double > aCharWidthSeq(
2064 pDXArray ?
2065 setupDXArray( pDXArray, nLen, rState ) :
2066 setupDXArray( rText,
2067 nStartPos,
2068 nLen,
2069 rVDev,
2070 rState ));
2071 const uno::Reference< rendering::XPolyPolygon2D > xTextPoly(
2072 ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
2073 rCanvas->getUNOCanvas()->getDevice(),
2074 aResultingPolyPolygon ) );
2076 if( rParms.maTextTransformation )
2078 return std::make_shared<OutlineAction>(
2079 rStartPoint,
2080 rReliefOffset,
2081 rReliefColor,
2082 rShadowOffset,
2083 rShadowColor,
2084 ::basegfx::utils::getRange(aResultingPolyPolygon),
2085 xTextPoly,
2086 aCharWidthSeq,
2087 rVDev,
2088 rCanvas,
2089 rState,
2090 *rParms.maTextTransformation );
2092 else
2094 return std::make_shared<OutlineAction>(
2095 rStartPoint,
2096 rReliefOffset,
2097 rReliefColor,
2098 rShadowOffset,
2099 rShadowColor,
2100 ::basegfx::utils::getRange(aResultingPolyPolygon),
2101 xTextPoly,
2102 aCharWidthSeq,
2103 rVDev,
2104 rCanvas,
2105 rState );
2109 } // namespace
2112 std::shared_ptr<Action> TextActionFactory::createTextAction( const ::Point& rStartPoint,
2113 const ::Size& rReliefOffset,
2114 const ::Color& rReliefColor,
2115 const ::Size& rShadowOffset,
2116 const ::Color& rShadowColor,
2117 const ::Color& rTextFillColor,
2118 const OUString& rText,
2119 sal_Int32 nStartPos,
2120 sal_Int32 nLen,
2121 const ::tools::Long* pDXArray,
2122 VirtualDevice& rVDev,
2123 const CanvasSharedPtr& rCanvas,
2124 const OutDevState& rState,
2125 const Renderer::Parameters& rParms,
2126 bool bSubsettable )
2128 const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2129 rVDev ) );
2130 // #143885# maintain (nearly) full precision positioning,
2131 // by circumventing integer-based OutDev-mapping
2132 const ::basegfx::B2DPoint aStartPoint(
2133 rState.mapModeTransform *
2134 ::basegfx::B2DPoint(rStartPoint.X() + aBaselineOffset.Width(),
2135 rStartPoint.Y() + aBaselineOffset.Height()) );
2137 const ::basegfx::B2DSize aReliefOffset(
2138 rState.mapModeTransform * vcl::unotools::b2DSizeFromSize( rReliefOffset ) );
2139 const ::basegfx::B2DSize aShadowOffset(
2140 rState.mapModeTransform * vcl::unotools::b2DSizeFromSize( rShadowOffset ) );
2142 if( rState.isTextOutlineModeSet )
2144 return createOutline(
2145 aStartPoint,
2146 aReliefOffset,
2147 rReliefColor,
2148 aShadowOffset,
2149 rShadowColor,
2150 rText,
2151 nStartPos,
2152 nLen,
2153 pDXArray,
2154 rVDev,
2155 rCanvas,
2156 rState,
2157 rParms );
2160 // convert DX array to device coordinate system (and
2161 // create it in the first place, if pDXArray is NULL)
2162 const uno::Sequence< double > aCharWidths(
2163 pDXArray ?
2164 setupDXArray( pDXArray, nLen, rState ) :
2165 setupDXArray( rText,
2166 nStartPos,
2167 nLen,
2168 rVDev,
2169 rState ));
2171 // determine type of text action to create
2172 // =======================================
2174 const ::Color aEmptyColor( COL_AUTO );
2176 std::shared_ptr<Action> ret;
2178 // no DX array, and no need to subset - no need to store
2179 // DX array, then.
2180 if( !pDXArray && !bSubsettable )
2182 // effects, or not?
2183 if( !rState.textOverlineStyle &&
2184 !rState.textUnderlineStyle &&
2185 !rState.textStrikeoutStyle &&
2186 rReliefColor == aEmptyColor &&
2187 rShadowColor == aEmptyColor &&
2188 rTextFillColor == aEmptyColor )
2190 // nope
2191 if( rParms.maTextTransformation )
2193 ret = std::make_shared<TextAction>(
2194 aStartPoint,
2195 rText,
2196 nStartPos,
2197 nLen,
2198 rCanvas,
2199 rState,
2200 *rParms.maTextTransformation );
2202 else
2204 ret = std::make_shared<TextAction>(
2205 aStartPoint,
2206 rText,
2207 nStartPos,
2208 nLen,
2209 rCanvas,
2210 rState );
2213 else
2215 // at least one of the effects requested
2216 if( rParms.maTextTransformation )
2217 ret = std::make_shared<EffectTextAction>(
2218 aStartPoint,
2219 aReliefOffset,
2220 rReliefColor,
2221 aShadowOffset,
2222 rShadowColor,
2223 rTextFillColor,
2224 rText,
2225 nStartPos,
2226 nLen,
2227 rVDev,
2228 rCanvas,
2229 rState,
2230 *rParms.maTextTransformation );
2231 else
2232 ret = std::make_shared<EffectTextAction>(
2233 aStartPoint,
2234 aReliefOffset,
2235 rReliefColor,
2236 aShadowOffset,
2237 rShadowColor,
2238 rTextFillColor,
2239 rText,
2240 nStartPos,
2241 nLen,
2242 rVDev,
2243 rCanvas,
2244 rState );
2247 else
2249 // DX array necessary - any effects?
2250 if( !rState.textOverlineStyle &&
2251 !rState.textUnderlineStyle &&
2252 !rState.textStrikeoutStyle &&
2253 rReliefColor == aEmptyColor &&
2254 rShadowColor == aEmptyColor &&
2255 rTextFillColor == aEmptyColor )
2257 // nope
2258 if( rParms.maTextTransformation )
2259 ret = std::make_shared<TextArrayAction>(
2260 aStartPoint,
2261 rText,
2262 nStartPos,
2263 nLen,
2264 aCharWidths,
2265 rCanvas,
2266 rState,
2267 *rParms.maTextTransformation );
2268 else
2269 ret = std::make_shared<TextArrayAction>(
2270 aStartPoint,
2271 rText,
2272 nStartPos,
2273 nLen,
2274 aCharWidths,
2275 rCanvas,
2276 rState );
2278 else
2280 // at least one of the effects requested
2281 if( rParms.maTextTransformation )
2282 ret = std::make_shared<EffectTextArrayAction>(
2283 aStartPoint,
2284 aReliefOffset,
2285 rReliefColor,
2286 aShadowOffset,
2287 rShadowColor,
2288 rTextFillColor,
2289 rText,
2290 nStartPos,
2291 nLen,
2292 aCharWidths,
2293 rVDev,
2294 rCanvas,
2295 rState,
2296 *rParms.maTextTransformation );
2297 else
2298 ret = std::make_shared<EffectTextArrayAction>(
2299 aStartPoint,
2300 aReliefOffset,
2301 rReliefColor,
2302 aShadowOffset,
2303 rShadowColor,
2304 rTextFillColor,
2305 rText,
2306 nStartPos,
2307 nLen,
2308 aCharWidths,
2309 rVDev,
2310 rCanvas,
2311 rState );
2314 return ret;
2318 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */