android: Update app-specific/MIME type icons
[LibreOffice.git] / cppcanvas / source / mtfrenderer / textaction.cxx
blobfb75f661fcadda646c677ad53c78f8ce875af51a
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& rOrigContext( io_rTextLayout->getText() );
443 if( rSubset.mnSubsetBegin == 0 &&
444 rSubset.mnSubsetEnd == rOrigContext.Length )
446 // full range, no need for subsetting
447 return;
450 uno::Reference< rendering::XTextLayout > xTextLayout(
451 createSubsetLayout( rOrigContext, 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 = 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& rViewState( mpCanvas->getViewState() );
903 const uno::Reference< rendering::XCanvas >& rCanvas( 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, rCanvas->getDevice()->getDeviceColorSpace());
911 auto xTextBounds = queryTextBounds(rCanvas);
912 // background of text
913 rCanvas->fillPolyPolygon(xTextBounds, rViewState, aLocalState);
916 // under/over lines
917 rCanvas->fillPolyPolygon( mxTextLines,
918 rViewState,
919 rRenderState );
921 rCanvas->drawText( maStringContext, mxFont,
922 rViewState,
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& 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 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& rViewState( mpCanvas->getViewState() );
1371 const uno::Reference< rendering::XCanvas >& rCanvas( 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, rCanvas->getDevice()->getDeviceColorSpace());
1379 auto xTextBounds = queryTextBounds(rCanvas);
1380 // background of text
1381 rCanvas->fillPolyPolygon(xTextBounds, rViewState, aLocalState);
1384 // under/over lines
1385 maTextLinesHelper.render(rRenderState, bNormalText);
1387 rCanvas->drawTextLayout( mxTextLayout,
1388 rViewState,
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& rViewState( 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 rViewState ),
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& rOrigContext( mxTextLayout->getText() );
1581 return rOrigContext.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 ::basegfx::B2DRectangle& rOutlineBounds,
1596 uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
1597 const uno::Sequence< double >& rOffsets,
1598 VirtualDevice const & rVDev,
1599 const CanvasSharedPtr& rCanvas,
1600 const OutDevState& rState );
1601 OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1602 const ::basegfx::B2DSize& rReliefOffset,
1603 const ::Color& rReliefColor,
1604 const ::basegfx::B2DSize& rShadowOffset,
1605 const ::Color& rShadowColor,
1606 const ::basegfx::B2DRectangle& rOutlineBounds,
1607 uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
1608 const uno::Sequence< double >& rOffsets,
1609 VirtualDevice const & rVDev,
1610 const CanvasSharedPtr& rCanvas,
1611 const OutDevState& rState,
1612 const ::basegfx::B2DHomMatrix& rTextTransform );
1614 OutlineAction(const OutlineAction&) = delete;
1615 const OutlineAction& operator=(const OutlineAction&) = delete;
1617 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1618 virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1619 const Subset& rSubset ) const override;
1621 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
1622 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1623 const Subset& rSubset ) const override;
1625 virtual sal_Int32 getActionCount() const override;
1627 private:
1628 // TextRenderer interface
1629 virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool bNormalText ) const override;
1631 // TODO(P2): This is potentially a real mass object
1632 // (every character might be a separate TextAction),
1633 // thus, make it as lightweight as possible. For
1634 // example, share common RenderState among several
1635 // TextActions, maybe using maOffsets for the
1636 // translation.
1638 uno::Reference< rendering::XPolyPolygon2D > mxTextPoly;
1640 const uno::Sequence< double > maOffsets;
1641 const CanvasSharedPtr mpCanvas;
1642 rendering::RenderState maState;
1643 double mnOutlineWidth;
1644 const uno::Sequence< double > maFillColor;
1645 const tools::TextLineInfo maTextLineInfo;
1646 ::basegfx::B2DSize maLinesOverallSize;
1647 const ::basegfx::B2DRectangle maOutlineBounds;
1648 uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
1649 const ::basegfx::B2DSize maReliefOffset;
1650 const ::Color maReliefColor;
1651 const ::basegfx::B2DSize maShadowOffset;
1652 const ::Color maShadowColor;
1653 const ::Color maTextFillColor;
1656 double calcOutlineWidth( const OutDevState& rState,
1657 VirtualDevice const & rVDev )
1659 const ::basegfx::B2DSize aFontSize( 0,
1660 rVDev.GetFont().GetFontHeight() / 64.0 );
1662 const double nOutlineWidth(
1663 (rState.mapModeTransform * aFontSize).getHeight() );
1665 return nOutlineWidth < 1.0 ? 1.0 : nOutlineWidth;
1668 OutlineAction::OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1669 const ::basegfx::B2DSize& rReliefOffset,
1670 const ::Color& rReliefColor,
1671 const ::basegfx::B2DSize& rShadowOffset,
1672 const ::Color& rShadowColor,
1673 const ::basegfx::B2DRectangle& rOutlineBounds,
1674 uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
1675 const uno::Sequence< double >& rOffsets,
1676 VirtualDevice const & rVDev,
1677 const CanvasSharedPtr& rCanvas,
1678 const OutDevState& rState ) :
1679 mxTextPoly(std::move( xTextPoly )),
1680 maOffsets( rOffsets ),
1681 mpCanvas( rCanvas ),
1682 mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
1683 maFillColor(
1684 vcl::unotools::colorToDoubleSequence(
1685 COL_WHITE,
1686 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1687 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1688 maOutlineBounds( rOutlineBounds ),
1689 maReliefOffset( rReliefOffset ),
1690 maReliefColor( rReliefColor ),
1691 maShadowOffset( rShadowOffset ),
1692 maShadowColor( rShadowColor )
1694 double nLayoutWidth = 0.0;
1696 initLayoutWidth(nLayoutWidth, rOffsets);
1698 initEffectLinePolyPolygon( maLinesOverallSize,
1699 mxTextLines,
1700 rCanvas,
1701 nLayoutWidth,
1702 maTextLineInfo );
1704 init( maState,
1705 rStartPoint,
1706 rState,
1707 rCanvas );
1710 OutlineAction::OutlineAction( const ::basegfx::B2DPoint& rStartPoint,
1711 const ::basegfx::B2DSize& rReliefOffset,
1712 const ::Color& rReliefColor,
1713 const ::basegfx::B2DSize& rShadowOffset,
1714 const ::Color& rShadowColor,
1715 const ::basegfx::B2DRectangle& rOutlineBounds,
1716 uno::Reference< rendering::XPolyPolygon2D > xTextPoly,
1717 const uno::Sequence< double >& rOffsets,
1718 VirtualDevice const & rVDev,
1719 const CanvasSharedPtr& rCanvas,
1720 const OutDevState& rState,
1721 const ::basegfx::B2DHomMatrix& rTextTransform ) :
1722 mxTextPoly(std::move( xTextPoly )),
1723 maOffsets( rOffsets ),
1724 mpCanvas( rCanvas ),
1725 mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
1726 maFillColor(
1727 vcl::unotools::colorToDoubleSequence(
1728 COL_WHITE,
1729 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1730 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1731 maOutlineBounds( rOutlineBounds ),
1732 maReliefOffset( rReliefOffset ),
1733 maReliefColor( rReliefColor ),
1734 maShadowOffset( rShadowOffset ),
1735 maShadowColor( rShadowColor )
1737 double nLayoutWidth = 0.0;
1738 initLayoutWidth(nLayoutWidth, rOffsets);
1740 initEffectLinePolyPolygon( maLinesOverallSize,
1741 mxTextLines,
1742 rCanvas,
1743 nLayoutWidth,
1744 maTextLineInfo );
1746 init( maState,
1747 rStartPoint,
1748 rState,
1749 rCanvas,
1750 rTextTransform );
1753 bool OutlineAction::operator()( const rendering::RenderState& rRenderState, const ::Color& /*rTextFillColor*/, bool /*bNormalText*/ ) const
1755 const rendering::ViewState& rViewState( mpCanvas->getViewState() );
1756 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
1758 rendering::StrokeAttributes aStrokeAttributes;
1760 aStrokeAttributes.StrokeWidth = mnOutlineWidth;
1761 aStrokeAttributes.MiterLimit = 1.0;
1762 aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
1763 aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
1764 aStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
1766 rendering::RenderState aLocalState( rRenderState );
1767 aLocalState.DeviceColor = maFillColor;
1769 // TODO(P1): implement caching
1771 // background of text
1772 rCanvas->fillPolyPolygon( mxTextPoly,
1773 rViewState,
1774 aLocalState );
1776 // border line of text
1777 rCanvas->strokePolyPolygon( mxTextPoly,
1778 rViewState,
1779 rRenderState,
1780 aStrokeAttributes );
1782 // underlines/strikethrough - background
1783 rCanvas->fillPolyPolygon( mxTextLines,
1784 rViewState,
1785 aLocalState );
1786 // underlines/strikethrough - border
1787 rCanvas->strokePolyPolygon( mxTextLines,
1788 rViewState,
1789 rRenderState,
1790 aStrokeAttributes );
1792 return true;
1795 bool OutlineAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1797 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction::render()" );
1798 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::EffectTextArrayAction: 0x" << std::hex << this );
1800 rendering::RenderState aLocalState( maState );
1801 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1803 return renderEffectText( *this,
1804 aLocalState,
1805 mpCanvas->getUNOCanvas(),
1806 maShadowColor,
1807 maShadowOffset,
1808 maReliefColor,
1809 maReliefOffset,
1810 maTextFillColor);
1813 #if 0 // see #if'ed out use in OutlineAction::renderSubset below:
1814 class OutlineTextArrayRenderHelper : public TextRenderer
1816 public:
1817 OutlineTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >& rCanvas,
1818 const uno::Reference< rendering::XPolyPolygon2D >& rTextPolygon,
1819 const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon,
1820 const rendering::ViewState& rViewState,
1821 double nOutlineWidth ) :
1822 maFillColor(
1823 vcl::unotools::colorToDoubleSequence(
1824 ::COL_WHITE,
1825 rCanvas->getDevice()->getDeviceColorSpace() )),
1826 mnOutlineWidth( nOutlineWidth ),
1827 mrCanvas( rCanvas ),
1828 mrTextPolygon( rTextPolygon ),
1829 mrLinePolygon( rLinePolygon ),
1830 mrViewState( rViewState )
1834 // TextRenderer interface
1835 virtual bool operator()( const rendering::RenderState& rRenderState ) const
1837 rendering::StrokeAttributes aStrokeAttributes;
1839 aStrokeAttributes.StrokeWidth = mnOutlineWidth;
1840 aStrokeAttributes.MiterLimit = 1.0;
1841 aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
1842 aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT;
1843 aStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
1845 rendering::RenderState aLocalState( rRenderState );
1846 aLocalState.DeviceColor = maFillColor;
1848 // TODO(P1): implement caching
1850 // background of text
1851 mrCanvas->fillPolyPolygon( mrTextPolygon,
1852 mrViewState,
1853 aLocalState );
1855 // border line of text
1856 mrCanvas->strokePolyPolygon( mrTextPolygon,
1857 mrViewState,
1858 rRenderState,
1859 aStrokeAttributes );
1861 // underlines/strikethrough - background
1862 mrCanvas->fillPolyPolygon( mrLinePolygon,
1863 mrViewState,
1864 aLocalState );
1865 // underlines/strikethrough - border
1866 mrCanvas->strokePolyPolygon( mrLinePolygon,
1867 mrViewState,
1868 rRenderState,
1869 aStrokeAttributes );
1871 return true;
1874 private:
1875 const uno::Sequence< double > maFillColor;
1876 double mnOutlineWidth;
1877 const uno::Reference< rendering::XCanvas >& mrCanvas;
1878 const uno::Reference< rendering::XPolyPolygon2D >& mrTextPolygon;
1879 const uno::Reference< rendering::XPolyPolygon2D >& mrLinePolygon;
1880 const rendering::ViewState& mrViewState;
1882 #endif
1884 bool OutlineAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
1885 const Subset& rSubset ) const
1887 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::OutlineAction::renderSubset()" );
1888 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::OutlineAction: 0x" << std::hex << this );
1890 if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
1891 return true; // empty range, render nothing
1893 #if 1
1894 // TODO(F3): Subsetting NYI for outline text!
1895 return render( rTransformation );
1896 #else
1897 const rendering::StringContext rOrigContext( mxTextLayout->getText() );
1899 if( rSubset.mnSubsetBegin == 0 &&
1900 rSubset.mnSubsetEnd == rOrigContext.Length )
1902 // full range, no need for subsetting
1903 return render( rTransformation );
1906 rendering::RenderState aLocalState( maState );
1907 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1910 // create and setup local Text polygon
1911 // ===================================
1913 uno::Reference< rendering::XPolyPolygon2D > xTextPolygon();
1915 // TODO(P3): Provide an API method for that!
1917 if( !xTextLayout.is() )
1918 return false;
1920 // render everything
1921 // =================
1923 return renderEffectText(
1924 OutlineTextArrayRenderHelper(
1925 xCanvas,
1926 mnOutlineWidth,
1927 xTextLayout,
1928 xTextLines,
1929 rViewState ),
1930 aLocalState,
1931 rViewState,
1932 xCanvas,
1933 maShadowColor,
1934 maShadowOffset,
1935 maReliefColor,
1936 maReliefOffset );
1937 #endif
1940 ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1942 rendering::RenderState aLocalState( maState );
1943 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1945 return calcEffectTextBounds( maOutlineBounds,
1946 ::basegfx::B2DRange(0, 0,
1947 maLinesOverallSize.getWidth(),
1948 maLinesOverallSize.getHeight()),
1949 maReliefOffset,
1950 maShadowOffset,
1951 aLocalState,
1952 mpCanvas->getViewState() );
1955 ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
1956 const Subset& /*rSubset*/ ) const
1958 SAL_WARN( "cppcanvas.emf", "OutlineAction::getBounds(): Subset not yet supported by this object" );
1960 return getBounds( rTransformation );
1963 sal_Int32 OutlineAction::getActionCount() const
1965 // TODO(F3): Subsetting NYI for outline text!
1966 return maOffsets.getLength();
1970 // Action factory methods
1973 /** Create an outline action
1975 This method extracts the polygonal outline from the
1976 text, and creates a properly setup OutlineAction from
1979 std::shared_ptr<Action> createOutline( const ::basegfx::B2DPoint& rStartPoint,
1980 const ::basegfx::B2DSize& rReliefOffset,
1981 const ::Color& rReliefColor,
1982 const ::basegfx::B2DSize& rShadowOffset,
1983 const ::Color& rShadowColor,
1984 const OUString& rText,
1985 sal_Int32 nStartPos,
1986 sal_Int32 nLen,
1987 KernArraySpan pDXArray,
1988 o3tl::span<const sal_Bool> pKashidaArray,
1989 VirtualDevice& rVDev,
1990 const CanvasSharedPtr& rCanvas,
1991 const OutDevState& rState,
1992 const Renderer::Parameters& rParms )
1994 // operate on raw DX array here (in logical coordinate
1995 // system), to have a higher resolution
1996 // PolyPolygon. That polygon is then converted to
1997 // device coordinate system.
1999 // #i68512# Temporarily switch off font rotation
2000 // (which is already contained in the render state
2001 // transformation matrix - otherwise, glyph polygons
2002 // will be rotated twice)
2003 const vcl::Font aOrigFont( rVDev.GetFont() );
2004 vcl::Font aUnrotatedFont( aOrigFont );
2005 aUnrotatedFont.SetOrientation(0_deg10);
2006 rVDev.SetFont( aUnrotatedFont );
2008 // TODO(F3): Don't understand parameter semantics of
2009 // GetTextOutlines()
2010 ::basegfx::B2DPolyPolygon aResultingPolyPolygon;
2011 PolyPolyVector aVCLPolyPolyVector;
2012 const bool bHaveOutlines( rVDev.GetTextOutlines( aVCLPolyPolyVector, rText,
2013 static_cast<sal_uInt16>(nStartPos),
2014 static_cast<sal_uInt16>(nStartPos),
2015 static_cast<sal_uInt16>(nLen),
2016 0, pDXArray, pKashidaArray ) );
2017 rVDev.SetFont(aOrigFont);
2019 if( !bHaveOutlines )
2020 return std::shared_ptr<Action>();
2022 // remove offsetting from mapmode transformation
2023 // (outline polygons must stay at origin, only need to
2024 // be scaled)
2025 ::basegfx::B2DHomMatrix aMapModeTransform(
2026 rState.mapModeTransform );
2027 aMapModeTransform.set(0,2, 0.0);
2028 aMapModeTransform.set(1,2, 0.0);
2030 for( const auto& rVCLPolyPolygon : aVCLPolyPolyVector )
2032 ::basegfx::B2DPolyPolygon aPolyPolygon = rVCLPolyPolygon.getB2DPolyPolygon();
2033 aPolyPolygon.transform( aMapModeTransform );
2035 // append result to collecting polypoly
2036 for( sal_uInt32 i=0; i<aPolyPolygon.count(); ++i )
2038 // #i47795# Ensure closed polygons (since
2039 // FreeType returns the glyph outlines
2040 // open)
2041 const ::basegfx::B2DPolygon& rPoly( aPolyPolygon.getB2DPolygon( i ) );
2042 const sal_uInt32 nCount( rPoly.count() );
2043 if( nCount<3 ||
2044 rPoly.isClosed() )
2046 // polygon either degenerate, or
2047 // already closed.
2048 aResultingPolyPolygon.append( rPoly );
2050 else
2052 ::basegfx::B2DPolygon aPoly(rPoly);
2053 aPoly.setClosed(true);
2055 aResultingPolyPolygon.append( aPoly );
2060 const uno::Sequence< double > aCharWidthSeq(
2061 !pDXArray.empty() ?
2062 setupDXArray( pDXArray, nLen, rState ) :
2063 setupDXArray( rText,
2064 nStartPos,
2065 nLen,
2066 rVDev,
2067 rState ));
2068 const uno::Reference< rendering::XPolyPolygon2D > xTextPoly(
2069 ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
2070 rCanvas->getUNOCanvas()->getDevice(),
2071 aResultingPolyPolygon ) );
2073 if( rParms.maTextTransformation )
2075 return std::make_shared<OutlineAction>(
2076 rStartPoint,
2077 rReliefOffset,
2078 rReliefColor,
2079 rShadowOffset,
2080 rShadowColor,
2081 ::basegfx::utils::getRange(aResultingPolyPolygon),
2082 xTextPoly,
2083 aCharWidthSeq,
2084 rVDev,
2085 rCanvas,
2086 rState,
2087 *rParms.maTextTransformation );
2089 else
2091 return std::make_shared<OutlineAction>(
2092 rStartPoint,
2093 rReliefOffset,
2094 rReliefColor,
2095 rShadowOffset,
2096 rShadowColor,
2097 ::basegfx::utils::getRange(aResultingPolyPolygon),
2098 xTextPoly,
2099 aCharWidthSeq,
2100 rVDev,
2101 rCanvas,
2102 rState );
2106 } // namespace
2109 std::shared_ptr<Action> TextActionFactory::createTextAction( const ::Point& rStartPoint,
2110 const ::Size& rReliefOffset,
2111 const ::Color& rReliefColor,
2112 const ::Size& rShadowOffset,
2113 const ::Color& rShadowColor,
2114 const ::Color& rTextFillColor,
2115 const OUString& rText,
2116 sal_Int32 nStartPos,
2117 sal_Int32 nLen,
2118 KernArraySpan pDXArray,
2119 o3tl::span<const sal_Bool> pKashidaArray,
2120 VirtualDevice& rVDev,
2121 const CanvasSharedPtr& rCanvas,
2122 const OutDevState& rState,
2123 const Renderer::Parameters& rParms,
2124 bool bSubsettable )
2126 const ::Size aBaselineOffset( tools::getBaselineOffset( rState,
2127 rVDev ) );
2128 // #143885# maintain (nearly) full precision positioning,
2129 // by circumventing integer-based OutDev-mapping
2130 const ::basegfx::B2DPoint aStartPoint(
2131 rState.mapModeTransform *
2132 ::basegfx::B2DPoint(rStartPoint.X() + aBaselineOffset.Width(),
2133 rStartPoint.Y() + aBaselineOffset.Height()) );
2135 const ::basegfx::B2DSize aReliefOffset(
2136 rState.mapModeTransform * vcl::unotools::b2DSizeFromSize( rReliefOffset ) );
2137 const ::basegfx::B2DSize aShadowOffset(
2138 rState.mapModeTransform * vcl::unotools::b2DSizeFromSize( rShadowOffset ) );
2140 if( rState.isTextOutlineModeSet )
2142 return createOutline(
2143 aStartPoint,
2144 aReliefOffset,
2145 rReliefColor,
2146 aShadowOffset,
2147 rShadowColor,
2148 rText,
2149 nStartPos,
2150 nLen,
2151 pDXArray,
2152 pKashidaArray,
2153 rVDev,
2154 rCanvas,
2155 rState,
2156 rParms );
2159 // convert DX array to device coordinate system (and
2160 // create it in the first place, if pDXArray is NULL)
2161 const uno::Sequence< double > aCharWidths(
2162 !pDXArray.empty() ?
2163 setupDXArray( pDXArray, nLen, rState ) :
2164 setupDXArray( rText,
2165 nStartPos,
2166 nLen,
2167 rVDev,
2168 rState ));
2170 const uno::Sequence< sal_Bool > aKashidas(pKashidaArray.data(), pKashidaArray.size());
2172 // determine type of text action to create
2173 // =======================================
2175 const ::Color aEmptyColor( COL_AUTO );
2177 std::shared_ptr<Action> ret;
2179 // no DX array, and no need to subset - no need to store
2180 // DX array, then.
2181 if( pDXArray.empty() && !bSubsettable )
2183 // effects, or not?
2184 if( !rState.textOverlineStyle &&
2185 !rState.textUnderlineStyle &&
2186 !rState.textStrikeoutStyle &&
2187 rReliefColor == aEmptyColor &&
2188 rShadowColor == aEmptyColor &&
2189 rTextFillColor == aEmptyColor )
2191 // nope
2192 if( rParms.maTextTransformation )
2194 ret = std::make_shared<TextAction>(
2195 aStartPoint,
2196 rText,
2197 nStartPos,
2198 nLen,
2199 rCanvas,
2200 rState,
2201 *rParms.maTextTransformation );
2203 else
2205 ret = std::make_shared<TextAction>(
2206 aStartPoint,
2207 rText,
2208 nStartPos,
2209 nLen,
2210 rCanvas,
2211 rState );
2214 else
2216 // at least one of the effects requested
2217 if( rParms.maTextTransformation )
2218 ret = std::make_shared<EffectTextAction>(
2219 aStartPoint,
2220 aReliefOffset,
2221 rReliefColor,
2222 aShadowOffset,
2223 rShadowColor,
2224 rTextFillColor,
2225 rText,
2226 nStartPos,
2227 nLen,
2228 rVDev,
2229 rCanvas,
2230 rState,
2231 *rParms.maTextTransformation );
2232 else
2233 ret = std::make_shared<EffectTextAction>(
2234 aStartPoint,
2235 aReliefOffset,
2236 rReliefColor,
2237 aShadowOffset,
2238 rShadowColor,
2239 rTextFillColor,
2240 rText,
2241 nStartPos,
2242 nLen,
2243 rVDev,
2244 rCanvas,
2245 rState );
2248 else
2250 // DX array necessary - any effects?
2251 if( !rState.textOverlineStyle &&
2252 !rState.textUnderlineStyle &&
2253 !rState.textStrikeoutStyle &&
2254 rReliefColor == aEmptyColor &&
2255 rShadowColor == aEmptyColor &&
2256 rTextFillColor == aEmptyColor )
2258 // nope
2259 if( rParms.maTextTransformation )
2260 ret = std::make_shared<TextArrayAction>(
2261 aStartPoint,
2262 rText,
2263 nStartPos,
2264 nLen,
2265 aCharWidths,
2266 aKashidas,
2267 rCanvas,
2268 rState,
2269 *rParms.maTextTransformation );
2270 else
2271 ret = std::make_shared<TextArrayAction>(
2272 aStartPoint,
2273 rText,
2274 nStartPos,
2275 nLen,
2276 aCharWidths,
2277 aKashidas,
2278 rCanvas,
2279 rState );
2281 else
2283 // at least one of the effects requested
2284 if( rParms.maTextTransformation )
2285 ret = std::make_shared<EffectTextArrayAction>(
2286 aStartPoint,
2287 aReliefOffset,
2288 rReliefColor,
2289 aShadowOffset,
2290 rShadowColor,
2291 rTextFillColor,
2292 rText,
2293 nStartPos,
2294 nLen,
2295 aCharWidths,
2296 aKashidas,
2297 rVDev,
2298 rCanvas,
2299 rState,
2300 *rParms.maTextTransformation );
2301 else
2302 ret = std::make_shared<EffectTextArrayAction>(
2303 aStartPoint,
2304 aReliefOffset,
2305 rReliefColor,
2306 aShadowOffset,
2307 rShadowColor,
2308 rTextFillColor,
2309 rText,
2310 nStartPos,
2311 nLen,
2312 aCharWidths,
2313 aKashidas,
2314 rVDev,
2315 rCanvas,
2316 rState );
2319 return ret;
2323 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */