diff options
Diffstat (limited to 'vcl/source/outdev/textline.cxx')
-rw-r--r-- | vcl/source/outdev/textline.cxx | 1026 |
1 files changed, 1026 insertions, 0 deletions
diff --git a/vcl/source/outdev/textline.cxx b/vcl/source/outdev/textline.cxx new file mode 100644 index 000000000..6ade6113f --- /dev/null +++ b/vcl/source/outdev/textline.cxx @@ -0,0 +1,1026 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <cassert> + +#include <sal/types.h> +#include <vcl/gdimtf.hxx> +#include <vcl/metaact.hxx> +#include <vcl/outdev.hxx> +#include <vcl/settings.hxx> +#include <vcl/virdev.hxx> + +#include <tools/helpers.hxx> + +#include <salgdi.hxx> +#include <impglyphitem.hxx> + +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/polygon/WaveLine.hxx> + +#define UNDERLINE_LAST LINESTYLE_BOLDWAVE +#define STRIKEOUT_LAST STRIKEOUT_X + +void OutputDevice::ImplInitTextLineSize() +{ + mpFontInstance->mxFontMetric->ImplInitTextLineSize( this ); +} + +void OutputDevice::ImplInitAboveTextLineSize() +{ + mpFontInstance->mxFontMetric->ImplInitAboveTextLineSize(); +} + +void OutputDevice::ImplDrawWavePixel( long nOriginX, long nOriginY, + long nCurX, long nCurY, + short nOrientation, + SalGraphics* pGraphics, + OutputDevice const * pOutDev, + bool bDrawPixAsRect, + long nPixWidth, long nPixHeight ) +{ + if ( nOrientation ) + { + Point aPoint( nOriginX, nOriginY ); + aPoint.RotateAround( nCurX, nCurY, nOrientation ); + } + + if ( bDrawPixAsRect ) + { + + pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight, pOutDev ); + } + else + { + pGraphics->DrawPixel( nCurX, nCurY, pOutDev ); + } +} + +void OutputDevice::ImplDrawWaveLine( long nBaseX, long nBaseY, + long nDistX, long nDistY, + long nWidth, long nHeight, + long nLineWidth, short nOrientation, + const Color& rColor ) +{ + if ( !nHeight ) + return; + + long nStartX = nBaseX + nDistX; + long nStartY = nBaseY + nDistY; + + // If the height is 1 pixel, it's enough output a line + if ( (nLineWidth == 1) && (nHeight == 1) ) + { + mpGraphics->SetLineColor( rColor ); + mbInitLineColor = true; + + long nEndX = nStartX+nWidth; + long nEndY = nStartY; + if ( nOrientation ) + { + Point aOriginPt( nBaseX, nBaseY ); + aOriginPt.RotateAround( nStartX, nStartY, nOrientation ); + aOriginPt.RotateAround( nEndX, nEndY, nOrientation ); + } + mpGraphics->DrawLine( nStartX, nStartY, nEndX, nEndY, this ); + } + else + { + long nCurX = nStartX; + long nCurY = nStartY; + long nDiffX = 2; + long nDiffY = nHeight-1; + long nCount = nWidth; + long nOffY = -1; + long nPixWidth; + long nPixHeight; + bool bDrawPixAsRect; + // On printers that output pixel via DrawRect() + if ( (GetOutDevType() == OUTDEV_PRINTER) || (nLineWidth > 1) ) + { + if ( mbLineColor || mbInitLineColor ) + { + mpGraphics->SetLineColor(); + mbInitLineColor = true; + } + mpGraphics->SetFillColor( rColor ); + mbInitFillColor = true; + bDrawPixAsRect = true; + nPixWidth = nLineWidth; + nPixHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY; + } + else + { + mpGraphics->SetLineColor( rColor ); + mbInitLineColor = true; + nPixWidth = 1; + nPixHeight = 1; + bDrawPixAsRect = false; + } + + if ( !nDiffY ) + { + while ( nWidth ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, this, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + nWidth--; + } + } + else + { + nCurY += nDiffY; + long nFreq = nCount / (nDiffX+nDiffY); + while ( nFreq-- ) + { + for( long i = nDiffY; i; --i ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, this, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + nCurY += nOffY; + } + for( long i = nDiffX; i; --i ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, this, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + } + nOffY = -nOffY; + } + nFreq = nCount % (nDiffX+nDiffY); + if ( nFreq ) + { + for( long i = nDiffY; i && nFreq; --i, --nFreq ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, this, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + nCurY += nOffY; + + } + for( long i = nDiffX; i && nFreq; --i, --nFreq ) + { + ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation, + mpGraphics, this, + bDrawPixAsRect, nPixWidth, nPixHeight ); + nCurX++; + } + } + } + } +} + +void OutputDevice::ImplDrawWaveTextLine( long nBaseX, long nBaseY, + long nDistX, long nDistY, long nWidth, + FontLineStyle eTextLine, + Color aColor, + bool bIsAbove ) +{ + LogicalFontInstance* pFontInstance = mpFontInstance.get(); + long nLineHeight; + long nLinePos; + + if ( bIsAbove ) + { + nLineHeight = pFontInstance->mxFontMetric->GetAboveWavelineUnderlineSize(); + nLinePos = pFontInstance->mxFontMetric->GetAboveWavelineUnderlineOffset(); + } + else + { + nLineHeight = pFontInstance->mxFontMetric->GetWavelineUnderlineSize(); + nLinePos = pFontInstance->mxFontMetric->GetWavelineUnderlineOffset(); + } + if ( (eTextLine == LINESTYLE_SMALLWAVE) && (nLineHeight > 3) ) + nLineHeight = 3; + + long nLineWidth = mnDPIX / 300; + if ( !nLineWidth ) + nLineWidth = 1; + + if ( eTextLine == LINESTYLE_BOLDWAVE ) + nLineWidth *= 2; + + nLinePos += nDistY - (nLineHeight / 2); + + long nLineWidthHeight = ((nLineWidth * mnDPIX) + (mnDPIY / 2)) / mnDPIY; + if ( eTextLine == LINESTYLE_DOUBLEWAVE ) + { + long nOrgLineHeight = nLineHeight; + nLineHeight /= 3; + if ( nLineHeight < 2 ) + { + if ( nOrgLineHeight > 1 ) + nLineHeight = 2; + else + nLineHeight = 1; + } + + long nLineDY = nOrgLineHeight-(nLineHeight*2); + if ( nLineDY < nLineWidthHeight ) + nLineDY = nLineWidthHeight; + + long nLineDY2 = nLineDY/2; + if ( !nLineDY2 ) + nLineDY2 = 1; + + nLinePos -= nLineWidthHeight-nLineDY2; + ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight, + nLineWidth, mpFontInstance->mnOrientation, aColor ); + nLinePos += nLineWidthHeight+nLineDY; + ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight, + nLineWidth, mpFontInstance->mnOrientation, aColor ); + } + else + { + nLinePos -= nLineWidthHeight/2; + ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight, + nLineWidth, mpFontInstance->mnOrientation, aColor ); + } +} + +void OutputDevice::ImplDrawStraightTextLine( long nBaseX, long nBaseY, + long nDistX, long nDistY, long nWidth, + FontLineStyle eTextLine, + Color aColor, + bool bIsAbove ) +{ + LogicalFontInstance* pFontInstance = mpFontInstance.get(); + long nLineHeight = 0; + long nLinePos = 0; + long nLinePos2 = 0; + + const long nY = nDistY; + + if ( eTextLine > UNDERLINE_LAST ) + eTextLine = LINESTYLE_SINGLE; + + switch ( eTextLine ) + { + case LINESTYLE_SINGLE: + case LINESTYLE_DOTTED: + case LINESTYLE_DASH: + case LINESTYLE_LONGDASH: + case LINESTYLE_DASHDOT: + case LINESTYLE_DASHDOTDOT: + if ( bIsAbove ) + { + nLineHeight = pFontInstance->mxFontMetric->GetAboveUnderlineSize(); + nLinePos = nY + pFontInstance->mxFontMetric->GetAboveUnderlineOffset(); + } + else + { + nLineHeight = pFontInstance->mxFontMetric->GetUnderlineSize(); + nLinePos = nY + pFontInstance->mxFontMetric->GetUnderlineOffset(); + } + break; + case LINESTYLE_BOLD: + case LINESTYLE_BOLDDOTTED: + case LINESTYLE_BOLDDASH: + case LINESTYLE_BOLDLONGDASH: + case LINESTYLE_BOLDDASHDOT: + case LINESTYLE_BOLDDASHDOTDOT: + if ( bIsAbove ) + { + nLineHeight = pFontInstance->mxFontMetric->GetAboveBoldUnderlineSize(); + nLinePos = nY + pFontInstance->mxFontMetric->GetAboveBoldUnderlineOffset(); + } + else + { + nLineHeight = pFontInstance->mxFontMetric->GetBoldUnderlineSize(); + nLinePos = nY + pFontInstance->mxFontMetric->GetBoldUnderlineOffset(); + } + break; + case LINESTYLE_DOUBLE: + if ( bIsAbove ) + { + nLineHeight = pFontInstance->mxFontMetric->GetAboveDoubleUnderlineSize(); + nLinePos = nY + pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset1(); + nLinePos2 = nY + pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset2(); + } + else + { + nLineHeight = pFontInstance->mxFontMetric->GetDoubleUnderlineSize(); + nLinePos = nY + pFontInstance->mxFontMetric->GetDoubleUnderlineOffset1(); + nLinePos2 = nY + pFontInstance->mxFontMetric->GetDoubleUnderlineOffset2(); + } + break; + default: + break; + } + + if ( nLineHeight ) + { + if ( mbLineColor || mbInitLineColor ) + { + mpGraphics->SetLineColor(); + mbInitLineColor = true; + } + mpGraphics->SetFillColor( aColor ); + mbInitFillColor = true; + + long nLeft = nDistX; + + switch ( eTextLine ) + { + case LINESTYLE_SINGLE: + case LINESTYLE_BOLD: + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); + break; + case LINESTYLE_DOUBLE: + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight ); + break; + case LINESTYLE_DOTTED: + case LINESTYLE_BOLDDOTTED: + { + long nDotWidth = nLineHeight*mnDPIY; + nDotWidth += mnDPIY/2; + nDotWidth /= mnDPIY; + + long nTempWidth = nDotWidth; + long nEnd = nLeft+nWidth; + while ( nLeft < nEnd ) + { + if ( nLeft+nTempWidth > nEnd ) + nTempWidth = nEnd-nLeft; + + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight ); + nLeft += nDotWidth*2; + } + } + break; + case LINESTYLE_DASH: + case LINESTYLE_LONGDASH: + case LINESTYLE_BOLDDASH: + case LINESTYLE_BOLDLONGDASH: + { + long nDotWidth = nLineHeight*mnDPIY; + nDotWidth += mnDPIY/2; + nDotWidth /= mnDPIY; + + long nMinDashWidth; + long nMinSpaceWidth; + long nSpaceWidth; + long nDashWidth; + if ( (eTextLine == LINESTYLE_LONGDASH) || + (eTextLine == LINESTYLE_BOLDLONGDASH) ) + { + nMinDashWidth = nDotWidth*6; + nMinSpaceWidth = nDotWidth*2; + nDashWidth = 200; + nSpaceWidth = 100; + } + else + { + nMinDashWidth = nDotWidth*4; + nMinSpaceWidth = (nDotWidth*150)/100; + nDashWidth = 100; + nSpaceWidth = 50; + } + nDashWidth = ((nDashWidth*mnDPIX)+1270)/2540; + nSpaceWidth = ((nSpaceWidth*mnDPIX)+1270)/2540; + // DashWidth will be increased if the line is getting too thick + // in proportion to the line's length + if ( nDashWidth < nMinDashWidth ) + nDashWidth = nMinDashWidth; + if ( nSpaceWidth < nMinSpaceWidth ) + nSpaceWidth = nMinSpaceWidth; + + long nTempWidth = nDashWidth; + long nEnd = nLeft+nWidth; + while ( nLeft < nEnd ) + { + if ( nLeft+nTempWidth > nEnd ) + nTempWidth = nEnd-nLeft; + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight ); + nLeft += nDashWidth+nSpaceWidth; + } + } + break; + case LINESTYLE_DASHDOT: + case LINESTYLE_BOLDDASHDOT: + { + long nDotWidth = nLineHeight*mnDPIY; + nDotWidth += mnDPIY/2; + nDotWidth /= mnDPIY; + + long nDashWidth = ((100*mnDPIX)+1270)/2540; + long nMinDashWidth = nDotWidth*4; + // DashWidth will be increased if the line is getting too thick + // in proportion to the line's length + if ( nDashWidth < nMinDashWidth ) + nDashWidth = nMinDashWidth; + + long nTempDotWidth = nDotWidth; + long nTempDashWidth = nDashWidth; + long nEnd = nLeft+nWidth; + while ( nLeft < nEnd ) + { + if ( nLeft+nTempDotWidth > nEnd ) + nTempDotWidth = nEnd-nLeft; + + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); + nLeft += nDotWidth*2; + if ( nLeft > nEnd ) + break; + + if ( nLeft+nTempDashWidth > nEnd ) + nTempDashWidth = nEnd-nLeft; + + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight ); + nLeft += nDashWidth+nDotWidth; + } + } + break; + case LINESTYLE_DASHDOTDOT: + case LINESTYLE_BOLDDASHDOTDOT: + { + long nDotWidth = nLineHeight*mnDPIY; + nDotWidth += mnDPIY/2; + nDotWidth /= mnDPIY; + + long nDashWidth = ((100*mnDPIX)+1270)/2540; + long nMinDashWidth = nDotWidth*4; + // DashWidth will be increased if the line is getting too thick + // in proportion to the line's length + if ( nDashWidth < nMinDashWidth ) + nDashWidth = nMinDashWidth; + + long nTempDotWidth = nDotWidth; + long nTempDashWidth = nDashWidth; + long nEnd = nLeft+nWidth; + while ( nLeft < nEnd ) + { + if ( nLeft+nTempDotWidth > nEnd ) + nTempDotWidth = nEnd-nLeft; + + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); + nLeft += nDotWidth*2; + if ( nLeft > nEnd ) + break; + + if ( nLeft+nTempDotWidth > nEnd ) + nTempDotWidth = nEnd-nLeft; + + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); + nLeft += nDotWidth*2; + if ( nLeft > nEnd ) + break; + + if ( nLeft+nTempDashWidth > nEnd ) + nTempDashWidth = nEnd-nLeft; + + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight ); + nLeft += nDashWidth+nDotWidth; + } + } + break; + default: + break; + } + } +} + +void OutputDevice::ImplDrawStrikeoutLine( long nBaseX, long nBaseY, + long nDistX, long nDistY, long nWidth, + FontStrikeout eStrikeout, + Color aColor ) +{ + LogicalFontInstance* pFontInstance = mpFontInstance.get(); + long nLineHeight = 0; + long nLinePos = 0; + long nLinePos2 = 0; + + long nY = nDistY; + + if ( eStrikeout > STRIKEOUT_LAST ) + eStrikeout = STRIKEOUT_SINGLE; + + switch ( eStrikeout ) + { + case STRIKEOUT_SINGLE: + nLineHeight = pFontInstance->mxFontMetric->GetStrikeoutSize(); + nLinePos = nY + pFontInstance->mxFontMetric->GetStrikeoutOffset(); + break; + case STRIKEOUT_BOLD: + nLineHeight = pFontInstance->mxFontMetric->GetBoldStrikeoutSize(); + nLinePos = nY + pFontInstance->mxFontMetric->GetBoldStrikeoutOffset(); + break; + case STRIKEOUT_DOUBLE: + nLineHeight = pFontInstance->mxFontMetric->GetDoubleStrikeoutSize(); + nLinePos = nY + pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset1(); + nLinePos2 = nY + pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset2(); + break; + default: + break; + } + + if ( nLineHeight ) + { + if ( mbLineColor || mbInitLineColor ) + { + mpGraphics->SetLineColor(); + mbInitLineColor = true; + } + mpGraphics->SetFillColor( aColor ); + mbInitFillColor = true; + + const long& nLeft = nDistX; + + switch ( eStrikeout ) + { + case STRIKEOUT_SINGLE: + case STRIKEOUT_BOLD: + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); + break; + case STRIKEOUT_DOUBLE: + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); + ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight ); + break; + default: + break; + } + } +} + +void OutputDevice::ImplDrawStrikeoutChar( long nBaseX, long nBaseY, + long nDistX, long nDistY, long nWidth, + FontStrikeout eStrikeout, + Color aColor ) +{ + // See qadevOOo/testdocs/StrikeThrough.odt for examples if you need + // to tweak this + if (!nWidth) + return; + + // prepare string for strikeout measurement + const char cStrikeoutChar = eStrikeout == STRIKEOUT_SLASH ? '/' : 'X'; + static const int nTestStrLen = 4; + static const int nMaxStrikeStrLen = 2048; + sal_Unicode aChars[nMaxStrikeStrLen+1]; // +1 for valgrind... + + for( int i = 0; i < nTestStrLen; ++i) + aChars[i] = cStrikeoutChar; + + const OUString aStrikeoutTest(aChars, nTestStrLen); + + // calculate approximation of strikeout atom size + long nStrikeoutWidth = 0; + std::unique_ptr<SalLayout> pLayout = ImplLayout( aStrikeoutTest, 0, nTestStrLen ); + if( pLayout ) + { + nStrikeoutWidth = pLayout->GetTextWidth() / (nTestStrLen * pLayout->GetUnitsPerPixel()); + } + if( nStrikeoutWidth <= 0 ) // sanity check + return; + + int nStrikeStrLen = (nWidth+(nStrikeoutWidth-1)) / nStrikeoutWidth; + if( nStrikeStrLen > nMaxStrikeStrLen ) + nStrikeStrLen = nMaxStrikeStrLen; + + // build the strikeout string + for( int i = nTestStrLen; i < nStrikeStrLen; ++i) + aChars[i] = cStrikeoutChar; + + const OUString aStrikeoutText(aChars, nStrikeStrLen); + + if( mpFontInstance->mnOrientation ) + { + Point aOriginPt(0, 0); + aOriginPt.RotateAround( nDistX, nDistY, mpFontInstance->mnOrientation ); + } + + nBaseX += nDistX; + nBaseY += nDistY; + + // strikeout text has to be left aligned + ComplexTextLayoutFlags nOrigTLM = mnTextLayoutMode; + mnTextLayoutMode = ComplexTextLayoutFlags::BiDiStrong; + pLayout = ImplLayout( aStrikeoutText, 0, aStrikeoutText.getLength() ); + mnTextLayoutMode = nOrigTLM; + + if( !pLayout ) + return; + + // draw the strikeout text + const Color aOldColor = GetTextColor(); + SetTextColor( aColor ); + ImplInitTextColor(); + + pLayout->DrawBase() = Point( nBaseX+mnTextOffX, nBaseY+mnTextOffY ); + + tools::Rectangle aPixelRect; + aPixelRect.SetLeft( nBaseX+mnTextOffX ); + aPixelRect.SetRight( aPixelRect.Left()+nWidth ); + aPixelRect.SetBottom( nBaseY+mpFontInstance->mxFontMetric->GetDescent() ); + aPixelRect.SetTop( nBaseY-mpFontInstance->mxFontMetric->GetAscent() ); + + if (mpFontInstance->mnOrientation) + { + tools::Polygon aPoly( aPixelRect ); + aPoly.Rotate( Point(nBaseX+mnTextOffX, nBaseY+mnTextOffY), mpFontInstance->mnOrientation); + aPixelRect = aPoly.GetBoundRect(); + } + + Push( PushFlags::CLIPREGION ); + IntersectClipRegion( PixelToLogic(aPixelRect) ); + if( mbInitClipRegion ) + InitClipRegion(); + + pLayout->DrawText( *mpGraphics ); + + Pop(); + + SetTextColor( aOldColor ); + ImplInitTextColor(); +} + +void OutputDevice::ImplDrawTextLine( long nX, long nY, + long nDistX, DeviceCoordinate nWidth, + FontStrikeout eStrikeout, + FontLineStyle eUnderline, + FontLineStyle eOverline, + bool bUnderlineAbove ) +{ + if ( !nWidth ) + return; + + Color aStrikeoutColor = GetTextColor(); + Color aUnderlineColor = GetTextLineColor(); + Color aOverlineColor = GetOverlineColor(); + bool bStrikeoutDone = false; + bool bUnderlineDone = false; + bool bOverlineDone = false; + + if ( IsRTLEnabled() ) + { + long nXAdd = nWidth - nDistX; + if( mpFontInstance->mnOrientation ) + nXAdd = FRound( nXAdd * cos( mpFontInstance->mnOrientation * F_PI1800 ) ); + + nX += nXAdd - 1; + } + + if ( !IsTextLineColor() ) + aUnderlineColor = GetTextColor(); + + if ( !IsOverlineColor() ) + aOverlineColor = GetTextColor(); + + if ( (eUnderline == LINESTYLE_SMALLWAVE) || + (eUnderline == LINESTYLE_WAVE) || + (eUnderline == LINESTYLE_DOUBLEWAVE) || + (eUnderline == LINESTYLE_BOLDWAVE) ) + { + ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); + bUnderlineDone = true; + } + if ( (eOverline == LINESTYLE_SMALLWAVE) || + (eOverline == LINESTYLE_WAVE) || + (eOverline == LINESTYLE_DOUBLEWAVE) || + (eOverline == LINESTYLE_BOLDWAVE) ) + { + ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, true ); + bOverlineDone = true; + } + + if ( (eStrikeout == STRIKEOUT_SLASH) || + (eStrikeout == STRIKEOUT_X) ) + { + ImplDrawStrikeoutChar( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor ); + bStrikeoutDone = true; + } + + if ( !bUnderlineDone ) + ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); + + if ( !bOverlineDone ) + ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, true ); + + if ( !bStrikeoutDone ) + ImplDrawStrikeoutLine( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor ); +} + +void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout, FontStrikeout eStrikeout, + FontLineStyle eUnderline, FontLineStyle eOverline, + bool bWordLine, bool bUnderlineAbove ) +{ + if( bWordLine ) + { + // draw everything relative to the layout base point + const Point aStartPt = rSalLayout.DrawBase(); + + // calculate distance of each word from the base point + Point aPos; + DeviceCoordinate nDist = 0; + DeviceCoordinate nWidth = 0; + const GlyphItem* pGlyph; + int nStart = 0; + while (rSalLayout.GetNextGlyph(&pGlyph, aPos, nStart)) + { + // calculate the boundaries of each word + if (!pGlyph->IsSpacing()) + { + if( !nWidth ) + { + // get the distance to the base point (as projected to baseline) + nDist = aPos.X() - aStartPt.X(); + if( mpFontInstance->mnOrientation ) + { + const long nDY = aPos.Y() - aStartPt.Y(); + const double fRad = mpFontInstance->mnOrientation * F_PI1800; + nDist = FRound( nDist*cos(fRad) - nDY*sin(fRad) ); + } + } + + // update the length of the textline + nWidth += pGlyph->m_nNewWidth; + } + else if( nWidth > 0 ) + { + // draw the textline for each word + ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth, + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + nWidth = 0; + } + } + + // draw textline for the last word + if( nWidth > 0 ) + { + ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth, + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + } + } + else + { + Point aStartPt = rSalLayout.GetDrawPosition(); + ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), 0, + rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel(), + eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + } +} + +void OutputDevice::ImplDrawMnemonicLine( long nX, long nY, long nWidth ) +{ + long nBaseX = nX; + if( /*HasMirroredGraphics() &&*/ IsRTLEnabled() ) + { + // add some strange offset + nX += 2; + // revert the hack that will be done later in ImplDrawTextLine + nX = nBaseX - nWidth - (nX - nBaseX - 1); + } + + ImplDrawTextLine( nX, nY, 0, nWidth, STRIKEOUT_NONE, LINESTYLE_SINGLE, LINESTYLE_NONE, false ); +} + +void OutputDevice::SetTextLineColor() +{ + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextLineColorAction( Color(), false ) ); + + maTextLineColor = COL_TRANSPARENT; + + if( mpAlphaVDev ) + mpAlphaVDev->SetTextLineColor(); +} + +void OutputDevice::SetTextLineColor( const Color& rColor ) +{ + + Color aColor( rColor ); + + if ( mnDrawMode & ( DrawModeFlags::BlackText | DrawModeFlags::WhiteText | + DrawModeFlags::GrayText | + DrawModeFlags::SettingsText ) ) + { + if ( mnDrawMode & DrawModeFlags::BlackText ) + { + aColor = COL_BLACK; + } + else if ( mnDrawMode & DrawModeFlags::WhiteText ) + { + aColor = COL_WHITE; + } + else if ( mnDrawMode & DrawModeFlags::GrayText ) + { + const sal_uInt8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + else if ( mnDrawMode & DrawModeFlags::SettingsText ) + { + aColor = GetSettings().GetStyleSettings().GetFontColor(); + } + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextLineColorAction( aColor, true ) ); + + maTextLineColor = aColor; + + if( mpAlphaVDev ) + mpAlphaVDev->SetTextLineColor( COL_BLACK ); +} + +void OutputDevice::SetOverlineColor() +{ + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaOverlineColorAction( Color(), false ) ); + + maOverlineColor = COL_TRANSPARENT; + + if( mpAlphaVDev ) + mpAlphaVDev->SetOverlineColor(); +} + +void OutputDevice::SetOverlineColor( const Color& rColor ) +{ + + Color aColor( rColor ); + + if ( mnDrawMode & ( DrawModeFlags::BlackText | DrawModeFlags::WhiteText | + DrawModeFlags::GrayText | + DrawModeFlags::SettingsText ) ) + { + if ( mnDrawMode & DrawModeFlags::BlackText ) + { + aColor = COL_BLACK; + } + else if ( mnDrawMode & DrawModeFlags::WhiteText ) + { + aColor = COL_WHITE; + } + else if ( mnDrawMode & DrawModeFlags::GrayText ) + { + const sal_uInt8 cLum = aColor.GetLuminance(); + aColor = Color( cLum, cLum, cLum ); + } + else if ( mnDrawMode & DrawModeFlags::SettingsText ) + { + aColor = GetSettings().GetStyleSettings().GetFontColor(); + } + } + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaOverlineColorAction( aColor, true ) ); + + maOverlineColor = aColor; + + if( mpAlphaVDev ) + mpAlphaVDev->SetOverlineColor( COL_BLACK ); +} + +void OutputDevice::DrawTextLine( const Point& rPos, long nWidth, + FontStrikeout eStrikeout, + FontLineStyle eUnderline, + FontLineStyle eOverline, + bool bUnderlineAbove ) +{ + assert(!is_double_buffered_window()); + + if ( mpMetaFile ) + mpMetaFile->AddAction( new MetaTextLineAction( rPos, nWidth, eStrikeout, eUnderline, eOverline ) ); + + if ( ((eUnderline == LINESTYLE_NONE) || (eUnderline == LINESTYLE_DONTKNOW)) && + ((eOverline == LINESTYLE_NONE) || (eOverline == LINESTYLE_DONTKNOW)) && + ((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) ) + { + return; + } + if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) + return; + + if( mbInitClipRegion ) + InitClipRegion(); + + if( mbOutputClipped ) + return; + + // initialize font if needed to get text offsets + // TODO: only needed for mnTextOff!=(0,0) + if (!InitFont()) + return; + + Point aPos = ImplLogicToDevicePixel( rPos ); + DeviceCoordinate fWidth; + fWidth = LogicWidthToDeviceCoordinate( nWidth ); + aPos += Point( mnTextOffX, mnTextOffY ); + ImplDrawTextLine( aPos.X(), aPos.X(), 0, fWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove ); + + if( mpAlphaVDev ) + mpAlphaVDev->DrawTextLine( rPos, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove ); +} + +void OutputDevice::DrawWaveLine(const Point& rStartPos, const Point& rEndPos, long nLineWidth) +{ + assert(!is_double_buffered_window()); + + if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) + return; + + // we need a graphics + if( !mpGraphics && !AcquireGraphics() ) + return; + + if ( mbInitClipRegion ) + InitClipRegion(); + + if ( mbOutputClipped ) + return; + + if (!InitFont()) + return; + + Point aStartPt = ImplLogicToDevicePixel(rStartPos); + Point aEndPt = ImplLogicToDevicePixel(rEndPos); + + long nStartX = aStartPt.X(); + long nStartY = aStartPt.Y(); + long nEndX = aEndPt.X(); + long nEndY = aEndPt.Y(); + double fOrientation = 0.0; + + // handle rotation + if (nStartY != nEndY || nStartX > nEndX) + { + long nLengthX = nEndX - nStartX; + fOrientation = std::atan2(nStartY - nEndY, (nLengthX == 0 ? 0.000000001 : nLengthX)); + fOrientation /= F_PI180; + // un-rotate the end point + aStartPt.RotateAround(nEndX, nEndY, -fOrientation * 10.0); + } + + long nWaveHeight = 3; + + // Handle HiDPI + float fScaleFactor = GetDPIScaleFactor(); + if (fScaleFactor > 1.0f) + { + nWaveHeight *= fScaleFactor; + + nStartY += fScaleFactor - 1; // Shift down additional pixel(s) to create more visual separation. + + // odd heights look better than even + if (nWaveHeight % 2 == 0) + { + nWaveHeight--; + } + } + + // #109280# make sure the waveline does not exceed the descent to avoid paint problems + LogicalFontInstance* pFontInstance = mpFontInstance.get(); + if (nWaveHeight > pFontInstance->mxFontMetric->GetWavelineUnderlineSize()) + { + nWaveHeight = pFontInstance->mxFontMetric->GetWavelineUnderlineSize(); + // tdf#124848 hairline + nLineWidth = 0; + } + + const basegfx::B2DRectangle aWaveLineRectangle(nStartX, nStartY, nEndX, nEndY + nWaveHeight); + const basegfx::B2DPolygon aWaveLinePolygon = basegfx::createWaveLinePolygon(aWaveLineRectangle); + const basegfx::B2DHomMatrix aRotationMatrix = basegfx::utils::createRotateAroundPoint(nStartX, nStartY, basegfx::deg2rad(-fOrientation)); + const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline); + + mpGraphics->SetLineColor(GetLineColor()); + mpGraphics->DrawPolyLine( + aRotationMatrix, + aWaveLinePolygon, + 0.0, + nLineWidth, + nullptr, // MM01 + basegfx::B2DLineJoin::NONE, + css::drawing::LineCap_BUTT, + basegfx::deg2rad(15.0), + bPixelSnapHairline, + this); + + if( mpAlphaVDev ) + mpAlphaVDev->DrawWaveLine( rStartPos, rEndPos, nLineWidth ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |