Files
loongoffice/vcl/source/outdev/textline.cxx
Noel Grandin 794d5ac4ac vcl: use enum for complex text layout constants
Since these constants are bitfield flags, we define some methods to make
working with them reasonably type safe.

Move the definitions to outdevstate.hxx, since we need the values there,
and that appears to be the "root most" header file.

Also dump TEXT_LAYOUT_BIDI_LTR constant, since it means the same thing
as TEXT_LAYOUT_DEFAULT (ie. 0), and leaving it in causes people to write
weird code thinking that it's a real flag.

Change-Id: Iddab86cd6c78181ceb8caa48e77e1f5a8e526343
Reviewed-on: https://gerrit.libreoffice.org/10676
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
Tested-by: Caolán McNamara <caolanm@redhat.com>
2014-08-20 09:01:26 -05:00

1048 lines
34 KiB
C++

/* -*- 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 <sal/types.h>
#include <vcl/outdev.hxx>
#include <vcl/virdev.hxx>
#include <vcl/settings.hxx>
#include <tools/helpers.hxx>
#include "salgdi.hxx"
#include "impfont.hxx"
#include "outdata.hxx"
#include <config_graphite.h>
#if ENABLE_GRAPHITE
#include "graphite_features.hxx"
#endif
#define UNDERLINE_LAST UNDERLINE_BOLDWAVE
#define STRIKEOUT_LAST STRIKEOUT_X
bool OutputDevice::ImplIsUnderlineAbove( const Font& rFont )
{
if ( !rFont.IsVertical() )
return false;
if( (LANGUAGE_JAPANESE == rFont.GetLanguage()) ||
(LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()) )
{
// the underline is right for Japanese only
return true;
}
return false;
}
void OutputDevice::ImplInitTextLineSize()
{
mpFontEntry->maMetric.ImplInitTextLineSize( this );
}
void OutputDevice::ImplInitAboveTextLineSize()
{
mpFontEntry->maMetric.ImplInitAboveTextLineSize();
}
void OutputDevice::ImplDrawWavePixel( long nOriginX, long nOriginY,
long nCurX, long nCurY,
short nOrientation,
SalGraphics* pGraphics,
OutputDevice* pOutDev,
bool bDrawPixAsRect,
long nPixWidth, long nPixHeight )
{
if ( nOrientation )
ImplRotatePos( nOriginX, nOriginY, 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 ouput a line
if ( (nLineWidth == 1) && (nHeight == 1) )
{
mpGraphics->SetLineColor( ImplColorToSal( rColor ) );
mbInitLineColor = true;
long nEndX = nStartX+nWidth;
long nEndY = nStartY;
if ( nOrientation )
{
ImplRotatePos( nBaseX, nBaseY, nStartX, nStartY, nOrientation );
ImplRotatePos( nBaseX, nBaseY, 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 nFreq;
long i;
long nPixWidth;
long nPixHeight;
bool bDrawPixAsRect;
// On printers that ouput pixel via DrawRect()
if ( (GetOutDevType() == OUTDEV_PRINTER) || (nLineWidth > 1) )
{
if ( mbLineColor || mbInitLineColor )
{
mpGraphics->SetLineColor();
mbInitLineColor = true;
}
mpGraphics->SetFillColor( ImplColorToSal( rColor ) );
mbInitFillColor = true;
bDrawPixAsRect = true;
nPixWidth = nLineWidth;
nPixHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY;
}
else
{
mpGraphics->SetLineColor( ImplColorToSal( 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;
nFreq = nCount / (nDiffX+nDiffY);
while ( nFreq-- )
{
for( i = nDiffY; i; --i )
{
ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
mpGraphics, this,
bDrawPixAsRect, nPixWidth, nPixHeight );
nCurX++;
nCurY += nOffY;
}
for( 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( i = nDiffY; i && nFreq; --i, --nFreq )
{
ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
mpGraphics, this,
bDrawPixAsRect, nPixWidth, nPixHeight );
nCurX++;
nCurY += nOffY;
}
for( 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,
FontUnderline eTextLine,
Color aColor,
bool bIsAbove )
{
ImplFontEntry* pFontEntry = mpFontEntry;
long nLineHeight;
long nLinePos;
if ( bIsAbove )
{
nLineHeight = pFontEntry->maMetric.mnAboveWUnderlineSize;
nLinePos = pFontEntry->maMetric.mnAboveWUnderlineOffset;
}
else
{
nLineHeight = pFontEntry->maMetric.mnWUnderlineSize;
nLinePos = pFontEntry->maMetric.mnWUnderlineOffset;
}
if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
nLineHeight = 3;
long nLineWidth = (mnDPIX / 300);
if ( !nLineWidth )
nLineWidth = 1;
if ( eTextLine == UNDERLINE_BOLDWAVE )
nLineWidth *= 2;
nLinePos += nDistY - (nLineHeight / 2);
long nLineWidthHeight = ((nLineWidth * mnDPIX) + (mnDPIY / 2)) / mnDPIY;
if ( eTextLine == UNDERLINE_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, mpFontEntry->mnOrientation, aColor );
nLinePos += nLineWidthHeight+nLineDY;
ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
nLineWidth, mpFontEntry->mnOrientation, aColor );
}
else
{
nLinePos -= nLineWidthHeight/2;
ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
nLineWidth, mpFontEntry->mnOrientation, aColor );
}
}
void OutputDevice::ImplDrawStraightTextLine( long nBaseX, long nBaseY,
long nDistX, long nDistY, long nWidth,
FontUnderline eTextLine,
Color aColor,
bool bIsAbove )
{
ImplFontEntry* pFontEntry = mpFontEntry;
long nLineHeight = 0;
long nLinePos = 0;
long nLinePos2 = 0;
const long nY = nDistY;
if ( eTextLine > UNDERLINE_LAST )
eTextLine = UNDERLINE_SINGLE;
switch ( eTextLine )
{
case UNDERLINE_SINGLE:
case UNDERLINE_DOTTED:
case UNDERLINE_DASH:
case UNDERLINE_LONGDASH:
case UNDERLINE_DASHDOT:
case UNDERLINE_DASHDOTDOT:
if ( bIsAbove )
{
nLineHeight = pFontEntry->maMetric.mnAboveUnderlineSize;
nLinePos = nY + pFontEntry->maMetric.mnAboveUnderlineOffset;
}
else
{
nLineHeight = pFontEntry->maMetric.mnUnderlineSize;
nLinePos = nY + pFontEntry->maMetric.mnUnderlineOffset;
}
break;
case UNDERLINE_BOLD:
case UNDERLINE_BOLDDOTTED:
case UNDERLINE_BOLDDASH:
case UNDERLINE_BOLDLONGDASH:
case UNDERLINE_BOLDDASHDOT:
case UNDERLINE_BOLDDASHDOTDOT:
if ( bIsAbove )
{
nLineHeight = pFontEntry->maMetric.mnAboveBUnderlineSize;
nLinePos = nY + pFontEntry->maMetric.mnAboveBUnderlineOffset;
}
else
{
nLineHeight = pFontEntry->maMetric.mnBUnderlineSize;
nLinePos = nY + pFontEntry->maMetric.mnBUnderlineOffset;
}
break;
case UNDERLINE_DOUBLE:
if ( bIsAbove )
{
nLineHeight = pFontEntry->maMetric.mnAboveDUnderlineSize;
nLinePos = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset1;
nLinePos2 = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset2;
}
else
{
nLineHeight = pFontEntry->maMetric.mnDUnderlineSize;
nLinePos = nY + pFontEntry->maMetric.mnDUnderlineOffset1;
nLinePos2 = nY + pFontEntry->maMetric.mnDUnderlineOffset2;
}
break;
default:
break;
}
if ( nLineHeight )
{
if ( mbLineColor || mbInitLineColor )
{
mpGraphics->SetLineColor();
mbInitLineColor = true;
}
mpGraphics->SetFillColor( ImplColorToSal( aColor ) );
mbInitFillColor = true;
long nLeft = nDistX;
switch ( eTextLine )
{
case UNDERLINE_SINGLE:
case UNDERLINE_BOLD:
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
break;
case UNDERLINE_DOUBLE:
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
break;
case UNDERLINE_DOTTED:
case UNDERLINE_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 UNDERLINE_DASH:
case UNDERLINE_LONGDASH:
case UNDERLINE_BOLDDASH:
case UNDERLINE_BOLDLONGDASH:
{
long nDotWidth = nLineHeight*mnDPIY;
nDotWidth += mnDPIY/2;
nDotWidth /= mnDPIY;
long nMinDashWidth;
long nMinSpaceWidth;
long nSpaceWidth;
long nDashWidth;
if ( (eTextLine == UNDERLINE_LONGDASH) ||
(eTextLine == UNDERLINE_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 UNDERLINE_DASHDOT:
case UNDERLINE_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 UNDERLINE_DASHDOTDOT:
case UNDERLINE_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 )
{
ImplFontEntry* pFontEntry = mpFontEntry;
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 = pFontEntry->maMetric.mnStrikeoutSize;
nLinePos = nY + pFontEntry->maMetric.mnStrikeoutOffset;
break;
case STRIKEOUT_BOLD:
nLineHeight = pFontEntry->maMetric.mnBStrikeoutSize;
nLinePos = nY + pFontEntry->maMetric.mnBStrikeoutOffset;
break;
case STRIKEOUT_DOUBLE:
nLineHeight = pFontEntry->maMetric.mnDStrikeoutSize;
nLinePos = nY + pFontEntry->maMetric.mnDStrikeoutOffset1;
nLinePos2 = nY + pFontEntry->maMetric.mnDStrikeoutOffset2;
break;
default:
break;
}
if ( nLineHeight )
{
if ( mbLineColor || mbInitLineColor )
{
mpGraphics->SetLineColor();
mbInitLineColor = true;
}
mpGraphics->SetFillColor( ImplColorToSal( 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
static 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;
SalLayout* pLayout = ImplLayout( aStrikeoutTest, 0, nTestStrLen );
if( pLayout )
{
nStrikeoutWidth = pLayout->GetTextWidth() / (nTestStrLen * pLayout->GetUnitsPerPixel());
pLayout->Release();
}
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( mpFontEntry->mnOrientation )
ImplRotatePos( 0, 0, nDistX, nDistY, mpFontEntry->mnOrientation );
nBaseX += nDistX;
nBaseY += nDistY;
// strikeout text has to be left aligned
ComplexTextLayoutMode nOrigTLM = mnTextLayoutMode;
mnTextLayoutMode = TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_COMPLEX_DISABLED;
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 );
Rectangle aPixelRect;
aPixelRect.Left() = nBaseX+mnTextOffX;
aPixelRect.Right() = aPixelRect.Left()+nWidth;
aPixelRect.Bottom() = nBaseY+mpFontEntry->maMetric.mnDescent;
aPixelRect.Top() = nBaseY-mpFontEntry->maMetric.mnAscent;
if (mpFontEntry->mnOrientation)
{
Polygon aPoly( aPixelRect );
aPoly.Rotate( Point(nBaseX+mnTextOffX, nBaseY+mnTextOffY), mpFontEntry->mnOrientation);
aPixelRect = aPoly.GetBoundRect();
}
Push( PUSH_CLIPREGION );
IntersectClipRegion( PixelToLogic(aPixelRect) );
if( mbInitClipRegion )
InitClipRegion();
pLayout->DrawText( *mpGraphics );
pLayout->Release();
Pop();
SetTextColor( aOldColor );
ImplInitTextColor();
}
void OutputDevice::ImplDrawTextLine( long nX, long nY,
long nDistX, DeviceCoordinate nWidth,
FontStrikeout eStrikeout,
FontUnderline eUnderline,
FontUnderline 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() )
{
// --- RTL --- mirror at basex
long nXAdd = nWidth - nDistX;
if( mpFontEntry->mnOrientation )
nXAdd = FRound( nXAdd * cos( mpFontEntry->mnOrientation * F_PI1800 ) );
nX += nXAdd - 1;
}
if ( !IsTextLineColor() )
aUnderlineColor = GetTextColor();
if ( !IsOverlineColor() )
aOverlineColor = GetTextColor();
if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
(eUnderline == UNDERLINE_WAVE) ||
(eUnderline == UNDERLINE_DOUBLEWAVE) ||
(eUnderline == UNDERLINE_BOLDWAVE) )
{
ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
bUnderlineDone = true;
}
if ( (eOverline == UNDERLINE_SMALLWAVE) ||
(eOverline == UNDERLINE_WAVE) ||
(eOverline == UNDERLINE_DOUBLEWAVE) ||
(eOverline == UNDERLINE_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,
FontUnderline eUnderline, FontUnderline 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;
DeviceCoordinate nAdvance = 0;
for( int nStart = 0;;)
{
// iterate through the layouted glyphs
sal_GlyphId aGlyphId;
if( !rSalLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart, &nAdvance ) )
break;
// calculate the boundaries of each word
if( !rSalLayout.IsSpacingGlyph( aGlyphId ) )
{
if( !nWidth )
{
// get the distance to the base point (as projected to baseline)
nDist = aPos.X() - aStartPt.X();
if( mpFontEntry->mnOrientation )
{
const long nDY = aPos.Y() - aStartPt.Y();
const double fRad = mpFontEntry->mnOrientation * F_PI1800;
nDist = FRound( nDist*cos(fRad) - nDY*sin(fRad) );
}
}
// update the length of the textline
nWidth += nAdvance;
}
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() )
{
// --- RTL ---
// 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, UNDERLINE_SINGLE, UNDERLINE_NONE, false );
}
void OutputDevice::SetTextLineColor()
{
if ( mpMetaFile )
mpMetaFile->AddAction( new MetaTextLineColorAction( Color(), false ) );
maTextLineColor = Color( COL_TRANSPARENT );
if( mpAlphaVDev )
mpAlphaVDev->SetTextLineColor();
}
void OutputDevice::SetTextLineColor( const Color& rColor )
{
Color aColor( rColor );
if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT |
DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT |
DRAWMODE_SETTINGSTEXT ) )
{
if ( mnDrawMode & DRAWMODE_BLACKTEXT )
{
aColor = Color( COL_BLACK );
}
else if ( mnDrawMode & DRAWMODE_WHITETEXT )
{
aColor = Color( COL_WHITE );
}
else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
{
const sal_uInt8 cLum = aColor.GetLuminance();
aColor = Color( cLum, cLum, cLum );
}
else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
{
aColor = GetSettings().GetStyleSettings().GetFontColor();
}
if( (mnDrawMode & DRAWMODE_GHOSTEDTEXT) &&
(aColor.GetColor() != COL_TRANSPARENT) )
{
aColor = Color( (aColor.GetRed() >> 1) | 0x80,
(aColor.GetGreen() >> 1) | 0x80,
(aColor.GetBlue() >> 1) | 0x80 );
}
}
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 = Color( COL_TRANSPARENT );
if( mpAlphaVDev )
mpAlphaVDev->SetOverlineColor();
}
void OutputDevice::SetOverlineColor( const Color& rColor )
{
Color aColor( rColor );
if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT |
DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT |
DRAWMODE_SETTINGSTEXT ) )
{
if ( mnDrawMode & DRAWMODE_BLACKTEXT )
{
aColor = Color( COL_BLACK );
}
else if ( mnDrawMode & DRAWMODE_WHITETEXT )
{
aColor = Color( COL_WHITE );
}
else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
{
const sal_uInt8 cLum = aColor.GetLuminance();
aColor = Color( cLum, cLum, cLum );
}
else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
{
aColor = GetSettings().GetStyleSettings().GetFontColor();
}
if( (mnDrawMode & DRAWMODE_GHOSTEDTEXT) &&
(aColor.GetColor() != COL_TRANSPARENT) )
{
aColor = Color( (aColor.GetRed() >> 1) | 0x80,
(aColor.GetGreen() >> 1) | 0x80,
(aColor.GetBlue() >> 1) | 0x80 );
}
}
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,
FontUnderline eUnderline,
FontUnderline eOverline,
bool bUnderlineAbove )
{
if ( mpMetaFile )
mpMetaFile->AddAction( new MetaTextLineAction( rPos, nWidth, eStrikeout, eUnderline, eOverline ) );
if ( ((eUnderline == UNDERLINE_NONE) || (eUnderline == UNDERLINE_DONTKNOW)) &&
((eOverline == UNDERLINE_NONE) || (eOverline == UNDERLINE_DONTKNOW)) &&
((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) )
{
return;
}
if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
return;
// we need a graphics
if( !mpGraphics && !AcquireGraphics() )
return;
if( mbInitClipRegion )
InitClipRegion();
if( mbOutputClipped )
return;
// initialize font if needed to get text offsets
// TODO: only needed for mnTextOff!=(0,0)
if( mbNewFont && !ImplNewFont() )
return;
if( mbInitFont )
InitFont();
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 )
{
if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
return;
// we need a graphics
if( !mpGraphics && !AcquireGraphics() )
return;
if ( mbInitClipRegion )
InitClipRegion();
if ( mbOutputClipped )
return;
if( mbNewFont && !ImplNewFont() )
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();
short nOrientation = 0;
// when rotated
if ( (nStartY != nEndY) || (nStartX > nEndX) )
{
long nDX = nEndX - nStartX;
double nO = atan2( -nEndY + nStartY, ((nDX == 0L) ? 0.000000001 : nDX) );
nO /= F_PI1800;
nOrientation = (short)nO;
ImplRotatePos( nStartX, nStartY, nEndX, nEndY, -nOrientation );
}
long nWaveHeight;
nWaveHeight = 3;
nStartY++;
nEndY++;
if (mnDPIScaleFactor > 1)
{
nWaveHeight *= mnDPIScaleFactor;
nStartY += mnDPIScaleFactor - 1; // Shift down additional pixel(s) to create more visual separation.
// odd heights look better than even
if (mnDPIScaleFactor % 2 == 0)
{
nWaveHeight--;
}
}
// #109280# make sure the waveline does not exceed the descent to avoid paint problems
ImplFontEntry* pFontEntry = mpFontEntry;
if( nWaveHeight > pFontEntry->maMetric.mnWUnderlineSize )
{
nWaveHeight = pFontEntry->maMetric.mnWUnderlineSize;
}
ImplDrawWaveLine(nStartX, nStartY, 0, 0,
nEndX-nStartX, nWaveHeight,
mnDPIScaleFactor, nOrientation, GetLineColor());
if( mpAlphaVDev )
mpAlphaVDev->DrawWaveLine( rStartPos, rEndPos );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */