xref: /AOO41X/main/cppcanvas/source/mtfrenderer/mtftools.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_cppcanvas.hxx"
30 
31 #include <canvas/debug.hxx>
32 #include <tools/diagnose_ex.h>
33 #include <canvas/verbosetrace.hxx>
34 #include <com/sun/star/rendering/RenderState.hpp>
35 #include <com/sun/star/rendering/XCanvas.hpp>
36 #include <basegfx/numeric/ftools.hxx>
37 #include <basegfx/tools/canvastools.hxx>
38 #include <basegfx/polygon/b2dpolygontools.hxx>
39 #include <basegfx/polygon/b2dpolygon.hxx>
40 #include <basegfx/range/b2drectangle.hxx>
41 #include <basegfx/vector/b2dvector.hxx>
42 #include <canvas/canvastools.hxx>
43 #include <vcl/gdimtf.hxx>
44 #include <vcl/metaact.hxx>
45 #include <vcl/virdev.hxx>
46 #include <vcl/metric.hxx>
47 #include <tools/poly.hxx>
48 #include "mtftools.hxx"
49 #include "outdevstate.hxx"
50 #include "polypolyaction.hxx"
51 #include <basegfx/matrix/b2dhommatrixtools.hxx>
52 
53 
54 
55 using namespace ::com::sun::star;
56 
57 namespace cppcanvas
58 {
59     namespace tools
60     {
61         void initRenderState( rendering::RenderState&					renderState,
62                               const ::cppcanvas::internal::OutDevState&	outdevState )
63         {
64             ::canvas::tools::initRenderState( renderState );
65             ::canvas::tools::setRenderStateTransform( renderState,
66                                                       outdevState.transform );
67             renderState.Clip = outdevState.xClipPoly;
68         }
69 
70         ::Size getBaselineOffset( const ::cppcanvas::internal::OutDevState&	outdevState,
71                                   const VirtualDevice&						rVDev )
72         {
73             const ::FontMetric& aMetric = rVDev.GetFontMetric();
74 
75             // calc offset for text output, the XCanvas always renders
76             // baseline offset.
77             switch( outdevState.textReferencePoint )
78             {
79                 case ALIGN_TOP:
80                     return ::Size( 0,
81                                    aMetric.GetIntLeading() + aMetric.GetAscent() );
82 
83                 default:
84                     ENSURE_OR_THROW( false,
85                                       "tools::getBaselineOffset(): Unexpected TextAlign value" );
86                     // FALLTHROUGH intended (to calm compiler warning - case won't happen)
87                 case ALIGN_BASELINE:
88                     return ::Size( 0, 0 );
89 
90                 case ALIGN_BOTTOM:
91                     return ::Size( 0,
92                                    -aMetric.GetDescent() );
93 
94             }
95         }
96 
97         ::basegfx::B2DHomMatrix& calcLogic2PixelLinearTransform( ::basegfx::B2DHomMatrix&	o_rMatrix,
98                                                                  const VirtualDevice& 		rVDev )
99         {
100             // select size value in the middle of the available range,
101             // to have headroom both when map mode scales up, and when
102             // it scales down.
103             const ::Size aSizeLogic( 0x00010000L,
104                                      0x00010000L );
105 
106             const ::Size aSizePixel( rVDev.LogicToPixel( aSizeLogic ) );
107 
108 			o_rMatrix = basegfx::tools::createScaleB2DHomMatrix(
109 				aSizePixel.Width() / (double)aSizeLogic.Width(),
110 				aSizePixel.Height() / (double)aSizeLogic.Height() );
111 
112             return o_rMatrix;
113         }
114 
115         ::basegfx::B2DHomMatrix& calcLogic2PixelAffineTransform( ::basegfx::B2DHomMatrix&	o_rMatrix,
116                                                                  const VirtualDevice& 		rVDev )
117         {
118             // retrieves scale
119             calcLogic2PixelLinearTransform(o_rMatrix, rVDev);
120 
121             // translate according to curr map mode/pref map mode offset
122             const ::Point  aEmptyPoint;
123             const ::Point& rTranslatedPoint(
124                 rVDev.LogicToPixel( aEmptyPoint ));
125 
126             o_rMatrix.translate(rTranslatedPoint.X(),
127                                 rTranslatedPoint.Y());
128 
129             return o_rMatrix;
130         }
131 
132         bool modifyClip( rendering::RenderState&							o_rRenderState,
133                          const struct ::cppcanvas::internal::OutDevState&	rOutdevState,
134                          const CanvasSharedPtr&								rCanvas,
135                          const ::basegfx::B2DPoint&							rOffset,
136                          const ::basegfx::B2DVector*						pScaling,
137                          const double*                                      pRotation )
138         {
139             const ::Point aEmptyPoint;
140 
141             const bool bOffsetting( !rOffset.equalZero() );
142             const bool bScaling( pScaling &&
143                                  pScaling->getX() != 1.0 &&
144                                  pScaling->getY() != 1.0 );
145             const bool bRotation( pRotation &&
146                                   *pRotation != 0.0 );
147 
148             if( !bOffsetting && !bScaling && !bRotation )
149                 return false; // nothing to do
150 
151             if( rOutdevState.clip.count() )
152             {
153                 // general polygon case
154 
155                 ::basegfx::B2DPolyPolygon aLocalClip( rOutdevState.clip );
156                 ::basegfx::B2DHomMatrix	  aTransform;
157 
158                 if( bOffsetting )
159                     aTransform.translate( -rOffset.getX(),
160                                           -rOffset.getY() );
161                 if( bScaling )
162                     aTransform.scale( 1.0/pScaling->getX(), 1.0/pScaling->getY() );
163 
164                 if( bRotation )
165                     aTransform.rotate( - *pRotation );
166 
167                 aLocalClip.transform( aTransform );
168 
169                 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
170                     rCanvas->getUNOCanvas()->getDevice(),
171                     aLocalClip );
172 
173                 return true;
174             }
175             else if( !rOutdevState.clipRect.IsEmpty() )
176             {
177                 // simple rect case
178 
179                 const ::Rectangle aLocalClipRect( rOutdevState.clipRect );
180 
181                 if( bRotation )
182                 {
183                     // rotation involved - convert to polygon first,
184                     // then transform that
185                     ::basegfx::B2DPolygon aLocalClip(
186                         ::basegfx::tools::createPolygonFromRect(
187                                 ::basegfx::B2DRectangle(
188                                     (double)(aLocalClipRect.Left()),
189                                     (double)(aLocalClipRect.Top()),
190                                     (double)(aLocalClipRect.Right()),
191                                     (double)(aLocalClipRect.Bottom()) ) ) );
192                     ::basegfx::B2DHomMatrix aTransform;
193 
194                     if( bOffsetting )
195                         aTransform.translate( -rOffset.getX(),
196                                               -rOffset.getY() );
197                     if( bScaling )
198                         aTransform.scale( 1.0/pScaling->getX(), 1.0/pScaling->getY() );
199 
200                     aTransform.rotate( - *pRotation );
201 
202                     aLocalClip.transform( aTransform );
203 
204                     o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
205                         rCanvas->getUNOCanvas()->getDevice(),
206                         ::basegfx::B2DPolyPolygon( aLocalClip ) );
207                 }
208                 else if( bScaling )
209                 {
210                     // scale and offset - do it on the fly, have to
211                     // convert to float anyway.
212                     o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
213                         rCanvas->getUNOCanvas()->getDevice(),
214                         ::basegfx::B2DPolyPolygon(
215                             ::basegfx::tools::createPolygonFromRect(
216                                 ::basegfx::B2DRectangle(
217                                     (double)(aLocalClipRect.Left() - rOffset.getX())/pScaling->getX(),
218                                     (double)(aLocalClipRect.Top() - rOffset.getY())/pScaling->getY(),
219                                     (double)(aLocalClipRect.Right() - rOffset.getX())/pScaling->getX(),
220                                     (double)(aLocalClipRect.Bottom() - rOffset.getY())/pScaling->getY() ) ) ) );
221                 }
222                 else
223                 {
224                     // offset only - do it on the fly, have to convert
225                     // to float anyway.
226                     o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
227                         rCanvas->getUNOCanvas()->getDevice(),
228                         ::basegfx::B2DPolyPolygon(
229                             ::basegfx::tools::createPolygonFromRect(
230                                 ::basegfx::B2DRectangle( aLocalClipRect.Left() - rOffset.getX(),
231                                                          aLocalClipRect.Top() - rOffset.getY(),
232                                                          aLocalClipRect.Right() - rOffset.getX(),
233                                                          aLocalClipRect.Bottom() - rOffset.getY() ) ) ) );
234                 }
235 
236                 return true;
237             }
238 
239             // empty clip, nothing to do
240             return false;
241         }
242 
243         bool modifyClip( rendering::RenderState&							o_rRenderState,
244                          const struct ::cppcanvas::internal::OutDevState&	rOutdevState,
245                          const CanvasSharedPtr&								rCanvas,
246                          const ::Point&										rOffset,
247                          const ::basegfx::B2DVector*						pScaling,
248                          const double*                                      pRotation )
249         {
250             return modifyClip( o_rRenderState,
251                                rOutdevState,
252                                rCanvas,
253                                ::basegfx::B2DPoint( rOffset.X(),
254                                                     rOffset.Y() ),
255                                pScaling,
256                                pRotation );
257         }
258 
259         bool modifyClip( rendering::RenderState&							o_rRenderState,
260                          const struct ::cppcanvas::internal::OutDevState&	rOutdevState,
261                          const CanvasSharedPtr&								rCanvas,
262                          const ::basegfx::B2DHomMatrix&						rTransform )
263         {
264             if( !rTransform.isIdentity() ||
265                 !rTransform.isInvertible() )
266                 return false; // nothing to do
267 
268             ::basegfx::B2DPolyPolygon aLocalClip;
269 
270             if( rOutdevState.clip.count() )
271             {
272                 aLocalClip = rOutdevState.clip;
273             }
274             else if( !rOutdevState.clipRect.IsEmpty() )
275             {
276                 const ::Rectangle aLocalClipRect( rOutdevState.clipRect );
277 
278                 aLocalClip = ::basegfx::B2DPolyPolygon(
279                     ::basegfx::tools::createPolygonFromRect(
280                         ::basegfx::B2DRectangle(
281                             aLocalClipRect.Left(),
282                             aLocalClipRect.Top(),
283                             aLocalClipRect.Right(),
284                             aLocalClipRect.Bottom() ) ) );
285             }
286             else
287             {
288                 // empty clip, nothing to do
289                 return false;
290             }
291 
292             // invert transformation and modify
293             ::basegfx::B2DHomMatrix aLocalTransform( rTransform );
294             aLocalTransform.invert();
295 
296             aLocalClip.transform( aLocalTransform );
297 
298             o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
299                 rCanvas->getUNOCanvas()->getDevice(),
300                 aLocalClip );
301 
302             return true;
303         }
304 
305         // create overline/underline/strikeout line info struct
306         TextLineInfo createTextLineInfo( const ::VirtualDevice& 					rVDev,
307                                          const ::cppcanvas::internal::OutDevState&	rState )
308         {
309             const sal_Bool bOldMode( rVDev.IsMapModeEnabled() );
310 
311             // #i68512# Force metric regeneration with mapmode enabled
312             // (prolly OutDev bug)
313             rVDev.GetFontMetric();
314 
315             // will restore map mode below
316             const_cast< ::VirtualDevice& >(rVDev).EnableMapMode( sal_False );
317 
318             const ::FontMetric aMetric = rVDev.GetFontMetric();
319 
320             TextLineInfo aTextInfo(
321                 (aMetric.GetDescent() + 2) / 4.0,
322                 ((aMetric.GetIntLeading() + 1.5) / 3.0),
323                 (aMetric.GetIntLeading() / 2.0) - aMetric.GetAscent(),
324                 aMetric.GetDescent() / 2.0,
325                 (aMetric.GetIntLeading() - aMetric.GetAscent()) / 3.0,
326                 rState.textOverlineStyle,
327                 rState.textUnderlineStyle,
328                 rState.textStrikeoutStyle );
329 
330             const_cast< ::VirtualDevice& >(rVDev).EnableMapMode( bOldMode );
331 
332             return aTextInfo;
333         }
334 
335         namespace
336         {
337             void appendRect( ::basegfx::B2DPolyPolygon& o_rPoly,
338                              const ::basegfx::B2DPoint& rStartPos,
339                              const double 				nX1,
340                              const double 				nY1,
341                              const double 				nX2,
342                              const double 				nY2 )
343             {
344                 const double x( rStartPos.getX() );
345                 const double y( rStartPos.getY() );
346 
347                 o_rPoly.append(
348                     ::basegfx::tools::createPolygonFromRect(
349                         ::basegfx::B2DRectangle( x + nX1, y + nY1, x + nX2, y + nY2 ) ) );
350             }
351 
352             void appendRect( ::basegfx::B2DPolyPolygon& o_rPoly,
353                              const double 				nX1,
354                              const double 				nY1,
355                              const double 				nX2,
356                              const double 				nY2 )
357             {
358                 o_rPoly.append(
359                     ::basegfx::tools::createPolygonFromRect(
360                         ::basegfx::B2DRectangle( nX1, nY1, nX2, nY2 ) ) );
361             }
362 
363             void appendDashes( ::basegfx::B2DPolyPolygon&	o_rPoly,
364                                const double 				nX,
365                                const double 				nY,
366                                const double 				nLineWidth,
367                                const double 				nLineHeight,
368                                const double 				nDashWidth,
369                                const double 				nDashSkip )
370             {
371                 const sal_Int32 nNumLoops(
372                     static_cast< sal_Int32 >(
373                         ::std::max( 1.0,
374                                     nLineWidth / nDashSkip ) + .5) );
375 
376                 double x = nX;
377                 for( sal_Int32 i=0; i<nNumLoops; ++i )
378                 {
379                     appendRect( o_rPoly,
380                                 x, 				nY,
381                                 x + nDashWidth, nY + nLineHeight );
382 
383                     x += nDashSkip;
384                 }
385             }
386         }
387 
388         // create line actions for text such as underline and
389         // strikeout
390         ::basegfx::B2DPolyPolygon createTextLinesPolyPolygon( const ::basegfx::B2DPoint rStartPos,
391                                                               const double&				rLineWidth,
392                                                               const TextLineInfo&		rTextLineInfo )
393         {
394             // fill the polypolygon with all text lines
395             ::basegfx::B2DPolyPolygon aTextLinesPolyPoly;
396 
397             switch( rTextLineInfo.mnOverlineStyle )
398             {
399                 case UNDERLINE_NONE:    	  // nothing to do
400                     // FALLTHROUGH intended
401                 case UNDERLINE_DONTKNOW:
402                     break;
403 
404                 case UNDERLINE_SMALLWAVE:     // TODO(F3): NYI
405                     // FALLTHROUGH intended
406                 case UNDERLINE_WAVE:          // TODO(F3): NYI
407                     // FALLTHROUGH intended
408                 case UNDERLINE_SINGLE:
409                     appendRect(
410                         aTextLinesPolyPoly,
411                         rStartPos,
412                         0,
413                         rTextLineInfo.mnOverlineOffset,
414                         rLineWidth,
415                         rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight );
416                     break;
417 
418                 case UNDERLINE_BOLDDOTTED:    // TODO(F3): NYI
419                     // FALLTHROUGH intended
420                 case UNDERLINE_BOLDDASH:      // TODO(F3): NYI
421                     // FALLTHROUGH intended
422                 case UNDERLINE_BOLDLONGDASH:  // TODO(F3): NYI
423                     // FALLTHROUGH intended
424                 case UNDERLINE_BOLDDASHDOT:   // TODO(F3): NYI
425                     // FALLTHROUGH intended
426                 case UNDERLINE_BOLDDASHDOTDOT:// TODO(F3): NYI
427                     // FALLTHROUGH intended
428                 case UNDERLINE_BOLDWAVE:      // TODO(F3): NYI
429                     // FALLTHROUGH intended
430                 case UNDERLINE_BOLD:
431                     appendRect(
432                         aTextLinesPolyPoly,
433                         rStartPos,
434                         0,
435                         rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight,
436                         rLineWidth,
437                         rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight );
438                     break;
439 
440                 case UNDERLINE_DOUBLEWAVE:    // TODO(F3): NYI
441                     // FALLTHROUGH intended
442                 case UNDERLINE_DOUBLE:
443                     appendRect(
444                         aTextLinesPolyPoly,
445                         rStartPos,
446                         0,
447                         rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight * 2.0 ,
448                         rLineWidth,
449                         rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight );
450 
451                     appendRect(
452                         aTextLinesPolyPoly,
453                         rStartPos,
454                         0,
455                         rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight,
456                         rLineWidth,
457                         rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight * 2.0 );
458                     break;
459 
460                 case UNDERLINE_DASHDOTDOT:    // TODO(F3): NYI
461                     // FALLTHROUGH intended
462                 case UNDERLINE_DOTTED:
463                     appendDashes(
464                         aTextLinesPolyPoly,
465                         rStartPos.getX(),
466                         rStartPos.getY() + rTextLineInfo.mnOverlineOffset,
467                         rLineWidth,
468                         rTextLineInfo.mnOverlineHeight,
469                         rTextLineInfo.mnOverlineHeight,
470                         2*rTextLineInfo.mnOverlineHeight );
471                     break;
472 
473                 case UNDERLINE_DASHDOT:       // TODO(F3): NYI
474                     // FALLTHROUGH intended
475                 case UNDERLINE_DASH:
476                     appendDashes(
477                         aTextLinesPolyPoly,
478                         rStartPos.getX(),
479                         rStartPos.getY() + rTextLineInfo.mnOverlineOffset,
480                         rLineWidth,
481                         rTextLineInfo.mnOverlineHeight,
482                         3*rTextLineInfo.mnOverlineHeight,
483                         6*rTextLineInfo.mnOverlineHeight );
484                     break;
485 
486                 case UNDERLINE_LONGDASH:
487                     appendDashes(
488                         aTextLinesPolyPoly,
489                         rStartPos.getX(),
490                         rStartPos.getY() + rTextLineInfo.mnOverlineOffset,
491                         rLineWidth,
492                         rTextLineInfo.mnOverlineHeight,
493                         6*rTextLineInfo.mnOverlineHeight,
494                         12*rTextLineInfo.mnOverlineHeight );
495                     break;
496 
497                 default:
498                     ENSURE_OR_THROW( false,
499                                       "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected overline case" );
500             }
501 
502             switch( rTextLineInfo.mnUnderlineStyle )
503             {
504                 case UNDERLINE_NONE:    	  // nothing to do
505                     // FALLTHROUGH intended
506                 case UNDERLINE_DONTKNOW:
507                     break;
508 
509                 case UNDERLINE_SMALLWAVE:     // TODO(F3): NYI
510                     // FALLTHROUGH intended
511                 case UNDERLINE_WAVE:          // TODO(F3): NYI
512                     // FALLTHROUGH intended
513                 case UNDERLINE_SINGLE:
514                     appendRect(
515                         aTextLinesPolyPoly,
516                         rStartPos,
517                         0,
518                         rTextLineInfo.mnUnderlineOffset,
519                         rLineWidth,
520                         rTextLineInfo.mnUnderlineOffset + rTextLineInfo.mnLineHeight );
521                     break;
522 
523                 case UNDERLINE_BOLDDOTTED:    // TODO(F3): NYI
524                     // FALLTHROUGH intended
525                 case UNDERLINE_BOLDDASH:      // TODO(F3): NYI
526                     // FALLTHROUGH intended
527                 case UNDERLINE_BOLDLONGDASH:  // TODO(F3): NYI
528                     // FALLTHROUGH intended
529                 case UNDERLINE_BOLDDASHDOT:   // TODO(F3): NYI
530                     // FALLTHROUGH intended
531                 case UNDERLINE_BOLDDASHDOTDOT:// TODO(F3): NYI
532                     // FALLTHROUGH intended
533                 case UNDERLINE_BOLDWAVE:      // TODO(F3): NYI
534                     // FALLTHROUGH intended
535                 case UNDERLINE_BOLD:
536                     appendRect(
537                         aTextLinesPolyPoly,
538                         rStartPos,
539                         0,
540                         rTextLineInfo.mnUnderlineOffset,
541                         rLineWidth,
542                         rTextLineInfo.mnUnderlineOffset + 2*rTextLineInfo.mnLineHeight );
543                     break;
544 
545                 case UNDERLINE_DOUBLEWAVE:    // TODO(F3): NYI
546                     // FALLTHROUGH intended
547                 case UNDERLINE_DOUBLE:
548                     appendRect(
549                         aTextLinesPolyPoly,
550                         rStartPos,
551                         0,
552                         rTextLineInfo.mnUnderlineOffset - rTextLineInfo.mnLineHeight,
553                         rLineWidth,
554                         rTextLineInfo.mnUnderlineOffset );
555 
556                     appendRect(
557                         aTextLinesPolyPoly,
558                         rStartPos,
559                         0,
560                         rTextLineInfo.mnUnderlineOffset + 2*rTextLineInfo.mnLineHeight,
561                         rLineWidth,
562                         rTextLineInfo.mnUnderlineOffset + 3*rTextLineInfo.mnLineHeight );
563                     break;
564 
565                 case UNDERLINE_DASHDOTDOT:    // TODO(F3): NYI
566                     // FALLTHROUGH intended
567                 case UNDERLINE_DOTTED:
568                     appendDashes(
569                         aTextLinesPolyPoly,
570                         rStartPos.getX(),
571                         rStartPos.getY() + rTextLineInfo.mnUnderlineOffset,
572                         rLineWidth,
573                         rTextLineInfo.mnLineHeight,
574                         rTextLineInfo.mnLineHeight,
575                         2*rTextLineInfo.mnLineHeight );
576                     break;
577 
578                 case UNDERLINE_DASHDOT:       // TODO(F3): NYI
579                     // FALLTHROUGH intended
580                 case UNDERLINE_DASH:
581                     appendDashes(
582                         aTextLinesPolyPoly,
583                         rStartPos.getX(),
584                         rStartPos.getY() + rTextLineInfo.mnUnderlineOffset,
585                         rLineWidth,
586                         rTextLineInfo.mnLineHeight,
587                         3*rTextLineInfo.mnLineHeight,
588                         6*rTextLineInfo.mnLineHeight );
589                     break;
590 
591                 case UNDERLINE_LONGDASH:
592                     appendDashes(
593                         aTextLinesPolyPoly,
594                         rStartPos.getX(),
595                         rStartPos.getY() + rTextLineInfo.mnUnderlineOffset,
596                         rLineWidth,
597                         rTextLineInfo.mnLineHeight,
598                         6*rTextLineInfo.mnLineHeight,
599                         12*rTextLineInfo.mnLineHeight );
600                     break;
601 
602                 default:
603                     ENSURE_OR_THROW( false,
604                                       "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected underline case" );
605             }
606 
607             switch( rTextLineInfo.mnStrikeoutStyle )
608             {
609                 case STRIKEOUT_NONE:    // nothing to do
610                     // FALLTHROUGH intended
611                 case STRIKEOUT_DONTKNOW:
612                     break;
613 
614                 case STRIKEOUT_SLASH:   // TODO(Q1): we should handle this in the text layer
615                     // FALLTHROUGH intended
616                 case STRIKEOUT_X:
617                     break;
618 
619                 case STRIKEOUT_SINGLE:
620                     appendRect(
621                         aTextLinesPolyPoly,
622                         rStartPos,
623                         0,
624                         rTextLineInfo.mnStrikeoutOffset,
625                         rLineWidth,
626                         rTextLineInfo.mnStrikeoutOffset + rTextLineInfo.mnLineHeight );
627                     break;
628 
629                 case STRIKEOUT_BOLD:
630                     appendRect(
631                         aTextLinesPolyPoly,
632                         rStartPos,
633                         0,
634                         rTextLineInfo.mnStrikeoutOffset,
635                         rLineWidth,
636                         rTextLineInfo.mnStrikeoutOffset + 2*rTextLineInfo.mnLineHeight );
637                     break;
638 
639                 case STRIKEOUT_DOUBLE:
640                     appendRect(
641                         aTextLinesPolyPoly,
642                         rStartPos,
643                         0,
644                         rTextLineInfo.mnStrikeoutOffset - rTextLineInfo.mnLineHeight,
645                         rLineWidth,
646                         rTextLineInfo.mnStrikeoutOffset );
647 
648                     appendRect(
649                         aTextLinesPolyPoly,
650                         rStartPos,
651                         0,
652                         rTextLineInfo.mnStrikeoutOffset + 2*rTextLineInfo.mnLineHeight,
653                         rLineWidth,
654                         rTextLineInfo.mnStrikeoutOffset + 3*rTextLineInfo.mnLineHeight );
655                     break;
656 
657                 default:
658                     ENSURE_OR_THROW( false,
659                                       "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected strikeout case" );
660             }
661 
662             return aTextLinesPolyPoly;
663         }
664 
665         ::basegfx::B2DRange calcDevicePixelBounds( const ::basegfx::B2DRange& 		rBounds,
666                                                    const rendering::ViewState&		viewState,
667                                                    const rendering::RenderState&	renderState )
668         {
669             ::basegfx::B2DHomMatrix aTransform;
670             ::canvas::tools::mergeViewAndRenderTransform( aTransform,
671                                                           viewState,
672                                                           renderState );
673 
674             ::basegfx::B2DRange aTransformedBounds;
675             return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds,
676                                                                rBounds,
677                                                                aTransform );
678         }
679 
680         // create line actions for text such as underline and
681         // strikeout
682         ::basegfx::B2DPolyPolygon createTextLinesPolyPolygon( const double&			rStartOffset,
683                                                               const double&			rLineWidth,
684                                                               const TextLineInfo&	rTextLineInfo )
685         {
686             return createTextLinesPolyPolygon(
687                 ::basegfx::B2DPoint( rStartOffset,
688                                      0.0 ),
689                 rLineWidth,
690                 rTextLineInfo );
691         }
692     }
693 }
694