1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_cppcanvas.hxx"
30 
31 #include <canvas/debug.hxx>
32 #include <tools/diagnose_ex.h>
33 #include <canvas/verbosetrace.hxx>
34 
35 #include <rtl/logfile.hxx>
36 
37 #include <com/sun/star/rendering/PathCapType.hpp>
38 #include <com/sun/star/rendering/PathJoinType.hpp>
39 #include <com/sun/star/rendering/XCanvas.hpp>
40 #include <com/sun/star/rendering/XCanvasFont.hpp>
41 
42 #include <basegfx/numeric/ftools.hxx>
43 #include <basegfx/matrix/b2dhommatrix.hxx>
44 #include <basegfx/range/b2drectangle.hxx>
45 #include <basegfx/vector/b2dsize.hxx>
46 #include <basegfx/polygon/b2dpolypolygontools.hxx>
47 #include <basegfx/polygon/b2dpolygontools.hxx>
48 #include <basegfx/matrix/b2dhommatrixtools.hxx>
49 
50 #include <tools/gen.hxx>
51 #include <vcl/canvastools.hxx>
52 #include <vcl/virdev.hxx>
53 
54 #include <basegfx/tools/canvastools.hxx>
55 #include <canvas/canvastools.hxx>
56 
57 #include <boost/scoped_array.hpp>
58 #include <boost/bind.hpp>
59 #include <boost/utility.hpp>
60 
61 #include "textaction.hxx"
62 #include "outdevstate.hxx"
63 #include "mtftools.hxx"
64 
65 
66 using namespace ::com::sun::star;
67 
68 namespace cppcanvas
69 {
70     namespace internal
71     {
72         namespace
73         {
74             void init( rendering::RenderState&					o_rRenderState,
75                        const ::basegfx::B2DPoint&				rStartPoint,
76                        const OutDevState& 						rState,
77                        const CanvasSharedPtr& 					rCanvas		 )
78             {
79                 tools::initRenderState(o_rRenderState,rState);
80 
81                 // #i36950# Offset clip back to origin (as it's also moved
82                 // by rStartPoint)
83                 // #i53964# Also take VCL font rotation into account,
84                 // since this, opposed to the FontMatrix rotation
85                 // elsewhere, _does_ get incorporated into the render
86                 // state transform.
87                 tools::modifyClip( o_rRenderState,
88                                    rState,
89                                    rCanvas,
90                                    rStartPoint,
91                                    NULL,
92                                    &rState.fontRotation );
93 
94                 basegfx::B2DHomMatrix aLocalTransformation(basegfx::tools::createRotateB2DHomMatrix(rState.fontRotation));
95                 aLocalTransformation.translate( rStartPoint.getX(),
96                                                 rStartPoint.getY() );
97                 ::canvas::tools::appendToRenderState( o_rRenderState,
98                                                       aLocalTransformation );
99 
100                 o_rRenderState.DeviceColor = rState.textColor;
101             }
102 
103             void init( rendering::RenderState&					o_rRenderState,
104                        const ::basegfx::B2DPoint&				rStartPoint,
105                        const OutDevState& 						rState,
106                        const CanvasSharedPtr& 					rCanvas,
107                        const ::basegfx::B2DHomMatrix&			rTextTransform	)
108             {
109                 init( o_rRenderState, rStartPoint, rState, rCanvas );
110 
111                 // TODO(F2): Also inversely-transform clip with
112                 // rTextTransform (which is actually rather hard, as the
113                 // text transform is _prepended_ to the render state)!
114 
115                 // prepend extra font transform to render state
116                 // (prepend it, because it's interpreted in the unit
117                 // rect coordinate space)
118                 ::canvas::tools::prependToRenderState( o_rRenderState,
119                                                        rTextTransform );
120             }
121 
122             void init( rendering::RenderState&						o_rRenderState,
123                        uno::Reference< rendering::XCanvasFont >&	o_rFont,
124                        const ::basegfx::B2DPoint&					rStartPoint,
125                        const OutDevState& 							rState,
126                        const CanvasSharedPtr& 						rCanvas		 )
127             {
128                 // ensure that o_rFont is valid. It is possible that
129                 // text actions are generated without previously
130                 // setting a font. Then, just take a default font
131                 if( !o_rFont.is() )
132                 {
133                     // Use completely default FontRequest
134                     const rendering::FontRequest aFontRequest;
135 
136                     geometry::Matrix2D aFontMatrix;
137                     ::canvas::tools::setIdentityMatrix2D( aFontMatrix );
138 
139                     o_rFont = rCanvas->getUNOCanvas()->createFont(
140                         aFontRequest,
141                         uno::Sequence< beans::PropertyValue >(),
142                         aFontMatrix );
143                 }
144 
145                 init( o_rRenderState,
146                       rStartPoint,
147                       rState,
148                       rCanvas );
149             }
150 
151             void init( rendering::RenderState&						o_rRenderState,
152                        uno::Reference< rendering::XCanvasFont >&	o_rFont,
153                        const ::basegfx::B2DPoint&					rStartPoint,
154                        const OutDevState& 							rState,
155                        const CanvasSharedPtr& 						rCanvas,
156                        const ::basegfx::B2DHomMatrix&				rTextTransform	)
157             {
158                 init( o_rRenderState, o_rFont, rStartPoint, rState, rCanvas );
159 
160                 // TODO(F2): Also inversely-transform clip with
161                 // rTextTransform (which is actually rather hard, as the
162                 // text transform is _prepended_ to the render state)!
163 
164                 // prepend extra font transform to render state
165                 // (prepend it, because it's interpreted in the unit
166                 // rect coordinate space)
167                 ::canvas::tools::prependToRenderState( o_rRenderState,
168                                                        rTextTransform );
169             }
170 
171             ::basegfx::B2DPolyPolygon textLinesFromLogicalOffsets( const uno::Sequence< double >&	rOffsets,
172                                                                    const tools::TextLineInfo&		rTextLineInfo )
173             {
174                 return tools::createTextLinesPolyPolygon(
175                     0.0,
176                     // extract character cell furthest to the right
177                     *(::std::max_element(
178                           rOffsets.getConstArray(),
179                           rOffsets.getConstArray() + rOffsets.getLength() )),
180                     rTextLineInfo );
181             }
182 
183             uno::Sequence< double > setupDXArray( const sal_Int32*	 pCharWidths,
184                                                   sal_Int32			 nLen,
185                                                   const OutDevState& rState )
186             {
187                 // convert character widths from logical units
188                 uno::Sequence< double > aCharWidthSeq( nLen );
189                 double*					pOutputWidths( aCharWidthSeq.getArray() );
190 
191                 // #143885# maintain (nearly) full precision of DX
192                 // array, by circumventing integer-based
193                 // OutDev-mapping
194                 const double nScale( rState.mapModeTransform.get(0,0) );
195                 for( int i = 0; i < nLen; ++i )
196                 {
197                     // TODO(F2): use correct scale direction
198                     *pOutputWidths++ = *pCharWidths++ * nScale;
199                 }
200 
201                 return aCharWidthSeq;
202             }
203 
204             uno::Sequence< double > setupDXArray( const ::String& 	 rText,
205                                                   sal_Int32			 nStartPos,
206                                                   sal_Int32			 nLen,
207                                                   VirtualDevice&	 rVDev,
208                                                   const OutDevState& rState )
209             {
210                 // no external DX array given, create one from given
211                 // string
212                 ::boost::scoped_array< sal_Int32 > pCharWidths( new sal_Int32[nLen] );
213 
214                 rVDev.GetTextArray( rText, pCharWidths.get(),
215                                     static_cast<sal_uInt16>(nStartPos),
216                                     static_cast<sal_uInt16>(nLen) );
217 
218                 return setupDXArray( pCharWidths.get(), nLen, rState );
219             }
220 
221             ::basegfx::B2DPoint adaptStartPoint( const ::basegfx::B2DPoint&		rStartPoint,
222                                                  const OutDevState& 			rState,
223                                                  const uno::Sequence< double >& rOffsets )
224             {
225                 ::basegfx::B2DPoint aLocalPoint( rStartPoint );
226 
227                 if( rState.textAlignment )
228                 {
229                     // text origin is right, not left. Modify start point
230                     // accordingly, because XCanvas::drawTextLayout()
231                     // always aligns left!
232 
233                     const double nOffset( rOffsets[ rOffsets.getLength()-1 ] );
234 
235                     // correct start point for rotated text: rotate around
236                     // former start point
237                     aLocalPoint.setX( aLocalPoint.getX() + cos( rState.fontRotation )*nOffset );
238                     aLocalPoint.setY( aLocalPoint.getY() + sin( rState.fontRotation )*nOffset );
239                 }
240 
241                 return aLocalPoint;
242             }
243 
244             /** Perform common setup for array text actions
245 
246             	This method creates the XTextLayout object and
247             	initializes it, e.g. with the logical advancements.
248              */
249             void initArrayAction( rendering::RenderState&					o_rRenderState,
250                                   uno::Reference< rendering::XTextLayout >& o_rTextLayout,
251                                   const ::basegfx::B2DPoint&				rStartPoint,
252                                   const ::rtl::OUString&					rText,
253                                   sal_Int32 								nStartPos,
254                                   sal_Int32 								nLen,
255                                   const uno::Sequence< double >&			rOffsets,
256                                   const CanvasSharedPtr&					rCanvas,
257                                   const OutDevState&						rState,
258                                   const ::basegfx::B2DHomMatrix*			pTextTransform )
259             {
260                 ENSURE_OR_THROW( rOffsets.getLength(),
261                                   "::cppcanvas::internal::initArrayAction(): zero-length DX array" );
262 
263                 const ::basegfx::B2DPoint aLocalStartPoint(
264                     adaptStartPoint( rStartPoint, rState, rOffsets ) );
265 
266                 uno::Reference< rendering::XCanvasFont > xFont( rState.xFont );
267 
268                 if( pTextTransform )
269                     init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas, *pTextTransform );
270                 else
271                     init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas );
272 
273                 o_rTextLayout = xFont->createTextLayout(
274                     rendering::StringContext( rText, nStartPos, nLen ),
275                     rState.textDirection,
276                     0 );
277 
278                 ENSURE_OR_THROW( o_rTextLayout.is(),
279                                   "::cppcanvas::internal::initArrayAction(): Invalid font" );
280 
281                 o_rTextLayout->applyLogicalAdvancements( rOffsets );
282             }
283 
284             double getLineWidth( ::VirtualDevice&                rVDev,
285                                  const OutDevState&              rState,
286                                  const rendering::StringContext& rStringContext )
287             {
288                 // TODO(F2): use correct scale direction
289                 const ::basegfx::B2DSize aSize( rVDev.GetTextWidth( rStringContext.Text,
290                                                                     static_cast<sal_uInt16>(rStringContext.StartPosition),
291                                                                     static_cast<sal_uInt16>(rStringContext.Length) ),
292                                     0 );
293 
294                 return (rState.mapModeTransform * aSize).getX();
295             }
296 
297             uno::Sequence< double >
298             	calcSubsetOffsets( rendering::RenderState&							io_rRenderState,
299                                    double&											o_rMinPos,
300                                    double&											o_rMaxPos,
301                                    const uno::Reference< rendering::XTextLayout >&	rOrigTextLayout,
302                                    const ::cppcanvas::internal::Action::Subset&		rSubset )
303             {
304                 ENSURE_OR_THROW( rSubset.mnSubsetEnd > rSubset.mnSubsetBegin,
305                                   "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
306 
307                 uno::Sequence< double > aOrigOffsets( rOrigTextLayout->queryLogicalAdvancements() );
308                 const double*			pOffsets( aOrigOffsets.getConstArray() );
309 
310                 ENSURE_OR_THROW( aOrigOffsets.getLength() >= rSubset.mnSubsetEnd,
311                                   "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );
312 
313                 // TODO(F3): It currently seems that for RTL text, the
314                 // DX offsets are nevertheless increasing in logical
315                 // text order (I'd expect they are decreasing,
316                 // mimicking the fact that the text is output
317                 // right-to-left). This breaks text effects for ALL
318                 // RTL languages.
319 
320                 // determine leftmost position in given subset range -
321                 // as the DX array contains the output positions
322                 // starting with the second character (the first is
323                 // assumed to have output position 0), correct begin
324                 // iterator.
325                 const double nMinPos( rSubset.mnSubsetBegin <= 0 ? 0 :
326                                       *(::std::min_element( pOffsets+rSubset.mnSubsetBegin-1,
327                                                             pOffsets+rSubset.mnSubsetEnd )) );
328 
329                 // determine rightmost position in given subset range
330                 // - as the DX array contains the output positions
331                 // starting with the second character (the first is
332                 // assumed to have output position 0), correct begin
333                 // iterator.
334                 const double nMaxPos(
335                     *(::std::max_element( pOffsets + (rSubset.mnSubsetBegin <= 0 ?
336                                                       0 : rSubset.mnSubsetBegin-1),
337                                           pOffsets + rSubset.mnSubsetEnd )) );
338 
339 
340                 // adapt render state, to move text output to given offset
341                 // -------------------------------------------------------
342 
343                 // TODO(F1): Strictly speaking, we also have to adapt
344                 // the clip here, which normally should _not_ move
345                 // with the output offset. Neglected for now, as it
346                 // does not matter for drawing layer output
347 
348                 if( rSubset.mnSubsetBegin > 0 )
349                 {
350                     ::basegfx::B2DHomMatrix aTranslation;
351                     if( rOrigTextLayout->getFont()->getFontRequest().FontDescription.IsVertical )
352                     {
353                         // vertical text -> offset in y direction
354                         aTranslation.translate( 0.0, nMinPos );
355                     }
356                     else
357                     {
358                         // horizontal text -> offset in x direction
359                         aTranslation.translate( nMinPos, 0.0 );
360                     }
361 
362                     ::canvas::tools::appendToRenderState( io_rRenderState,
363                                                           aTranslation );
364                 }
365 
366 
367                 // reduce DX array to given substring
368                 // ----------------------------------
369 
370                 const sal_Int32			nNewElements( rSubset.mnSubsetEnd - rSubset.mnSubsetBegin );
371                 uno::Sequence< double > aAdaptedOffsets( nNewElements );
372                 double*					pAdaptedOffsets( aAdaptedOffsets.getArray() );
373 
374                 // move to new output position (subtract nMinPos,
375                 // which is the new '0' position), copy only the range
376                 // as given by rSubset.
377                 ::std::transform( pOffsets + rSubset.mnSubsetBegin,
378                                   pOffsets + rSubset.mnSubsetEnd,
379                                   pAdaptedOffsets,
380                                   ::boost::bind( ::std::minus<double>(),
381                                                  _1,
382                                                  nMinPos ) );
383 
384                 o_rMinPos = nMinPos;
385                 o_rMaxPos = nMaxPos;
386 
387                 return aAdaptedOffsets;
388             }
389 
390             uno::Reference< rendering::XTextLayout >
391 	            createSubsetLayout( const rendering::StringContext&					rOrigContext,
392                                     const ::cppcanvas::internal::Action::Subset&	rSubset,
393                                     const uno::Reference< rendering::XTextLayout >&	rOrigTextLayout )
394             {
395                 // create temporary new text layout with subset string
396                 // ---------------------------------------------------
397 
398                 const sal_Int32 nNewStartPos( rOrigContext.StartPosition + ::std::min(
399                                                   rSubset.mnSubsetBegin, rOrigContext.Length-1 ) );
400                 const sal_Int32 nNewLength( ::std::max(
401                                                 ::std::min(
402                                                     rSubset.mnSubsetEnd - rSubset.mnSubsetBegin,
403                                                     rOrigContext.Length ),
404                                                 sal_Int32( 0 ) ) );
405 
406                 const rendering::StringContext aContext( rOrigContext.Text,
407                                                          nNewStartPos,
408                                                          nNewLength );
409 
410                 uno::Reference< rendering::XTextLayout > xTextLayout(
411                     rOrigTextLayout->getFont()->createTextLayout( aContext,
412                                                                   rOrigTextLayout->getMainTextDirection(),
413                                                                   0 ),
414                     uno::UNO_QUERY_THROW );
415 
416                 return xTextLayout;
417             }
418 
419             /** Setup subset text layout
420 
421             	@param io_rTextLayout
422                 Must contain original (full set) text layout on input,
423                 will contain subsetted text layout (or empty
424                 reference, for empty subsets) on output.
425 
426                 @param io_rRenderState
427                 Must contain original render state on input, will
428                 contain shifted render state concatenated with
429                 rTransformation on output.
430 
431                 @param rTransformation
432                 Additional transformation, to be prepended to render
433                 state
434 
435                 @param rSubset
436                 Subset to prepare
437              */
438             void createSubsetLayout( uno::Reference< rendering::XTextLayout >&	io_rTextLayout,
439                                      rendering::RenderState&					io_rRenderState,
440                                      double&									o_rMinPos,
441                                      double&									o_rMaxPos,
442                                      const ::basegfx::B2DHomMatrix&				rTransformation,
443                                      const Action::Subset&						rSubset )
444             {
445                 ::canvas::tools::prependToRenderState(io_rRenderState, rTransformation);
446 
447                 if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
448                 {
449                      // empty range, empty layout
450                     io_rTextLayout.clear();
451 
452                     return;
453                 }
454 
455                 ENSURE_OR_THROW( io_rTextLayout.is(),
456                                   "createSubsetLayout(): Invalid input layout" );
457 
458                 const rendering::StringContext& rOrigContext( io_rTextLayout->getText() );
459 
460                 if( rSubset.mnSubsetBegin == 0 &&
461                     rSubset.mnSubsetEnd == rOrigContext.Length )
462                 {
463                     // full range, no need for subsetting
464                     return;
465                 }
466 
467                 uno::Reference< rendering::XTextLayout > xTextLayout(
468                     createSubsetLayout( rOrigContext, rSubset, io_rTextLayout ) );
469 
470                 if( xTextLayout.is() )
471                 {
472                     xTextLayout->applyLogicalAdvancements(
473                         calcSubsetOffsets( io_rRenderState,
474                                            o_rMinPos,
475                                            o_rMaxPos,
476                                            io_rTextLayout,
477                                            rSubset ) );
478                 }
479 
480                 io_rTextLayout = xTextLayout;
481             }
482 
483 
484             /** Interface for renderEffectText functor below.
485 
486             	This is interface is used from the renderEffectText()
487             	method below, to call the client implementation.
488              */
489             class TextRenderer
490             {
491             public:
492                 virtual ~TextRenderer() {}
493 
494                 /// Render text with given RenderState
495                 virtual bool operator()( const rendering::RenderState& rRenderState ) const = 0;
496             };
497 
498             /** Render effect text.
499 
500             	@param rRenderer
501                 Functor object, will be called to render the actual
502                 part of the text effect (the text itself and the means
503                 to render it are unknown to this method)
504              */
505             bool renderEffectText( const TextRenderer& 							rRenderer,
506                                    const rendering::RenderState&				rRenderState,
507                                    const rendering::ViewState&			 		/*rViewState*/,
508                                    const uno::Reference< rendering::XCanvas >&	xCanvas,
509                                    const ::Color&								rShadowColor,
510                                    const ::basegfx::B2DSize&					rShadowOffset,
511                                    const ::Color&								rReliefColor,
512                                    const ::basegfx::B2DSize&					rReliefOffset )
513             {
514                 ::Color aEmptyColor( COL_AUTO );
515                 uno::Reference<rendering::XColorSpace> xColorSpace(
516                     xCanvas->getDevice()->getDeviceColorSpace() );
517 
518                 // draw shadow text, if enabled
519                 if( rShadowColor != aEmptyColor )
520                 {
521                     rendering::RenderState aShadowState( rRenderState );
522                     ::basegfx::B2DHomMatrix aTranslate;
523 
524                     aTranslate.translate( rShadowOffset.getX(),
525                                           rShadowOffset.getY() );
526 
527                     ::canvas::tools::appendToRenderState(aShadowState, aTranslate);
528 
529                     aShadowState.DeviceColor =
530                         ::vcl::unotools::colorToDoubleSequence( rShadowColor,
531                                                                 xColorSpace );
532 
533                     rRenderer( aShadowState );
534                 }
535 
536                 // draw relief text, if enabled
537                 if( rReliefColor != aEmptyColor )
538                 {
539                     rendering::RenderState aReliefState( rRenderState );
540                     ::basegfx::B2DHomMatrix aTranslate;
541 
542                     aTranslate.translate( rReliefOffset.getX(),
543                                           rReliefOffset.getY() );
544 
545                     ::canvas::tools::appendToRenderState(aReliefState, aTranslate);
546 
547                     aReliefState.DeviceColor =
548                         ::vcl::unotools::colorToDoubleSequence( rReliefColor,
549                                                                 xColorSpace );
550 
551                     rRenderer( aReliefState );
552                 }
553 
554                 // draw normal text
555                 rRenderer( rRenderState );
556 
557                 return true;
558             }
559 
560 
561             ::basegfx::B2DRange calcEffectTextBounds( const ::basegfx::B2DRange& 	rTextBounds,
562                                                       const ::basegfx::B2DRange& 	rLineBounds,
563                                                       const ::basegfx::B2DSize&		rReliefOffset,
564                                                       const ::basegfx::B2DSize&		rShadowOffset,
565                                                       const rendering::RenderState&	rRenderState,
566                                                       const rendering::ViewState&   rViewState )
567             {
568                 ::basegfx::B2DRange aBounds( rTextBounds );
569 
570                 // add extends of text lines
571                 aBounds.expand( rLineBounds );
572 
573                 // TODO(Q3): Provide this functionality at the B2DRange
574                 ::basegfx::B2DRange aTotalBounds( aBounds );
575                 aTotalBounds.expand(
576                     ::basegfx::B2DRange( aBounds.getMinX() + rReliefOffset.getX(),
577                                          aBounds.getMinY() + rReliefOffset.getY(),
578                                          aBounds.getMaxX() + rReliefOffset.getX(),
579                                          aBounds.getMaxY() + rReliefOffset.getY() ) );
580                 aTotalBounds.expand(
581                     ::basegfx::B2DRange( aBounds.getMinX() + rShadowOffset.getX(),
582                                          aBounds.getMinY() + rShadowOffset.getY(),
583                                          aBounds.getMaxX() + rShadowOffset.getX(),
584                                          aBounds.getMaxY() + rShadowOffset.getY() ) );
585 
586                 return tools::calcDevicePixelBounds( aTotalBounds,
587                                                      rViewState,
588                                                      rRenderState );
589             }
590 
591             void initEffectLinePolyPolygon( ::basegfx::B2DSize& 							o_rOverallSize,
592                                             uno::Reference< rendering::XPolyPolygon2D >&	o_rTextLines,
593                                             const CanvasSharedPtr&							rCanvas,
594                                             const uno::Sequence< double >&					rOffsets,
595                                             const tools::TextLineInfo						rLineInfo	)
596             {
597                 const ::basegfx::B2DPolyPolygon aPoly(
598                     textLinesFromLogicalOffsets(
599                         rOffsets,
600                         rLineInfo ) );
601 
602                 o_rOverallSize = ::basegfx::tools::getRange( aPoly ).getRange();
603 
604                 o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
605                     rCanvas->getUNOCanvas()->getDevice(),
606                     aPoly );
607             }
608 
609             void initEffectLinePolyPolygon( ::basegfx::B2DSize& 							o_rOverallSize,
610                                             uno::Reference< rendering::XPolyPolygon2D >&	o_rTextLines,
611                                             const CanvasSharedPtr&							rCanvas,
612                                             double                                          nLineWidth,
613                                             const tools::TextLineInfo						rLineInfo	)
614             {
615                 const ::basegfx::B2DPolyPolygon aPoly(
616                     tools::createTextLinesPolyPolygon( 0.0, nLineWidth,
617                                                        rLineInfo ) );
618 
619                 o_rOverallSize = ::basegfx::tools::getRange( aPoly ).getRange();
620 
621                 o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
622                     rCanvas->getUNOCanvas()->getDevice(),
623                     aPoly );
624             }
625 
626 
627             // -------------------------------------------------------------------------
628 
629             class TextAction : public Action, private ::boost::noncopyable
630             {
631             public:
632                 TextAction( const ::basegfx::B2DPoint& 	rStartPoint,
633                             const ::rtl::OUString&		rString,
634                             sal_Int32 					nStartPos,
635                             sal_Int32 					nLen,
636                             const CanvasSharedPtr&		rCanvas,
637                             const OutDevState&			rState );
638 
639                 TextAction( const ::basegfx::B2DPoint& 		rStartPoint,
640                             const ::rtl::OUString&			rString,
641                             sal_Int32 						nStartPos,
642                             sal_Int32 						nLen,
643                             const CanvasSharedPtr&			rCanvas,
644                             const OutDevState&				rState,
645                             const ::basegfx::B2DHomMatrix&	rTextTransform );
646 
647                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
648                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
649                                      const Subset&					rSubset ) const;
650 
651                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
652                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
653                                                        const Subset&					rSubset ) const;
654 
655                 virtual sal_Int32 getActionCount() const;
656 
657             private:
658                 // TODO(P2): This is potentially a real mass object
659                 // (every character might be a separate TextAction),
660                 // thus, make it as lightweight as possible. For
661                 // example, share common RenderState among several
662                 // TextActions, maybe using maOffsets for the
663                 // translation.
664 
665                 uno::Reference< rendering::XCanvasFont >	mxFont;
666                 const rendering::StringContext			  	maStringContext;
667                 const CanvasSharedPtr						mpCanvas;
668                 rendering::RenderState						maState;
669                 const sal_Int8								maTextDirection;
670             };
671 
672             TextAction::TextAction( const ::basegfx::B2DPoint& 	rStartPoint,
673                                     const ::rtl::OUString&		rString,
674                                     sal_Int32 					nStartPos,
675                                     sal_Int32 					nLen,
676                                     const CanvasSharedPtr&		rCanvas,
677                                     const OutDevState&			rState	) :
678                 mxFont( rState.xFont ),
679                 maStringContext( rString, nStartPos, nLen ),
680                 mpCanvas( rCanvas ),
681                 maState(),
682                 maTextDirection( rState.textDirection )
683             {
684                 init( maState, mxFont,
685                       rStartPoint,
686                       rState, rCanvas );
687 
688                 ENSURE_OR_THROW( mxFont.is(),
689                                   "::cppcanvas::internal::TextAction(): Invalid font" );
690             }
691 
692             TextAction::TextAction( const ::basegfx::B2DPoint& 		rStartPoint,
693                                     const ::rtl::OUString&			rString,
694                                     sal_Int32 						nStartPos,
695                                     sal_Int32 						nLen,
696                                     const CanvasSharedPtr&			rCanvas,
697                                     const OutDevState&				rState,
698                                     const ::basegfx::B2DHomMatrix&	rTextTransform ) :
699                 mxFont( rState.xFont ),
700                 maStringContext( rString, nStartPos, nLen ),
701                 mpCanvas( rCanvas ),
702                 maState(),
703                 maTextDirection( rState.textDirection )
704             {
705                 init( maState, mxFont,
706                       rStartPoint,
707                       rState, rCanvas, rTextTransform );
708 
709                 ENSURE_OR_THROW( mxFont.is(),
710                                   "::cppcanvas::internal::TextAction(): Invalid font" );
711             }
712 
713             bool TextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
714             {
715                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextAction::render()" );
716                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextAction: 0x%X", this );
717 
718                 rendering::RenderState aLocalState( maState );
719                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
720 
721                 mpCanvas->getUNOCanvas()->drawText( maStringContext, mxFont,
722                                                     mpCanvas->getViewState(), aLocalState, maTextDirection );
723 
724                 return true;
725             }
726 
727             bool TextAction::render( const ::basegfx::B2DHomMatrix&	rTransformation,
728                                      const Subset&					/*rSubset*/ ) const
729             {
730                 OSL_ENSURE( false,
731                             "TextAction::render(): Subset not supported by this object" );
732 
733                 // TODO(P1): Retrieve necessary font metric info for
734                 // TextAction from XCanvas. Currently, the
735                 // TextActionFactory does not generate this object for
736                 // _subsettable_ text
737                 return render( rTransformation );
738             }
739 
740             ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
741             {
742                 // create XTextLayout, to have the
743                 // XTextLayout::queryTextBounds() method available
744                 uno::Reference< rendering::XTextLayout > xTextLayout(
745                     mxFont->createTextLayout(
746                         maStringContext,
747                         maTextDirection,
748                         0 ) );
749 
750                 rendering::RenderState aLocalState( maState );
751                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
752 
753                 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
754                                                          xTextLayout->queryTextBounds() ),
755                                                      mpCanvas->getViewState(),
756                                                      aLocalState );
757             }
758 
759             ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
760                                                        const Subset&					/*rSubset*/ ) const
761             {
762                 OSL_ENSURE( false,
763                             "TextAction::getBounds(): Subset not supported by this object" );
764 
765                 // TODO(P1): Retrieve necessary font metric info for
766                 // TextAction from XCanvas. Currently, the
767                 // TextActionFactory does not generate this object for
768                 // _subsettable_ text
769                 return getBounds( rTransformation );
770             }
771 
772             sal_Int32 TextAction::getActionCount() const
773             {
774                 // TODO(P1): Retrieve necessary font metric info for
775                 // TextAction from XCanvas. Currently, the
776                 // TextActionFactory does not generate this object for
777                 // _subsettable_ text
778                 return 1;
779             }
780 
781 
782             // -------------------------------------------------------------------------
783 
784             class EffectTextAction :
785                 public Action,
786                 public TextRenderer,
787                 private ::boost::noncopyable
788             {
789             public:
790                 EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
791                                   const ::basegfx::B2DSize&	 rReliefOffset,
792                                   const ::Color&			 rReliefColor,
793                                   const ::basegfx::B2DSize&	 rShadowOffset,
794                                   const ::Color&			 rShadowColor,
795                                   const ::rtl::OUString& 	 rText,
796                                   sal_Int32 				 nStartPos,
797                                   sal_Int32 				 nLen,
798                                   VirtualDevice&			 rVDev,
799                                   const CanvasSharedPtr&	 rCanvas,
800                                   const OutDevState& 		 rState );
801 
802                 EffectTextAction( const ::basegfx::B2DPoint&        rStartPoint,
803                                   const ::basegfx::B2DSize&			rReliefOffset,
804                                   const ::Color&					rReliefColor,
805                                   const ::basegfx::B2DSize&			rShadowOffset,
806                                   const ::Color&					rShadowColor,
807                                   const ::rtl::OUString& 			rText,
808                                   sal_Int32 						nStartPos,
809                                   sal_Int32 						nLen,
810                                   VirtualDevice&					rVDev,
811                                   const CanvasSharedPtr&			rCanvas,
812                                   const OutDevState& 				rState,
813                                   const ::basegfx::B2DHomMatrix&	rTextTransform );
814 
815                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
816                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
817                                      const Subset&					rSubset ) const;
818 
819                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
820                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
821                                                        const Subset&					rSubset ) const;
822 
823                 virtual sal_Int32 getActionCount() const;
824 
825             private:
826                 /// Interface TextRenderer
827                 virtual bool operator()( const rendering::RenderState& rRenderState ) const;
828 
829                 // TODO(P2): This is potentially a real mass object
830                 // (every character might be a separate TextAction),
831                 // thus, make it as lightweight as possible. For
832                 // example, share common RenderState among several
833                 // TextActions, maybe using maOffsets for the
834                 // translation.
835 
836                 uno::Reference< rendering::XCanvasFont >	mxFont;
837                 const rendering::StringContext			  	maStringContext;
838                 const CanvasSharedPtr						mpCanvas;
839                 rendering::RenderState						maState;
840                 const tools::TextLineInfo					maTextLineInfo;
841                 ::basegfx::B2DSize							maLinesOverallSize;
842                 const double								mnLineWidth;
843                 uno::Reference< rendering::XPolyPolygon2D >	mxTextLines;
844                 const ::basegfx::B2DSize					maReliefOffset;
845                 const ::Color								maReliefColor;
846                 const ::basegfx::B2DSize					maShadowOffset;
847                 const ::Color								maShadowColor;
848                 const sal_Int8								maTextDirection;
849             };
850 
851             EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
852                                                 const ::basegfx::B2DSize&  rReliefOffset,
853                                                 const ::Color&             rReliefColor,
854                                                 const ::basegfx::B2DSize&  rShadowOffset,
855                                                 const ::Color&             rShadowColor,
856                                                 const ::rtl::OUString&     rText,
857                                                 sal_Int32                  nStartPos,
858                                                 sal_Int32                  nLen,
859                                                 VirtualDevice&             rVDev,
860                                                 const CanvasSharedPtr&     rCanvas,
861                                                 const OutDevState&         rState ) :
862                 mxFont( rState.xFont ),
863                 maStringContext( rText, nStartPos, nLen ),
864                 mpCanvas( rCanvas ),
865                 maState(),
866                 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
867                 maLinesOverallSize(),
868                 mnLineWidth( getLineWidth( rVDev, rState, maStringContext ) ),
869                 mxTextLines(),
870                 maReliefOffset( rReliefOffset ),
871                 maReliefColor( rReliefColor ),
872                 maShadowOffset( rShadowOffset ),
873                 maShadowColor( rShadowColor ),
874                 maTextDirection( rState.textDirection )
875             {
876                 initEffectLinePolyPolygon( maLinesOverallSize,
877                                            mxTextLines,
878                                            rCanvas,
879                                            mnLineWidth,
880                                            maTextLineInfo );
881 
882                 init( maState, mxFont,
883                       rStartPoint,
884                       rState, rCanvas );
885 
886                 ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
887                                   "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
888             }
889 
890             EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint&		rStartPoint,
891                                                 const ::basegfx::B2DSize&		rReliefOffset,
892                                                 const ::Color&					rReliefColor,
893                                                 const ::basegfx::B2DSize&		rShadowOffset,
894                                                 const ::Color&					rShadowColor,
895                                                 const ::rtl::OUString& 			rText,
896                                                 sal_Int32 						nStartPos,
897                                                 sal_Int32 						nLen,
898                                                 VirtualDevice&					rVDev,
899                                                 const CanvasSharedPtr&			rCanvas,
900                                                 const OutDevState& 				rState,
901                                                 const ::basegfx::B2DHomMatrix&	rTextTransform ) :
902                 mxFont( rState.xFont ),
903                 maStringContext( rText, nStartPos, nLen ),
904                 mpCanvas( rCanvas ),
905                 maState(),
906                 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
907                 maLinesOverallSize(),
908                 mnLineWidth( getLineWidth( rVDev, rState, maStringContext ) ),
909                 mxTextLines(),
910                 maReliefOffset( rReliefOffset ),
911                 maReliefColor( rReliefColor ),
912                 maShadowOffset( rShadowOffset ),
913                 maShadowColor( rShadowColor ),
914                 maTextDirection( rState.textDirection )
915             {
916                 initEffectLinePolyPolygon( maLinesOverallSize,
917                                            mxTextLines,
918                                            rCanvas,
919                                            mnLineWidth,
920                                            maTextLineInfo );
921 
922                 init( maState, mxFont,
923                       rStartPoint,
924                       rState, rCanvas, rTextTransform );
925 
926                 ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
927                                   "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
928             }
929 
930             bool EffectTextAction::operator()( const rendering::RenderState& rRenderState ) const
931             {
932                 const rendering::ViewState& rViewState( mpCanvas->getViewState() );
933                 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
934 
935                 rCanvas->fillPolyPolygon( mxTextLines,
936                                           rViewState,
937                                           rRenderState );
938 
939                 rCanvas->drawText( maStringContext, mxFont,
940                                    rViewState,
941                                    rRenderState,
942                                    maTextDirection );
943 
944                 return true;
945             }
946 
947             bool EffectTextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
948             {
949                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextAction::render()" );
950                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextAction: 0x%X", this );
951 
952                 rendering::RenderState aLocalState( maState );
953                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
954 
955                 return renderEffectText( *this,
956                                          aLocalState,
957                                          mpCanvas->getViewState(),
958                                          mpCanvas->getUNOCanvas(),
959                                          maShadowColor,
960                                          maShadowOffset,
961                                          maReliefColor,
962                                          maReliefOffset );
963             }
964 
965             bool EffectTextAction::render( const ::basegfx::B2DHomMatrix&	rTransformation,
966                                            const Subset&					/*rSubset*/ ) const
967             {
968                 OSL_ENSURE( false,
969                             "EffectTextAction::render(): Subset not supported by this object" );
970 
971                 // TODO(P1): Retrieve necessary font metric info for
972                 // TextAction from XCanvas. Currently, the
973                 // TextActionFactory does not generate this object for
974                 // subsettable text
975                 return render( rTransformation );
976             }
977 
978             ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
979             {
980                 // create XTextLayout, to have the
981                 // XTextLayout::queryTextBounds() method available
982                 uno::Reference< rendering::XTextLayout > xTextLayout(
983                     mxFont->createTextLayout(
984                         maStringContext,
985                         maTextDirection,
986                         0 ) );
987 
988                 rendering::RenderState aLocalState( maState );
989                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
990 
991                 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
992                                                  xTextLayout->queryTextBounds() ),
993                                              ::basegfx::B2DRange( 0,0,
994                                                                   maLinesOverallSize.getX(),
995                                                                   maLinesOverallSize.getY() ),
996                                              maReliefOffset,
997                                              maShadowOffset,
998                                              aLocalState,
999                                              mpCanvas->getViewState() );
1000             }
1001 
1002             ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
1003                                                              const Subset&					/*rSubset*/ ) const
1004             {
1005                 OSL_ENSURE( false,
1006                             "EffectTextAction::getBounds(): Subset not supported by this object" );
1007 
1008                 // TODO(P1): Retrieve necessary font metric info for
1009                 // TextAction from XCanvas. Currently, the
1010                 // TextActionFactory does not generate this object for
1011                 // _subsettable_ text
1012                 return getBounds( rTransformation );
1013             }
1014 
1015             sal_Int32 EffectTextAction::getActionCount() const
1016             {
1017                 // TODO(P1): Retrieve necessary font metric info for
1018                 // TextAction from XCanvas. Currently, the
1019                 // TextActionFactory does not generate this object for
1020                 // subsettable text
1021                 return 1;
1022             }
1023 
1024 
1025             // -------------------------------------------------------------------------
1026 
1027             class TextArrayAction : public Action, private ::boost::noncopyable
1028             {
1029             public:
1030                 TextArrayAction( const ::basegfx::B2DPoint& 	rStartPoint,
1031                                  const ::rtl::OUString&			rString,
1032                                  sal_Int32 						nStartPos,
1033                                  sal_Int32 						nLen,
1034                                  const uno::Sequence< double >&	rOffsets,
1035                                  const CanvasSharedPtr&			rCanvas,
1036                                  const OutDevState&				rState );
1037 
1038                 TextArrayAction( const ::basegfx::B2DPoint& 	rStartPoint,
1039                                  const ::rtl::OUString&			rString,
1040                                  sal_Int32 						nStartPos,
1041                                  sal_Int32 						nLen,
1042                                  const uno::Sequence< double >&	rOffsets,
1043                                  const CanvasSharedPtr&			rCanvas,
1044                                  const OutDevState&				rState,
1045                                  const ::basegfx::B2DHomMatrix&	rTextTransform );
1046 
1047                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
1048                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
1049                                      const Subset&					rSubset ) const;
1050 
1051                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
1052                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
1053                                                        const Subset&					rSubset ) const;
1054 
1055                 virtual sal_Int32 getActionCount() const;
1056 
1057             private:
1058                 // TODO(P2): This is potentially a real mass object
1059                 // (every character might be a separate TextAction),
1060                 // thus, make it as lightweight as possible. For
1061                 // example, share common RenderState among several
1062                 // TextActions, maybe using maOffsets for the
1063                 // translation.
1064 
1065                 uno::Reference< rendering::XTextLayout >	mxTextLayout;
1066                 const CanvasSharedPtr						mpCanvas;
1067                 rendering::RenderState						maState;
1068             };
1069 
1070             TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint& 		rStartPoint,
1071                                               const ::rtl::OUString&			rString,
1072                                               sal_Int32 						nStartPos,
1073                                               sal_Int32 						nLen,
1074                                               const uno::Sequence< double >&	rOffsets,
1075                                               const CanvasSharedPtr&			rCanvas,
1076                                               const OutDevState&				rState ) :
1077                 mxTextLayout(),
1078                 mpCanvas( rCanvas ),
1079                 maState()
1080             {
1081                 initArrayAction( maState,
1082                                  mxTextLayout,
1083                                  rStartPoint,
1084                                  rString,
1085                                  nStartPos,
1086                                  nLen,
1087                                  rOffsets,
1088                                  rCanvas,
1089                                  rState, NULL );
1090             }
1091 
1092             TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint& 		rStartPoint,
1093                                               const ::rtl::OUString&			rString,
1094                                               sal_Int32 						nStartPos,
1095                                               sal_Int32 						nLen,
1096                                               const uno::Sequence< double >&	rOffsets,
1097                                               const CanvasSharedPtr&			rCanvas,
1098                                               const OutDevState&				rState,
1099                                               const ::basegfx::B2DHomMatrix&	rTextTransform ) :
1100                 mxTextLayout(),
1101                 mpCanvas( rCanvas ),
1102                 maState()
1103             {
1104                 initArrayAction( maState,
1105                                  mxTextLayout,
1106                                  rStartPoint,
1107                                  rString,
1108                                  nStartPos,
1109                                  nLen,
1110                                  rOffsets,
1111                                  rCanvas,
1112                                  rState,
1113                                  &rTextTransform );
1114             }
1115 
1116             bool TextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1117             {
1118                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::render()" );
1119                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextArrayAction: 0x%X", this );
1120 
1121                 rendering::RenderState aLocalState( maState );
1122                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1123 
1124 #ifdef SPECIAL_DEBUG
1125                 aLocalState.Clip.clear();
1126                 aLocalState.DeviceColor =
1127                     ::vcl::unotools::colorToDoubleSequence( mpCanvas->getUNOCanvas()->getDevice(),
1128                                                             ::Color( 0x80FF0000 ) );
1129 
1130                 if( maState.Clip.is() )
1131                     mpCanvas->getUNOCanvas()->drawPolyPolygon( maState.Clip,
1132                                                                mpCanvas->getViewState(),
1133                                                                aLocalState );
1134 
1135                 aLocalState.DeviceColor = maState.DeviceColor;
1136 #endif
1137 
1138                 mpCanvas->getUNOCanvas()->drawTextLayout( mxTextLayout,
1139                                                           mpCanvas->getViewState(),
1140                                                           aLocalState );
1141 
1142                 return true;
1143             }
1144 
1145             bool TextArrayAction::render( const ::basegfx::B2DHomMatrix&	rTransformation,
1146                                           const Subset&						rSubset ) const
1147             {
1148                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::render( subset )" );
1149                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextArrayAction: 0x%X", this );
1150 
1151                 rendering::RenderState 						aLocalState( maState );
1152                 uno::Reference< rendering::XTextLayout >	xTextLayout( mxTextLayout );
1153 
1154                 double nDummy0, nDummy1;
1155                 createSubsetLayout( xTextLayout,
1156                                     aLocalState,
1157                                     nDummy0,
1158                                     nDummy1,
1159                                     rTransformation,
1160                                     rSubset );
1161 
1162                 if( !xTextLayout.is() )
1163                     return true; // empty layout, render nothing
1164 
1165                 mpCanvas->getUNOCanvas()->drawTextLayout( xTextLayout,
1166                                                           mpCanvas->getViewState(),
1167                                                           aLocalState );
1168 
1169                 return true;
1170             }
1171 
1172             ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1173             {
1174                 rendering::RenderState aLocalState( maState );
1175                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1176 
1177                 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1178                                                          mxTextLayout->queryTextBounds() ),
1179                                                      mpCanvas->getViewState(),
1180                                                      aLocalState );
1181             }
1182 
1183             ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
1184                                                             const Subset&					rSubset ) const
1185             {
1186                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::TextArrayAction::getBounds( subset )" );
1187                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::TextArrayAction: 0x%X", this );
1188 
1189                 rendering::RenderState 						aLocalState( maState );
1190                 uno::Reference< rendering::XTextLayout >	xTextLayout( mxTextLayout );
1191 
1192                 double nDummy0, nDummy1;
1193                 createSubsetLayout( xTextLayout,
1194                                     aLocalState,
1195                                     nDummy0,
1196                                     nDummy1,
1197                                     rTransformation,
1198                                     rSubset );
1199 
1200                 if( !xTextLayout.is() )
1201                     return ::basegfx::B2DRange(); // empty layout, empty bounds
1202 
1203                 return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1204                                                          xTextLayout->queryTextBounds() ),
1205                                                      mpCanvas->getViewState(),
1206                                                      aLocalState );
1207             }
1208 
1209             sal_Int32 TextArrayAction::getActionCount() const
1210             {
1211                 const rendering::StringContext& rOrigContext( mxTextLayout->getText() );
1212 
1213                 return rOrigContext.Length;
1214             }
1215 
1216 
1217             // -------------------------------------------------------------------------
1218 
1219             class EffectTextArrayAction :
1220                 public Action,
1221                 public TextRenderer,
1222                 private ::boost::noncopyable
1223             {
1224             public:
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 ::rtl::OUString& 			rText,
1231                                        sal_Int32 						nStartPos,
1232                                        sal_Int32 						nLen,
1233                                        const uno::Sequence< double >&	rOffsets,
1234                                        VirtualDevice&					rVDev,
1235                                        const CanvasSharedPtr&			rCanvas,
1236                                        const OutDevState& 				rState	);
1237                 EffectTextArrayAction( const ::basegfx::B2DPoint&		rStartPoint,
1238                                        const ::basegfx::B2DSize&		rReliefOffset,
1239                                        const ::Color&					rReliefColor,
1240                                        const ::basegfx::B2DSize&		rShadowOffset,
1241                                        const ::Color&					rShadowColor,
1242                                        const ::rtl::OUString& 			rText,
1243                                        sal_Int32 						nStartPos,
1244                                        sal_Int32 						nLen,
1245                                        const uno::Sequence< double >&	rOffsets,
1246                                        VirtualDevice&					rVDev,
1247                                        const CanvasSharedPtr&			rCanvas,
1248                                        const OutDevState& 				rState,
1249                                        const ::basegfx::B2DHomMatrix&	rTextTransform );
1250 
1251                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
1252                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
1253                                      const Subset&					rSubset ) const;
1254 
1255                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
1256                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
1257                                                        const Subset&					rSubset ) const;
1258 
1259                 virtual sal_Int32 getActionCount() const;
1260 
1261             private:
1262                 // TextRenderer interface
1263                 virtual bool operator()( const rendering::RenderState& rRenderState ) const;
1264 
1265                 // TODO(P2): This is potentially a real mass object
1266                 // (every character might be a separate TextAction),
1267                 // thus, make it as lightweight as possible. For
1268                 // example, share common RenderState among several
1269                 // TextActions, maybe using maOffsets for the
1270                 // translation.
1271 
1272                 uno::Reference< rendering::XTextLayout >		mxTextLayout;
1273                 const CanvasSharedPtr							mpCanvas;
1274                 rendering::RenderState							maState;
1275                 const tools::TextLineInfo						maTextLineInfo;
1276                 ::basegfx::B2DSize								maLinesOverallSize;
1277                 uno::Reference< rendering::XPolyPolygon2D >		mxTextLines;
1278                 const ::basegfx::B2DSize						maReliefOffset;
1279                 const ::Color									maReliefColor;
1280                 const ::basegfx::B2DSize						maShadowOffset;
1281                 const ::Color									maShadowColor;
1282             };
1283 
1284             EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint&		rStartPoint,
1285                                                           const ::basegfx::B2DSize&			rReliefOffset,
1286                                                           const ::Color&					rReliefColor,
1287                                                           const ::basegfx::B2DSize&			rShadowOffset,
1288                                                           const ::Color&					rShadowColor,
1289                                                           const ::rtl::OUString& 			rText,
1290                                                           sal_Int32 						nStartPos,
1291                                                           sal_Int32 						nLen,
1292                                                           const uno::Sequence< double >&	rOffsets,
1293                                                           VirtualDevice&					rVDev,
1294                                                           const CanvasSharedPtr&			rCanvas,
1295                                                           const OutDevState& 				rState	) :
1296                 mxTextLayout(),
1297                 mpCanvas( rCanvas ),
1298                 maState(),
1299                 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1300                 maLinesOverallSize(),
1301                 mxTextLines(),
1302                 maReliefOffset( rReliefOffset ),
1303                 maReliefColor( rReliefColor ),
1304                 maShadowOffset( rShadowOffset ),
1305                 maShadowColor( rShadowColor )
1306             {
1307                 initEffectLinePolyPolygon( maLinesOverallSize,
1308                                            mxTextLines,
1309                                            rCanvas,
1310                                            rOffsets,
1311                                            maTextLineInfo );
1312 
1313                 initArrayAction( maState,
1314                                  mxTextLayout,
1315                                  rStartPoint,
1316                                  rText,
1317                                  nStartPos,
1318                                  nLen,
1319                                  rOffsets,
1320                                  rCanvas,
1321                                  rState, NULL );
1322             }
1323 
1324             EffectTextArrayAction::EffectTextArrayAction( const ::basegfx::B2DPoint&		rStartPoint,
1325                                                           const ::basegfx::B2DSize&			rReliefOffset,
1326                                                           const ::Color&					rReliefColor,
1327                                                           const ::basegfx::B2DSize&			rShadowOffset,
1328                                                           const ::Color&					rShadowColor,
1329                                                           const ::rtl::OUString& 			rText,
1330                                                           sal_Int32 						nStartPos,
1331                                                           sal_Int32 						nLen,
1332                                                           const uno::Sequence< double >&	rOffsets,
1333                                                           VirtualDevice&					rVDev,
1334                                                           const CanvasSharedPtr&			rCanvas,
1335                                                           const OutDevState& 				rState,
1336                                                           const ::basegfx::B2DHomMatrix&	rTextTransform ) :
1337                 mxTextLayout(),
1338                 mpCanvas( rCanvas ),
1339                 maState(),
1340                 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1341                 maLinesOverallSize(),
1342                 mxTextLines(),
1343                 maReliefOffset( rReliefOffset ),
1344                 maReliefColor( rReliefColor ),
1345                 maShadowOffset( rShadowOffset ),
1346                 maShadowColor( rShadowColor )
1347             {
1348                 initEffectLinePolyPolygon( maLinesOverallSize,
1349                                            mxTextLines,
1350                                            rCanvas,
1351                                            rOffsets,
1352                                            maTextLineInfo );
1353 
1354                 initArrayAction( maState,
1355                                  mxTextLayout,
1356                                  rStartPoint,
1357                                  rText,
1358                                  nStartPos,
1359                                  nLen,
1360                                  rOffsets,
1361                                  rCanvas,
1362                                  rState,
1363                                  &rTextTransform );
1364             }
1365 
1366             bool EffectTextArrayAction::operator()( const rendering::RenderState& rRenderState ) const
1367             {
1368                 const rendering::ViewState& rViewState( mpCanvas->getViewState() );
1369                 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
1370 
1371                 rCanvas->fillPolyPolygon( mxTextLines,
1372                                           rViewState,
1373                                           rRenderState );
1374 
1375                 rCanvas->drawTextLayout( mxTextLayout,
1376                                          rViewState,
1377                                          rRenderState );
1378 
1379                 return true;
1380             }
1381 
1382             bool EffectTextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1383             {
1384                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::render()" );
1385                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this );
1386 
1387                 rendering::RenderState aLocalState( maState );
1388                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1389 
1390                 return renderEffectText( *this,
1391                                          aLocalState,
1392                                          mpCanvas->getViewState(),
1393                                          mpCanvas->getUNOCanvas(),
1394                                          maShadowColor,
1395                                          maShadowOffset,
1396                                          maReliefColor,
1397                                          maReliefOffset );
1398             }
1399 
1400             class EffectTextArrayRenderHelper : public TextRenderer
1401             {
1402             public:
1403                 EffectTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >&		rCanvas,
1404                                              const uno::Reference< rendering::XTextLayout >& 	rTextLayout,
1405                                              const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon,
1406                                              const rendering::ViewState&			 			rViewState ) :
1407                     mrCanvas( rCanvas ),
1408                     mrTextLayout( rTextLayout ),
1409                     mrLinePolygon( rLinePolygon ),
1410                     mrViewState( rViewState )
1411                 {
1412                 }
1413 
1414                 // TextRenderer interface
1415                 virtual bool operator()( const rendering::RenderState& rRenderState ) const
1416                 {
1417                     mrCanvas->fillPolyPolygon( mrLinePolygon,
1418                                                mrViewState,
1419                                                rRenderState );
1420 
1421                     mrCanvas->drawTextLayout( mrTextLayout,
1422                                               mrViewState,
1423                                               rRenderState );
1424 
1425                     return true;
1426                 }
1427 
1428             private:
1429                 const uno::Reference< rendering::XCanvas >&			mrCanvas;
1430                 const uno::Reference< rendering::XTextLayout >&		mrTextLayout;
1431                 const uno::Reference< rendering::XPolyPolygon2D >&	mrLinePolygon;
1432                 const rendering::ViewState&			 				mrViewState;
1433             };
1434 
1435             bool EffectTextArrayAction::render( const ::basegfx::B2DHomMatrix&	rTransformation,
1436                                                 const Subset&					rSubset ) const
1437             {
1438                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::render( subset )" );
1439                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this );
1440 
1441                 rendering::RenderState 					 aLocalState( maState );
1442                 uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1443                 const geometry::RealRectangle2D          aTextBounds( mxTextLayout->queryTextBounds() );
1444 
1445                 double nMinPos(0.0);
1446                 double nMaxPos(aTextBounds.X2 - aTextBounds.X1);
1447 
1448                 createSubsetLayout( xTextLayout,
1449                                     aLocalState,
1450                                     nMinPos,
1451                                     nMaxPos,
1452                                     rTransformation,
1453                                     rSubset );
1454 
1455                 if( !xTextLayout.is() )
1456                     return true; // empty layout, render nothing
1457 
1458 
1459                 // create and setup local line polygon
1460                 // ===================================
1461 
1462                 uno::Reference< rendering::XCanvas > xCanvas( mpCanvas->getUNOCanvas() );
1463                 const rendering::ViewState&			 rViewState( mpCanvas->getViewState() );
1464 
1465                 uno::Reference< rendering::XPolyPolygon2D > xTextLines(
1466                     ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1467                         xCanvas->getDevice(),
1468                         tools::createTextLinesPolyPolygon(
1469                             0.0, nMaxPos - nMinPos,
1470                             maTextLineInfo ) ) );
1471 
1472 
1473                 // render everything
1474                 // =================
1475 
1476                 return renderEffectText(
1477                     EffectTextArrayRenderHelper( xCanvas,
1478                                                  xTextLayout,
1479                                                  xTextLines,
1480                                                  rViewState ),
1481                     aLocalState,
1482                     rViewState,
1483                     xCanvas,
1484                     maShadowColor,
1485                     maShadowOffset,
1486                     maReliefColor,
1487                     maReliefOffset );
1488             }
1489 
1490             ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1491             {
1492                 rendering::RenderState aLocalState( maState );
1493                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1494 
1495                 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1496                                                  mxTextLayout->queryTextBounds() ),
1497                                              ::basegfx::B2DRange( 0,0,
1498                                                                   maLinesOverallSize.getX(),
1499                                                                   maLinesOverallSize.getY() ),
1500                                              maReliefOffset,
1501                                              maShadowOffset,
1502                                              aLocalState,
1503                                              mpCanvas->getViewState() );
1504             }
1505 
1506             ::basegfx::B2DRange EffectTextArrayAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
1507                                                                   const Subset&						rSubset ) const
1508             {
1509                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::getBounds( subset )" );
1510                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this );
1511 
1512                 rendering::RenderState 					 aLocalState( maState );
1513                 uno::Reference< rendering::XTextLayout > xTextLayout( mxTextLayout );
1514                 const geometry::RealRectangle2D          aTextBounds( mxTextLayout->queryTextBounds() );
1515 
1516                 double nMinPos(0.0);
1517                 double nMaxPos(aTextBounds.X2 - aTextBounds.X1);
1518 
1519                 createSubsetLayout( xTextLayout,
1520                                     aLocalState,
1521                                     nMinPos,
1522                                     nMaxPos,
1523                                     rTransformation,
1524                                     rSubset );
1525 
1526                 if( !xTextLayout.is() )
1527                     return ::basegfx::B2DRange(); // empty layout, empty bounds
1528 
1529 
1530                 // create and setup local line polygon
1531                 // ===================================
1532 
1533                 const ::basegfx::B2DPolyPolygon aPoly(
1534                     tools::createTextLinesPolyPolygon(
1535                         0.0, nMaxPos - nMinPos,
1536                         maTextLineInfo ) );
1537 
1538                 return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
1539                                                  xTextLayout->queryTextBounds() ),
1540                                              ::basegfx::tools::getRange( aPoly ),
1541                                              maReliefOffset,
1542                                              maShadowOffset,
1543                                              aLocalState,
1544                                              mpCanvas->getViewState() );
1545             }
1546 
1547             sal_Int32 EffectTextArrayAction::getActionCount() const
1548             {
1549                 const rendering::StringContext& rOrigContext( mxTextLayout->getText() );
1550 
1551                 return rOrigContext.Length;
1552             }
1553 
1554 
1555             // -------------------------------------------------------------------------
1556 
1557             class OutlineAction :
1558                 public Action,
1559                 public TextRenderer,
1560                 private ::boost::noncopyable
1561             {
1562             public:
1563                 OutlineAction( const ::basegfx::B2DPoint&							rStartPoint,
1564                                const ::basegfx::B2DSize&							rReliefOffset,
1565                                const ::Color&										rReliefColor,
1566                                const ::basegfx::B2DSize&							rShadowOffset,
1567                                const ::Color&										rShadowColor,
1568                                const ::basegfx::B2DRectangle&						rOutlineBounds,
1569                                const uno::Reference< rendering::XPolyPolygon2D >&	rTextPoly,
1570                                const ::std::vector< sal_Int32 >& 					rPolygonGlyphMap,
1571                                const uno::Sequence< double >&						rOffsets,
1572                                VirtualDevice&										rVDev,
1573                                const CanvasSharedPtr&								rCanvas,
1574                                const OutDevState& 									rState	);
1575                 OutlineAction( const ::basegfx::B2DPoint&							rStartPoint,
1576                                const ::basegfx::B2DSize&							rReliefOffset,
1577                                const ::Color&										rReliefColor,
1578                                const ::basegfx::B2DSize&							rShadowOffset,
1579                                const ::Color&										rShadowColor,
1580                                const ::basegfx::B2DRectangle&						rOutlineBounds,
1581                                const uno::Reference< rendering::XPolyPolygon2D >&	rTextPoly,
1582                                const ::std::vector< sal_Int32 >& 					rPolygonGlyphMap,
1583                                const uno::Sequence< double >&						rOffsets,
1584                                VirtualDevice&										rVDev,
1585                                const CanvasSharedPtr&								rCanvas,
1586                                const OutDevState& 									rState,
1587                                const ::basegfx::B2DHomMatrix&						rTextTransform );
1588 
1589                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const;
1590                 virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation,
1591                                      const Subset&					rSubset ) const;
1592 
1593                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const;
1594                 virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
1595                                                        const Subset&					rSubset ) const;
1596 
1597                 virtual sal_Int32 getActionCount() const;
1598 
1599             private:
1600                 // TextRenderer interface
1601                 virtual bool operator()( const rendering::RenderState& rRenderState ) const;
1602 
1603                 // TODO(P2): This is potentially a real mass object
1604                 // (every character might be a separate TextAction),
1605                 // thus, make it as lightweight as possible. For
1606                 // example, share common RenderState among several
1607                 // TextActions, maybe using maOffsets for the
1608                 // translation.
1609 
1610                 uno::Reference< rendering::XPolyPolygon2D >			mxTextPoly;
1611 
1612                 /** This vector denotes the index of the start polygon
1613                     for the respective glyph sequence.
1614 
1615                     To get a polygon index range for a given character
1616                     index i, take [ maPolygonGlyphMap[i],
1617                     maPolygonGlyphMap[i+1] ). Note that this is wrong
1618                     for BiDi
1619                  */
1620                 const ::std::vector< sal_Int32 > 					maPolygonGlyphMap;
1621                 const uno::Sequence< double >						maOffsets;
1622                 const CanvasSharedPtr								mpCanvas;
1623                 rendering::RenderState								maState;
1624                 double												mnOutlineWidth;
1625                 const uno::Sequence< double >						maFillColor;
1626                 const tools::TextLineInfo							maTextLineInfo;
1627                 ::basegfx::B2DSize									maLinesOverallSize;
1628                 const ::basegfx::B2DRectangle						maOutlineBounds;
1629                 uno::Reference< rendering::XPolyPolygon2D >			mxTextLines;
1630                 const ::basegfx::B2DSize							maReliefOffset;
1631                 const ::Color										maReliefColor;
1632                 const ::basegfx::B2DSize							maShadowOffset;
1633                 const ::Color										maShadowColor;
1634             };
1635 
1636             double calcOutlineWidth( const OutDevState& rState,
1637                                      VirtualDevice&     rVDev )
1638             {
1639                 const ::basegfx::B2DSize aFontSize( 0,
1640                                                     rVDev.GetFont().GetHeight() / 64.0 );
1641 
1642                 const double nOutlineWidth(
1643                     (rState.mapModeTransform * aFontSize).getY() );
1644 
1645                 return nOutlineWidth < 1.0 ? 1.0 : nOutlineWidth;
1646             }
1647 
1648             OutlineAction::OutlineAction( const ::basegfx::B2DPoint&							rStartPoint,
1649                                           const ::basegfx::B2DSize&								rReliefOffset,
1650                                           const ::Color&										rReliefColor,
1651                                           const ::basegfx::B2DSize&								rShadowOffset,
1652                                           const ::Color&										rShadowColor,
1653                                           const ::basegfx::B2DRectangle&						rOutlineBounds,
1654                                           const uno::Reference< rendering::XPolyPolygon2D >& 	rTextPoly,
1655                                           const ::std::vector< sal_Int32 >& 					rPolygonGlyphMap,
1656                                           const uno::Sequence< double >&						rOffsets,
1657                                           VirtualDevice&										rVDev,
1658                                           const CanvasSharedPtr&								rCanvas,
1659                                           const OutDevState& 									rState	) :
1660                 mxTextPoly( rTextPoly ),
1661                 maPolygonGlyphMap( rPolygonGlyphMap ),
1662                 maOffsets( rOffsets ),
1663                 mpCanvas( rCanvas ),
1664                 maState(),
1665                 mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
1666                 maFillColor(
1667                     ::vcl::unotools::colorToDoubleSequence(
1668                         ::Color( COL_WHITE ),
1669                         rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1670                 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1671                 maLinesOverallSize(),
1672                 maOutlineBounds( rOutlineBounds ),
1673                 mxTextLines(),
1674                 maReliefOffset( rReliefOffset ),
1675                 maReliefColor( rReliefColor ),
1676                 maShadowOffset( rShadowOffset ),
1677                 maShadowColor( rShadowColor )
1678             {
1679                 initEffectLinePolyPolygon( maLinesOverallSize,
1680                                            mxTextLines,
1681                                            rCanvas,
1682                                            rOffsets,
1683                                            maTextLineInfo );
1684 
1685                 init( maState,
1686                       rStartPoint,
1687                       rState,
1688                       rCanvas );
1689             }
1690 
1691             OutlineAction::OutlineAction( const ::basegfx::B2DPoint&							rStartPoint,
1692                                           const ::basegfx::B2DSize&								rReliefOffset,
1693                                           const ::Color&										rReliefColor,
1694                                           const ::basegfx::B2DSize&								rShadowOffset,
1695                                           const ::Color&										rShadowColor,
1696                                           const ::basegfx::B2DRectangle&						rOutlineBounds,
1697                                           const uno::Reference< rendering::XPolyPolygon2D >& 	rTextPoly,
1698                                           const ::std::vector< sal_Int32 >& 					rPolygonGlyphMap,
1699                                           const uno::Sequence< double >&						rOffsets,
1700                                           VirtualDevice&										rVDev,
1701                                           const CanvasSharedPtr&								rCanvas,
1702                                           const OutDevState& 									rState,
1703                                           const ::basegfx::B2DHomMatrix&						rTextTransform ) :
1704                 mxTextPoly( rTextPoly ),
1705                 maPolygonGlyphMap( rPolygonGlyphMap ),
1706                 maOffsets( rOffsets ),
1707                 mpCanvas( rCanvas ),
1708                 maState(),
1709                 mnOutlineWidth( calcOutlineWidth(rState,rVDev) ),
1710                 maFillColor(
1711                     ::vcl::unotools::colorToDoubleSequence(
1712                         ::Color( COL_WHITE ),
1713                         rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() )),
1714                 maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
1715                 maLinesOverallSize(),
1716                 maOutlineBounds( rOutlineBounds ),
1717                 mxTextLines(),
1718                 maReliefOffset( rReliefOffset ),
1719                 maReliefColor( rReliefColor ),
1720                 maShadowOffset( rShadowOffset ),
1721                 maShadowColor( rShadowColor )
1722             {
1723                 initEffectLinePolyPolygon( maLinesOverallSize,
1724                                            mxTextLines,
1725                                            rCanvas,
1726                                            rOffsets,
1727                                            maTextLineInfo );
1728 
1729                 init( maState,
1730                       rStartPoint,
1731                       rState,
1732                       rCanvas,
1733                       rTextTransform );
1734             }
1735 
1736             bool OutlineAction::operator()( const rendering::RenderState& rRenderState ) const
1737             {
1738                 const rendering::ViewState& 				rViewState( mpCanvas->getViewState() );
1739                 const uno::Reference< rendering::XCanvas >& rCanvas( mpCanvas->getUNOCanvas() );
1740 
1741                 rendering::StrokeAttributes aStrokeAttributes;
1742 
1743                 aStrokeAttributes.StrokeWidth  = mnOutlineWidth;
1744                 aStrokeAttributes.MiterLimit   = 1.0;
1745                 aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
1746                 aStrokeAttributes.EndCapType   = rendering::PathCapType::BUTT;
1747                 aStrokeAttributes.JoinType     = rendering::PathJoinType::MITER;
1748 
1749                 rendering::RenderState aLocalState( rRenderState );
1750                 aLocalState.DeviceColor = maFillColor;
1751 
1752                 // TODO(P1): implement caching
1753 
1754                 // background of text
1755                 rCanvas->fillPolyPolygon( mxTextPoly,
1756                                           rViewState,
1757                                           aLocalState );
1758 
1759                 // border line of text
1760                 rCanvas->strokePolyPolygon( mxTextPoly,
1761                                             rViewState,
1762                                             rRenderState,
1763                                             aStrokeAttributes );
1764 
1765                 // underlines/strikethrough - background
1766                 rCanvas->fillPolyPolygon( mxTextLines,
1767                                           rViewState,
1768                                           aLocalState );
1769                 // underlines/strikethrough - border
1770                 rCanvas->strokePolyPolygon( mxTextLines,
1771                                             rViewState,
1772                                             rRenderState,
1773                                             aStrokeAttributes );
1774 
1775                 return true;
1776             }
1777 
1778             bool OutlineAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
1779             {
1780                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::EffectTextArrayAction::render()" );
1781                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::EffectTextArrayAction: 0x%X", this );
1782 
1783                 rendering::RenderState aLocalState( maState );
1784                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1785 
1786                 return renderEffectText( *this,
1787                                          aLocalState,
1788                                          mpCanvas->getViewState(),
1789                                          mpCanvas->getUNOCanvas(),
1790                                          maShadowColor,
1791                                          maShadowOffset,
1792                                          maReliefColor,
1793                                          maReliefOffset );
1794             }
1795 
1796             class OutlineTextArrayRenderHelper : public TextRenderer
1797             {
1798             public:
1799                 OutlineTextArrayRenderHelper( const uno::Reference< rendering::XCanvas >&		 rCanvas,
1800                                               const uno::Reference< rendering::XPolyPolygon2D >& rTextPolygon,
1801                                               const uno::Reference< rendering::XPolyPolygon2D >& rLinePolygon,
1802                                               const rendering::ViewState&			 			 rViewState,
1803                                               double											 nOutlineWidth ) :
1804                     maFillColor(
1805                         ::vcl::unotools::colorToDoubleSequence(
1806                             ::Color( COL_WHITE ),
1807                             rCanvas->getDevice()->getDeviceColorSpace() )),
1808                     mnOutlineWidth( nOutlineWidth ),
1809                     mrCanvas( rCanvas ),
1810                     mrTextPolygon( rTextPolygon ),
1811                     mrLinePolygon( rLinePolygon ),
1812                     mrViewState( rViewState )
1813                 {
1814                 }
1815 
1816                 // TextRenderer interface
1817                 virtual bool operator()( const rendering::RenderState& rRenderState ) const
1818                 {
1819                     rendering::StrokeAttributes aStrokeAttributes;
1820 
1821                     aStrokeAttributes.StrokeWidth  = mnOutlineWidth;
1822                     aStrokeAttributes.MiterLimit   = 1.0;
1823                     aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
1824                     aStrokeAttributes.EndCapType   = rendering::PathCapType::BUTT;
1825                     aStrokeAttributes.JoinType     = rendering::PathJoinType::MITER;
1826 
1827                     rendering::RenderState aLocalState( rRenderState );
1828                     aLocalState.DeviceColor = maFillColor;
1829 
1830                     // TODO(P1): implement caching
1831 
1832                     // background of text
1833                     mrCanvas->fillPolyPolygon( mrTextPolygon,
1834                                                mrViewState,
1835                                                aLocalState );
1836 
1837                     // border line of text
1838                     mrCanvas->strokePolyPolygon( mrTextPolygon,
1839                                                  mrViewState,
1840                                                  rRenderState,
1841                                                  aStrokeAttributes );
1842 
1843                     // underlines/strikethrough - background
1844                     mrCanvas->fillPolyPolygon( mrLinePolygon,
1845                                                mrViewState,
1846                                                aLocalState );
1847                     // underlines/strikethrough - border
1848                     mrCanvas->strokePolyPolygon( mrLinePolygon,
1849                                                  mrViewState,
1850                                                  rRenderState,
1851                                                  aStrokeAttributes );
1852 
1853                     return true;
1854                 }
1855 
1856             private:
1857                 const uno::Sequence< double >						maFillColor;
1858                 double												mnOutlineWidth;
1859                 const uno::Reference< rendering::XCanvas >&			mrCanvas;
1860                 const uno::Reference< rendering::XPolyPolygon2D >&	mrTextPolygon;
1861                 const uno::Reference< rendering::XPolyPolygon2D >&	mrLinePolygon;
1862                 const rendering::ViewState&			 				mrViewState;
1863             };
1864 
1865             bool OutlineAction::render( const ::basegfx::B2DHomMatrix&	rTransformation,
1866                                         const Subset&					rSubset ) const
1867             {
1868                 RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::OutlineAction::render( subset )" );
1869                 RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::cppcanvas::internal::OutlineAction: 0x%X", this );
1870 
1871                 if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
1872                     return true; // empty range, render nothing
1873 
1874 #if 1
1875                 // TODO(F3): Subsetting NYI for outline text!
1876                 return render( rTransformation );
1877 #else
1878                 const rendering::StringContext rOrigContext( mxTextLayout->getText() );
1879 
1880                 if( rSubset.mnSubsetBegin == 0 &&
1881                     rSubset.mnSubsetEnd == rOrigContext.Length )
1882                 {
1883                     // full range, no need for subsetting
1884                     return render( rTransformation );
1885                 }
1886 
1887                 rendering::RenderState aLocalState( maState );
1888                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1889 
1890 
1891                 // create and setup local Text polygon
1892                 // ===================================
1893 
1894                 uno::Reference< rendering::XPolyPolygon2D > xTextPolygon();
1895 
1896                 // TODO(P3): Provide an API method for that!
1897 
1898                 if( !xTextLayout.is() )
1899                     return false;
1900 
1901                 // render everything
1902                 // =================
1903 
1904                 return renderEffectText(
1905                     OutlineTextArrayRenderHelper(
1906                         xCanvas,
1907                         mnOutlineWidth,
1908                         xTextLayout,
1909                         xTextLines,
1910                         rViewState ),
1911                     aLocalState,
1912                     rViewState,
1913                     xCanvas,
1914                     maShadowColor,
1915                     maShadowOffset,
1916                     maReliefColor,
1917                     maReliefOffset );
1918 #endif
1919             }
1920 
1921             ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
1922             {
1923                 rendering::RenderState aLocalState( maState );
1924                 ::canvas::tools::prependToRenderState(aLocalState, rTransformation);
1925 
1926                 return calcEffectTextBounds( maOutlineBounds,
1927                                              ::basegfx::B2DRange( 0,0,
1928                                                                   maLinesOverallSize.getX(),
1929                                                                   maLinesOverallSize.getY() ),
1930                                              maReliefOffset,
1931                                              maShadowOffset,
1932                                              aLocalState,
1933                                              mpCanvas->getViewState() );
1934             }
1935 
1936             ::basegfx::B2DRange OutlineAction::getBounds( const ::basegfx::B2DHomMatrix&	rTransformation,
1937                                                           const Subset&						/*rSubset*/ ) const
1938             {
1939                 OSL_ENSURE( false,
1940                             "OutlineAction::getBounds(): Subset not yet supported by this object" );
1941 
1942                 return getBounds( rTransformation );
1943             }
1944 
1945             sal_Int32 OutlineAction::getActionCount() const
1946             {
1947                 // TODO(F3): Subsetting NYI for outline text!
1948                 return maOffsets.getLength();
1949             }
1950 
1951 
1952             // ======================================================================
1953             //
1954             // Action factory methods
1955             //
1956             // ======================================================================
1957 
1958             /** Create an outline action
1959 
1960             	This method extracts the polygonal outline from the
1961             	text, and creates a properly setup OutlineAction from
1962             	it.
1963              */
1964             ActionSharedPtr createOutline( const ::basegfx::B2DPoint&		rStartPoint,
1965                                            const ::basegfx::B2DSize&		rReliefOffset,
1966                                            const ::Color&					rReliefColor,
1967                                            const ::basegfx::B2DSize&        rShadowOffset,
1968                                            const ::Color&					rShadowColor,
1969                                            const String& 					rText,
1970                                            sal_Int32 						nStartPos,
1971                                            sal_Int32 						nLen,
1972                                            const sal_Int32*					pDXArray,
1973                                            VirtualDevice&					rVDev,
1974                                            const CanvasSharedPtr&			rCanvas,
1975                                            const OutDevState& 				rState,
1976                                            const Renderer::Parameters& 		rParms	)
1977             {
1978                 // operate on raw DX array here (in logical coordinate
1979                 // system), to have a higher resolution
1980                 // PolyPolygon. That polygon is then converted to
1981                 // device coordinate system.
1982 
1983                 // #i68512# Temporarily switch off font rotation
1984                 // (which is already contained in the render state
1985                 // transformation matrix - otherwise, glyph polygons
1986                 // will be rotated twice)
1987                 const ::Font aOrigFont( rVDev.GetFont() );
1988                 ::Font       aUnrotatedFont( aOrigFont );
1989                 aUnrotatedFont.SetOrientation(0);
1990                 rVDev.SetFont( aUnrotatedFont );
1991 
1992                 // TODO(F3): Don't understand parameter semantics of
1993                 // GetTextOutlines()
1994                 ::basegfx::B2DPolyPolygon aResultingPolyPolygon;
1995                 PolyPolyVector aVCLPolyPolyVector;
1996                 const bool bHaveOutlines( rVDev.GetTextOutlines( aVCLPolyPolyVector, rText,
1997                                                                  static_cast<sal_uInt16>(nStartPos),
1998                                                                  static_cast<sal_uInt16>(nStartPos),
1999                                                                  static_cast<sal_uInt16>(nLen),
2000                                                                  sal_True, 0, pDXArray ) );
2001                 rVDev.SetFont(aOrigFont);
2002 
2003                 if( !bHaveOutlines )
2004                     return ActionSharedPtr();
2005 
2006                 ::std::vector< sal_Int32 > aPolygonGlyphMap;
2007 
2008                 // first glyph starts at polygon index 0
2009                 aPolygonGlyphMap.push_back( 0 );
2010 
2011                 // remove offsetting from mapmode transformation
2012                 // (outline polygons must stay at origin, only need to
2013                 // be scaled)
2014                 ::basegfx::B2DHomMatrix aMapModeTransform(
2015                     rState.mapModeTransform );
2016                 aMapModeTransform.set(0,2, 0.0);
2017                 aMapModeTransform.set(1,2, 0.0);
2018 
2019                 PolyPolyVector::const_iterator 		 aIter( aVCLPolyPolyVector.begin() );
2020                 const PolyPolyVector::const_iterator aEnd( aVCLPolyPolyVector.end() );
2021                 for( ; aIter!= aEnd; ++aIter )
2022                 {
2023                     ::basegfx::B2DPolyPolygon aPolyPolygon;
2024 
2025                     aPolyPolygon = aIter->getB2DPolyPolygon();
2026                     aPolyPolygon.transform( aMapModeTransform );
2027 
2028                     // append result to collecting polypoly
2029                     for( sal_uInt32 i=0; i<aPolyPolygon.count(); ++i )
2030                     {
2031                         // #i47795# Ensure closed polygons (since
2032                         // FreeType returns the glyph outlines
2033                         // open)
2034                         const ::basegfx::B2DPolygon& rPoly( aPolyPolygon.getB2DPolygon( i ) );
2035                         const sal_uInt32 nCount( rPoly.count() );
2036                         if( nCount<3 ||
2037                             rPoly.isClosed() )
2038                         {
2039                             // polygon either degenerate, or
2040                             // already closed.
2041                             aResultingPolyPolygon.append( rPoly );
2042                         }
2043                         else
2044                         {
2045                             ::basegfx::B2DPolygon aPoly(rPoly);
2046                             aPoly.setClosed(true);
2047 
2048                             aResultingPolyPolygon.append( aPoly );
2049                         }
2050                     }
2051 
2052                     // TODO(F3): Depending on the semantics of
2053                     // GetTextOutlines(), this here is wrong!
2054 
2055                     // calc next glyph index
2056                     aPolygonGlyphMap.push_back( aResultingPolyPolygon.count() );
2057                 }
2058 
2059                 const uno::Sequence< double > aCharWidthSeq(
2060                     pDXArray ?
2061                     setupDXArray( pDXArray, nLen, rState ) :
2062                     setupDXArray( rText,
2063                                   nStartPos,
2064                                   nLen,
2065                                   rVDev,
2066                                   rState ));
2067                 const uno::Reference< rendering::XPolyPolygon2D > xTextPoly(
2068                     ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
2069                         rCanvas->getUNOCanvas()->getDevice(),
2070                         aResultingPolyPolygon ) );
2071 
2072                 if( rParms.maTextTransformation.is_initialized() )
2073                 {
2074                     return ActionSharedPtr(
2075                         new OutlineAction(
2076                             rStartPoint,
2077                             rReliefOffset,
2078                             rReliefColor,
2079                             rShadowOffset,
2080                             rShadowColor,
2081                             ::basegfx::tools::getRange(aResultingPolyPolygon),
2082                             xTextPoly,
2083                             aPolygonGlyphMap,
2084                             aCharWidthSeq,
2085                             rVDev,
2086                             rCanvas,
2087                             rState,
2088                             *rParms.maTextTransformation ) );
2089                 }
2090                 else
2091                 {
2092                     return ActionSharedPtr(
2093                         new OutlineAction(
2094                             rStartPoint,
2095                             rReliefOffset,
2096                             rReliefColor,
2097                             rShadowOffset,
2098                             rShadowColor,
2099                             ::basegfx::tools::getRange(aResultingPolyPolygon),
2100                             xTextPoly,
2101                             aPolygonGlyphMap,
2102                             aCharWidthSeq,
2103                             rVDev,
2104                             rCanvas,
2105                             rState	) );
2106                 }
2107             }
2108 
2109         } // namespace
2110 
2111 
2112         // ---------------------------------------------------------------------------------
2113 
2114 		ActionSharedPtr TextActionFactory::createTextAction( const ::Point&					rStartPoint,
2115                                                              const ::Size&					rReliefOffset,
2116                                                              const ::Color&					rReliefColor,
2117                                                              const ::Size&					rShadowOffset,
2118                                                              const ::Color&					rShadowColor,
2119                                                              const String& 					rText,
2120                                                              sal_Int32 						nStartPos,
2121                                                              sal_Int32 						nLen,
2122                                                              const sal_Int32*				pDXArray,
2123                                                              VirtualDevice&					rVDev,
2124                                                              const CanvasSharedPtr&			rCanvas,
2125                                                              const OutDevState& 			rState,
2126                                                              const Renderer::Parameters& 	rParms,
2127                                                              bool							bSubsettable	)
2128 		{
2129             const ::Size  aBaselineOffset( tools::getBaselineOffset( rState,
2130                                                                      rVDev ) );
2131             // #143885# maintain (nearly) full precision positioning,
2132             // by circumventing integer-based OutDev-mapping
2133             const ::basegfx::B2DPoint aStartPoint(
2134                 rState.mapModeTransform *
2135                 ::basegfx::B2DPoint(rStartPoint.X() + aBaselineOffset.Width(),
2136                                     rStartPoint.Y() + aBaselineOffset.Height()) );
2137 
2138             const ::basegfx::B2DSize aReliefOffset(
2139                 rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize( rReliefOffset ) );
2140             const ::basegfx::B2DSize aShadowOffset(
2141                 rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize( rShadowOffset ) );
2142 
2143             if( rState.isTextOutlineModeSet )
2144             {
2145                 return createOutline(
2146                     		aStartPoint,
2147                             aReliefOffset,
2148                             rReliefColor,
2149                             aShadowOffset,
2150                             rShadowColor,
2151                             rText,
2152                             nStartPos,
2153                             nLen,
2154                             pDXArray,
2155                             rVDev,
2156                             rCanvas,
2157                             rState,
2158                             rParms );
2159             }
2160 
2161             // convert DX array to device coordinate system (and
2162             // create it in the first place, if pDXArray is NULL)
2163             const uno::Sequence< double > aCharWidths(
2164                 pDXArray ?
2165                 setupDXArray( pDXArray, nLen, rState ) :
2166                 setupDXArray( rText,
2167                               nStartPos,
2168                               nLen,
2169                               rVDev,
2170                               rState ));
2171 
2172             // determine type of text action to create
2173             // =======================================
2174 
2175             const ::Color aEmptyColor( COL_AUTO );
2176 
2177             // no DX array, and no need to subset - no need to store
2178             // DX array, then.
2179             if( !pDXArray && !bSubsettable )
2180             {
2181                 // effects, or not?
2182                 if( !rState.textOverlineStyle &&
2183                     !rState.textUnderlineStyle &&
2184                     !rState.textStrikeoutStyle &&
2185                     rReliefColor == aEmptyColor &&
2186                     rShadowColor == aEmptyColor )
2187                 {
2188                     // nope
2189                     if( rParms.maTextTransformation.is_initialized() )
2190                     {
2191                         return ActionSharedPtr( new TextAction(
2192                                                     aStartPoint,
2193                                                     rText,
2194                                                     nStartPos,
2195                                                     nLen,
2196                                                     rCanvas,
2197                                                     rState,
2198                                                     *rParms.maTextTransformation ) );
2199                     }
2200                     else
2201                     {
2202                         return ActionSharedPtr( new TextAction(
2203                                                     aStartPoint,
2204                                                     rText,
2205                                                     nStartPos,
2206                                                     nLen,
2207                                                     rCanvas,
2208                                                     rState ) );
2209                     }
2210                 }
2211                 else
2212                 {
2213                     // at least one of the effects requested
2214                     if( rParms.maTextTransformation.is_initialized() )
2215                         return ActionSharedPtr( new EffectTextAction(
2216                                                     aStartPoint,
2217                                                     aReliefOffset,
2218                                                     rReliefColor,
2219                                                     aShadowOffset,
2220                                                     rShadowColor,
2221                                                     rText,
2222                                                     nStartPos,
2223                                                     nLen,
2224                                                     rVDev,
2225                                                     rCanvas,
2226                                                     rState,
2227                                                     *rParms.maTextTransformation ) );
2228                     else
2229                         return ActionSharedPtr( new EffectTextAction(
2230                                                     aStartPoint,
2231                                                     aReliefOffset,
2232                                                     rReliefColor,
2233                                                     aShadowOffset,
2234                                                     rShadowColor,
2235                                                     rText,
2236                                                     nStartPos,
2237                                                     nLen,
2238                                                     rVDev,
2239                                                     rCanvas,
2240                                                     rState ) );
2241                 }
2242             }
2243             else
2244             {
2245                 // DX array necessary - any effects?
2246                 if( !rState.textOverlineStyle &&
2247                     !rState.textUnderlineStyle &&
2248                     !rState.textStrikeoutStyle &&
2249                     rReliefColor == aEmptyColor &&
2250                     rShadowColor == aEmptyColor )
2251                 {
2252                     // nope
2253                     if( rParms.maTextTransformation.is_initialized() )
2254                         return ActionSharedPtr( new TextArrayAction(
2255                                                     aStartPoint,
2256                                                     rText,
2257                                                     nStartPos,
2258                                                     nLen,
2259                                                     aCharWidths,
2260                                                     rCanvas,
2261                                                     rState,
2262                                                     *rParms.maTextTransformation ) );
2263                     else
2264                         return ActionSharedPtr( new TextArrayAction(
2265                                                     aStartPoint,
2266                                                     rText,
2267                                                     nStartPos,
2268                                                     nLen,
2269                                                     aCharWidths,
2270                                                     rCanvas,
2271                                                     rState ) );
2272                 }
2273                 else
2274                 {
2275                     // at least one of the effects requested
2276                     if( rParms.maTextTransformation.is_initialized() )
2277                         return ActionSharedPtr( new EffectTextArrayAction(
2278                                                     aStartPoint,
2279                                                     aReliefOffset,
2280                                                     rReliefColor,
2281                                                     aShadowOffset,
2282                                                     rShadowColor,
2283                                                     rText,
2284                                                     nStartPos,
2285                                                     nLen,
2286                                                     aCharWidths,
2287                                                     rVDev,
2288                                                     rCanvas,
2289                                                     rState,
2290                                                     *rParms.maTextTransformation ) );
2291                     else
2292                         return ActionSharedPtr( new EffectTextArrayAction(
2293                                                     aStartPoint,
2294                                                     aReliefOffset,
2295                                                     rReliefColor,
2296                                                     aShadowOffset,
2297                                                     rShadowColor,
2298                                                     rText,
2299                                                     nStartPos,
2300                                                     nLen,
2301                                                     aCharWidths,
2302                                                     rVDev,
2303                                                     rCanvas,
2304                                                     rState ) );
2305                 }
2306             }
2307 #if defined __GNUC__
2308 #if __GNUC__ == 4 && __GNUC_MINOR__ >= 1
2309             // Unreachable; to avoid bogus warning:
2310             return ActionSharedPtr();
2311 #endif
2312 #endif
2313         }
2314     }
2315 }
2316