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