1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/types.h>
21 #include <basegfx/matrix/b2dhommatrixtools.hxx>
22 #include <basegfx/polygon/WaveLine.hxx>
23 #include <tools/helpers.hxx>
24 #include <o3tl/hash_combine.hxx>
25 #include <o3tl/lru_map.hxx>
26 #include <comphelper/configuration.hxx>
27 #include <tools/lazydelete.hxx>
28 #include <vcl/metaact.hxx>
29 #include <vcl/settings.hxx>
30 #include <vcl/virdev.hxx>
31 #include <vcl/skia/SkiaHelper.hxx>
33 #include <drawmode.hxx>
35 #include <impglyphitem.hxx>
39 #define UNDERLINE_LAST LINESTYLE_BOLDWAVE
40 #define STRIKEOUT_LAST STRIKEOUT_X
43 struct WavyLineCache final
45 WavyLineCache () : m_aItems( 10 ) {}
47 bool find( Color aLineColor
, size_t nLineWidth
, size_t nWaveHeight
, size_t nWordWidth
, BitmapEx
& rOutput
)
49 Key aKey
= { nWaveHeight
, sal_uInt32(aLineColor
) };
50 auto item
= m_aItems
.find( aKey
);
51 if ( item
== m_aItems
.end() )
54 if ( item
->second
.m_aLineWidth
!= nLineWidth
|| item
->second
.m_aWordWidth
< nWordWidth
)
58 rOutput
= item
->second
.m_Bitmap
;
62 void insert( const BitmapEx
& aBitmap
, const Color
& aLineColor
, const size_t nLineWidth
, const size_t nWaveHeight
, const size_t nWordWidth
, BitmapEx
& rOutput
)
64 Key aKey
= { nWaveHeight
, sal_uInt32(aLineColor
) };
65 m_aItems
.insert( std::pair
< Key
, WavyLineCacheItem
>( aKey
, { nLineWidth
, nWordWidth
, aBitmap
} ) );
70 struct WavyLineCacheItem
81 bool operator ==( const Key
& rOther
) const
83 return ( m_aFirst
== rOther
.m_aFirst
&& m_aSecond
== rOther
.m_aSecond
);
89 size_t operator() ( const Key
& rKey
) const
92 o3tl::hash_combine(aSeed
, rKey
.m_aFirst
);
93 o3tl::hash_combine(aSeed
, rKey
.m_aSecond
);
98 o3tl::lru_map
< Key
, WavyLineCacheItem
, Hash
> m_aItems
;
102 void OutputDevice::ImplInitTextLineSize()
104 mpFontInstance
->mxFontMetric
->ImplInitTextLineSize( this );
107 void OutputDevice::ImplInitAboveTextLineSize()
109 mpFontInstance
->mxFontMetric
->ImplInitAboveTextLineSize( this );
112 void OutputDevice::ImplDrawWavePixel( tools::Long nOriginX
, tools::Long nOriginY
,
113 tools::Long nCurX
, tools::Long nCurY
,
115 Degree10 nOrientation
,
116 SalGraphics
* pGraphics
,
117 const OutputDevice
& rOutDev
,
118 tools::Long nPixWidth
, tools::Long nPixHeight
)
122 Point
aPoint( nOriginX
, nOriginY
);
123 aPoint
.RotateAround( nCurX
, nCurY
, nOrientation
);
126 if (shouldDrawWavePixelAsRect(nWidth
))
128 pGraphics
->DrawRect( nCurX
, nCurY
, nPixWidth
, nPixHeight
, rOutDev
);
132 pGraphics
->DrawPixel( nCurX
, nCurY
, rOutDev
);
136 bool OutputDevice::shouldDrawWavePixelAsRect(tools::Long nLineWidth
) const
144 void OutputDevice::SetWaveLineColors(Color
const& rColor
, tools::Long nLineWidth
)
146 // On printers that output pixel via DrawRect()
149 if (mbLineColor
|| mbInitLineColor
)
151 mpGraphics
->SetLineColor();
152 mbInitLineColor
= true;
155 mpGraphics
->SetFillColor( rColor
);
156 mbInitFillColor
= true;
160 mpGraphics
->SetLineColor( rColor
);
161 mbInitLineColor
= true;
165 Size
OutputDevice::GetWaveLineSize(tools::Long nLineWidth
) const
168 return Size(nLineWidth
, ((nLineWidth
*mnDPIX
)+(mnDPIY
/2))/mnDPIY
);
173 void OutputDevice::ImplDrawWaveLine( tools::Long nBaseX
, tools::Long nBaseY
,
174 tools::Long nDistX
, tools::Long nDistY
,
175 tools::Long nWidth
, tools::Long nHeight
,
176 tools::Long nLineWidth
, Degree10 nOrientation
,
177 const Color
& rColor
)
182 tools::Long nStartX
= nBaseX
+ nDistX
;
183 tools::Long nStartY
= nBaseY
+ nDistY
;
185 // If the height is 1 pixel, it's enough output a line
186 if ( (nLineWidth
== 1) && (nHeight
== 1) )
188 mpGraphics
->SetLineColor( rColor
);
189 mbInitLineColor
= true;
191 tools::Long nEndX
= nStartX
+nWidth
;
192 tools::Long nEndY
= nStartY
;
195 Point
aOriginPt( nBaseX
, nBaseY
);
196 aOriginPt
.RotateAround( nStartX
, nStartY
, nOrientation
);
197 aOriginPt
.RotateAround( nEndX
, nEndY
, nOrientation
);
199 mpGraphics
->DrawLine( nStartX
, nStartY
, nEndX
, nEndY
, *this );
203 tools::Long nCurX
= nStartX
;
204 tools::Long nCurY
= nStartY
;
205 tools::Long nDiffX
= 2;
206 tools::Long nDiffY
= nHeight
-1;
207 tools::Long nCount
= nWidth
;
208 tools::Long nOffY
= -1;
210 SetWaveLineColors(rColor
, nLineWidth
);
211 Size
aSize(GetWaveLineSize(nLineWidth
));
213 tools::Long nPixWidth
= aSize
.Width();
214 tools::Long nPixHeight
= aSize
.Height();
220 ImplDrawWavePixel( nBaseX
, nBaseY
, nCurX
, nCurY
, nLineWidth
, nOrientation
,
222 nPixWidth
, nPixHeight
);
230 tools::Long nFreq
= nCount
/ (nDiffX
+nDiffY
);
233 for( tools::Long i
= nDiffY
; i
; --i
)
235 ImplDrawWavePixel( nBaseX
, nBaseY
, nCurX
, nCurY
, nLineWidth
, nOrientation
,
237 nPixWidth
, nPixHeight
);
241 for( tools::Long i
= nDiffX
; i
; --i
)
243 ImplDrawWavePixel( nBaseX
, nBaseY
, nCurX
, nCurY
, nLineWidth
, nOrientation
,
245 nPixWidth
, nPixHeight
);
250 nFreq
= nCount
% (nDiffX
+nDiffY
);
253 for( tools::Long i
= nDiffY
; i
&& nFreq
; --i
, --nFreq
)
255 ImplDrawWavePixel( nBaseX
, nBaseY
, nCurX
, nCurY
, nLineWidth
, nOrientation
,
257 nPixWidth
, nPixHeight
);
262 for( tools::Long i
= nDiffX
; i
&& nFreq
; --i
, --nFreq
)
264 ImplDrawWavePixel( nBaseX
, nBaseY
, nCurX
, nCurY
, nLineWidth
, nOrientation
,
266 nPixWidth
, nPixHeight
);
274 void OutputDevice::ImplDrawWaveTextLine( tools::Long nBaseX
, tools::Long nBaseY
,
275 tools::Long nDistX
, tools::Long nDistY
, tools::Long nWidth
,
276 FontLineStyle eTextLine
,
280 static bool bFuzzing
= comphelper::IsFuzzing();
281 if (bFuzzing
&& nWidth
> 10000)
283 SAL_WARN("vcl.gdi", "drawLine, skipping suspicious WaveTextLine of length: "
284 << nWidth
<< " for fuzzing performance");
288 LogicalFontInstance
* pFontInstance
= mpFontInstance
.get();
289 tools::Long nLineHeight
;
290 tools::Long nLinePos
;
294 nLineHeight
= pFontInstance
->mxFontMetric
->GetAboveWavelineUnderlineSize();
295 nLinePos
= pFontInstance
->mxFontMetric
->GetAboveWavelineUnderlineOffset();
299 nLineHeight
= pFontInstance
->mxFontMetric
->GetWavelineUnderlineSize();
300 nLinePos
= pFontInstance
->mxFontMetric
->GetWavelineUnderlineOffset();
302 if ( (eTextLine
== LINESTYLE_SMALLWAVE
) && (nLineHeight
> 3) )
305 tools::Long nLineWidth
= mnDPIX
/ 300;
309 if ( eTextLine
== LINESTYLE_BOLDWAVE
)
312 nLinePos
+= nDistY
- (nLineHeight
/ 2);
314 tools::Long nLineWidthHeight
= ((nLineWidth
* mnDPIX
) + (mnDPIY
/ 2)) / mnDPIY
;
315 if ( eTextLine
== LINESTYLE_DOUBLEWAVE
)
317 tools::Long nOrgLineHeight
= nLineHeight
;
319 if ( nLineHeight
< 2 )
321 if ( nOrgLineHeight
> 1 )
327 tools::Long nLineDY
= nOrgLineHeight
-(nLineHeight
*2);
328 if ( nLineDY
< nLineWidthHeight
)
329 nLineDY
= nLineWidthHeight
;
331 tools::Long nLineDY2
= nLineDY
/2;
335 nLinePos
-= nLineWidthHeight
-nLineDY2
;
336 ImplDrawWaveLine( nBaseX
, nBaseY
, nDistX
, nLinePos
, nWidth
, nLineHeight
,
337 nLineWidth
, mpFontInstance
->mnOrientation
, aColor
);
338 nLinePos
+= nLineWidthHeight
+nLineDY
;
339 ImplDrawWaveLine( nBaseX
, nBaseY
, nDistX
, nLinePos
, nWidth
, nLineHeight
,
340 nLineWidth
, mpFontInstance
->mnOrientation
, aColor
);
344 nLinePos
-= nLineWidthHeight
/2;
345 ImplDrawWaveLine( nBaseX
, nBaseY
, nDistX
, nLinePos
, nWidth
, nLineHeight
,
346 nLineWidth
, mpFontInstance
->mnOrientation
, aColor
);
350 void OutputDevice::ImplDrawStraightTextLine( tools::Long nBaseX
, tools::Long nBaseY
,
351 tools::Long nDistX
, tools::Long nDistY
, tools::Long nWidth
,
352 FontLineStyle eTextLine
,
356 static bool bFuzzing
= comphelper::IsFuzzing();
357 if (bFuzzing
&& nWidth
> 25000)
359 SAL_WARN("vcl.gdi", "drawLine, skipping suspicious TextLine of length: "
360 << nWidth
<< " for fuzzing performance");
364 LogicalFontInstance
* pFontInstance
= mpFontInstance
.get();
365 tools::Long nLineHeight
= 0;
366 tools::Long nLinePos
= 0;
367 tools::Long nLinePos2
= 0;
369 const tools::Long nY
= nDistY
;
371 if ( eTextLine
> UNDERLINE_LAST
)
372 eTextLine
= LINESTYLE_SINGLE
;
376 case LINESTYLE_SINGLE
:
377 case LINESTYLE_DOTTED
:
379 case LINESTYLE_LONGDASH
:
380 case LINESTYLE_DASHDOT
:
381 case LINESTYLE_DASHDOTDOT
:
384 nLineHeight
= pFontInstance
->mxFontMetric
->GetAboveUnderlineSize();
385 nLinePos
= nY
+ pFontInstance
->mxFontMetric
->GetAboveUnderlineOffset();
389 nLineHeight
= pFontInstance
->mxFontMetric
->GetUnderlineSize();
390 nLinePos
= nY
+ pFontInstance
->mxFontMetric
->GetUnderlineOffset();
394 case LINESTYLE_BOLDDOTTED
:
395 case LINESTYLE_BOLDDASH
:
396 case LINESTYLE_BOLDLONGDASH
:
397 case LINESTYLE_BOLDDASHDOT
:
398 case LINESTYLE_BOLDDASHDOTDOT
:
401 nLineHeight
= pFontInstance
->mxFontMetric
->GetAboveBoldUnderlineSize();
402 nLinePos
= nY
+ pFontInstance
->mxFontMetric
->GetAboveBoldUnderlineOffset();
406 nLineHeight
= pFontInstance
->mxFontMetric
->GetBoldUnderlineSize();
407 nLinePos
= nY
+ pFontInstance
->mxFontMetric
->GetBoldUnderlineOffset();
410 case LINESTYLE_DOUBLE
:
413 nLineHeight
= pFontInstance
->mxFontMetric
->GetAboveDoubleUnderlineSize();
414 nLinePos
= nY
+ pFontInstance
->mxFontMetric
->GetAboveDoubleUnderlineOffset1();
415 nLinePos2
= nY
+ pFontInstance
->mxFontMetric
->GetAboveDoubleUnderlineOffset2();
419 nLineHeight
= pFontInstance
->mxFontMetric
->GetDoubleUnderlineSize();
420 nLinePos
= nY
+ pFontInstance
->mxFontMetric
->GetDoubleUnderlineOffset1();
421 nLinePos2
= nY
+ pFontInstance
->mxFontMetric
->GetDoubleUnderlineOffset2();
431 if ( mbLineColor
|| mbInitLineColor
)
433 mpGraphics
->SetLineColor();
434 mbInitLineColor
= true;
436 mpGraphics
->SetFillColor( aColor
);
437 mbInitFillColor
= true;
439 tools::Long nLeft
= nDistX
;
443 case LINESTYLE_SINGLE
:
445 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos
, nWidth
, nLineHeight
);
447 case LINESTYLE_DOUBLE
:
448 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos
, nWidth
, nLineHeight
);
449 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos2
, nWidth
, nLineHeight
);
451 case LINESTYLE_DOTTED
:
452 case LINESTYLE_BOLDDOTTED
:
454 tools::Long nDotWidth
= nLineHeight
*mnDPIY
;
455 nDotWidth
+= mnDPIY
/2;
458 tools::Long nTempWidth
= nDotWidth
;
459 tools::Long nEnd
= nLeft
+nWidth
;
460 while ( nLeft
< nEnd
)
462 if ( nLeft
+nTempWidth
> nEnd
)
463 nTempWidth
= nEnd
-nLeft
;
465 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos
, nTempWidth
, nLineHeight
);
466 nLeft
+= nDotWidth
*2;
471 case LINESTYLE_LONGDASH
:
472 case LINESTYLE_BOLDDASH
:
473 case LINESTYLE_BOLDLONGDASH
:
475 tools::Long nDotWidth
= nLineHeight
*mnDPIY
;
476 nDotWidth
+= mnDPIY
/2;
479 tools::Long nMinDashWidth
;
480 tools::Long nMinSpaceWidth
;
481 tools::Long nSpaceWidth
;
482 tools::Long nDashWidth
;
483 if ( (eTextLine
== LINESTYLE_LONGDASH
) ||
484 (eTextLine
== LINESTYLE_BOLDLONGDASH
) )
486 nMinDashWidth
= nDotWidth
*6;
487 nMinSpaceWidth
= nDotWidth
*2;
493 nMinDashWidth
= nDotWidth
*4;
494 nMinSpaceWidth
= (nDotWidth
*150)/100;
498 nDashWidth
= o3tl::convert(nDashWidth
* mnDPIX
, o3tl::Length::mm100
, o3tl::Length::in
);
499 nSpaceWidth
= o3tl::convert(nSpaceWidth
* mnDPIX
, o3tl::Length::mm100
, o3tl::Length::in
);
500 // DashWidth will be increased if the line is getting too thick
501 // in proportion to the line's length
502 if ( nDashWidth
< nMinDashWidth
)
503 nDashWidth
= nMinDashWidth
;
504 if ( nSpaceWidth
< nMinSpaceWidth
)
505 nSpaceWidth
= nMinSpaceWidth
;
507 tools::Long nTempWidth
= nDashWidth
;
508 tools::Long nEnd
= nLeft
+nWidth
;
509 while ( nLeft
< nEnd
)
511 if ( nLeft
+nTempWidth
> nEnd
)
512 nTempWidth
= nEnd
-nLeft
;
513 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos
, nTempWidth
, nLineHeight
);
514 nLeft
+= nDashWidth
+nSpaceWidth
;
518 case LINESTYLE_DASHDOT
:
519 case LINESTYLE_BOLDDASHDOT
:
521 tools::Long nDotWidth
= nLineHeight
*mnDPIY
;
522 nDotWidth
+= mnDPIY
/2;
525 tools::Long nDashWidth
= o3tl::convert(100 * mnDPIX
, o3tl::Length::mm100
, o3tl::Length::in
);
526 tools::Long nMinDashWidth
= nDotWidth
*4;
527 // DashWidth will be increased if the line is getting too thick
528 // in proportion to the line's length
529 if ( nDashWidth
< nMinDashWidth
)
530 nDashWidth
= nMinDashWidth
;
532 tools::Long nTempDotWidth
= nDotWidth
;
533 tools::Long nTempDashWidth
= nDashWidth
;
534 tools::Long nEnd
= nLeft
+nWidth
;
535 while ( nLeft
< nEnd
)
537 if ( nLeft
+nTempDotWidth
> nEnd
)
538 nTempDotWidth
= nEnd
-nLeft
;
540 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos
, nTempDotWidth
, nLineHeight
);
541 nLeft
+= nDotWidth
*2;
545 if ( nLeft
+nTempDashWidth
> nEnd
)
546 nTempDashWidth
= nEnd
-nLeft
;
548 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos
, nTempDashWidth
, nLineHeight
);
549 nLeft
+= nDashWidth
+nDotWidth
;
553 case LINESTYLE_DASHDOTDOT
:
554 case LINESTYLE_BOLDDASHDOTDOT
:
556 tools::Long nDotWidth
= nLineHeight
*mnDPIY
;
557 nDotWidth
+= mnDPIY
/2;
560 tools::Long nDashWidth
= o3tl::convert(100 * mnDPIX
, o3tl::Length::mm100
, o3tl::Length::in
);
561 tools::Long nMinDashWidth
= nDotWidth
*4;
562 // DashWidth will be increased if the line is getting too thick
563 // in proportion to the line's length
564 if ( nDashWidth
< nMinDashWidth
)
565 nDashWidth
= nMinDashWidth
;
567 tools::Long nTempDotWidth
= nDotWidth
;
568 tools::Long nTempDashWidth
= nDashWidth
;
569 tools::Long nEnd
= nLeft
+nWidth
;
570 while ( nLeft
< nEnd
)
572 if ( nLeft
+nTempDotWidth
> nEnd
)
573 nTempDotWidth
= nEnd
-nLeft
;
575 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos
, nTempDotWidth
, nLineHeight
);
576 nLeft
+= nDotWidth
*2;
580 if ( nLeft
+nTempDotWidth
> nEnd
)
581 nTempDotWidth
= nEnd
-nLeft
;
583 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos
, nTempDotWidth
, nLineHeight
);
584 nLeft
+= nDotWidth
*2;
588 if ( nLeft
+nTempDashWidth
> nEnd
)
589 nTempDashWidth
= nEnd
-nLeft
;
591 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos
, nTempDashWidth
, nLineHeight
);
592 nLeft
+= nDashWidth
+nDotWidth
;
601 void OutputDevice::ImplDrawStrikeoutLine( tools::Long nBaseX
, tools::Long nBaseY
,
602 tools::Long nDistX
, tools::Long nDistY
, tools::Long nWidth
,
603 FontStrikeout eStrikeout
,
606 LogicalFontInstance
* pFontInstance
= mpFontInstance
.get();
607 tools::Long nLineHeight
= 0;
608 tools::Long nLinePos
= 0;
609 tools::Long nLinePos2
= 0;
611 tools::Long nY
= nDistY
;
613 if ( eStrikeout
> STRIKEOUT_LAST
)
614 eStrikeout
= STRIKEOUT_SINGLE
;
616 switch ( eStrikeout
)
618 case STRIKEOUT_SINGLE
:
619 nLineHeight
= pFontInstance
->mxFontMetric
->GetStrikeoutSize();
620 nLinePos
= nY
+ pFontInstance
->mxFontMetric
->GetStrikeoutOffset();
623 nLineHeight
= pFontInstance
->mxFontMetric
->GetBoldStrikeoutSize();
624 nLinePos
= nY
+ pFontInstance
->mxFontMetric
->GetBoldStrikeoutOffset();
626 case STRIKEOUT_DOUBLE
:
627 nLineHeight
= pFontInstance
->mxFontMetric
->GetDoubleStrikeoutSize();
628 nLinePos
= nY
+ pFontInstance
->mxFontMetric
->GetDoubleStrikeoutOffset1();
629 nLinePos2
= nY
+ pFontInstance
->mxFontMetric
->GetDoubleStrikeoutOffset2();
638 if ( mbLineColor
|| mbInitLineColor
)
640 mpGraphics
->SetLineColor();
641 mbInitLineColor
= true;
643 mpGraphics
->SetFillColor( aColor
);
644 mbInitFillColor
= true;
646 const tools::Long
& nLeft
= nDistX
;
648 switch ( eStrikeout
)
650 case STRIKEOUT_SINGLE
:
652 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos
, nWidth
, nLineHeight
);
654 case STRIKEOUT_DOUBLE
:
655 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos
, nWidth
, nLineHeight
);
656 ImplDrawTextRect( nBaseX
, nBaseY
, nLeft
, nLinePos2
, nWidth
, nLineHeight
);
663 void OutputDevice::ImplDrawStrikeoutChar( tools::Long nBaseX
, tools::Long nBaseY
,
664 tools::Long nDistX
, tools::Long nDistY
, tools::Long nWidth
,
665 FontStrikeout eStrikeout
,
668 // See qadevOOo/testdocs/StrikeThrough.odt for examples if you need
673 // prepare string for strikeout measurement
674 const char cStrikeoutChar
= eStrikeout
== STRIKEOUT_SLASH
? '/' : 'X';
675 static const int nTestStrLen
= 4;
676 static const int nMaxStrikeStrLen
= 2048;
677 sal_Unicode aChars
[nMaxStrikeStrLen
+1]; // +1 for valgrind...
679 for( int i
= 0; i
< nTestStrLen
; ++i
)
680 aChars
[i
] = cStrikeoutChar
;
682 const OUString
aStrikeoutTest(aChars
, nTestStrLen
);
684 // calculate approximation of strikeout atom size
685 tools::Long nStrikeoutWidth
= 0;
686 std::unique_ptr
<SalLayout
> pLayout
= ImplLayout( aStrikeoutTest
, 0, nTestStrLen
);
689 nStrikeoutWidth
= pLayout
->GetTextWidth() / nTestStrLen
;
691 if( nStrikeoutWidth
<= 0 ) // sanity check
694 int nStrikeStrLen
= (nWidth
+(nStrikeoutWidth
-1)) / nStrikeoutWidth
;
695 if( nStrikeStrLen
> nMaxStrikeStrLen
)
696 nStrikeStrLen
= nMaxStrikeStrLen
;
697 else if (nStrikeStrLen
< 0)
700 // build the strikeout string
701 for( int i
= nTestStrLen
; i
< nStrikeStrLen
; ++i
)
702 aChars
[i
] = cStrikeoutChar
;
704 const OUString
aStrikeoutText(aChars
, nStrikeStrLen
);
706 if( mpFontInstance
->mnOrientation
)
708 Point
aOriginPt(0, 0);
709 aOriginPt
.RotateAround( nDistX
, nDistY
, mpFontInstance
->mnOrientation
);
715 // strikeout text has to be left aligned
716 vcl::text::ComplexTextLayoutFlags nOrigTLM
= mnTextLayoutMode
;
717 mnTextLayoutMode
= vcl::text::ComplexTextLayoutFlags::BiDiStrong
;
718 pLayout
= ImplLayout( aStrikeoutText
, 0, aStrikeoutText
.getLength() );
719 mnTextLayoutMode
= nOrigTLM
;
724 // draw the strikeout text
725 const Color aOldColor
= GetTextColor();
726 SetTextColor( aColor
);
729 pLayout
->DrawBase() = basegfx::B2DPoint(nBaseX
+ mnTextOffX
, nBaseY
+ mnTextOffY
);
731 tools::Rectangle aPixelRect
;
732 aPixelRect
.SetLeft( nBaseX
+mnTextOffX
);
733 aPixelRect
.SetRight( aPixelRect
.Left()+nWidth
);
734 aPixelRect
.SetBottom( nBaseY
+mpFontInstance
->mxFontMetric
->GetDescent() );
735 aPixelRect
.SetTop( nBaseY
-mpFontInstance
->mxFontMetric
->GetAscent() );
737 if (mpFontInstance
->mnOrientation
)
739 tools::Polygon
aPoly( aPixelRect
);
740 aPoly
.Rotate( Point(nBaseX
+mnTextOffX
, nBaseY
+mnTextOffY
), mpFontInstance
->mnOrientation
);
741 aPixelRect
= aPoly
.GetBoundRect();
744 Push( vcl::PushFlags::CLIPREGION
);
745 IntersectClipRegion( PixelToLogic(aPixelRect
) );
746 if( mbInitClipRegion
)
749 pLayout
->DrawText( *mpGraphics
);
753 SetTextColor( aOldColor
);
757 void OutputDevice::ImplDrawTextLine( tools::Long nX
, tools::Long nY
,
758 tools::Long nDistX
, double nWidth
,
759 FontStrikeout eStrikeout
,
760 FontLineStyle eUnderline
,
761 FontLineStyle eOverline
,
762 bool bUnderlineAbove
)
767 Color aStrikeoutColor
= GetTextColor();
768 Color aUnderlineColor
= GetTextLineColor();
769 Color aOverlineColor
= GetOverlineColor();
770 bool bStrikeoutDone
= false;
771 bool bUnderlineDone
= false;
772 bool bOverlineDone
= false;
774 if ( IsRTLEnabled() )
776 tools::Long nXAdd
= nWidth
- nDistX
;
777 if( mpFontInstance
->mnOrientation
)
778 nXAdd
= basegfx::fround
<tools::Long
>( nXAdd
* cos( toRadians(mpFontInstance
->mnOrientation
) ) );
783 if ( !IsTextLineColor() )
784 aUnderlineColor
= GetTextColor();
786 if ( !IsOverlineColor() )
787 aOverlineColor
= GetTextColor();
789 if ( (eUnderline
== LINESTYLE_SMALLWAVE
) ||
790 (eUnderline
== LINESTYLE_WAVE
) ||
791 (eUnderline
== LINESTYLE_DOUBLEWAVE
) ||
792 (eUnderline
== LINESTYLE_BOLDWAVE
) )
794 ImplDrawWaveTextLine( nX
, nY
, nDistX
, 0, nWidth
, eUnderline
, aUnderlineColor
, bUnderlineAbove
);
795 bUnderlineDone
= true;
797 if ( (eOverline
== LINESTYLE_SMALLWAVE
) ||
798 (eOverline
== LINESTYLE_WAVE
) ||
799 (eOverline
== LINESTYLE_DOUBLEWAVE
) ||
800 (eOverline
== LINESTYLE_BOLDWAVE
) )
802 ImplDrawWaveTextLine( nX
, nY
, nDistX
, 0, nWidth
, eOverline
, aOverlineColor
, true );
803 bOverlineDone
= true;
806 if ( (eStrikeout
== STRIKEOUT_SLASH
) ||
807 (eStrikeout
== STRIKEOUT_X
) )
809 ImplDrawStrikeoutChar( nX
, nY
, nDistX
, 0, nWidth
, eStrikeout
, aStrikeoutColor
);
810 bStrikeoutDone
= true;
813 if ( !bUnderlineDone
)
814 ImplDrawStraightTextLine( nX
, nY
, nDistX
, 0, nWidth
, eUnderline
, aUnderlineColor
, bUnderlineAbove
);
816 if ( !bOverlineDone
)
817 ImplDrawStraightTextLine( nX
, nY
, nDistX
, 0, nWidth
, eOverline
, aOverlineColor
, true );
819 if ( !bStrikeoutDone
)
820 ImplDrawStrikeoutLine( nX
, nY
, nDistX
, 0, nWidth
, eStrikeout
, aStrikeoutColor
);
823 void OutputDevice::ImplDrawTextLines( SalLayout
& rSalLayout
, FontStrikeout eStrikeout
,
824 FontLineStyle eUnderline
, FontLineStyle eOverline
,
825 bool bWordLine
, bool bUnderlineAbove
)
829 // draw everything relative to the layout base point
830 const basegfx::B2DPoint aStartPt
= rSalLayout
.DrawBase();
832 // calculate distance of each word from the base point
833 basegfx::B2DPoint aPos
;
836 const GlyphItem
* pGlyph
;
838 while (rSalLayout
.GetNextGlyph(&pGlyph
, aPos
, nStart
))
840 // calculate the boundaries of each word
841 if (!pGlyph
->IsSpacing())
845 // get the distance to the base point (as projected to baseline)
846 nDist
= aPos
.getX() - aStartPt
.getX();
847 if( mpFontInstance
->mnOrientation
)
849 const double nDY
= aPos
.getY() - aStartPt
.getY();
850 const double fRad
= toRadians(mpFontInstance
->mnOrientation
);
851 nDist
= basegfx::fround
<tools::Long
>(nDist
* cos(fRad
) - nDY
* sin(fRad
));
855 // update the length of the textline
856 nWidth
+= pGlyph
->newWidth();
858 else if( nWidth
> 0 )
860 // draw the textline for each word
861 ImplDrawTextLine( aStartPt
.getX(), aStartPt
.getY(), nDist
, nWidth
,
862 eStrikeout
, eUnderline
, eOverline
, bUnderlineAbove
);
867 // draw textline for the last word
870 ImplDrawTextLine( aStartPt
.getX(), aStartPt
.getY(), nDist
, nWidth
,
871 eStrikeout
, eUnderline
, eOverline
, bUnderlineAbove
);
876 basegfx::B2DPoint aStartPt
= rSalLayout
.GetDrawPosition();
877 ImplDrawTextLine( aStartPt
.getX(), aStartPt
.getY(), 0,
878 rSalLayout
.GetTextWidth(),
879 eStrikeout
, eUnderline
, eOverline
, bUnderlineAbove
);
883 void OutputDevice::ImplDrawMnemonicLine( tools::Long nX
, tools::Long nY
, tools::Long nWidth
)
885 tools::Long nBaseX
= nX
;
886 if( /*HasMirroredGraphics() &&*/ IsRTLEnabled() )
888 // revert the hack that will be done later in ImplDrawTextLine
889 nX
= nBaseX
- nWidth
- (nX
- nBaseX
- 1);
892 ImplDrawTextLine( nX
, nY
, 0, nWidth
, STRIKEOUT_NONE
, LINESTYLE_SINGLE
, LINESTYLE_NONE
, false );
895 void OutputDevice::SetTextLineColor()
899 mpMetaFile
->AddAction( new MetaTextLineColorAction( Color(), false ) );
901 maTextLineColor
= COL_TRANSPARENT
;
904 mpAlphaVDev
->SetTextLineColor();
907 void OutputDevice::SetTextLineColor( const Color
& rColor
)
909 Color
aColor(vcl::drawmode::GetTextColor(rColor
, GetDrawMode(), GetSettings().GetStyleSettings()));
912 mpMetaFile
->AddAction( new MetaTextLineColorAction( aColor
, true ) );
914 maTextLineColor
= aColor
;
917 mpAlphaVDev
->SetTextLineColor( COL_ALPHA_OPAQUE
);
920 void OutputDevice::SetOverlineColor()
924 mpMetaFile
->AddAction( new MetaOverlineColorAction( Color(), false ) );
926 maOverlineColor
= COL_TRANSPARENT
;
929 mpAlphaVDev
->SetOverlineColor();
932 void OutputDevice::SetOverlineColor( const Color
& rColor
)
934 Color
aColor(vcl::drawmode::GetTextColor(rColor
, GetDrawMode(), GetSettings().GetStyleSettings()));
937 mpMetaFile
->AddAction( new MetaOverlineColorAction( aColor
, true ) );
939 maOverlineColor
= aColor
;
942 mpAlphaVDev
->SetOverlineColor( COL_ALPHA_OPAQUE
);
945 void OutputDevice::DrawTextLine( const Point
& rPos
, tools::Long nWidth
,
946 FontStrikeout eStrikeout
,
947 FontLineStyle eUnderline
,
948 FontLineStyle eOverline
,
949 bool bUnderlineAbove
)
951 assert(!is_double_buffered_window());
954 mpMetaFile
->AddAction( new MetaTextLineAction( rPos
, nWidth
, eStrikeout
, eUnderline
, eOverline
) );
956 if ( ((eUnderline
== LINESTYLE_NONE
) || (eUnderline
== LINESTYLE_DONTKNOW
)) &&
957 ((eOverline
== LINESTYLE_NONE
) || (eOverline
== LINESTYLE_DONTKNOW
)) &&
958 ((eStrikeout
== STRIKEOUT_NONE
) || (eStrikeout
== STRIKEOUT_DONTKNOW
)) )
962 if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
965 if( mbInitClipRegion
)
968 if( mbOutputClipped
)
971 // initialize font if needed to get text offsets
972 // TODO: only needed for mnTextOff!=(0,0)
976 Point aPos
= ImplLogicToDevicePixel( rPos
);
977 double fWidth
= ImplLogicWidthToDeviceSubPixel(nWidth
);
978 aPos
+= Point( mnTextOffX
, mnTextOffY
);
979 ImplDrawTextLine( aPos
.X(), aPos
.X(), 0, fWidth
, eStrikeout
, eUnderline
, eOverline
, bUnderlineAbove
);
982 mpAlphaVDev
->DrawTextLine( rPos
, nWidth
, eStrikeout
, eUnderline
, eOverline
, bUnderlineAbove
);
985 void OutputDevice::DrawWaveLine(const Point
& rStartPos
, const Point
& rEndPos
, tools::Long nLineWidth
, tools::Long nWaveHeight
)
987 assert(!is_double_buffered_window());
989 if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
992 // we need a graphics
993 if( !mpGraphics
&& !AcquireGraphics() )
997 if ( mbInitClipRegion
)
1000 if ( mbOutputClipped
)
1006 Point aStartPt
= ImplLogicToDevicePixel(rStartPos
);
1007 Point aEndPt
= ImplLogicToDevicePixel(rEndPos
);
1009 tools::Long nStartX
= aStartPt
.X();
1010 tools::Long nStartY
= aStartPt
.Y();
1011 tools::Long nEndX
= aEndPt
.X();
1012 tools::Long nEndY
= aEndPt
.Y();
1013 double fOrientation
= 0.0;
1016 if (nStartY
!= nEndY
|| nStartX
> nEndX
)
1018 fOrientation
= basegfx::rad2deg(std::atan2(nStartY
- nEndY
, nEndX
- nStartX
));
1019 // un-rotate the end point
1020 aStartPt
.RotateAround(nEndX
, nEndY
, Degree10(static_cast<sal_Int16
>(-fOrientation
* 10.0)));
1024 float fScaleFactor
= GetDPIScaleFactor();
1025 if (fScaleFactor
> 1.0f
)
1027 nWaveHeight
*= fScaleFactor
;
1029 nStartY
+= fScaleFactor
- 1; // Shift down additional pixel(s) to create more visual separation.
1031 // odd heights look better than even
1032 if (nWaveHeight
% 2 == 0)
1038 // #109280# make sure the waveline does not exceed the descent to avoid paint problems
1039 LogicalFontInstance
* pFontInstance
= mpFontInstance
.get();
1040 if (nWaveHeight
> pFontInstance
->mxFontMetric
->GetWavelineUnderlineSize()
1041 // tdf#153223 polyline with lineheight >0 not drawn when skia is off
1043 || !SkiaHelper::isVCLSkiaEnabled()
1047 nWaveHeight
= pFontInstance
->mxFontMetric
->GetWavelineUnderlineSize();
1048 // tdf#124848 hairline
1052 if ( fOrientation
== 0.0 )
1054 static tools::DeleteOnDeinit
< WavyLineCache
> snLineCache
{};
1055 if ( !snLineCache
.get() )
1057 WavyLineCache
& rLineCache
= *snLineCache
.get();
1058 BitmapEx aWavylinebmp
;
1059 if ( !rLineCache
.find( GetLineColor(), nLineWidth
, nWaveHeight
, nEndX
- nStartX
, aWavylinebmp
) )
1061 size_t nWordLength
= nEndX
- nStartX
;
1062 // start with something big to avoid updating it frequently
1063 nWordLength
= nWordLength
< 1024 ? 1024 : nWordLength
;
1064 ScopedVclPtrInstance
< VirtualDevice
> pVirtDev( *this, DeviceFormat::WITH_ALPHA
);
1065 pVirtDev
->SetOutputSizePixel( Size( nWordLength
, nWaveHeight
* 2 ), false );
1066 pVirtDev
->SetLineColor( GetLineColor() );
1067 pVirtDev
->SetBackground( Wallpaper( COL_TRANSPARENT
) );
1069 pVirtDev
->SetAntialiasing( AntialiasingFlags::Enable
);
1070 pVirtDev
->ImplDrawWaveLineBezier( 0, 0, nWordLength
, 0, nWaveHeight
, fOrientation
, nLineWidth
);
1071 BitmapEx
aBitmapEx(pVirtDev
->GetBitmapEx(Point(0, 0), pVirtDev
->GetOutputSize()));
1073 // Ideally we don't need this block, but in the split rgb surface + separate alpha surface
1074 // with Antialiasing enabled and the svp/cairo backend we get both surfaces antialiased
1075 // so their combination of aliases merge to overly wash-out the color. Hack it by taking just
1076 // the alpha surface and use it to blend the original solid line color
1077 Bitmap
aSolidColor(aBitmapEx
.GetBitmap());
1078 aSolidColor
.Erase(GetLineColor());
1079 aBitmapEx
= BitmapEx(aSolidColor
, aBitmapEx
.GetAlphaMask());
1081 rLineCache
.insert( aBitmapEx
, GetLineColor(), nLineWidth
, nWaveHeight
, nWordLength
, aWavylinebmp
);
1083 if ( aWavylinebmp
.ImplGetBitmapSalBitmap() != nullptr )
1085 Size
_size( nEndX
- nStartX
, aWavylinebmp
.GetSizePixel().Height() );
1086 DrawBitmapEx(Point( rStartPos
.X(), rStartPos
.Y() ), PixelToLogic( _size
), Point(), _size
, aWavylinebmp
);
1091 ImplDrawWaveLineBezier( nStartX
, nStartY
, nEndX
, nEndY
, nWaveHeight
, fOrientation
, nLineWidth
);
1094 void OutputDevice::ImplDrawWaveLineBezier(tools::Long nStartX
, tools::Long nStartY
, tools::Long nEndX
, tools::Long nEndY
, tools::Long nWaveHeight
, double fOrientation
, tools::Long nLineWidth
)
1096 // we need a graphics
1097 if( !mpGraphics
&& !AcquireGraphics() )
1101 if ( mbInitClipRegion
)
1104 if ( mbOutputClipped
)
1110 const basegfx::B2DRectangle
aWaveLineRectangle(nStartX
, nStartY
, nEndX
, nEndY
+ nWaveHeight
);
1111 const basegfx::B2DPolygon aWaveLinePolygon
= basegfx::createWaveLinePolygon(aWaveLineRectangle
);
1112 const basegfx::B2DHomMatrix aRotationMatrix
= basegfx::utils::createRotateAroundPoint(nStartX
, nStartY
, basegfx::deg2rad(-fOrientation
));
1113 const bool bPixelSnapHairline(mnAntialiasing
& AntialiasingFlags::PixelSnapHairline
);
1115 mpGraphics
->SetLineColor(GetLineColor());
1116 mpGraphics
->DrawPolyLine(
1122 basegfx::B2DLineJoin::NONE
,
1123 css::drawing::LineCap_BUTT
,
1124 basegfx::deg2rad(15.0),
1129 mpAlphaVDev
->ImplDrawWaveLineBezier(nStartX
, nStartY
, nEndX
, nEndY
, nWaveHeight
, fOrientation
, nLineWidth
);
1132 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */