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