nss: upgrade to release 3.73
[LibreOffice.git] / vcl / source / outdev / textline.cxx
blob09ee52f6339a04569dfb6eed34811a74af3d4dbe
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <cassert>
22 #include <sal/types.h>
23 #include <vcl/gdimtf.hxx>
24 #include <vcl/metaact.hxx>
25 #include <vcl/outdev.hxx>
26 #include <vcl/settings.hxx>
27 #include <vcl/virdev.hxx>
29 #include <tools/helpers.hxx>
31 #include <salgdi.hxx>
32 #include <impglyphitem.hxx>
34 #include <basegfx/matrix/b2dhommatrixtools.hxx>
35 #include <basegfx/polygon/WaveLine.hxx>
37 #define UNDERLINE_LAST LINESTYLE_BOLDWAVE
38 #define STRIKEOUT_LAST STRIKEOUT_X
40 void OutputDevice::ImplInitTextLineSize()
42 mpFontInstance->mxFontMetric->ImplInitTextLineSize( this );
45 void OutputDevice::ImplInitAboveTextLineSize()
47 mpFontInstance->mxFontMetric->ImplInitAboveTextLineSize();
50 void OutputDevice::ImplDrawWavePixel( tools::Long nOriginX, tools::Long nOriginY,
51 tools::Long nCurX, tools::Long nCurY,
52 Degree10 nOrientation,
53 SalGraphics* pGraphics,
54 OutputDevice const * pOutDev,
55 bool bDrawPixAsRect,
56 tools::Long nPixWidth, tools::Long nPixHeight )
58 if ( nOrientation )
60 Point aPoint( nOriginX, nOriginY );
61 aPoint.RotateAround( nCurX, nCurY, nOrientation );
64 if ( bDrawPixAsRect )
67 pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight, pOutDev );
69 else
71 pGraphics->DrawPixel( nCurX, nCurY, pOutDev );
75 void OutputDevice::ImplDrawWaveLine( tools::Long nBaseX, tools::Long nBaseY,
76 tools::Long nDistX, tools::Long nDistY,
77 tools::Long nWidth, tools::Long nHeight,
78 tools::Long nLineWidth, Degree10 nOrientation,
79 const Color& rColor )
81 if ( !nHeight )
82 return;
84 tools::Long nStartX = nBaseX + nDistX;
85 tools::Long nStartY = nBaseY + nDistY;
87 // If the height is 1 pixel, it's enough output a line
88 if ( (nLineWidth == 1) && (nHeight == 1) )
90 mpGraphics->SetLineColor( rColor );
91 mbInitLineColor = true;
93 tools::Long nEndX = nStartX+nWidth;
94 tools::Long nEndY = nStartY;
95 if ( nOrientation )
97 Point aOriginPt( nBaseX, nBaseY );
98 aOriginPt.RotateAround( nStartX, nStartY, nOrientation );
99 aOriginPt.RotateAround( nEndX, nEndY, nOrientation );
101 mpGraphics->DrawLine( nStartX, nStartY, nEndX, nEndY, this );
103 else
105 tools::Long nCurX = nStartX;
106 tools::Long nCurY = nStartY;
107 tools::Long nDiffX = 2;
108 tools::Long nDiffY = nHeight-1;
109 tools::Long nCount = nWidth;
110 tools::Long nOffY = -1;
111 tools::Long nPixWidth;
112 tools::Long nPixHeight;
113 bool bDrawPixAsRect;
114 // On printers that output pixel via DrawRect()
115 if ( (GetOutDevType() == OUTDEV_PRINTER) || (nLineWidth > 1) )
117 if ( mbLineColor || mbInitLineColor )
119 mpGraphics->SetLineColor();
120 mbInitLineColor = true;
122 mpGraphics->SetFillColor( rColor );
123 mbInitFillColor = true;
124 bDrawPixAsRect = true;
125 nPixWidth = nLineWidth;
126 nPixHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY;
128 else
130 mpGraphics->SetLineColor( rColor );
131 mbInitLineColor = true;
132 nPixWidth = 1;
133 nPixHeight = 1;
134 bDrawPixAsRect = false;
137 if ( !nDiffY )
139 while ( nWidth )
141 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
142 mpGraphics, this,
143 bDrawPixAsRect, nPixWidth, nPixHeight );
144 nCurX++;
145 nWidth--;
148 else
150 nCurY += nDiffY;
151 tools::Long nFreq = nCount / (nDiffX+nDiffY);
152 while ( nFreq-- )
154 for( tools::Long i = nDiffY; i; --i )
156 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
157 mpGraphics, this,
158 bDrawPixAsRect, nPixWidth, nPixHeight );
159 nCurX++;
160 nCurY += nOffY;
162 for( tools::Long i = nDiffX; i; --i )
164 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
165 mpGraphics, this,
166 bDrawPixAsRect, nPixWidth, nPixHeight );
167 nCurX++;
169 nOffY = -nOffY;
171 nFreq = nCount % (nDiffX+nDiffY);
172 if ( nFreq )
174 for( tools::Long i = nDiffY; i && nFreq; --i, --nFreq )
176 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
177 mpGraphics, this,
178 bDrawPixAsRect, nPixWidth, nPixHeight );
179 nCurX++;
180 nCurY += nOffY;
183 for( tools::Long i = nDiffX; i && nFreq; --i, --nFreq )
185 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
186 mpGraphics, this,
187 bDrawPixAsRect, nPixWidth, nPixHeight );
188 nCurX++;
195 void OutputDevice::ImplDrawWaveTextLine( tools::Long nBaseX, tools::Long nBaseY,
196 tools::Long nDistX, tools::Long nDistY, tools::Long nWidth,
197 FontLineStyle eTextLine,
198 Color aColor,
199 bool bIsAbove )
201 LogicalFontInstance* pFontInstance = mpFontInstance.get();
202 tools::Long nLineHeight;
203 tools::Long nLinePos;
205 if ( bIsAbove )
207 nLineHeight = pFontInstance->mxFontMetric->GetAboveWavelineUnderlineSize();
208 nLinePos = pFontInstance->mxFontMetric->GetAboveWavelineUnderlineOffset();
210 else
212 nLineHeight = pFontInstance->mxFontMetric->GetWavelineUnderlineSize();
213 nLinePos = pFontInstance->mxFontMetric->GetWavelineUnderlineOffset();
215 if ( (eTextLine == LINESTYLE_SMALLWAVE) && (nLineHeight > 3) )
216 nLineHeight = 3;
218 tools::Long nLineWidth = mnDPIX / 300;
219 if ( !nLineWidth )
220 nLineWidth = 1;
222 if ( eTextLine == LINESTYLE_BOLDWAVE )
223 nLineWidth *= 2;
225 nLinePos += nDistY - (nLineHeight / 2);
227 tools::Long nLineWidthHeight = ((nLineWidth * mnDPIX) + (mnDPIY / 2)) / mnDPIY;
228 if ( eTextLine == LINESTYLE_DOUBLEWAVE )
230 tools::Long nOrgLineHeight = nLineHeight;
231 nLineHeight /= 3;
232 if ( nLineHeight < 2 )
234 if ( nOrgLineHeight > 1 )
235 nLineHeight = 2;
236 else
237 nLineHeight = 1;
240 tools::Long nLineDY = nOrgLineHeight-(nLineHeight*2);
241 if ( nLineDY < nLineWidthHeight )
242 nLineDY = nLineWidthHeight;
244 tools::Long nLineDY2 = nLineDY/2;
245 if ( !nLineDY2 )
246 nLineDY2 = 1;
248 nLinePos -= nLineWidthHeight-nLineDY2;
249 ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
250 nLineWidth, mpFontInstance->mnOrientation, aColor );
251 nLinePos += nLineWidthHeight+nLineDY;
252 ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
253 nLineWidth, mpFontInstance->mnOrientation, aColor );
255 else
257 nLinePos -= nLineWidthHeight/2;
258 ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
259 nLineWidth, mpFontInstance->mnOrientation, aColor );
263 void OutputDevice::ImplDrawStraightTextLine( tools::Long nBaseX, tools::Long nBaseY,
264 tools::Long nDistX, tools::Long nDistY, tools::Long nWidth,
265 FontLineStyle eTextLine,
266 Color aColor,
267 bool bIsAbove )
269 LogicalFontInstance* pFontInstance = mpFontInstance.get();
270 tools::Long nLineHeight = 0;
271 tools::Long nLinePos = 0;
272 tools::Long nLinePos2 = 0;
274 const tools::Long nY = nDistY;
276 if ( eTextLine > UNDERLINE_LAST )
277 eTextLine = LINESTYLE_SINGLE;
279 switch ( eTextLine )
281 case LINESTYLE_SINGLE:
282 case LINESTYLE_DOTTED:
283 case LINESTYLE_DASH:
284 case LINESTYLE_LONGDASH:
285 case LINESTYLE_DASHDOT:
286 case LINESTYLE_DASHDOTDOT:
287 if ( bIsAbove )
289 nLineHeight = pFontInstance->mxFontMetric->GetAboveUnderlineSize();
290 nLinePos = nY + pFontInstance->mxFontMetric->GetAboveUnderlineOffset();
292 else
294 nLineHeight = pFontInstance->mxFontMetric->GetUnderlineSize();
295 nLinePos = nY + pFontInstance->mxFontMetric->GetUnderlineOffset();
297 break;
298 case LINESTYLE_BOLD:
299 case LINESTYLE_BOLDDOTTED:
300 case LINESTYLE_BOLDDASH:
301 case LINESTYLE_BOLDLONGDASH:
302 case LINESTYLE_BOLDDASHDOT:
303 case LINESTYLE_BOLDDASHDOTDOT:
304 if ( bIsAbove )
306 nLineHeight = pFontInstance->mxFontMetric->GetAboveBoldUnderlineSize();
307 nLinePos = nY + pFontInstance->mxFontMetric->GetAboveBoldUnderlineOffset();
309 else
311 nLineHeight = pFontInstance->mxFontMetric->GetBoldUnderlineSize();
312 nLinePos = nY + pFontInstance->mxFontMetric->GetBoldUnderlineOffset();
314 break;
315 case LINESTYLE_DOUBLE:
316 if ( bIsAbove )
318 nLineHeight = pFontInstance->mxFontMetric->GetAboveDoubleUnderlineSize();
319 nLinePos = nY + pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset1();
320 nLinePos2 = nY + pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset2();
322 else
324 nLineHeight = pFontInstance->mxFontMetric->GetDoubleUnderlineSize();
325 nLinePos = nY + pFontInstance->mxFontMetric->GetDoubleUnderlineOffset1();
326 nLinePos2 = nY + pFontInstance->mxFontMetric->GetDoubleUnderlineOffset2();
328 break;
329 default:
330 break;
333 if ( !nLineHeight )
334 return;
336 if ( mbLineColor || mbInitLineColor )
338 mpGraphics->SetLineColor();
339 mbInitLineColor = true;
341 mpGraphics->SetFillColor( aColor );
342 mbInitFillColor = true;
344 tools::Long nLeft = nDistX;
346 switch ( eTextLine )
348 case LINESTYLE_SINGLE:
349 case LINESTYLE_BOLD:
350 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
351 break;
352 case LINESTYLE_DOUBLE:
353 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
354 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
355 break;
356 case LINESTYLE_DOTTED:
357 case LINESTYLE_BOLDDOTTED:
359 tools::Long nDotWidth = nLineHeight*mnDPIY;
360 nDotWidth += mnDPIY/2;
361 nDotWidth /= mnDPIY;
363 tools::Long nTempWidth = nDotWidth;
364 tools::Long nEnd = nLeft+nWidth;
365 while ( nLeft < nEnd )
367 if ( nLeft+nTempWidth > nEnd )
368 nTempWidth = nEnd-nLeft;
370 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
371 nLeft += nDotWidth*2;
374 break;
375 case LINESTYLE_DASH:
376 case LINESTYLE_LONGDASH:
377 case LINESTYLE_BOLDDASH:
378 case LINESTYLE_BOLDLONGDASH:
380 tools::Long nDotWidth = nLineHeight*mnDPIY;
381 nDotWidth += mnDPIY/2;
382 nDotWidth /= mnDPIY;
384 tools::Long nMinDashWidth;
385 tools::Long nMinSpaceWidth;
386 tools::Long nSpaceWidth;
387 tools::Long nDashWidth;
388 if ( (eTextLine == LINESTYLE_LONGDASH) ||
389 (eTextLine == LINESTYLE_BOLDLONGDASH) )
391 nMinDashWidth = nDotWidth*6;
392 nMinSpaceWidth = nDotWidth*2;
393 nDashWidth = 200;
394 nSpaceWidth = 100;
396 else
398 nMinDashWidth = nDotWidth*4;
399 nMinSpaceWidth = (nDotWidth*150)/100;
400 nDashWidth = 100;
401 nSpaceWidth = 50;
403 nDashWidth = ((nDashWidth*mnDPIX)+1270)/2540;
404 nSpaceWidth = ((nSpaceWidth*mnDPIX)+1270)/2540;
405 // DashWidth will be increased if the line is getting too thick
406 // in proportion to the line's length
407 if ( nDashWidth < nMinDashWidth )
408 nDashWidth = nMinDashWidth;
409 if ( nSpaceWidth < nMinSpaceWidth )
410 nSpaceWidth = nMinSpaceWidth;
412 tools::Long nTempWidth = nDashWidth;
413 tools::Long nEnd = nLeft+nWidth;
414 while ( nLeft < nEnd )
416 if ( nLeft+nTempWidth > nEnd )
417 nTempWidth = nEnd-nLeft;
418 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
419 nLeft += nDashWidth+nSpaceWidth;
422 break;
423 case LINESTYLE_DASHDOT:
424 case LINESTYLE_BOLDDASHDOT:
426 tools::Long nDotWidth = nLineHeight*mnDPIY;
427 nDotWidth += mnDPIY/2;
428 nDotWidth /= mnDPIY;
430 tools::Long nDashWidth = ((100*mnDPIX)+1270)/2540;
431 tools::Long nMinDashWidth = nDotWidth*4;
432 // DashWidth will be increased if the line is getting too thick
433 // in proportion to the line's length
434 if ( nDashWidth < nMinDashWidth )
435 nDashWidth = nMinDashWidth;
437 tools::Long nTempDotWidth = nDotWidth;
438 tools::Long nTempDashWidth = nDashWidth;
439 tools::Long nEnd = nLeft+nWidth;
440 while ( nLeft < nEnd )
442 if ( nLeft+nTempDotWidth > nEnd )
443 nTempDotWidth = nEnd-nLeft;
445 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
446 nLeft += nDotWidth*2;
447 if ( nLeft > nEnd )
448 break;
450 if ( nLeft+nTempDashWidth > nEnd )
451 nTempDashWidth = nEnd-nLeft;
453 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
454 nLeft += nDashWidth+nDotWidth;
457 break;
458 case LINESTYLE_DASHDOTDOT:
459 case LINESTYLE_BOLDDASHDOTDOT:
461 tools::Long nDotWidth = nLineHeight*mnDPIY;
462 nDotWidth += mnDPIY/2;
463 nDotWidth /= mnDPIY;
465 tools::Long nDashWidth = ((100*mnDPIX)+1270)/2540;
466 tools::Long nMinDashWidth = nDotWidth*4;
467 // DashWidth will be increased if the line is getting too thick
468 // in proportion to the line's length
469 if ( nDashWidth < nMinDashWidth )
470 nDashWidth = nMinDashWidth;
472 tools::Long nTempDotWidth = nDotWidth;
473 tools::Long nTempDashWidth = nDashWidth;
474 tools::Long nEnd = nLeft+nWidth;
475 while ( nLeft < nEnd )
477 if ( nLeft+nTempDotWidth > nEnd )
478 nTempDotWidth = nEnd-nLeft;
480 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
481 nLeft += nDotWidth*2;
482 if ( nLeft > nEnd )
483 break;
485 if ( nLeft+nTempDotWidth > nEnd )
486 nTempDotWidth = nEnd-nLeft;
488 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
489 nLeft += nDotWidth*2;
490 if ( nLeft > nEnd )
491 break;
493 if ( nLeft+nTempDashWidth > nEnd )
494 nTempDashWidth = nEnd-nLeft;
496 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
497 nLeft += nDashWidth+nDotWidth;
500 break;
501 default:
502 break;
506 void OutputDevice::ImplDrawStrikeoutLine( tools::Long nBaseX, tools::Long nBaseY,
507 tools::Long nDistX, tools::Long nDistY, tools::Long nWidth,
508 FontStrikeout eStrikeout,
509 Color aColor )
511 LogicalFontInstance* pFontInstance = mpFontInstance.get();
512 tools::Long nLineHeight = 0;
513 tools::Long nLinePos = 0;
514 tools::Long nLinePos2 = 0;
516 tools::Long nY = nDistY;
518 if ( eStrikeout > STRIKEOUT_LAST )
519 eStrikeout = STRIKEOUT_SINGLE;
521 switch ( eStrikeout )
523 case STRIKEOUT_SINGLE:
524 nLineHeight = pFontInstance->mxFontMetric->GetStrikeoutSize();
525 nLinePos = nY + pFontInstance->mxFontMetric->GetStrikeoutOffset();
526 break;
527 case STRIKEOUT_BOLD:
528 nLineHeight = pFontInstance->mxFontMetric->GetBoldStrikeoutSize();
529 nLinePos = nY + pFontInstance->mxFontMetric->GetBoldStrikeoutOffset();
530 break;
531 case STRIKEOUT_DOUBLE:
532 nLineHeight = pFontInstance->mxFontMetric->GetDoubleStrikeoutSize();
533 nLinePos = nY + pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset1();
534 nLinePos2 = nY + pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset2();
535 break;
536 default:
537 break;
540 if ( !nLineHeight )
541 return;
543 if ( mbLineColor || mbInitLineColor )
545 mpGraphics->SetLineColor();
546 mbInitLineColor = true;
548 mpGraphics->SetFillColor( aColor );
549 mbInitFillColor = true;
551 const tools::Long& nLeft = nDistX;
553 switch ( eStrikeout )
555 case STRIKEOUT_SINGLE:
556 case STRIKEOUT_BOLD:
557 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
558 break;
559 case STRIKEOUT_DOUBLE:
560 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
561 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
562 break;
563 default:
564 break;
568 void OutputDevice::ImplDrawStrikeoutChar( tools::Long nBaseX, tools::Long nBaseY,
569 tools::Long nDistX, tools::Long nDistY, tools::Long nWidth,
570 FontStrikeout eStrikeout,
571 Color aColor )
573 // See qadevOOo/testdocs/StrikeThrough.odt for examples if you need
574 // to tweak this
575 if (!nWidth)
576 return;
578 // prepare string for strikeout measurement
579 const char cStrikeoutChar = eStrikeout == STRIKEOUT_SLASH ? '/' : 'X';
580 static const int nTestStrLen = 4;
581 static const int nMaxStrikeStrLen = 2048;
582 sal_Unicode aChars[nMaxStrikeStrLen+1]; // +1 for valgrind...
584 for( int i = 0; i < nTestStrLen; ++i)
585 aChars[i] = cStrikeoutChar;
587 const OUString aStrikeoutTest(aChars, nTestStrLen);
589 // calculate approximation of strikeout atom size
590 tools::Long nStrikeoutWidth = 0;
591 std::unique_ptr<SalLayout> pLayout = ImplLayout( aStrikeoutTest, 0, nTestStrLen );
592 if( pLayout )
594 nStrikeoutWidth = pLayout->GetTextWidth() / (nTestStrLen * pLayout->GetUnitsPerPixel());
596 if( nStrikeoutWidth <= 0 ) // sanity check
597 return;
599 int nStrikeStrLen = (nWidth+(nStrikeoutWidth-1)) / nStrikeoutWidth;
600 if( nStrikeStrLen > nMaxStrikeStrLen )
601 nStrikeStrLen = nMaxStrikeStrLen;
603 // build the strikeout string
604 for( int i = nTestStrLen; i < nStrikeStrLen; ++i)
605 aChars[i] = cStrikeoutChar;
607 const OUString aStrikeoutText(aChars, nStrikeStrLen);
609 if( mpFontInstance->mnOrientation )
611 Point aOriginPt(0, 0);
612 aOriginPt.RotateAround( nDistX, nDistY, mpFontInstance->mnOrientation );
615 nBaseX += nDistX;
616 nBaseY += nDistY;
618 // strikeout text has to be left aligned
619 ComplexTextLayoutFlags nOrigTLM = mnTextLayoutMode;
620 mnTextLayoutMode = ComplexTextLayoutFlags::BiDiStrong;
621 pLayout = ImplLayout( aStrikeoutText, 0, aStrikeoutText.getLength() );
622 mnTextLayoutMode = nOrigTLM;
624 if( !pLayout )
625 return;
627 // draw the strikeout text
628 const Color aOldColor = GetTextColor();
629 SetTextColor( aColor );
630 ImplInitTextColor();
632 pLayout->DrawBase() = Point( nBaseX+mnTextOffX, nBaseY+mnTextOffY );
634 tools::Rectangle aPixelRect;
635 aPixelRect.SetLeft( nBaseX+mnTextOffX );
636 aPixelRect.SetRight( aPixelRect.Left()+nWidth );
637 aPixelRect.SetBottom( nBaseY+mpFontInstance->mxFontMetric->GetDescent() );
638 aPixelRect.SetTop( nBaseY-mpFontInstance->mxFontMetric->GetAscent() );
640 if (mpFontInstance->mnOrientation)
642 tools::Polygon aPoly( aPixelRect );
643 aPoly.Rotate( Point(nBaseX+mnTextOffX, nBaseY+mnTextOffY), mpFontInstance->mnOrientation);
644 aPixelRect = aPoly.GetBoundRect();
647 Push( PushFlags::CLIPREGION );
648 IntersectClipRegion( PixelToLogic(aPixelRect) );
649 if( mbInitClipRegion )
650 InitClipRegion();
652 pLayout->DrawText( *mpGraphics );
654 Pop();
656 SetTextColor( aOldColor );
657 ImplInitTextColor();
660 void OutputDevice::ImplDrawTextLine( tools::Long nX, tools::Long nY,
661 tools::Long nDistX, DeviceCoordinate nWidth,
662 FontStrikeout eStrikeout,
663 FontLineStyle eUnderline,
664 FontLineStyle eOverline,
665 bool bUnderlineAbove )
667 if ( !nWidth )
668 return;
670 Color aStrikeoutColor = GetTextColor();
671 Color aUnderlineColor = GetTextLineColor();
672 Color aOverlineColor = GetOverlineColor();
673 bool bStrikeoutDone = false;
674 bool bUnderlineDone = false;
675 bool bOverlineDone = false;
677 if ( IsRTLEnabled() )
679 tools::Long nXAdd = nWidth - nDistX;
680 if( mpFontInstance->mnOrientation )
681 nXAdd = FRound( nXAdd * cos( mpFontInstance->mnOrientation.get() * F_PI1800 ) );
683 nX += nXAdd - 1;
686 if ( !IsTextLineColor() )
687 aUnderlineColor = GetTextColor();
689 if ( !IsOverlineColor() )
690 aOverlineColor = GetTextColor();
692 if ( (eUnderline == LINESTYLE_SMALLWAVE) ||
693 (eUnderline == LINESTYLE_WAVE) ||
694 (eUnderline == LINESTYLE_DOUBLEWAVE) ||
695 (eUnderline == LINESTYLE_BOLDWAVE) )
697 ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
698 bUnderlineDone = true;
700 if ( (eOverline == LINESTYLE_SMALLWAVE) ||
701 (eOverline == LINESTYLE_WAVE) ||
702 (eOverline == LINESTYLE_DOUBLEWAVE) ||
703 (eOverline == LINESTYLE_BOLDWAVE) )
705 ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, true );
706 bOverlineDone = true;
709 if ( (eStrikeout == STRIKEOUT_SLASH) ||
710 (eStrikeout == STRIKEOUT_X) )
712 ImplDrawStrikeoutChar( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor );
713 bStrikeoutDone = true;
716 if ( !bUnderlineDone )
717 ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
719 if ( !bOverlineDone )
720 ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, true );
722 if ( !bStrikeoutDone )
723 ImplDrawStrikeoutLine( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor );
726 void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout, FontStrikeout eStrikeout,
727 FontLineStyle eUnderline, FontLineStyle eOverline,
728 bool bWordLine, bool bUnderlineAbove )
730 if( bWordLine )
732 // draw everything relative to the layout base point
733 const Point aStartPt = rSalLayout.DrawBase();
735 // calculate distance of each word from the base point
736 Point aPos;
737 DeviceCoordinate nDist = 0;
738 DeviceCoordinate nWidth = 0;
739 const GlyphItem* pGlyph;
740 int nStart = 0;
741 while (rSalLayout.GetNextGlyph(&pGlyph, aPos, nStart))
743 // calculate the boundaries of each word
744 if (!pGlyph->IsSpacing())
746 if( !nWidth )
748 // get the distance to the base point (as projected to baseline)
749 nDist = aPos.X() - aStartPt.X();
750 if( mpFontInstance->mnOrientation )
752 const tools::Long nDY = aPos.Y() - aStartPt.Y();
753 const double fRad = mpFontInstance->mnOrientation.get() * F_PI1800;
754 nDist = FRound( nDist*cos(fRad) - nDY*sin(fRad) );
758 // update the length of the textline
759 nWidth += pGlyph->m_nNewWidth;
761 else if( nWidth > 0 )
763 // draw the textline for each word
764 ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth,
765 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
766 nWidth = 0;
770 // draw textline for the last word
771 if( nWidth > 0 )
773 ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth,
774 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
777 else
779 Point aStartPt = rSalLayout.GetDrawPosition();
780 ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), 0,
781 rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel(),
782 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
786 void OutputDevice::ImplDrawMnemonicLine( tools::Long nX, tools::Long nY, tools::Long nWidth )
788 tools::Long nBaseX = nX;
789 if( /*HasMirroredGraphics() &&*/ IsRTLEnabled() )
791 // add some strange offset
792 nX += 2;
793 // revert the hack that will be done later in ImplDrawTextLine
794 nX = nBaseX - nWidth - (nX - nBaseX - 1);
797 ImplDrawTextLine( nX, nY, 0, nWidth, STRIKEOUT_NONE, LINESTYLE_SINGLE, LINESTYLE_NONE, false );
800 void OutputDevice::SetTextLineColor()
803 if ( mpMetaFile )
804 mpMetaFile->AddAction( new MetaTextLineColorAction( Color(), false ) );
806 maTextLineColor = COL_TRANSPARENT;
808 if( mpAlphaVDev )
809 mpAlphaVDev->SetTextLineColor();
812 void OutputDevice::SetTextLineColor( const Color& rColor )
815 Color aColor( rColor );
817 if ( mnDrawMode & ( DrawModeFlags::BlackText | DrawModeFlags::WhiteText |
818 DrawModeFlags::GrayText |
819 DrawModeFlags::SettingsText ) )
821 if ( mnDrawMode & DrawModeFlags::BlackText )
823 aColor = COL_BLACK;
825 else if ( mnDrawMode & DrawModeFlags::WhiteText )
827 aColor = COL_WHITE;
829 else if ( mnDrawMode & DrawModeFlags::GrayText )
831 const sal_uInt8 cLum = aColor.GetLuminance();
832 aColor = Color( cLum, cLum, cLum );
834 else if ( mnDrawMode & DrawModeFlags::SettingsText )
836 aColor = GetSettings().GetStyleSettings().GetFontColor();
840 if ( mpMetaFile )
841 mpMetaFile->AddAction( new MetaTextLineColorAction( aColor, true ) );
843 maTextLineColor = aColor;
845 if( mpAlphaVDev )
846 mpAlphaVDev->SetTextLineColor( COL_BLACK );
849 void OutputDevice::SetOverlineColor()
852 if ( mpMetaFile )
853 mpMetaFile->AddAction( new MetaOverlineColorAction( Color(), false ) );
855 maOverlineColor = COL_TRANSPARENT;
857 if( mpAlphaVDev )
858 mpAlphaVDev->SetOverlineColor();
861 void OutputDevice::SetOverlineColor( const Color& rColor )
864 Color aColor( rColor );
866 if ( mnDrawMode & ( DrawModeFlags::BlackText | DrawModeFlags::WhiteText |
867 DrawModeFlags::GrayText |
868 DrawModeFlags::SettingsText ) )
870 if ( mnDrawMode & DrawModeFlags::BlackText )
872 aColor = COL_BLACK;
874 else if ( mnDrawMode & DrawModeFlags::WhiteText )
876 aColor = COL_WHITE;
878 else if ( mnDrawMode & DrawModeFlags::GrayText )
880 const sal_uInt8 cLum = aColor.GetLuminance();
881 aColor = Color( cLum, cLum, cLum );
883 else if ( mnDrawMode & DrawModeFlags::SettingsText )
885 aColor = GetSettings().GetStyleSettings().GetFontColor();
889 if ( mpMetaFile )
890 mpMetaFile->AddAction( new MetaOverlineColorAction( aColor, true ) );
892 maOverlineColor = aColor;
894 if( mpAlphaVDev )
895 mpAlphaVDev->SetOverlineColor( COL_BLACK );
898 void OutputDevice::DrawTextLine( const Point& rPos, tools::Long nWidth,
899 FontStrikeout eStrikeout,
900 FontLineStyle eUnderline,
901 FontLineStyle eOverline,
902 bool bUnderlineAbove )
904 assert(!is_double_buffered_window());
906 if ( mpMetaFile )
907 mpMetaFile->AddAction( new MetaTextLineAction( rPos, nWidth, eStrikeout, eUnderline, eOverline ) );
909 if ( ((eUnderline == LINESTYLE_NONE) || (eUnderline == LINESTYLE_DONTKNOW)) &&
910 ((eOverline == LINESTYLE_NONE) || (eOverline == LINESTYLE_DONTKNOW)) &&
911 ((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) )
913 return;
915 if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
916 return;
918 if( mbInitClipRegion )
919 InitClipRegion();
921 if( mbOutputClipped )
922 return;
924 // initialize font if needed to get text offsets
925 // TODO: only needed for mnTextOff!=(0,0)
926 if (!InitFont())
927 return;
929 Point aPos = ImplLogicToDevicePixel( rPos );
930 DeviceCoordinate fWidth;
931 fWidth = LogicWidthToDeviceCoordinate( nWidth );
932 aPos += Point( mnTextOffX, mnTextOffY );
933 ImplDrawTextLine( aPos.X(), aPos.X(), 0, fWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove );
935 if( mpAlphaVDev )
936 mpAlphaVDev->DrawTextLine( rPos, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove );
939 void OutputDevice::DrawWaveLine(const Point& rStartPos, const Point& rEndPos, tools::Long nLineWidth)
941 assert(!is_double_buffered_window());
943 if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
944 return;
946 // we need a graphics
947 if( !mpGraphics && !AcquireGraphics() )
948 return;
950 if ( mbInitClipRegion )
951 InitClipRegion();
953 if ( mbOutputClipped )
954 return;
956 if (!InitFont())
957 return;
959 Point aStartPt = ImplLogicToDevicePixel(rStartPos);
960 Point aEndPt = ImplLogicToDevicePixel(rEndPos);
962 tools::Long nStartX = aStartPt.X();
963 tools::Long nStartY = aStartPt.Y();
964 tools::Long nEndX = aEndPt.X();
965 tools::Long nEndY = aEndPt.Y();
966 double fOrientation = 0.0;
968 // handle rotation
969 if (nStartY != nEndY || nStartX > nEndX)
971 tools::Long nLengthX = nEndX - nStartX;
972 fOrientation = std::atan2(nStartY - nEndY, (nLengthX == 0 ? 0.000000001 : nLengthX));
973 fOrientation /= F_PI180;
974 // un-rotate the end point
975 aStartPt.RotateAround(nEndX, nEndY, Degree10(static_cast<sal_Int16>(-fOrientation * 10.0)));
978 tools::Long nWaveHeight = 3;
980 // Handle HiDPI
981 float fScaleFactor = GetDPIScaleFactor();
982 if (fScaleFactor > 1.0f)
984 nWaveHeight *= fScaleFactor;
986 nStartY += fScaleFactor - 1; // Shift down additional pixel(s) to create more visual separation.
988 // odd heights look better than even
989 if (nWaveHeight % 2 == 0)
991 nWaveHeight--;
995 // #109280# make sure the waveline does not exceed the descent to avoid paint problems
996 LogicalFontInstance* pFontInstance = mpFontInstance.get();
997 if (nWaveHeight > pFontInstance->mxFontMetric->GetWavelineUnderlineSize())
999 nWaveHeight = pFontInstance->mxFontMetric->GetWavelineUnderlineSize();
1000 // tdf#124848 hairline
1001 nLineWidth = 0;
1004 const basegfx::B2DRectangle aWaveLineRectangle(nStartX, nStartY, nEndX, nEndY + nWaveHeight);
1005 const basegfx::B2DPolygon aWaveLinePolygon = basegfx::createWaveLinePolygon(aWaveLineRectangle);
1006 const basegfx::B2DHomMatrix aRotationMatrix = basegfx::utils::createRotateAroundPoint(nStartX, nStartY, basegfx::deg2rad(-fOrientation));
1007 const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
1009 mpGraphics->SetLineColor(GetLineColor());
1010 mpGraphics->DrawPolyLine(
1011 aRotationMatrix,
1012 aWaveLinePolygon,
1013 0.0,
1014 nLineWidth,
1015 nullptr, // MM01
1016 basegfx::B2DLineJoin::NONE,
1017 css::drawing::LineCap_BUTT,
1018 basegfx::deg2rad(15.0),
1019 bPixelSnapHairline,
1020 this);
1022 if( mpAlphaVDev )
1023 mpAlphaVDev->DrawWaveLine( rStartPos, rEndPos, nLineWidth );
1026 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */