Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/source/outdev/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 39 kB image not shown  

Quelle  textline.cxx   Sprache: 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 <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/polygon/WaveLine.hxx>
#include <tools/helpers.hxx>
#include <o3tl/hash_combine.hxx>
#include <o3tl/lru_map.hxx>
#include <comphelper/configuration.hxx>
#include <tools/lazydelete.hxx>
#include <vcl/dropcache.hxx>
#include <vcl/metaact.hxx>
#include <vcl/settings.hxx>
#include <vcl/virdev.hxx>
#include <vcl/skia/SkiaHelper.hxx>

#include <drawmode.hxx>
#include <salgdi.hxx>
#include <impglyphitem.hxx>

#include <cassert>

#define UNDERLINE_LAST      LINESTYLE_BOLDWAVE
#define STRIKEOUT_LAST      STRIKEOUT_X

namespace {
    struct WavyLineCache final : public CacheOwner
    {
        WavyLineCache () : m_aItems( 10 ) {}

        bool find( Color aLineColor, size_t nLineWidth, size_t nWaveHeight, size_t nWordWidth, BitmapEx& rOutput )
        {
            Key aKey = { nWaveHeight, sal_uInt32(aLineColor) };
            auto item = m_aItems.find( aKey );
            if ( item == m_aItems.end() )
                return false;
            // needs update
            if ( item->second.m_aLineWidth != nLineWidth || item->second.m_aWordWidth < nWordWidth )
            {
                return false;
            }
            rOutput = item->second.m_Bitmap;
            return true;
        }

        void insert( const BitmapEx& aBitmap, const Color& aLineColor, const size_t nLineWidth, const size_t nWaveHeight, const size_t nWordWidth, BitmapEx& rOutput )
        {
            Key aKey = { nWaveHeight, sal_uInt32(aLineColor) };
            m_aItems.insert( std::pair< Key, WavyLineCacheItem>( aKey, { nLineWidth, nWordWidth, aBitmap } ) );
            rOutput = aBitmap;
        }

        virtual void dropCaches() override
        {
            m_aItems.clear();
        }

        virtual void dumpState(rtl::OStringBuffer& rState) override
        {
            rState.append("\nWavyLineCache:\t");
            rState.append(static_cast<sal_Int32>(m_aItems.size()));
        }

        private:
        struct WavyLineCacheItem
        {
            size_t m_aLineWidth;
            size_t m_aWordWidth;
            BitmapEx m_Bitmap;
        };

        struct Key
        {
            size_t m_aFirst;
            size_t m_aSecond;
            bool operator ==( const Key& rOther ) const
            {
                return ( m_aFirst == rOther.m_aFirst && m_aSecond == rOther.m_aSecond );
            }
        };

        struct Hash
        {
            size_t operator() ( const Key& rKey ) const
            {
                size_t aSeed = 0;
                o3tl::hash_combine(aSeed, rKey.m_aFirst);
                o3tl::hash_combine(aSeed, rKey.m_aSecond);
                return aSeed;
            }
        };

        o3tl::lru_map< Key, WavyLineCacheItem, Hash > m_aItems;
    };
}

void OutputDevice::ImplInitTextLineSize()
{
    mpFontInstance->mxFontMetric->ImplInitTextLineSize( this );
}

void OutputDevice::ImplInitAboveTextLineSize()
{
    mpFontInstance->mxFontMetric->ImplInitAboveTextLineSize( this );
}

void OutputDevice::ImplDrawWavePixel( tools::Long nOriginX, tools::Long nOriginY,
                                      tools::Long nCurX, tools::Long nCurY,
                                      tools::Long nWidth,
                                      Degree10 nOrientation,
                                      SalGraphics* pGraphics,
                                      const OutputDevice& rOutDev,
                                      tools::Long nPixWidth, tools::Long nPixHeight )
{
    if (nOrientation)
    {
        Point aPoint( nOriginX, nOriginY );
        aPoint.RotateAround( nCurX, nCurY, nOrientation );
    }

    if (shouldDrawWavePixelAsRect(nWidth))
    {
        pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight, rOutDev );
    }
    else
    {
        pGraphics->DrawPixel( nCurX, nCurY, rOutDev );
    }
}

bool OutputDevice::shouldDrawWavePixelAsRect(tools::Long nLineWidth) const
{
    if (nLineWidth > 1)
        return true;

    return false;
}

void OutputDevice::SetWaveLineColors(Color const& rColor, tools::Long nLineWidth)
{
    // On printers that output pixel via DrawRect()
    if (nLineWidth > 1)
    {
        if (mbLineColor || mbInitLineColor)
        {
            mpGraphics->SetLineColor();
            mbInitLineColor = true;
        }

        mpGraphics->SetFillColor( rColor );
        mbInitFillColor = true;
    }
    else
    {
        mpGraphics->SetLineColor( rColor );
        mbInitLineColor = true;
    }
}

Size OutputDevice::GetWaveLineSize(tools::Long nLineWidth) const
{
    if (nLineWidth > 1)
        return Size(nLineWidth, ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY);

    return Size(1, 1);
}

void OutputDevice::ImplDrawWaveLine( tools::Long nBaseX, tools::Long nBaseY,
                                     tools::Long nDistX, tools::Long nDistY,
                                     tools::Long nWidth, tools::Long nHeight,
                                     tools::Long nLineWidth, Degree10 nOrientation,
                                     const Color& rColor )
{
    if ( !nHeight )
        return;

    tools::Long nStartX = nBaseX + nDistX;
    tools::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;

        tools::Long nEndX = nStartX+nWidth;
        tools::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
    {
        tools::Long    nCurX = nStartX;
        tools::Long    nCurY = nStartY;
        tools::Long    nDiffX = 2;
        tools::Long    nDiffY = nHeight-1;
        tools::Long    nCount = nWidth;
        tools::Long    nOffY = -1;

        SetWaveLineColors(rColor, nLineWidth);
        Size aSize(GetWaveLineSize(nLineWidth));

        tools::Long nPixWidth = aSize.Width();
        tools::Long nPixHeight = aSize.Height();

        if ( !nDiffY )
        {
            while ( nWidth )
            {
                ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation,
                                   mpGraphics, *this,
                                   nPixWidth, nPixHeight );
                nCurX++;
                nWidth--;
            }
        }
        else
        {
            nCurY += nDiffY;
            tools::Long nFreq = nCount / (nDiffX+nDiffY);
            while ( nFreq-- )
            {
                for( tools::Long i = nDiffY; i; --i )
                {
                    ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation,
                                       mpGraphics, *this,
                                       nPixWidth, nPixHeight );
                    nCurX++;
                    nCurY += nOffY;
                }
                for( tools::Long i = nDiffX; i; --i )
                {
                    ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation,
                                       mpGraphics, *this,
                                       nPixWidth, nPixHeight );
                    nCurX++;
                }
                nOffY = -nOffY;
            }
            nFreq = nCount % (nDiffX+nDiffY);
            if ( nFreq )
            {
                for( tools::Long i = nDiffY; i && nFreq; --i, --nFreq )
                {
                    ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation,
                                       mpGraphics, *this,
                                       nPixWidth, nPixHeight );
                    nCurX++;
                    nCurY += nOffY;

                }
                for( tools::Long i = nDiffX; i && nFreq; --i, --nFreq )
                {
                    ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation,
                                       mpGraphics, *this,
                                       nPixWidth, nPixHeight );
                    nCurX++;
                }
            }
        }
    }
}

void OutputDevice::ImplDrawWaveTextLine( tools::Long nBaseX, tools::Long nBaseY,
                                         tools::Long nDistX, tools::Long nDistY,
                                         tools::Long nWidth, tools::Long nLayoutWidth,
                                         FontLineStyle eTextLine,
                                         Color aColor,
                                         bool bIsAbove )
{
    static bool bFuzzing = comphelper::IsFuzzing();
    if (bFuzzing && nLayoutWidth > 10000)
    {
        SAL_WARN("vcl.gdi""drawLine, skipping suspicious WaveTextLine of length: "
                                << nLayoutWidth << " for fuzzing performance");
        return;
    }

    LogicalFontInstance* pFontInstance = mpFontInstance.get();
    tools::Long            nLineHeight;
    tools::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;

    tools::Long nLineWidth = mnDPIX / 300;
    if ( !nLineWidth )
        nLineWidth = 1;

    if ( eTextLine == LINESTYLE_BOLDWAVE )
        nLineWidth *= 2;

    nLinePos += nDistY - (nLineHeight / 2);

    tools::Long nLineWidthHeight = ((nLineWidth * mnDPIX) + (mnDPIY / 2)) / mnDPIY;
    if ( eTextLine == LINESTYLE_DOUBLEWAVE )
    {
        tools::Long nOrgLineHeight = nLineHeight;
        nLineHeight /= 3;
        if ( nLineHeight < 2 )
        {
            if ( nOrgLineHeight > 1 )
                nLineHeight = 2;
            else
                nLineHeight = 1;
        }

        tools::Long nLineDY = nOrgLineHeight-(nLineHeight*2);
        if ( nLineDY < nLineWidthHeight )
            nLineDY = nLineWidthHeight;

        tools::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( tools::Long nBaseX, tools::Long nBaseY,
                                             tools::Long nDistX, tools::Long nDistY, tools::Long nWidth,
                                             FontLineStyle eTextLine,
                                             Color aColor,
                                             bool bIsAbove )
{
    static bool bFuzzing = comphelper::IsFuzzing();
    if (bFuzzing && nWidth > 25000)
    {
        SAL_WARN("vcl.gdi""drawLine, skipping suspicious TextLine of length: "
                                << nWidth << " for fuzzing performance");
        return;
    }

    LogicalFontInstance*  pFontInstance = mpFontInstance.get();
    tools::Long            nLineHeight = 0;
    tools::Long            nLinePos  = 0;
    tools::Long            nLinePos2 = 0;

    const tools::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 )
        return;

    if ( mbLineColor || mbInitLineColor )
    {
        mpGraphics->SetLineColor();
        mbInitLineColor = true;
    }
    mpGraphics->SetFillColor( aColor );
    mbInitFillColor = true;

    tools::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:
        {
            tools::Long nDotWidth = nLineHeight*mnDPIY;
            nDotWidth += mnDPIY/2;
            nDotWidth /= mnDPIY;

            tools::Long nTempWidth = nDotWidth;
            tools::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:
        {
            tools::Long nDotWidth = nLineHeight*mnDPIY;
            nDotWidth += mnDPIY/2;
            nDotWidth /= mnDPIY;

            tools::Long nMinDashWidth;
            tools::Long nMinSpaceWidth;
            tools::Long nSpaceWidth;
            tools::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 = o3tl::convert(nDashWidth * mnDPIX, o3tl::Length::mm100, o3tl::Length::in);
            nSpaceWidth = o3tl::convert(nSpaceWidth * mnDPIX, o3tl::Length::mm100, o3tl::Length::in);
            // 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;

            tools::Long nTempWidth = nDashWidth;
            tools::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:
        {
            tools::Long nDotWidth = nLineHeight*mnDPIY;
            nDotWidth += mnDPIY/2;
            nDotWidth /= mnDPIY;

            tools::Long nDashWidth = o3tl::convert(100 * mnDPIX, o3tl::Length::mm100, o3tl::Length::in);
            tools::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;

            tools::Long nTempDotWidth = nDotWidth;
            tools::Long nTempDashWidth = nDashWidth;
            tools::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:
        {
            tools::Long nDotWidth = nLineHeight*mnDPIY;
            nDotWidth += mnDPIY/2;
            nDotWidth /= mnDPIY;

            tools::Long nDashWidth = o3tl::convert(100 * mnDPIX, o3tl::Length::mm100, o3tl::Length::in);
            tools::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;

            tools::Long nTempDotWidth = nDotWidth;
            tools::Long nTempDashWidth = nDashWidth;
            tools::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( tools::Long nBaseX, tools::Long nBaseY,
                                          tools::Long nDistX, tools::Long nDistY, tools::Long nWidth,
                                          FontStrikeout eStrikeout,
                                          Color aColor )
{
    LogicalFontInstance*  pFontInstance = mpFontInstance.get();
    tools::Long            nLineHeight = 0;
    tools::Long            nLinePos  = 0;
    tools::Long            nLinePos2 = 0;

    tools::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 )
        return;

    if ( mbLineColor || mbInitLineColor )
    {
        mpGraphics->SetLineColor();
        mbInitLineColor = true;
    }
    mpGraphics->SetFillColor( aColor );
    mbInitFillColor = true;

    const tools::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( tools::Long nBaseX, tools::Long nBaseY,
                                          tools::Long nDistX, tools::Long nDistY, tools::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...

    forint i = 0; i < nTestStrLen; ++i)
        aChars[i] = cStrikeoutChar;

    const OUString aStrikeoutTest(aChars, nTestStrLen);

    // calculate approximation of strikeout atom size
    tools::Long nStrikeoutWidth = 0;
    std::unique_ptr<SalLayout> pLayout = ImplLayout( aStrikeoutTest, 0, nTestStrLen );
    if( pLayout )
    {
        nStrikeoutWidth = pLayout->GetTextWidth() / nTestStrLen;
    }
    if( nStrikeoutWidth <= 0 ) // sanity check
        return;

    int nStrikeStrLen = (nWidth+(nStrikeoutWidth-1)) / nStrikeoutWidth;
    if( nStrikeStrLen > nMaxStrikeStrLen )
        nStrikeStrLen = nMaxStrikeStrLen;
    else if (nStrikeStrLen < 0)
        nStrikeStrLen = 0;

    // build the strikeout string
    forint 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
    vcl::text::ComplexTextLayoutFlags nOrigTLM = mnTextLayoutMode;
    mnTextLayoutMode = vcl::text::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() = basegfx::B2DPoint(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( vcl::PushFlags::CLIPREGION );
    IntersectClipRegion( PixelToLogic(aPixelRect) );
    if( mbInitClipRegion )
        InitClipRegion();

    pLayout->DrawText( *mpGraphics );

    Pop();

    SetTextColor( aOldColor );
    ImplInitTextColor();
}

void OutputDevice::ImplDrawTextLine( tools::Long nX, tools::Long nY,
                                     tools::Long nDistX, double nWidth,
                                     double nLayoutWidth,
                                     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() )
    {
        tools::Long nXAdd = nWidth - nDistX;
        if( mpFontInstance->mnOrientation )
            nXAdd = basegfx::fround<tools::Long>( nXAdd * cos( toRadians(mpFontInstance->mnOrientation) ) );

        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, nLayoutWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
        bUnderlineDone = true;
    }
    if ( (eOverline == LINESTYLE_SMALLWAVE) ||
         (eOverline == LINESTYLE_WAVE) ||
         (eOverline == LINESTYLE_DOUBLEWAVE) ||
         (eOverline == LINESTYLE_BOLDWAVE) )
    {
        ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, nLayoutWidth, eOverline, aOverlineColortrue );
        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 )
{
    double nLayoutWidth = rSalLayout.GetTextWidth();
    if( bWordLine )
    {
        // draw everything relative to the layout base point
        const basegfx::B2DPoint aStartPt = rSalLayout.DrawBase();

        // calculate distance of each word from the base point
        basegfx::B2DPoint aPos;
        double nDist = 0;
        double 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.getX() - aStartPt.getX();
                    if( mpFontInstance->mnOrientation )
                    {
                        const double nDY = aPos.getY() - aStartPt.getY();
                        const double fRad = toRadians(mpFontInstance->mnOrientation);
                        nDist = basegfx::fround<tools::Long>(nDist * cos(fRad) - nDY * sin(fRad));
                    }
                }

                // update the length of the textline
                nWidth += pGlyph->newWidth();
            }
            else if( nWidth > 0 )
            {
                // draw the textline for each word
                ImplDrawTextLine( aStartPt.getX(), aStartPt.getY(), nDist, nWidth, nLayoutWidth,
                                  eStrikeout, eUnderline, eOverline, bUnderlineAbove );
                nWidth = 0;
            }
        }

        // draw textline for the last word
        if( nWidth > 0 )
        {
            ImplDrawTextLine( aStartPt.getX(), aStartPt.getY(), nDist, nWidth, nLayoutWidth,
                              eStrikeout, eUnderline, eOverline, bUnderlineAbove );
        }
    }
    else
    {
        basegfx::B2DPoint aStartPt = rSalLayout.GetDrawPosition();
        ImplDrawTextLine( aStartPt.getX(), aStartPt.getY(), 0,
                          nLayoutWidth, nLayoutWidth,
                          eStrikeout, eUnderline, eOverline, bUnderlineAbove );
    }
}

void OutputDevice::ImplDrawMnemonicLine( tools::Long nX, tools::Long nY, tools::Long nWidth )
{
    tools::Long nBaseX = nX;
    if/*HasMirroredGraphics() &&*/ IsRTLEnabled() )
    {
        // revert the hack that will be done later in ImplDrawTextLine
        nX = nBaseX - nWidth - (nX - nBaseX - 1);
    }

    ImplDrawTextLine( nX, nY, 0, nWidth, 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(vcl::drawmode::GetTextColor(rColor, GetDrawMode(), GetSettings().GetStyleSettings()));

    if ( mpMetaFile )
        mpMetaFile->AddAction( new MetaTextLineColorAction( aColor, true ) );

    maTextLineColor = aColor;

    if( mpAlphaVDev )
        mpAlphaVDev->SetTextLineColor( COL_ALPHA_OPAQUE );
}

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(vcl::drawmode::GetTextColor(rColor, GetDrawMode(), GetSettings().GetStyleSettings()));

    if ( mpMetaFile )
        mpMetaFile->AddAction( new MetaOverlineColorAction( aColor, true ) );

    maOverlineColor = aColor;

    if( mpAlphaVDev )
        mpAlphaVDev->SetOverlineColor( COL_ALPHA_OPAQUE );
}

void OutputDevice::DrawTextLine( const Point& rPos, tools::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 );
    double fWidth = ImplLogicWidthToDeviceSubPixel(nWidth);
    aPos += Point( mnTextOffX, mnTextOffY );
    ImplDrawTextLine( aPos.X(), aPos.X(), 0, fWidth, fWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove );

    if( mpAlphaVDev )
        mpAlphaVDev->DrawTextLine( rPos, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove );
}

void OutputDevice::DrawWaveLine(const Point& rStartPos, const Point& rEndPos, tools::Long nLineWidth, tools::Long nWaveHeight)
{
    assert(!is_double_buffered_window());

    if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
        return;

    // we need a graphics
    if( !mpGraphics && !AcquireGraphics() )
        return;
    assert(mpGraphics);

    if ( mbInitClipRegion )
        InitClipRegion();

    if ( mbOutputClipped )
        return;

    if (!InitFont())
        return;

    Point aStartPt = ImplLogicToDevicePixel(rStartPos);
    Point aEndPt = ImplLogicToDevicePixel(rEndPos);

    tools::Long nStartX = aStartPt.X();
    tools::Long nStartY = aStartPt.Y();
    tools::Long nEndX = aEndPt.X();
    tools::Long nEndY = aEndPt.Y();
    double fOrientation = 0.0;

    // handle rotation
    if (nStartY != nEndY || nStartX > nEndX)
    {
        fOrientation = basegfx::rad2deg(std::atan2(nStartY - nEndY, nEndX - nStartX));
        // un-rotate the end point
        aStartPt.RotateAround(nEndX, nEndY, Degree10(static_cast<sal_Int16>(-fOrientation * 10.0)));
    }

    // 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()
    // tdf#153223 polyline with lineheight >0 not drawn when skia is off
#ifdef MACOSX
        || !SkiaHelper::isVCLSkiaEnabled()
#endif
       )
    {
        nWaveHeight = pFontInstance->mxFontMetric->GetWavelineUnderlineSize();
        // tdf#124848 hairline
        nLineWidth = 0;
    }

    if ( fOrientation == 0.0 )
    {
        static tools::DeleteOnDeinit< WavyLineCache > snLineCache {};
        if ( !snLineCache.get() )
            return;
        WavyLineCache& rLineCache = *snLineCache.get();
        BitmapEx aWavylinebmp;
        if ( !rLineCache.find( GetLineColor(), nLineWidth, nWaveHeight, nEndX - nStartX, aWavylinebmp ) )
        {
            size_t nWordLength = nEndX - nStartX;
            // start with something big to avoid updating it frequently
            nWordLength = nWordLength < 1024 ? 1024 : nWordLength;
            ScopedVclPtrInstance< VirtualDevice > pVirtDev( *this, DeviceFormat::WITH_ALPHA );
            pVirtDev->SetOutputSizePixel( Size( nWordLength, nWaveHeight * 2 ), false );
            pVirtDev->SetLineColor( GetLineColor() );
            pVirtDev->SetBackground( Wallpaper( COL_TRANSPARENT ) );
            pVirtDev->Erase();
            pVirtDev->SetAntialiasing( AntialiasingFlags::Enable );
            pVirtDev->ImplDrawWaveLineBezier( 0, 0, nWordLength, 0, nWaveHeight, fOrientation, nLineWidth );
            BitmapEx aBitmapEx(pVirtDev->GetBitmapEx(Point(0, 0), pVirtDev->GetOutputSize()));

            // Ideally we don't need this block, but in the split rgb surface + separate alpha surface
            // with Antialiasing enabled and the svp/cairo backend we get both surfaces antialiased
            // so their combination of aliases merge to overly wash-out the color. Hack it by taking just
            // the alpha surface and use it to blend the original solid line color
            Bitmap aSolidColor(aBitmapEx.GetBitmap());
            aSolidColor.Erase(GetLineColor());
            aBitmapEx = BitmapEx(aSolidColor, aBitmapEx.GetAlphaMask());

            rLineCache.insert( aBitmapEx, GetLineColor(), nLineWidth, nWaveHeight, nWordLength, aWavylinebmp );
        }
        if ( aWavylinebmp.ImplGetBitmapSalBitmap() != nullptr )
        {
            Size _size( nEndX - nStartX, aWavylinebmp.GetSizePixel().Height() );
            DrawBitmapEx(Point( rStartPos.X(), rStartPos.Y() ), PixelToLogic( _size ), Point(), _size, aWavylinebmp);
        }
        return;
    }

    ImplDrawWaveLineBezier( nStartX, nStartY, nEndX, nEndY, nWaveHeight, fOrientation, nLineWidth );
}

void OutputDevice::ImplDrawWaveLineBezier(tools::Long nStartX, tools::Long nStartY, tools::Long nEndX, tools::Long nEndY, tools::Long nWaveHeight, double fOrientation, tools::Long nLineWidth)
{
    // we need a graphics
    if( !mpGraphics && !AcquireGraphics() )
        return;
    assert(mpGraphics);

    if ( mbInitClipRegion )
        InitClipRegion();

    if ( mbOutputClipped )
        return;

    if (!InitFont())
        return;

    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->ImplDrawWaveLineBezier(nStartX, nStartY, nEndX, nEndY, nWaveHeight, fOrientation, nLineWidth);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=96 H=97 G=96

¤ Dauer der Verarbeitung: 0.12 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.