1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 
21 #include <tools/diagnose_ex.h>
22 #include <com/sun/star/rendering/XCanvas.hpp>
23 #include <basegfx/utils/canvastools.hxx>
24 #include <basegfx/polygon/b2dpolygontools.hxx>
25 #include <basegfx/polygon/b2dpolygon.hxx>
26 #include <basegfx/range/b2drectangle.hxx>
27 #include <basegfx/vector/b2dvector.hxx>
28 #include <canvas/canvastools.hxx>
29 #include <rtl/math.hxx>
30 #include <vcl/canvastools.hxx>
31 #include <vcl/virdev.hxx>
32 #include <vcl/metric.hxx>
33 #include "mtftools.hxx"
34 #include <outdevstate.hxx>
35 #include <basegfx/matrix/b2dhommatrixtools.hxx>
36 
37 
38 using namespace ::com::sun::star;
39 
40 namespace cppcanvas::tools
41 {
initRenderState(rendering::RenderState & renderState,const::cppcanvas::internal::OutDevState & outdevState)42         void initRenderState( rendering::RenderState&                   renderState,
43                               const ::cppcanvas::internal::OutDevState& outdevState )
44         {
45             ::canvas::tools::initRenderState( renderState );
46             ::canvas::tools::setRenderStateTransform( renderState,
47                                                       outdevState.transform );
48             renderState.Clip = outdevState.xClipPoly;
49         }
50 
getBaselineOffset(const::cppcanvas::internal::OutDevState & outdevState,const VirtualDevice & rVDev)51         ::Size getBaselineOffset( const ::cppcanvas::internal::OutDevState& outdevState,
52                                   const VirtualDevice&                      rVDev )
53         {
54             const ::FontMetric& aMetric = rVDev.GetFontMetric();
55 
56             // calc offset for text output, the XCanvas always renders
57             // baseline offset.
58             switch( outdevState.textReferencePoint )
59             {
60                 case ALIGN_TOP:
61                     return ::Size( 0,
62                                    aMetric.GetInternalLeading() + aMetric.GetAscent() );
63 
64                 default:
65                     ENSURE_OR_THROW( false,
66                                       "tools::getBaselineOffset(): Unexpected TextAlign value" );
67                     // FALLTHROUGH intended (to calm compiler warning - case won't happen)
68                 case ALIGN_BASELINE:
69                     return ::Size( 0, 0 );
70 
71                 case ALIGN_BOTTOM:
72                     return ::Size( 0,
73                                    -aMetric.GetDescent() );
74 
75             }
76         }
77 
calcLogic2PixelLinearTransform(::basegfx::B2DHomMatrix & o_rMatrix,const VirtualDevice & rVDev)78         ::basegfx::B2DHomMatrix& calcLogic2PixelLinearTransform( ::basegfx::B2DHomMatrix&   o_rMatrix,
79                                                                  const VirtualDevice&       rVDev )
80         {
81             // select size value in the middle of the available range,
82             // to have headroom both when map mode scales up, and when
83             // it scales down.
84             const ::Size aSizeLogic( 0x00010000L,
85                                      0x00010000L );
86 
87             const ::Size aSizePixel( rVDev.LogicToPixel( aSizeLogic ) );
88 
89             o_rMatrix = basegfx::utils::createScaleB2DHomMatrix(
90                 aSizePixel.Width() / static_cast<double>(aSizeLogic.Width()),
91                 aSizePixel.Height() / static_cast<double>(aSizeLogic.Height()) );
92 
93             return o_rMatrix;
94         }
95 
calcLogic2PixelAffineTransform(::basegfx::B2DHomMatrix & o_rMatrix,const VirtualDevice & rVDev)96         ::basegfx::B2DHomMatrix& calcLogic2PixelAffineTransform( ::basegfx::B2DHomMatrix&   o_rMatrix,
97                                                                  const VirtualDevice&       rVDev )
98         {
99             // retrieves scale
100             calcLogic2PixelLinearTransform(o_rMatrix, rVDev);
101 
102             // translate according to curr map mode/pref map mode offset
103             const ::Point  aEmptyPoint;
104             const ::Point& rTranslatedPoint(
105                 rVDev.LogicToPixel( aEmptyPoint ));
106 
107             o_rMatrix.translate(rTranslatedPoint.X(),
108                                 rTranslatedPoint.Y());
109 
110             return o_rMatrix;
111         }
112 
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)113         bool modifyClip( rendering::RenderState&                            o_rRenderState,
114                          const struct ::cppcanvas::internal::OutDevState&   rOutdevState,
115                          const CanvasSharedPtr&                             rCanvas,
116                          const ::basegfx::B2DPoint&                         rOffset,
117                          const ::basegfx::B2DVector*                        pScaling,
118                          const double*                                      pRotation )
119         {
120             const bool bOffsetting( !rOffset.equalZero() );
121             const bool bScaling( pScaling &&
122                                  !rtl::math::approxEqual(pScaling->getX(), 1.0) &&
123                                  !rtl::math::approxEqual(pScaling->getY(), 1.0) );
124             const bool bRotation( pRotation &&
125                                   *pRotation != 0.0 );
126 
127             if( !bOffsetting && !bScaling && !bRotation )
128                 return false; // nothing to do
129 
130             if( rOutdevState.clip.count() )
131             {
132                 // general polygon case
133 
134                 ::basegfx::B2DPolyPolygon aLocalClip( rOutdevState.clip );
135                 ::basegfx::B2DHomMatrix   aTransform;
136 
137                 if( bOffsetting )
138                     aTransform.translate( -rOffset.getX(),
139                                           -rOffset.getY() );
140                 if( bScaling )
141                     aTransform.scale( 1.0/pScaling->getX(), 1.0/pScaling->getY() );
142 
143                 if( bRotation )
144                     aTransform.rotate( - *pRotation );
145 
146                 aLocalClip.transform( aTransform );
147 
148                 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
149                     rCanvas->getUNOCanvas()->getDevice(),
150                     aLocalClip );
151 
152                 return true;
153             }
154             else if( !rOutdevState.clipRect.IsEmpty() )
155             {
156                 // simple rect case
157 
158                 const ::tools::Rectangle aLocalClipRect( rOutdevState.clipRect );
159 
160                 if( bRotation )
161                 {
162                     // rotation involved - convert to polygon first,
163                     // then transform that
164                     ::basegfx::B2DPolygon aLocalClip(
165                         ::basegfx::utils::createPolygonFromRect(
166                                     vcl::unotools::b2DRectangleFromRectangle(aLocalClipRect) ) );
167                     ::basegfx::B2DHomMatrix aTransform;
168 
169                     if( bOffsetting )
170                         aTransform.translate( -rOffset.getX(),
171                                               -rOffset.getY() );
172                     if( bScaling )
173                         aTransform.scale( 1.0/pScaling->getX(), 1.0/pScaling->getY() );
174 
175                     aTransform.rotate( - *pRotation );
176 
177                     aLocalClip.transform( aTransform );
178 
179                     o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
180                         rCanvas->getUNOCanvas()->getDevice(),
181                         ::basegfx::B2DPolyPolygon( aLocalClip ) );
182                 }
183                 else if( bScaling )
184                 {
185                     // scale and offset - do it on the fly, have to
186                     // convert to float anyway.
187                     o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
188                         rCanvas->getUNOCanvas()->getDevice(),
189                         ::basegfx::B2DPolyPolygon(
190                             ::basegfx::utils::createPolygonFromRect(
191                                 ::basegfx::B2DRectangle(
192                                     (aLocalClipRect.Left() - rOffset.getX())/pScaling->getX(),
193                                     (aLocalClipRect.Top() - rOffset.getY())/pScaling->getY(),
194                                     (aLocalClipRect.Right() - rOffset.getX())/pScaling->getX(),
195                                     (aLocalClipRect.Bottom() - rOffset.getY())/pScaling->getY() ) ) ) );
196                 }
197                 else
198                 {
199                     // offset only - do it on the fly, have to convert
200                     // to float anyway.
201                     o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
202                         rCanvas->getUNOCanvas()->getDevice(),
203                         ::basegfx::B2DPolyPolygon(
204                             ::basegfx::utils::createPolygonFromRect(
205                                 ::basegfx::B2DRectangle( aLocalClipRect.Left() - rOffset.getX(),
206                                                          aLocalClipRect.Top() - rOffset.getY(),
207                                                          aLocalClipRect.Right() - rOffset.getX(),
208                                                          aLocalClipRect.Bottom() - rOffset.getY() ) ) ) );
209                 }
210 
211                 return true;
212             }
213 
214             // empty clip, nothing to do
215             return false;
216         }
217 
218         // create overline/underline/strikeout line info struct
createTextLineInfo(const::VirtualDevice & rVDev,const::cppcanvas::internal::OutDevState & rState)219         TextLineInfo createTextLineInfo( const ::VirtualDevice&                     rVDev,
220                                          const ::cppcanvas::internal::OutDevState&  rState )
221         {
222             const bool bOldMode( rVDev.IsMapModeEnabled() );
223 
224             // #i68512# Force metric regeneration with mapmode enabled
225             // (prolly OutDev bug)
226             rVDev.GetFontMetric();
227 
228             // will restore map mode below
229             const_cast< ::VirtualDevice& >(rVDev).EnableMapMode( false );
230 
231             const ::FontMetric aMetric = rVDev.GetFontMetric();
232 
233             TextLineInfo aTextInfo(
234                 (aMetric.GetDescent() + 2) / 4.0,
235                 ((aMetric.GetInternalLeading() + 1.5) / 3.0),
236                 (aMetric.GetInternalLeading() / 2.0) - aMetric.GetAscent(),
237                 aMetric.GetDescent() / 2.0,
238                 (aMetric.GetInternalLeading() - aMetric.GetAscent()) / 3.0,
239                 rState.textOverlineStyle,
240                 rState.textUnderlineStyle,
241                 rState.textStrikeoutStyle );
242 
243             const_cast< ::VirtualDevice& >(rVDev).EnableMapMode( bOldMode );
244 
245             return aTextInfo;
246         }
247 
248         namespace
249         {
appendWaveline(::basegfx::B2DPolyPolygon & o_rPoly,const::basegfx::B2DPoint & rStartPos,const double nStartOffset,const double nWidth,const double nHeight,sal_Int8 nLineStyle)250             void appendWaveline( ::basegfx::B2DPolyPolygon& o_rPoly,
251                              const ::basegfx::B2DPoint& rStartPos,
252                              const double               nStartOffset,
253                              const double               nWidth,
254                              const double               nHeight,
255                              sal_Int8                   nLineStyle)
256             {
257                 const double x(rStartPos.getX());
258                 const double y(rStartPos.getY() + nStartOffset + nHeight);
259                 double nWaveWidth = nHeight * 10.6 * 0.25;
260                 // Offset for the double line.
261                 double nOffset = 0.0;
262 
263                 if (nLineStyle == LINESTYLE_DOUBLEWAVE)
264                     nOffset = -nHeight * 0.5;
265                 else
266                     nWaveWidth *= 2.0;
267 
268                 basegfx::B2DPolygon aLine;
269                 aLine.append(basegfx::B2DPoint(x, y + nOffset));
270                 aLine.append(basegfx::B2DPoint(x + nWidth, y + nOffset));
271 
272                 o_rPoly.append(::basegfx::utils::createWaveline(aLine, nWaveWidth, nWaveWidth * 0.5));
273 
274                 if (nLineStyle == LINESTYLE_DOUBLEWAVE)
275                 {
276                     nOffset = nHeight * 1.2;
277 
278                     basegfx::B2DPolygon aLine2;
279                     aLine2.append(basegfx::B2DPoint(x, y + nOffset));
280                     aLine2.append(basegfx::B2DPoint(x + nWidth, y + nOffset));
281                     o_rPoly.append(::basegfx::utils::createWaveline(aLine2, nWaveWidth, nWaveWidth * 0.5));
282                 }
283             }
284 
appendRect(::basegfx::B2DPolyPolygon & o_rPoly,const::basegfx::B2DPoint & rStartPos,const double nX1,const double nY1,const double nX2,const double nY2)285             void appendRect( ::basegfx::B2DPolyPolygon& o_rPoly,
286                              const ::basegfx::B2DPoint& rStartPos,
287                              const double               nX1,
288                              const double               nY1,
289                              const double               nX2,
290                              const double               nY2 )
291             {
292                 const double x( rStartPos.getX() );
293                 const double y( rStartPos.getY() );
294 
295                 o_rPoly.append(
296                     ::basegfx::utils::createPolygonFromRect(
297                         ::basegfx::B2DRectangle( x + nX1, y + nY1, x + nX2, y + nY2 ) ) );
298             }
299 
appendRect(::basegfx::B2DPolyPolygon & o_rPoly,const double nX1,const double nY1,const double nX2,const double nY2)300             void appendRect( ::basegfx::B2DPolyPolygon& o_rPoly,
301                              const double               nX1,
302                              const double               nY1,
303                              const double               nX2,
304                              const double               nY2 )
305             {
306                 o_rPoly.append(
307                     ::basegfx::utils::createPolygonFromRect(
308                         ::basegfx::B2DRectangle( nX1, nY1, nX2, nY2 ) ) );
309             }
310 
appendDashes(::basegfx::B2DPolyPolygon & o_rPoly,const double nX,double nY,const double nLineWidth,double nLineHeight,sal_Int8 nLineStyle,bool bIsOverline)311             bool appendDashes( ::basegfx::B2DPolyPolygon&   o_rPoly,
312                                const double                 nX,
313                                double                       nY,
314                                const double                 nLineWidth,
315                                double                       nLineHeight,
316                                sal_Int8                     nLineStyle,
317                                bool                         bIsOverline)
318             {
319                 static const int aDottedArray[]     = { 1, 1, 0};               // DOTTED LINE
320                 static const int aDotDashArray[]    = { 1, 1, 4, 1, 0};         // DASHDOT
321                 static const int aDashDotDotArray[] = { 1, 1, 1, 1, 4, 1, 0};   // DASHDOTDOT
322                 static const int aDashedArray[]     = { 5, 2, 0};               // DASHED LINE
323                 static const int aLongDashArray[]   = { 7, 2, 0};               // LONGDASH
324                 const int *pArray = nullptr;
325                 bool bIsBold = false;
326 
327                 switch(nLineStyle)
328                 {
329                     case LINESTYLE_BOLDDOTTED:
330                         bIsBold = true;
331                         [[fallthrough]];
332                     case LINESTYLE_DOTTED:
333                         pArray = aDottedArray;
334                     break;
335 
336                     case LINESTYLE_BOLDDASH:
337                         bIsBold = true;
338                         [[fallthrough]];
339                     case LINESTYLE_DASH:
340                         pArray = aDashedArray;
341                     break;
342 
343                     case LINESTYLE_BOLDLONGDASH:
344                         bIsBold = true;
345                         [[fallthrough]];
346                     case LINESTYLE_LONGDASH:
347                         pArray = aLongDashArray;
348                     break;
349 
350                     case LINESTYLE_BOLDDASHDOT:
351                         bIsBold = true;
352                         [[fallthrough]];
353                     case LINESTYLE_DASHDOT:
354                         pArray = aDotDashArray;
355                     break;
356                     case LINESTYLE_BOLDDASHDOTDOT:
357                         bIsBold = true;
358                         [[fallthrough]];
359                     case LINESTYLE_DASHDOTDOT:
360                         pArray = aDashDotDotArray;
361                     break;
362                 }
363 
364                 if (!pArray)
365                     return false;
366 
367                 if (bIsBold)
368                 {
369                     if (bIsOverline)
370                         nY -= nLineHeight;
371 
372                     nLineHeight *= 2;
373                 }
374 
375                 const double nEnd = nX + nLineWidth;
376                 sal_Int32 nIndex = 0;
377                 bool bAppend = true;
378                 double nX1 = nX;
379 
380                 while(nX1 < nEnd)
381                 {
382                     if (pArray[nIndex] == 0)
383                         nIndex = 0;
384 
385                     const double nX2 = std::min(nEnd, nX1 + pArray[nIndex] * nLineHeight);
386 
387                     if (bAppend)
388                         appendRect(o_rPoly, nX1, nY, nX2, nY + nLineHeight);
389 
390                     nX1 = nX2;
391 
392                     ++nIndex;
393 
394                     bAppend = !bAppend;
395                 }
396                 return true;
397             }
398 
399             // create line actions for text such as underline and
400             // strikeout
createOverlinePolyPolygon(::basegfx::B2DPolyPolygon & rTextLinesPolyPoly,const::basegfx::B2DPoint & rStartPos,const double & rLineWidth,const TextLineInfo & rTextLineInfo)401             void createOverlinePolyPolygon(::basegfx::B2DPolyPolygon& rTextLinesPolyPoly,
402                                             const ::basegfx::B2DPoint& rStartPos,
403                                             const double&              rLineWidth,
404                                             const TextLineInfo&        rTextLineInfo)
405             {
406                 switch( rTextLineInfo.mnOverlineStyle )
407                 {
408                     case LINESTYLE_NONE:          // nothing to do
409                     case LINESTYLE_DONTKNOW:
410                         break;
411 
412                     case LINESTYLE_DOUBLEWAVE:
413                     case LINESTYLE_SMALLWAVE:
414                     case LINESTYLE_BOLDWAVE:
415                     case LINESTYLE_WAVE:
416                         appendWaveline(
417                             rTextLinesPolyPoly,
418                             rStartPos,
419                             rTextLineInfo.mnOverlineOffset,
420                             rLineWidth,
421                             rTextLineInfo.mnOverlineHeight,
422                             rTextLineInfo.mnOverlineStyle);
423 
424                         break;
425                     case LINESTYLE_SINGLE:
426                         appendRect(
427                             rTextLinesPolyPoly,
428                             rStartPos,
429                             0,
430                             rTextLineInfo.mnOverlineOffset,
431                             rLineWidth,
432                             rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight );
433                         break;
434                     case LINESTYLE_BOLD:
435                         appendRect(
436                             rTextLinesPolyPoly,
437                             rStartPos,
438                             0,
439                             rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight,
440                             rLineWidth,
441                             rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight );
442                         break;
443 
444                     case LINESTYLE_DOUBLE:
445                         appendRect(
446                             rTextLinesPolyPoly,
447                             rStartPos,
448                             0,
449                             rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight * 2.0 ,
450                             rLineWidth,
451                             rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight );
452 
453                         appendRect(
454                             rTextLinesPolyPoly,
455                             rStartPos,
456                             0,
457                             rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight,
458                             rLineWidth,
459                             rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight * 2.0 );
460                         break;
461 
462                     default:
463                         if (!appendDashes(
464                             rTextLinesPolyPoly,
465                             rStartPos.getX(),
466                             rStartPos.getY() + rTextLineInfo.mnOverlineOffset,
467                             rLineWidth,
468                             rTextLineInfo.mnOverlineHeight,
469                             rTextLineInfo.mnOverlineStyle,
470                             true))
471                         {
472                             ENSURE_OR_THROW( false,
473                                           "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected overline case" );
474                         }
475                 }
476             }
477 
createUnderlinePolyPolygon(::basegfx::B2DPolyPolygon & rTextLinesPolyPoly,const::basegfx::B2DPoint & rStartPos,const double & rLineWidth,const TextLineInfo & rTextLineInfo)478             void createUnderlinePolyPolygon(::basegfx::B2DPolyPolygon& rTextLinesPolyPoly,
479                                             const ::basegfx::B2DPoint& rStartPos,
480                                             const double&              rLineWidth,
481                                             const TextLineInfo&        rTextLineInfo )
482             {
483 
484                 switch( rTextLineInfo.mnUnderlineStyle )
485                 {
486                     case LINESTYLE_NONE:          // nothing to do
487                     case LINESTYLE_DONTKNOW:
488                         break;
489 
490                     case LINESTYLE_DOUBLEWAVE:
491                     case LINESTYLE_SMALLWAVE:
492                     case LINESTYLE_BOLDWAVE:
493                     case LINESTYLE_WAVE:
494                         appendWaveline(
495                             rTextLinesPolyPoly,
496                             rStartPos,
497                             rTextLineInfo.mnUnderlineOffset,
498                             rLineWidth,
499                             rTextLineInfo.mnLineHeight,
500                             rTextLineInfo.mnUnderlineStyle);
501                         break;
502                     case LINESTYLE_SINGLE:
503                         appendRect(
504                             rTextLinesPolyPoly,
505                             rStartPos,
506                             0,
507                             rTextLineInfo.mnUnderlineOffset,
508                             rLineWidth,
509                             rTextLineInfo.mnUnderlineOffset + rTextLineInfo.mnLineHeight );
510                         break;
511 
512                     case LINESTYLE_BOLD:
513                         appendRect(
514                             rTextLinesPolyPoly,
515                             rStartPos,
516                             0,
517                             rTextLineInfo.mnUnderlineOffset,
518                             rLineWidth,
519                             rTextLineInfo.mnUnderlineOffset + 2*rTextLineInfo.mnLineHeight );
520                         break;
521 
522                     case LINESTYLE_DOUBLE:
523                         appendRect(
524                             rTextLinesPolyPoly,
525                             rStartPos,
526                             0,
527                             rTextLineInfo.mnUnderlineOffset - rTextLineInfo.mnLineHeight,
528                             rLineWidth,
529                             rTextLineInfo.mnUnderlineOffset );
530 
531                         appendRect(
532                             rTextLinesPolyPoly,
533                             rStartPos,
534                             0,
535                             rTextLineInfo.mnUnderlineOffset + 2*rTextLineInfo.mnLineHeight,
536                             rLineWidth,
537                             rTextLineInfo.mnUnderlineOffset + 3*rTextLineInfo.mnLineHeight );
538                         break;
539 
540                     default:
541                         if (!appendDashes(
542                             rTextLinesPolyPoly,
543                             rStartPos.getX(),
544                             rStartPos.getY() + rTextLineInfo.mnUnderlineOffset,
545                             rLineWidth,
546                             rTextLineInfo.mnLineHeight,
547                             rTextLineInfo.mnUnderlineStyle,
548                             false))
549                         {
550                             ENSURE_OR_THROW( false,
551                                           "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected underline case" );
552                         }
553                 }
554             }
555 
createStrikeoutPolyPolygon(::basegfx::B2DPolyPolygon & rTextLinesPolyPoly,const::basegfx::B2DPoint & rStartPos,const double & rLineWidth,const TextLineInfo & rTextLineInfo)556             void createStrikeoutPolyPolygon(::basegfx::B2DPolyPolygon& rTextLinesPolyPoly,
557                                             const ::basegfx::B2DPoint& rStartPos,
558                                             const double&              rLineWidth,
559                                             const TextLineInfo&        rTextLineInfo)
560             {
561                 switch( rTextLineInfo.mnStrikeoutStyle )
562                 {
563                     case STRIKEOUT_NONE:    // nothing to do
564                     case STRIKEOUT_DONTKNOW:
565                         break;
566 
567                     case STRIKEOUT_SLASH:   // TODO(Q1): we should handle this in the text layer
568                     case STRIKEOUT_X:
569                         break;
570 
571                     case STRIKEOUT_SINGLE:
572                         appendRect(
573                             rTextLinesPolyPoly,
574                             rStartPos,
575                             0,
576                             rTextLineInfo.mnStrikeoutOffset,
577                             rLineWidth,
578                             rTextLineInfo.mnStrikeoutOffset + rTextLineInfo.mnLineHeight );
579                         break;
580 
581                     case STRIKEOUT_BOLD:
582                         appendRect(
583                             rTextLinesPolyPoly,
584                             rStartPos,
585                             0,
586                             rTextLineInfo.mnStrikeoutOffset,
587                             rLineWidth,
588                             rTextLineInfo.mnStrikeoutOffset + 2*rTextLineInfo.mnLineHeight );
589                         break;
590 
591                     case STRIKEOUT_DOUBLE:
592                         appendRect(
593                             rTextLinesPolyPoly,
594                             rStartPos,
595                             0,
596                             rTextLineInfo.mnStrikeoutOffset - rTextLineInfo.mnLineHeight,
597                             rLineWidth,
598                             rTextLineInfo.mnStrikeoutOffset );
599 
600                         appendRect(
601                             rTextLinesPolyPoly,
602                             rStartPos,
603                             0,
604                             rTextLineInfo.mnStrikeoutOffset + 2*rTextLineInfo.mnLineHeight,
605                             rLineWidth,
606                             rTextLineInfo.mnStrikeoutOffset + 3*rTextLineInfo.mnLineHeight );
607                         break;
608 
609                     default:
610                         ENSURE_OR_THROW( false,
611                                           "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected strikeout case" );
612                 }
613             }
614         }
615 
createTextLinesPolyPolygon(const::basegfx::B2DPoint & rStartPos,const double & rLineWidth,const TextLineInfo & rTextLineInfo)616         ::basegfx::B2DPolyPolygon createTextLinesPolyPolygon( const ::basegfx::B2DPoint& rStartPos,
617                                                               const double&              rLineWidth,
618                                                               const TextLineInfo&        rTextLineInfo )
619         {
620             // fill the polypolygon with all text lines
621             ::basegfx::B2DPolyPolygon aTextLinesPolyPoly;
622 
623             createOverlinePolyPolygon(aTextLinesPolyPoly, rStartPos, rLineWidth, rTextLineInfo);
624             createUnderlinePolyPolygon(aTextLinesPolyPoly, rStartPos, rLineWidth, rTextLineInfo);
625             createStrikeoutPolyPolygon(aTextLinesPolyPoly, rStartPos, rLineWidth, rTextLineInfo);
626             return aTextLinesPolyPoly;
627         }
628 
calcDevicePixelBounds(const::basegfx::B2DRange & rBounds,const rendering::ViewState & viewState,const rendering::RenderState & renderState)629         ::basegfx::B2DRange calcDevicePixelBounds( const ::basegfx::B2DRange&       rBounds,
630                                                    const rendering::ViewState&      viewState,
631                                                    const rendering::RenderState&    renderState )
632         {
633             ::basegfx::B2DHomMatrix aTransform;
634             ::canvas::tools::mergeViewAndRenderTransform( aTransform,
635                                                           viewState,
636                                                           renderState );
637 
638             ::basegfx::B2DRange aTransformedBounds;
639             return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds,
640                                                                rBounds,
641                                                                aTransform );
642         }
643 
644         // create line actions for text such as underline and
645         // strikeout
createTextLinesPolyPolygon(const double & rStartOffset,const double & rLineWidth,const TextLineInfo & rTextLineInfo)646         ::basegfx::B2DPolyPolygon createTextLinesPolyPolygon( const double&         rStartOffset,
647                                                               const double&         rLineWidth,
648                                                               const TextLineInfo&   rTextLineInfo )
649         {
650             return createTextLinesPolyPolygon(
651                 ::basegfx::B2DPoint( rStartOffset,
652                                      0.0 ),
653                 rLineWidth,
654                 rTextLineInfo );
655         }
656 
createTextLinesPolyPolygon(const double & rStartOffset,const double & rLineWidth,const TextLineInfo & rTextLineInfo,::basegfx::B2DPolyPolygon & rOverlinePolyPoly,::basegfx::B2DPolyPolygon & rUnderlinePolyPoly,::basegfx::B2DPolyPolygon & rStrikeoutPolyPoly)657         void createTextLinesPolyPolygon( const double&              rStartOffset,
658                                          const double&              rLineWidth,
659                                          const TextLineInfo&        rTextLineInfo,
660                                          ::basegfx::B2DPolyPolygon& rOverlinePolyPoly,
661                                          ::basegfx::B2DPolyPolygon& rUnderlinePolyPoly,
662                                          ::basegfx::B2DPolyPolygon& rStrikeoutPolyPoly )
663         {
664             ::basegfx::B2DPoint aStartPos(rStartOffset, 0.0);
665 
666             createOverlinePolyPolygon(rOverlinePolyPoly, aStartPos, rLineWidth, rTextLineInfo);
667             createUnderlinePolyPolygon(rUnderlinePolyPoly, aStartPos, rLineWidth, rTextLineInfo);
668             createStrikeoutPolyPolygon(rStrikeoutPolyPoly, aStartPos, rLineWidth, rTextLineInfo);
669         }
670 }
671 
672 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
673