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