tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / cppcanvas / source / mtfrenderer / textaction.cxx
blob120f8b48f0bb4bfc2008ed98fb9aead1ade25361
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 <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>
36 #include <utility>
37 #include <vcl/canvastools.hxx>
38 #include <vcl/virdev.hxx>
40 #include <basegfx/utils/canvastools.hxx>
41 #include <canvas/canvastools.hxx>
42 #include <memory>
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
55 namespace
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
65 // by rStartPoint)
66 // #i53964# Also take VCL font rotation into account,
67 // since this, opposed to the FontMatrix rotation
68 // elsewhere, _does_ get incorporated into the render
69 // state transform.
70 tools::modifyClip( o_rRenderState,
71 rState,
72 rCanvas,
73 rStartPoint,
74 nullptr,
75 &rState.fontRotation );
77 basegfx::B2DHomMatrix aLocalTransformation(basegfx::utils::createRotateB2DHomMatrix(rState.fontRotation));
78 aLocalTransformation.translate( rStartPoint.getX(),
79 rStartPoint.getY() );
80 ::canvas::tools::appendToRenderState( o_rRenderState,
81 aLocalTransformation );
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,
102 rTextTransform );
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
114 if( !o_rFont.is() )
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(
123 aFontRequest,
124 uno::Sequence< beans::PropertyValue >(),
125 aFontMatrix );
128 init( o_rRenderState,
129 rStartPoint,
130 rState,
131 rCanvas );
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,
151 rTextTransform );
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,
162 sal_Int32 nLen,
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
171 // OutDev-mapping
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,
183 sal_Int32 nStartPos,
184 sal_Int32 nLen,
185 VirtualDevice const & rVDev,
186 const OutDevState& rState )
188 // no external DX array given, create one from given
189 // string
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 );
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 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 );
245 if( pTextTransform )
246 init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas, *pTextTransform );
247 else
248 init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas );
250 o_rTextLayout = xFont->createTextLayout(
251 rendering::StringContext( rText, nStartPos, nLen ),
252 rState.textDirection,
253 0 );
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) ),
271 0 );
273 return (rState.mapModeTransform * aSize).getWidth();
276 uno::Sequence< double >
277 calcSubsetOffsets( rendering::RenderState& io_rRenderState,
278 double& o_rMinPos,
279 double& o_rMaxPos,
280 const uno::Reference< rendering::XTextLayout >& rOrigTextLayout,
281 double nLayoutWidth,
282 const ::cppcanvas::internal::Action::Subset& rSubset )
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
298 // iterator.
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
307 // iterator.
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 ---|
319 // |<- nOffset ->| |
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
332 if (nOffset > 0.0)
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);
340 else
342 // horizontal text -> offset in x direction
343 aTranslation.translate(nOffset, 0.0);
346 ::canvas::tools::appendToRenderState( io_rRenderState,
347 aTranslation );
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,
363 pAdaptedOffsets,
364 [nMinPos](double aPos) { return aPos - nMinPos; } );
366 o_rMinPos = nMinPos;
367 o_rMaxPos = nMaxPos;
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(
383 std::min(
384 rSubset.mnSubsetEnd - rSubset.mnSubsetBegin,
385 rOrigContext.Length ),
386 sal_Int32( 0 ) ) );
388 const rendering::StringContext aContext( rOrigContext.Text,
389 nNewStartPos,
390 nNewLength );
392 uno::Reference< rendering::XTextLayout > xTextLayout(
393 rOrigTextLayout->getFont()->createTextLayout( aContext,
394 rOrigTextLayout->getMainTextDirection(),
395 0 ),
396 uno::UNO_SET_THROW );
398 return xTextLayout;
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
415 state
417 @param rSubset
418 Subset to prepare
420 void createSubsetLayout( uno::Reference< rendering::XTextLayout >& io_rTextLayout,
421 double nLayoutWidth,
422 rendering::RenderState& io_rRenderState,
423 double& o_rMinPos,
424 double& o_rMaxPos,
425 const ::basegfx::B2DHomMatrix& rTransformation,
426 const Action::Subset& rSubset )
428 ::canvas::tools::prependToRenderState(io_rRenderState, rTransformation);
430 if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
432 // empty range, empty layout
433 io_rTextLayout.clear();
435 return;
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
447 return;
450 uno::Reference< rendering::XTextLayout > xTextLayout(
451 createSubsetLayout( aOrigContext, rSubset, io_rTextLayout ) );
453 if( xTextLayout.is() )
455 xTextLayout->applyLogicalAdvancements(
456 calcSubsetOffsets( io_rRenderState,
457 o_rMinPos,
458 o_rMaxPos,
459 io_rTextLayout,
460 nLayoutWidth,
461 rSubset ) );
462 uno::Sequence< sal_Bool > aOrigKashidaPositions(io_rTextLayout->queryKashidaPositions());
463 uno::Sequence< sal_Bool > aKashidaPositions(aOrigKashidaPositions.getArray() + rSubset.mnSubsetBegin,
464 rSubset.mnSubsetEnd - rSubset.mnSubsetBegin);
465 xTextLayout->applyKashidaPositions(aKashidaPositions);
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.
477 class TextRenderer
479 public:
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.
488 @param rRenderer
489 Functor object, will be called to render the actual
490 part of the text effect (the text itself and the means
491 to render it are unknown to this method)
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,
519 xColorSpace );
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,
537 xColorSpace );
539 rRenderer( aReliefState, rTextFillColor, false );
542 // draw normal text
543 rRenderer( rRenderState, rTextFillColor, true );
545 return 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 );
563 aTotalBounds.expand(
564 ::basegfx::B2DRange( aBounds.getMinX() + rReliefOffset.getWidth(),
565 aBounds.getMinY() + rReliefOffset.getHeight(),
566 aBounds.getMaxX() + rReliefOffset.getWidth(),
567 aBounds.getMaxY() + rReliefOffset.getHeight() ) );
568 aTotalBounds.expand(
569 ::basegfx::B2DRange( aBounds.getMinX() + rShadowOffset.getWidth(),
570 aBounds.getMinY() + rShadowOffset.getHeight(),
571 aBounds.getMaxX() + rShadowOffset.getWidth(),
572 aBounds.getMaxY() + rShadowOffset.getHeight() ) );
574 return tools::calcDevicePixelBounds( aTotalBounds,
575 rViewState,
576 rRenderState );
579 void initEffectLinePolyPolygon( ::basegfx::B2DSize& o_rOverallSize,
580 uno::Reference< rendering::XPolyPolygon2D >& o_rTextLines,
581 const CanvasSharedPtr& rCanvas,
582 double nLineWidth,
583 const tools::TextLineInfo& rLineInfo )
585 const ::basegfx::B2DPolyPolygon aPoly(
586 tools::createTextLinesPolyPolygon( 0.0, nLineWidth,
587 rLineInfo ) );
588 auto aRange = basegfx::utils::getRange( aPoly ).getRange();
589 o_rOverallSize = basegfx::B2DSize(aRange.getX(), aRange.getY());
591 o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
592 rCanvas->getUNOCanvas()->getDevice(),
593 aPoly );
597 class TextAction : public Action
599 public:
600 TextAction( const ::basegfx::B2DPoint& rStartPoint,
601 const OUString& rString,
602 sal_Int32 nStartPos,
603 sal_Int32 nLen,
604 const CanvasSharedPtr& rCanvas,
605 const OutDevState& rState );
607 TextAction( const ::basegfx::B2DPoint& rStartPoint,
608 const OUString& rString,
609 sal_Int32 nStartPos,
610 sal_Int32 nLen,
611 const CanvasSharedPtr& rCanvas,
612 const OutDevState& rState,
613 const ::basegfx::B2DHomMatrix& rTextTransform );
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;
628 private:
629 // TODO(P2): This is potentially a real mass object
630 // (every character might be a separate TextAction),
631 // thus, make it as lightweight as possible. For
632 // example, share common RenderState among several
633 // TextActions, maybe using maOffsets for the
634 // translation.
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,
645 sal_Int32 nStartPos,
646 sal_Int32 nLen,
647 const CanvasSharedPtr& rCanvas,
648 const OutDevState& rState ) :
649 mxFont( rState.xFont ),
650 maStringContext( rString, nStartPos, nLen ),
651 mpCanvas( rCanvas ),
652 maTextDirection( rState.textDirection )
654 init( maState, mxFont,
655 rStartPoint,
656 rState, rCanvas );
658 ENSURE_OR_THROW( mxFont.is(),
659 "::cppcanvas::internal::TextAction(): Invalid font" );
662 TextAction::TextAction( const ::basegfx::B2DPoint& rStartPoint,
663 const OUString& rString,
664 sal_Int32 nStartPos,
665 sal_Int32 nLen,
666 const CanvasSharedPtr& rCanvas,
667 const OutDevState& rState,
668 const ::basegfx::B2DHomMatrix& rTextTransform ) :
669 mxFont( rState.xFont ),
670 maStringContext( rString, nStartPos, nLen ),
671 mpCanvas( rCanvas ),
672 maTextDirection( rState.textDirection )
674 init( maState, mxFont,
675 rStartPoint,
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 );
693 return true;
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(
714 maStringContext,
715 maTextDirection,
716 0 ) );
718 rendering::RenderState aLocalState( maState );
719 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
721 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
722 xTextLayout->queryTextBounds() ),
723 mpCanvas->getViewState(),
724 aLocalState );
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
745 return 1;
749 class EffectTextAction :
750 public Action,
751 public TextRenderer
753 public:
754 EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
755 const ::basegfx::B2DSize& rReliefOffset,
756 const ::Color& rReliefColor,
757 const ::basegfx::B2DSize& rShadowOffset,
758 const ::Color& rShadowColor,
759 const ::Color& rTextFillColor,
760 const OUString& rText,
761 sal_Int32 nStartPos,
762 sal_Int32 nLen,
763 VirtualDevice const & rVDev,
764 const CanvasSharedPtr& rCanvas,
765 const OutDevState& rState );
767 EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
768 const ::basegfx::B2DSize& rReliefOffset,
769 const ::Color& rReliefColor,
770 const ::basegfx::B2DSize& rShadowOffset,
771 const ::Color& rShadowColor,
772 const ::Color& rTextFillColor,
773 const OUString& rText,
774 sal_Int32 nStartPos,
775 sal_Int32 nLen,
776 VirtualDevice const & rVDev,
777 const CanvasSharedPtr& rCanvas,
778 const OutDevState& rState,
779 const ::basegfx::B2DHomMatrix& rTextTransform );
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;
794 private:
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
806 // translation.
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,
830 sal_Int32 nStartPos,
831 sal_Int32 nLen,
832 VirtualDevice const & rVDev,
833 const CanvasSharedPtr& rCanvas,
834 const OutDevState& rState ) :
835 mxFont( rState.xFont ),
836 maStringContext( rText, nStartPos, nLen ),
837 mpCanvas( rCanvas ),
838 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
839 maReliefOffset( rReliefOffset ),
840 maReliefColor( rReliefColor ),
841 maShadowOffset( rShadowOffset ),
842 maShadowColor( rShadowColor ),
843 maTextFillColor( rTextFillColor ),
844 maTextDirection( rState.textDirection )
846 const double nLineWidth(getLineWidth( rVDev, rState, maStringContext ));
847 initEffectLinePolyPolygon( maLinesOverallSize,
848 mxTextLines,
849 rCanvas,
850 nLineWidth,
851 maTextLineInfo );
853 init( maState, mxFont,
854 rStartPoint,
855 rState, rCanvas );
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,
868 sal_Int32 nStartPos,
869 sal_Int32 nLen,
870 VirtualDevice const & rVDev,
871 const CanvasSharedPtr& rCanvas,
872 const OutDevState& rState,
873 const ::basegfx::B2DHomMatrix& rTextTransform ) :
874 mxFont( rState.xFont ),
875 maStringContext( rText, nStartPos, nLen ),
876 mpCanvas( rCanvas ),
877 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
878 maReliefOffset( rReliefOffset ),
879 maReliefColor( rReliefColor ),
880 maShadowOffset( rShadowOffset ),
881 maShadowColor( rShadowColor ),
882 maTextFillColor( rTextFillColor ),
883 maTextDirection( rState.textDirection )
885 const double nLineWidth( getLineWidth( rVDev, rState, maStringContext ) );
886 initEffectLinePolyPolygon( maLinesOverallSize,
887 mxTextLines,
888 rCanvas,
889 nLineWidth,
890 maTextLineInfo );
892 init( maState, mxFont,
893 rStartPoint,
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);
916 // under/over lines
917 aCanvas->fillPolyPolygon( mxTextLines,
918 aViewState,
919 rRenderState );
921 aCanvas->drawText( maStringContext, mxFont,
922 aViewState,
923 rRenderState,
924 maTextDirection );
926 return true;
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,
938 aLocalState,
939 mpCanvas->getUNOCanvas(),
940 maShadowColor,
941 maShadowOffset,
942 maReliefColor,
943 maReliefOffset,
944 maTextFillColor);
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
955 // subsettable text
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(
965 maStringContext,
966 maTextDirection,
967 0 ) );
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(
986 queryTextBounds() ),
987 ::basegfx::B2DRange( 0,0,
988 maLinesOverallSize.getWidth(),
989 maLinesOverallSize.getHeight() ),
990 maReliefOffset,
991 maShadowOffset,
992 aLocalState,
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
1013 // subsettable text
1014 return 1;
1018 class TextArrayAction : public Action
1020 public:
1021 TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1022 const OUString& rString,
1023 sal_Int32 nStartPos,
1024 sal_Int32 nLen,
1025 const uno::Sequence< double >& rOffsets,
1026 const uno::Sequence< sal_Bool >& rKashidas,
1027 const CanvasSharedPtr& rCanvas,
1028 const OutDevState& rState );
1030 TextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1031 const OUString& rString,
1032 sal_Int32 nStartPos,
1033 sal_Int32 nLen,
1034 const uno::Sequence< double >& rOffsets,
1035 const uno::Sequence< sal_Bool >& rKashidas,
1036 const CanvasSharedPtr& rCanvas,
1037 const OutDevState& rState,
1038 const ::basegfx::B2DHomMatrix& rTextTransform );
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 uno::Sequence< sal_Bool >& rKashidas,
1073 const CanvasSharedPtr& rCanvas,
1074 const OutDevState& rState ) :
1075 mpCanvas( rCanvas )
1077 initLayoutWidth(mnLayoutWidth, rOffsets);
1079 initArrayAction( maState,
1080 mxTextLayout,
1081 rStartPoint,
1082 rString,
1083 nStartPos,
1084 nLen,
1085 rOffsets,
1086 rKashidas,
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 uno::Sequence< sal_Bool >& rKashidas,
1097 const CanvasSharedPtr& rCanvas,
1098 const OutDevState& rState,
1099 const ::basegfx::B2DHomMatrix& rTextTransform ) :
1100 mpCanvas( rCanvas )
1102 initLayoutWidth(mnLayoutWidth, rOffsets);
1104 initArrayAction( maState,
1105 mxTextLayout,
1106 rStartPoint,
1107 rString,
1108 nStartPos,
1109 nLen,
1110 rOffsets,
1111 rKashidas,
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 aOrigContext( mxTextLayout->getText() );
1202 return aOrigContext.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 const uno::Sequence< sal_Bool >& rKashidas,
1222 VirtualDevice const & rVDev,
1223 const CanvasSharedPtr& rCanvas,
1224 const OutDevState& rState );
1225 EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1226 const ::basegfx::B2DSize& rReliefOffset,
1227 const ::Color& rReliefColor,
1228 const ::basegfx::B2DSize& rShadowOffset,
1229 const ::Color& rShadowColor,
1230 const ::Color& rTextFillColor,
1231 const OUString& rText,
1232 sal_Int32 nStartPos,
1233 sal_Int32 nLen,
1234 const uno::Sequence< double >& rOffsets,
1235 const uno::Sequence< sal_Bool >& rKashidas,
1236 VirtualDevice const & rVDev,
1237 const CanvasSharedPtr& rCanvas,
1238 const OutDevState& rState,
1239 const ::basegfx::B2DHomMatrix& rTextTransform );
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;
1254 private:
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
1265 // translation.
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,
1288 sal_Int32 nLen,
1289 const uno::Sequence< double >& rOffsets,
1290 const uno::Sequence< sal_Bool >& rKashidas,
1291 VirtualDevice const & rVDev,
1292 const CanvasSharedPtr& rCanvas,
1293 const OutDevState& rState ) :
1294 mpCanvas( rCanvas ),
1295 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1296 maTextLinesHelper(mpCanvas, rState),
1297 maReliefOffset( rReliefOffset ),
1298 maReliefColor( rReliefColor ),
1299 maShadowOffset( rShadowOffset ),
1300 maShadowColor( rShadowColor ),
1301 maTextFillColor( rTextFillColor )
1303 initLayoutWidth(mnLayoutWidth, rOffsets);
1305 maTextLinesHelper.init(mnLayoutWidth, maTextLineInfo);
1307 initArrayAction( maState,
1308 mxTextLayout,
1309 rStartPoint,
1310 rText,
1311 nStartPos,
1312 nLen,
1313 rOffsets,
1314 rKashidas,
1315 rCanvas,
1316 rState, nullptr );
1319 EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint& rStartPoint,
1320 const ::basegfx::B2DSize& rReliefOffset,
1321 const ::Color& rReliefColor,
1322 const ::basegfx::B2DSize& rShadowOffset,
1323 const ::Color& rShadowColor,
1324 const ::Color& rTextFillColor,
1325 const OUString& rText,
1326 sal_Int32 nStartPos,
1327 sal_Int32 nLen,
1328 const uno::Sequence< double >& rOffsets,
1329 const uno::Sequence< sal_Bool >& rKashidas,
1330 VirtualDevice const & rVDev,
1331 const CanvasSharedPtr& rCanvas,
1332 const OutDevState& rState,
1333 const ::basegfx::B2DHomMatrix& rTextTransform ) :
1334 mpCanvas( rCanvas ),
1335 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1336 maTextLinesHelper(mpCanvas, rState),
1337 maReliefOffset( rReliefOffset ),
1338 maReliefColor( rReliefColor ),
1339 maShadowOffset( rShadowOffset ),
1340 maShadowColor( rShadowColor ),
1341 maTextFillColor( rTextFillColor )
1343 initLayoutWidth(mnLayoutWidth, rOffsets);
1345 maTextLinesHelper.init(mnLayoutWidth, maTextLineInfo);
1347 initArrayAction( maState,
1348 mxTextLayout,
1349 rStartPoint,
1350 rText,
1351 nStartPos,
1352 nLen,
1353 rOffsets,
1354 rKashidas,
1355 rCanvas,
1356 rState,
1357 &rTextTransform );
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);
1384 // under/over lines
1385 maTextLinesHelper.render(rRenderState, bNormalText);
1387 aCanvas->drawTextLayout( mxTextLayout,
1388 aViewState,
1389 rRenderState );
1391 return true;
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,
1403 aLocalState,
1404 mpCanvas->getUNOCanvas(),
1405 maShadowColor,
1406 maShadowOffset,
1407 maReliefColor,
1408 maReliefOffset,
1409 maTextFillColor);
1412 class EffectTextArrayRenderHelper : public TextRenderer
1414 public:
1415 EffectTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas,
1416 const uno::Reference< rendering::XTextLayout >& rTextLayout,
1417 const TextLinesHelper& rTextLinesHelper,
1418 const rendering::ViewState& rViewState ) :
1419 mrCanvas( rCanvas ),
1420 mrTextLayout( rTextLayout ),
1421 mrTextLinesHelper( rTextLinesHelper ),
1422 mrViewState( rViewState )
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,
1443 mrViewState,
1444 rRenderState );
1446 return true;
1449 private:
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,
1479 mnLayoutWidth,
1480 aLocalState,
1481 nMinPos,
1482 nMaxPos,
1483 rTransformation,
1484 rSubset );
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,
1505 xTextLayout,
1506 aHelper,
1507 aViewState ),
1508 aLocalState,
1509 xCanvas,
1510 maShadowColor,
1511 maShadowOffset,
1512 maReliefColor,
1513 maReliefOffset,
1514 maTextFillColor);
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,
1527 aSize.getWidth(),
1528 aSize.getHeight()),
1529 maReliefOffset,
1530 maShadowOffset,
1531 aLocalState,
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,
1549 mnLayoutWidth,
1550 aLocalState,
1551 nMinPos,
1552 nMaxPos,
1553 rTransformation,
1554 rSubset );
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,
1566 maTextLineInfo ) );
1568 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1569 xTextLayout->queryTextBounds() ),
1570 ::basegfx::utils::getRange( aPoly ),
1571 maReliefOffset,
1572 maShadowOffset,
1573 aLocalState,
1574 mpCanvas->getViewState() );
1577 sal_Int32 EffectTextArrayAction::getActionCount() const
1579 const rendering::StringContext aOrigContext( mxTextLayout->getText() );
1581 return aOrigContext.Length;
1585 class OutlineAction :
1586 public Action,
1587 public TextRenderer
1589 public:
1590 OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1591 const ::basegfx::B2DSize& rReliefOffset,
1592 const ::Color& rReliefColor,
1593 const ::basegfx::B2DSize& rShadowOffset,
1594 const ::Color& rShadowColor,
1595 const ::Color& rFillColor,
1596 uno::Reference< rendering::XPolyPolygon2D > xFillPoly,
1597 const ::basegfx::B2DRectangle& rOutlineBounds,
1598 uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
1599 const uno::Sequence< double >& rOffsets,
1600 VirtualDevice const & rVDev,
1601 const CanvasSharedPtr& rCanvas,
1602 const OutDevState& rState );
1603 OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1604 const ::basegfx::B2DSize& rReliefOffset,
1605 const ::Color& rReliefColor,
1606 const ::basegfx::B2DSize& rShadowOffset,
1607 const ::Color& rShadowColor,
1608 const ::Color& rFillColor,
1609 uno::Reference< rendering::XPolyPolygon2D > xFillPoly,
1610 const ::basegfx::B2DRectangle& rOutlineBounds,
1611 uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
1612 const uno::Sequence< double >& rOffsets,
1613 VirtualDevice const & rVDev,
1614 const CanvasSharedPtr& rCanvas,
1615 const OutDevState& rState,
1616 const ::basegfx::B2DHomMatrix& rTextTransform );
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;
1631 private:
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
1640 // translation.
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) ),
1691 maFillColor(
1692 vcl::unotools::colorToDoubleSequence(
1693 COL_WHITE,
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,
1709 mxTextLines,
1710 rCanvas,
1711 nLayoutWidth,
1712 maTextLineInfo );
1714 init( maState,
1715 rStartPoint,
1716 rState,
1717 rCanvas );
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) ),
1738 maFillColor(
1739 vcl::unotools::colorToDoubleSequence(
1740 COL_WHITE,
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,
1755 mxTextLines,
1756 rCanvas,
1757 nLayoutWidth,
1758 maTextLineInfo );
1760 init( maState,
1761 rStartPoint,
1762 rState,
1763 rCanvas,
1764 rTextTransform );
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,
1795 aViewState,
1796 aLocalState );
1798 // border line of text
1799 xCanvas->strokePolyPolygon( mxTextPoly,
1800 aViewState,
1801 rRenderState,
1802 aStrokeAttributes );
1804 // underlines/strikethrough - background
1805 xCanvas->fillPolyPolygon( mxTextLines,
1806 aViewState,
1807 aLocalState );
1808 // underlines/strikethrough - border
1809 xCanvas->strokePolyPolygon( mxTextLines,
1810 aViewState,
1811 rRenderState,
1812 aStrokeAttributes );
1814 return true;
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,
1826 aLocalState,
1827 mpCanvas->getUNOCanvas(),
1828 maShadowColor,
1829 maShadowOffset,
1830 maReliefColor,
1831 maReliefOffset,
1832 maTextFillColor);
1835 #if 0 // see #if'ed out use in OutlineAction::renderSubset below:
1836 class OutlineTextArrayRenderHelper : public TextRenderer
1838 public:
1839 OutlineTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas,
1840 const uno::Reference< rendering::XPolyPolygon2D >& rTextPolygon,
1841 const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon,
1842 const rendering::ViewState& rViewState,
1843 double nOutlineWidth ) :
1844 maFillColor(
1845 vcl::unotools::colorToDoubleSequence(
1846 ::COL_WHITE,
1847 rCanvas->getDevice()->getDeviceColorSpace() )),
1848 mnOutlineWidth( nOutlineWidth ),
1849 mrCanvas( rCanvas ),
1850 mrTextPolygon( rTextPolygon ),
1851 mrLinePolygon( rLinePolygon ),
1852 mrViewState( rViewState )
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,
1874 mrViewState,
1875 aLocalState );
1877 // border line of text
1878 mrCanvas->strokePolyPolygon( mrTextPolygon,
1879 mrViewState,
1880 rRenderState,
1881 aStrokeAttributes );
1883 // underlines/strikethrough - background
1884 mrCanvas->fillPolyPolygon( mrLinePolygon,
1885 mrViewState,
1886 aLocalState );
1887 // underlines/strikethrough - border
1888 mrCanvas->strokePolyPolygon( mrLinePolygon,
1889 mrViewState,
1890 rRenderState,
1891 aStrokeAttributes );
1893 return true;
1896 private:
1897 const uno::Sequence< double > maFillColor;
1898 double mnOutlineWidth;
1899 const uno::Reference< rendering::XCanvas >& mrCanvas;
1900 const uno::Reference< rendering::XPolyPolygon2D >& mrTextPolygon;
1901 const uno::Reference< rendering::XPolyPolygon2D >& mrLinePolygon;
1902 const rendering::ViewState& mrViewState;
1904 #endif
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
1915 #if 1
1916 // TODO(F3): Subsetting NYI for outline text!
1917 return render( rTransformation );
1918 #else
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() )
1940 return false;
1942 // render everything
1943 // =================
1945 return renderEffectText(
1946 OutlineTextArrayRenderHelper(
1947 xCanvas,
1948 mnOutlineWidth,
1949 xTextLayout,
1950 xTextLines,
1951 rViewState ),
1952 aLocalState,
1953 rViewState,
1954 xCanvas,
1955 maShadowColor,
1956 maShadowOffset,
1957 maReliefColor,
1958 maReliefOffset );
1959 #endif
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()),
1971 maReliefOffset,
1972 maShadowOffset,
1973 aLocalState,
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,
2009 sal_Int32 nLen,
2010 KernArraySpan pDXArray,
2011 std::span<const sal_Bool> pKashidaArray,
2012 VirtualDevice& rVDev,
2013 const CanvasSharedPtr& rCanvas,
2014 const OutDevState& rState,
2015 const Renderer::Parameters& rParms )
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
2047 // be scaled)
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
2063 // open)
2064 const ::basegfx::B2DPolygon& rPoly( aPolyPolygon.getB2DPolygon( i ) );
2065 const sal_uInt32 nCount( rPoly.count() );
2066 if( nCount<3 ||
2067 rPoly.isClosed() )
2069 // polygon either degenerate, or
2070 // already closed.
2071 aResultingPolyPolygon.append( rPoly );
2073 else
2075 ::basegfx::B2DPolygon aPoly(rPoly);
2076 aPoly.setClosed(true);
2078 aResultingPolyPolygon.append( aPoly );
2083 const uno::Sequence< double > aCharWidthSeq(
2084 !pDXArray.empty() ?
2085 setupDXArray( pDXArray, nLen, rState ) :
2086 setupDXArray( rText,
2087 nStartPos,
2088 nLen,
2089 rVDev,
2090 rState ));
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(
2103 aStringContext,
2104 rState.textDirection,
2105 0 ) );
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(),
2112 aTextBoundsPoly);
2115 if( rParms.maTextTransformation )
2117 return std::make_shared<OutlineAction>(
2118 rStartPoint,
2119 rReliefOffset,
2120 rReliefColor,
2121 rShadowOffset,
2122 rShadowColor,
2123 rTextFillColor,
2124 xTextBoundsPoly,
2125 ::basegfx::utils::getRange(aResultingPolyPolygon),
2126 xTextPoly,
2127 aCharWidthSeq,
2128 rVDev,
2129 rCanvas,
2130 rState,
2131 *rParms.maTextTransformation );
2133 else
2135 return std::make_shared<OutlineAction>(
2136 rStartPoint,
2137 rReliefOffset,
2138 rReliefColor,
2139 rShadowOffset,
2140 rShadowColor,
2141 rTextFillColor,
2142 xTextBoundsPoly,
2143 ::basegfx::utils::getRange(aResultingPolyPolygon),
2144 xTextPoly,
2145 aCharWidthSeq,
2146 rVDev,
2147 rCanvas,
2148 rState );
2152 } // namespace
2155 std::shared_ptr<Action> TextActionFactory::createTextAction( const ::Point& rStartPoint,
2156 const ::Size& rReliefOffset,
2157 const ::Color& rReliefColor,
2158 const ::Size& rShadowOffset,
2159 const ::Color& rShadowColor,
2160 const ::Color& rTextFillColor,
2161 const OUString& rText,
2162 sal_Int32 nStartPos,
2163 sal_Int32 nLen,
2164 KernArraySpan pDXArray,
2165 std::span<const sal_Bool> pKashidaArray,
2166 VirtualDevice& rVDev,
2167 const CanvasSharedPtr& rCanvas,
2168 const OutDevState& rState,
2169 const Renderer::Parameters& rParms,
2170 bool bSubsettable )
2172 const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2173 rVDev ) );
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(
2189 aStartPoint,
2190 aReliefOffset,
2191 rReliefColor,
2192 aShadowOffset,
2193 rShadowColor,
2194 rTextFillColor,
2195 rText,
2196 nStartPos,
2197 nLen,
2198 pDXArray,
2199 pKashidaArray,
2200 rVDev,
2201 rCanvas,
2202 rState,
2203 rParms );
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(
2209 !pDXArray.empty() ?
2210 setupDXArray( pDXArray, nLen, rState ) :
2211 setupDXArray( rText,
2212 nStartPos,
2213 nLen,
2214 rVDev,
2215 rState ));
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
2227 // DX array, then.
2228 if( pDXArray.empty() && !bSubsettable )
2230 // effects, or not?
2231 if( !rState.textOverlineStyle &&
2232 !rState.textUnderlineStyle &&
2233 !rState.textStrikeoutStyle &&
2234 rReliefColor == aEmptyColor &&
2235 rShadowColor == aEmptyColor &&
2236 rTextFillColor == aEmptyColor )
2238 // nope
2239 if( rParms.maTextTransformation )
2241 ret = std::make_shared<TextAction>(
2242 aStartPoint,
2243 rText,
2244 nStartPos,
2245 nLen,
2246 rCanvas,
2247 rState,
2248 *rParms.maTextTransformation );
2250 else
2252 ret = std::make_shared<TextAction>(
2253 aStartPoint,
2254 rText,
2255 nStartPos,
2256 nLen,
2257 rCanvas,
2258 rState );
2261 else
2263 // at least one of the effects requested
2264 if( rParms.maTextTransformation )
2265 ret = std::make_shared<EffectTextAction>(
2266 aStartPoint,
2267 aReliefOffset,
2268 rReliefColor,
2269 aShadowOffset,
2270 rShadowColor,
2271 rTextFillColor,
2272 rText,
2273 nStartPos,
2274 nLen,
2275 rVDev,
2276 rCanvas,
2277 rState,
2278 *rParms.maTextTransformation );
2279 else
2280 ret = std::make_shared<EffectTextAction>(
2281 aStartPoint,
2282 aReliefOffset,
2283 rReliefColor,
2284 aShadowOffset,
2285 rShadowColor,
2286 rTextFillColor,
2287 rText,
2288 nStartPos,
2289 nLen,
2290 rVDev,
2291 rCanvas,
2292 rState );
2295 else
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 )
2305 // nope
2306 if( rParms.maTextTransformation )
2307 ret = std::make_shared<TextArrayAction>(
2308 aStartPoint,
2309 rText,
2310 nStartPos,
2311 nLen,
2312 aCharWidths,
2313 aKashidas,
2314 rCanvas,
2315 rState,
2316 *rParms.maTextTransformation );
2317 else
2318 ret = std::make_shared<TextArrayAction>(
2319 aStartPoint,
2320 rText,
2321 nStartPos,
2322 nLen,
2323 aCharWidths,
2324 aKashidas,
2325 rCanvas,
2326 rState );
2328 else
2330 // at least one of the effects requested
2331 if( rParms.maTextTransformation )
2332 ret = std::make_shared<EffectTextArrayAction>(
2333 aStartPoint,
2334 aReliefOffset,
2335 rReliefColor,
2336 aShadowOffset,
2337 rShadowColor,
2338 rTextFillColor,
2339 rText,
2340 nStartPos,
2341 nLen,
2342 aCharWidths,
2343 aKashidas,
2344 rVDev,
2345 rCanvas,
2346 rState,
2347 *rParms.maTextTransformation );
2348 else
2349 ret = std::make_shared<EffectTextArrayAction>(
2350 aStartPoint,
2351 aReliefOffset,
2352 rReliefColor,
2353 aShadowOffset,
2354 rShadowColor,
2355 rTextFillColor,
2356 rText,
2357 nStartPos,
2358 nLen,
2359 aCharWidths,
2360 aKashidas,
2361 rVDev,
2362 rCanvas,
2363 rState );
2366 return ret;
2370 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */