Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/cppcanvas/source/mtfrenderer/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 138 kB image not shown  

Quelle  implrenderer.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 <comphelper/diagnose_ex.hxx>
#include <tools/debug.hxx>
#include <utility>
#include <vcl/svapp.hxx>
#include <comphelper/propertysequence.hxx>
#include <comphelper/propertyvalue.hxx>
#include <cppcanvas/canvas.hxx>
#include <com/sun/star/rendering/XGraphicDevice.hpp>
#include <com/sun/star/rendering/TexturingMode.hpp>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/rendering/PanoseProportion.hpp>
#include <com/sun/star/rendering/XCanvasFont.hpp>
#include <com/sun/star/rendering/XCanvas.hpp>
#include <com/sun/star/rendering/PathCapType.hpp>
#include <com/sun/star/rendering/PathJoinType.hpp>
#include <basegfx/utils/canvastools.hxx>
#include <basegfx/utils/gradienttools.hxx>
#include <basegfx/numeric/ftools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/vector/b2dsize.hxx>
#include <basegfx/range/b2drectangle.hxx>
#include <basegfx/point/b2dpoint.hxx>
#include <basegfx/tuple/b2dtuple.hxx>
#include <basegfx/polygon/b2dpolygonclipper.hxx>
#include <canvas/canvastools.hxx>
#include <rtl/ustrbuf.hxx>
#include <vcl/canvastools.hxx>
#include <vcl/gdimtf.hxx>
#include <vcl/metaact.hxx>
#include <vcl/virdev.hxx>
#include <vcl/metric.hxx>
#include <vcl/graphictools.hxx>
#include <vcl/BitmapPalette.hxx>
#include <tools/poly.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <implrenderer.hxx>
#include <tools.hxx>
#include <outdevstate.hxx>
#include <action.hxx>
#include <sal/log.hxx>
#include "bitmapaction.hxx"
#include "lineaction.hxx"
#include "pointaction.hxx"
#include "polypolyaction.hxx"
#include "textaction.hxx"
#include "transparencygroupaction.hxx"
#include <vector>
#include <algorithm>
#include <memory>
#include <string_view>
#include "mtftools.hxx"

using namespace ::com::sun::star;


// free support functions
// ======================
namespace
{
    template < class MetaActionType > void setStateColor( MetaActionType*                   pAct,
                                                          bool&                             rIsColorSet,
                                                          uno::Sequence< double >&          rColorSequence,
                                                          const cppcanvas::CanvasSharedPtr& rCanvas )
    {
        rIsColorSet = pAct->IsSetting();
        if (!rIsColorSet)
            return;

        ::Color aColor( pAct->GetColor() );

        // force alpha part of color to
        // opaque. transparent painting is done
        // explicitly via MetaActionType::Transparent
        aColor.SetAlpha(255);
        //aColor.SetTransparency(128);

        rColorSequence = vcl::unotools::colorToDoubleSequence(
            aColor,
            rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
    }

    void setupStrokeAttributes( rendering::StrokeAttributes&                          o_rStrokeAttributes,
                                const ::cppcanvas::internal::ActionFactoryParameters& rParms,
                                const LineInfo&                                       rLineInfo                 )
    {
        const ::basegfx::B2DSize aWidth( rLineInfo.GetWidth(), 0 );
        o_rStrokeAttributes.StrokeWidth =
            (rParms.mrStates.getState().mapModeTransform * aWidth).getLength();

        // setup reasonable defaults
        o_rStrokeAttributes.MiterLimit   = 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0
        o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
        o_rStrokeAttributes.EndCapType   = rendering::PathCapType::BUTT;

        switch (rLineInfo.GetLineJoin())
        {
            case basegfx::B2DLineJoin::NONE:
                o_rStrokeAttributes.JoinType = rendering::PathJoinType::NONE;
                break;
            case basegfx::B2DLineJoin::Bevel:
                o_rStrokeAttributes.JoinType = rendering::PathJoinType::BEVEL;
                break;
            case basegfx::B2DLineJoin::Miter:
                o_rStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
                break;
            case basegfx::B2DLineJoin::Round:
                o_rStrokeAttributes.JoinType = rendering::PathJoinType::ROUND;
                break;
        }

        switch(rLineInfo.GetLineCap())
        {
            default/* css::drawing::LineCap_BUTT */
            {
                o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
                o_rStrokeAttributes.EndCapType   = rendering::PathCapType::BUTT;
                break;
            }
            case css::drawing::LineCap_ROUND:
            {
                o_rStrokeAttributes.StartCapType = rendering::PathCapType::ROUND;
                o_rStrokeAttributes.EndCapType   = rendering::PathCapType::ROUND;
                break;
            }
            case css::drawing::LineCap_SQUARE:
            {
                o_rStrokeAttributes.StartCapType = rendering::PathCapType::SQUARE;
                o_rStrokeAttributes.EndCapType   = rendering::PathCapType::SQUARE;
                break;
            }
        }

        if( LineStyle::Dash != rLineInfo.GetStyle() )
            return;

        const ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );

        // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.

        // interpret dash info only if explicitly enabled as
        // style
        const ::basegfx::B2DSize aDistance( rLineInfo.GetDistance(), 0 );
        const double nDistance( (rState.mapModeTransform * aDistance).getLength() );

        const ::basegfx::B2DSize aDashLen( rLineInfo.GetDashLen(), 0 );
        const double nDashLen( (rState.mapModeTransform * aDashLen).getLength() );

        const ::basegfx::B2DSize aDotLen( rLineInfo.GetDotLen(), 0 );
        const double nDotLen( (rState.mapModeTransform * aDotLen).getLength() );

        const sal_Int32 nNumArryEntries( 2*rLineInfo.GetDashCount() +
                                         2*rLineInfo.GetDotCount() );

        o_rStrokeAttributes.DashArray.realloc( nNumArryEntries );
        double* pDashArray = o_rStrokeAttributes.DashArray.getArray();


        // iteratively fill dash array, first with dashes, then
        // with dots.


        sal_Int32 nCurrEntry=0;

        for( sal_Int32 i=0; i<rLineInfo.GetDashCount(); ++i )
        {
            pDashArray[nCurrEntry++] = nDashLen;
            pDashArray[nCurrEntry++] = nDistance;
        }
        for( sal_Int32 i=0; i<rLineInfo.GetDotCount(); ++i )
        {
            pDashArray[nCurrEntry++] = nDotLen;
            pDashArray[nCurrEntry++] = nDistance;
        }
    }


    /** Create masked BitmapEx, where the white areas of rBitmap are
        transparent, and the other appear in rMaskColor.
     */

    BitmapEx createMaskBmpEx( const Bitmap&  rBitmap,
                              const ::Color& rMaskColor )
    {
        const ::Color aWhite( COL_WHITE );
        BitmapPalette aBiLevelPalette{
            aWhite, rMaskColor
        };

        AlphaMask aMask( rBitmap.CreateAlphaMask( aWhite ));
        Bitmap aSolid( rBitmap.GetSizePixel(),
                       vcl::PixelFormat::N8_BPP,
                       &aBiLevelPalette );
        aSolid.Erase( rMaskColor );

        return BitmapEx( aSolid, aMask );
    }

    OUString convertToLocalizedNumerals(std::u16string_view rStr,
        LanguageType eTextLanguage)
    {
        OUStringBuffer aBuf(rStr);
        for (sal_Int32 i = 0; i < aBuf.getLength(); ++i)
        {
            sal_Unicode nChar = aBuf[i];
            if (nChar >= '0' && nChar <= '9')
                aBuf[i] = GetLocalizedChar(nChar, eTextLanguage);
        }
        return aBuf.makeStringAndClear();
    }
}

namespace cppcanvas::internal
{
        // state stack manipulators

        void VectorOfOutDevStates::clearStateStack()
        {
            m_aStates.clear();
            const OutDevState aDefaultState;
            m_aStates.push_back(aDefaultState);
        }

        OutDevState& VectorOfOutDevStates::getState()
        {
            return m_aStates.back();
        }

        const OutDevState& VectorOfOutDevStates::getState() const
        {
            return m_aStates.back();
        }

        void VectorOfOutDevStates::pushState(vcl::PushFlags nFlags)
        {
            m_aStates.push_back( getState() );
            getState().pushFlags = nFlags;
        }

        void VectorOfOutDevStates::popState()
        {
            if( getState().pushFlags != vcl::PushFlags::ALL )
            {
                // a state is pushed which is incomplete, i.e. does not
                // restore everything to the previous stack level when
                // popped.
                // That means, we take the old state, and restore every
                // OutDevState member whose flag is set, from the new to the
                // old state. Then the new state gets overwritten by the
                // calculated state

                // preset to-be-calculated new state with old state
                OutDevState aCalculatedNewState( getState() );

                // selectively copy to-be-restored content over saved old
                // state
                m_aStates.pop_back();

                const OutDevState& rNewState( getState() );

                if( aCalculatedNewState.pushFlags & vcl::PushFlags::LINECOLOR )
                {
                    aCalculatedNewState.lineColor      = rNewState.lineColor;
                    aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet;
                }

                if( aCalculatedNewState.pushFlags & vcl::PushFlags::FILLCOLOR )
                {
                    aCalculatedNewState.fillColor      = rNewState.fillColor;
                    aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet;
                }

                if( aCalculatedNewState.pushFlags & vcl::PushFlags::FONT )
                {
                    aCalculatedNewState.xFont                   = rNewState.xFont;
                    aCalculatedNewState.fontRotation            = rNewState.fontRotation;
                    aCalculatedNewState.textReliefStyle         = rNewState.textReliefStyle;
                    aCalculatedNewState.textOverlineStyle       = rNewState.textOverlineStyle;
                    aCalculatedNewState.textUnderlineStyle      = rNewState.textUnderlineStyle;
                    aCalculatedNewState.textStrikeoutStyle      = rNewState.textStrikeoutStyle;
                    aCalculatedNewState.textEmphasisMark        = rNewState.textEmphasisMark;
                    aCalculatedNewState.isTextEffectShadowSet   = rNewState.isTextEffectShadowSet;
                    aCalculatedNewState.isTextWordUnderlineSet  = rNewState.isTextWordUnderlineSet;
                    aCalculatedNewState.isTextOutlineModeSet    = rNewState.isTextOutlineModeSet;
                }

                if( aCalculatedNewState.pushFlags & vcl::PushFlags::TEXTCOLOR )
                {
                    aCalculatedNewState.textColor = rNewState.textColor;
                }

                if( aCalculatedNewState.pushFlags & vcl::PushFlags::MAPMODE )
                {
                    aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform;
                }

                if( aCalculatedNewState.pushFlags & vcl::PushFlags::CLIPREGION )
                {
                    aCalculatedNewState.clip        = rNewState.clip;
                    aCalculatedNewState.clipRect    = rNewState.clipRect;
                    aCalculatedNewState.xClipPoly   = rNewState.xClipPoly;
                }

                // TODO(F2): Raster ops NYI
                // if( (aCalculatedNewState.pushFlags & vcl::PushFlags::RASTEROP) )
                // {
                // }

                if( aCalculatedNewState.pushFlags & vcl::PushFlags::TEXTFILLCOLOR )
                {
                    aCalculatedNewState.textFillColor      = rNewState.textFillColor;
                    aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet;
                }

                if( aCalculatedNewState.pushFlags & vcl::PushFlags::TEXTALIGN )
                {
                    aCalculatedNewState.textReferencePoint = rNewState.textReferencePoint;
                }

                // TODO(F1): Refpoint handling NYI
                // if( (aCalculatedNewState.pushFlags & vcl::PushFlags::REFPOINT) )
                // {
                // }

                if( aCalculatedNewState.pushFlags & vcl::PushFlags::TEXTLINECOLOR )
                {
                    aCalculatedNewState.textLineColor      = rNewState.textLineColor;
                    aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet;
                }

                if( aCalculatedNewState.pushFlags & vcl::PushFlags::OVERLINECOLOR )
                {
                    aCalculatedNewState.textOverlineColor = rNewState.textOverlineColor;
                    aCalculatedNewState.isTextOverlineColorSet = rNewState.isTextOverlineColorSet;
                }

                if( aCalculatedNewState.pushFlags & vcl::PushFlags::TEXTLAYOUTMODE )
                {
                    aCalculatedNewState.textAlignment = rNewState.textAlignment;
                    aCalculatedNewState.textDirection = rNewState.textDirection;
                }

                // TODO(F2): Text language handling NYI
                // if( (aCalculatedNewState.pushFlags & vcl::PushFlags::TEXTLANGUAGE) )
                // {
                // }

                // always copy push mode
                aCalculatedNewState.pushFlags = rNewState.pushFlags;

                // flush to stack
                getState() = std::move(aCalculatedNewState);
            }
            else
            {
                m_aStates.pop_back();
            }
        }

        bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon& rPolyPoly,
                                                const ActionFactoryParameters&   rParms )
        {
            const OutDevState& rState( rParms.mrStates.getState() );
            if( (!rState.isLineColorSet &&
                 !rState.isFillColorSet) ||
                (!rState.lineColor.hasElements() &&
                 !rState.fillColor.hasElements()) )
            {
                return false;
            }

            std::shared_ptr<Action> pPolyAction(
                internal::PolyPolyActionFactory::createPolyPolyAction(
                    rPolyPoly, rParms.mrCanvas, rState ) );

            if( pPolyAction )
            {
                maActions.emplace_back(
                        pPolyAction,
                        rParms.mrCurrActionIndex );

                rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
            }

            return true;
        }

        bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon&   rPoly,
                                                const ActionFactoryParameters& rParms )
        {
            return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly ),
                                        rParms );
        }

        void ImplRenderer::skipContent( GDIMetaFile& rMtf,
                                        const char*  pCommentString,
                                        sal_Int32&   io_rCurrActionIndex )
        {
            ENSURE_OR_THROW( pCommentString,
                              "ImplRenderer::skipContent(): NULL string given" );

            MetaAction* pCurrAct;
            while( (pCurrAct=rMtf.NextAction()) != nullptr )
            {
                // increment action index, we've skipped an action.
                ++io_rCurrActionIndex;

                if( pCurrAct->GetType() == MetaActionType::COMMENT &&
                    static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase(
                        pCommentString) )
                {
                    // requested comment found, done
                    return;
                }
            }

            // EOF
        }

        bool ImplRenderer::isActionContained( GDIMetaFile&   rMtf,
                                              const char*    pCommentString,
                                              MetaActionType nType )
        {
            ENSURE_OR_THROW( pCommentString,
                              "ImplRenderer::isActionContained(): NULL string given" );

            bool bRet( false );

            // at least _one_ call to GDIMetaFile::NextAction() is
            // executed
            size_t nPos( 1 );

            MetaAction* pCurrAct;
            while( (pCurrAct=rMtf.NextAction()) != nullptr )
            {
                if( pCurrAct->GetType() == nType )
                {
                    bRet = true// action type found
                    break;
                }

                if( pCurrAct->GetType() == MetaActionType::COMMENT &&
                    static_cast<MetaCommentAction*>(pCurrAct)->GetComment().equalsIgnoreAsciiCase(
                        pCommentString) )
                {
                    // delimiting end comment found, done
                    bRet = false// not yet found
                    break;
                }

                ++nPos;
            }

            // rewind metafile to previous position (this method must
            // not change the current metaaction)
            while (nPos)
            {
                --nPos;
                rMtf.WindPrev();
            }

            if( !pCurrAct )
            {
                // EOF, and not yet found
                bRet = false;
            }

            return bRet;
        }

        void ImplRenderer::createGradientAction( const ::tools::PolyPolygon&    rPoly,
                                                 const ::Gradient&              rGradient,
                                                 const ActionFactoryParameters& rParms,
                                                 bool                           bIsPolygonRectangle,
                                                 bool                           bSubsettableActions )
        {
            DBG_TESTSOLARMUTEX();

            ::basegfx::B2DPolyPolygon aDevicePoly( rPoly.getB2DPolyPolygon() );
            aDevicePoly.transform( rParms.mrStates.getState().mapModeTransform );

            // decide, whether this gradient can be rendered natively
            // by the canvas, or must be emulated via VCL gradient
            // action extraction.
            const sal_uInt16 nSteps( rGradient.GetSteps() );

            if// step count is infinite, can use native canvas
                // gradients here
                nSteps == 0 ||
                // step count is sufficiently high, such that no
                // discernible difference should be visible.
                nSteps > 64 )
            {
                uno::Reference< lang::XMultiServiceFactory> xFactory(
                    rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );

                if( xFactory.is() )
                {
                    rendering::Texture aTexture;

                    aTexture.RepeatModeX = rendering::TexturingMode::CLAMP;
                    aTexture.RepeatModeY = rendering::TexturingMode::CLAMP;
                    aTexture.Alpha = 1.0;


                    // setup start/end color values


                    // scale color coefficients with gradient intensities
                    const sal_uInt16 nStartIntensity( rGradient.GetStartIntensity() );
                    ::Color aVCLStartColor( rGradient.GetStartColor() );
                    aVCLStartColor.SetRed( static_cast<sal_uInt8>(aVCLStartColor.GetRed() * nStartIntensity / 100) );
                    aVCLStartColor.SetGreen( static_cast<sal_uInt8>(aVCLStartColor.GetGreen() * nStartIntensity / 100) );
                    aVCLStartColor.SetBlue( static_cast<sal_uInt8>(aVCLStartColor.GetBlue() * nStartIntensity / 100) );

                    const sal_uInt16 nEndIntensity( rGradient.GetEndIntensity() );
                    ::Color aVCLEndColor( rGradient.GetEndColor() );
                    aVCLEndColor.SetRed( static_cast<sal_uInt8>(aVCLEndColor.GetRed() * nEndIntensity / 100) );
                    aVCLEndColor.SetGreen( static_cast<sal_uInt8>(aVCLEndColor.GetGreen() * nEndIntensity / 100) );
                    aVCLEndColor.SetBlue( static_cast<sal_uInt8>(aVCLEndColor.GetBlue() * nEndIntensity / 100) );

                    uno::Reference<rendering::XColorSpace> xColorSpace(
                        rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());
                    const uno::Sequence< double > aStartColor(
                        vcl::unotools::colorToDoubleSequence( aVCLStartColor,
                                                                xColorSpace ));
                    const uno::Sequence< double > aEndColor(
                        vcl::unotools::colorToDoubleSequence( aVCLEndColor,
                                                                xColorSpace ));

                    uno::Sequence< uno::Sequence < double > > aColors;
                    uno::Sequence< double > aStops;

                    if( rGradient.GetStyle() == css::awt::GradientStyle_AXIAL )
                    {
                        aStops = { 0.0, 0.5, 1.0 };
                        aColors = { aEndColor, aStartColor, aEndColor };
                    }
                    else
                    {
                        aStops = { 0.0, 1.0 };
                        aColors = { aStartColor, aEndColor };
                    }

                    const ::basegfx::B2DRectangle aBounds(
                        ::basegfx::utils::getRange(aDevicePoly) );
                    const ::basegfx::B2DVector aOffset(
                        rGradient.GetOfsX() / 100.0,
                        rGradient.GetOfsY() / 100.0);
                    double fRotation = toRadians( rGradient.GetAngle() );
                    const double fBorder( rGradient.GetBorder() / 100.0 );

                    basegfx::B2DHomMatrix aRot90;
                    aRot90.rotate(M_PI_2);

                    basegfx::ODFGradientInfo aGradInfo;
                    OUString aGradientService;
                    switch( rGradient.GetStyle() )
                    {
                        case css::awt::GradientStyle_LINEAR:
                            aGradInfo = basegfx::utils::createLinearODFGradientInfo(
                                                                        aBounds,
                                                                        nSteps,
                                                                        fBorder,
                                                                        fRotation);
                            // map ODF to svg gradient orientation - x
                            // instead of y direction
                            aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aRot90);
                            aGradientService = "LinearGradient";
                            break;

                        case css::awt::GradientStyle_AXIAL:
                        {
                            // Adapt the border so that it is suitable
                            // for the axial gradient.  An axial
                            // gradient consists of two linear
                            // gradients.  Each of those covers half
                            // of the total size.  In order to
                            // compensate for the condensed display of
                            // the linear gradients, we have to
                            // enlarge the area taken up by the actual
                            // gradient (1-fBorder).  After that we
                            // have to turn the result back into a
                            // border value, hence the second (left
                            // most 1-...
                            const double fAxialBorder (1-2*(1-fBorder));
                            aGradInfo = basegfx::utils::createAxialODFGradientInfo(
                                                                        aBounds,
                                                                        nSteps,
                                                                        fAxialBorder,
                                                                        fRotation);
                            // map ODF to svg gradient orientation - x
                            // instead of y direction
                            aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aRot90);

                            // map ODF axial gradient to 3-stop linear
                            // gradient - shift left by 0.5
                            basegfx::B2DHomMatrix aShift;

                            aShift.translate(-0.5,0);
                            aGradInfo.setTextureTransform(aGradInfo.getTextureTransform() * aShift);
                            aGradientService = "LinearGradient";
                            break;
                        }

                        case css::awt::GradientStyle_RADIAL:
                            aGradInfo = basegfx::utils::createRadialODFGradientInfo(
                                                                        aBounds,
                                                                        aOffset,
                                                                        nSteps,
                                                                        fBorder);
                            aGradientService = "EllipticalGradient";
                            break;

                        case css::awt::GradientStyle_ELLIPTICAL:
                            aGradInfo = basegfx::utils::createEllipticalODFGradientInfo(
                                                                            aBounds,
                                                                            aOffset,
                                                                            nSteps,
                                                                            fBorder,
                                                                            fRotation);
                            aGradientService = "EllipticalGradient";
                            break;

                        case css::awt::GradientStyle_SQUARE:
                            aGradInfo = basegfx::utils::createSquareODFGradientInfo(
                                                                        aBounds,
                                                                        aOffset,
                                                                        nSteps,
                                                                        fBorder,
                                                                        fRotation);
                            aGradientService = "RectangularGradient";
                            break;

                        case css::awt::GradientStyle_RECT:
                            aGradInfo = basegfx::utils::createRectangularODFGradientInfo(
                                                                             aBounds,
                                                                             aOffset,
                                                                             nSteps,
                                                                             fBorder,
                                                                             fRotation);
                            aGradientService = "RectangularGradient";
                            break;

                        default:
                            ENSURE_OR_THROW( false,
                                             "ImplRenderer::createGradientAction(): Unexpected gradient type" );
                            break;
                    }

                    ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform,
                                                                    aGradInfo.getTextureTransform() );

                    uno::Sequence<uno::Any> args(comphelper::InitAnyPropertySequence(
                    {
                        {"Colors", uno::Any(aColors)},
                        {"Stops", uno::Any(aStops)},
                        {"AspectRatio", uno::Any(aGradInfo.getAspectRatio())},
                    }));
                    aTexture.Gradient.set(
                        xFactory->createInstanceWithArguments(aGradientService,
                                                              args),
                        uno::UNO_QUERY);
                    if( aTexture.Gradient.is() )
                    {
                        std::shared_ptr<Action> pPolyAction(
                            internal::PolyPolyActionFactory::createPolyPolyAction(
                                aDevicePoly,
                                rParms.mrCanvas,
                                rParms.mrStates.getState(),
                                aTexture ) );

                        if( pPolyAction )
                        {
                            maActions.emplace_back(
                                    pPolyAction,
                                    rParms.mrCurrActionIndex );

                            rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
                        }

                        // done, using native gradients
                        return;
                    }
                }
            }

            // cannot currently use native canvas gradients, as a
            // finite step size is given (this funny feature is not
            // supported by the XCanvas API)
            rParms.mrStates.pushState(vcl::PushFlags::ALL);

            if( !bIsPolygonRectangle )
            {
                // only clip, if given polygon is not a rectangle in
                // the first place (the gradient is always limited to
                // the given bound rect)
                updateClipping(
                    aDevicePoly,
                    rParms,
                    true );
            }

            GDIMetaFile aTmpMtf;
            Gradient aGradient(rGradient);
            aGradient.AddGradientActions( rPoly.GetBoundRect(), aTmpMtf );

            createActions( aTmpMtf, rParms, bSubsettableActions );

            rParms.mrStates.popState();
        }

        uno::Reference< rendering::XCanvasFont > ImplRenderer::createFont( double&                        o_rFontRotation,
                                                                           const vcl::Font&               rFont,
                                                                           const ActionFactoryParameters& rParms )
        {
            rendering::FontRequest aFontRequest;

            if( rParms.mrParms.maFontName )
                aFontRequest.FontDescription.FamilyName = *rParms.mrParms.maFontName;
            else
                aFontRequest.FontDescription.FamilyName = rFont.GetFamilyName();

            aFontRequest.FontDescription.StyleName = rFont.GetStyleName();

            aFontRequest.FontDescription.IsSymbolFont = (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) ? util::TriState_YES : util::TriState_NO;
            aFontRequest.FontDescription.IsVertical = rFont.IsVertical() ? util::TriState_YES : util::TriState_NO;

            // TODO(F2): improve vclenum->panose conversion
            aFontRequest.FontDescription.FontDescription.Weight =
                rParms.mrParms.maFontWeight ?
                *rParms.mrParms.maFontWeight :
                ::canvas::tools::numeric_cast<sal_Int8>( ::basegfx::fround( rFont.GetWeight() ) );
            aFontRequest.FontDescription.FontDescription.Letterform =
                rParms.mrParms.maFontLetterForm ?
                *rParms.mrParms.maFontLetterForm :
                (rFont.GetItalic() == ITALIC_NONE) ? 0 : 9;
            aFontRequest.FontDescription.FontDescription.Proportion =
                (rFont.GetPitch() == PITCH_FIXED)
                    ? rendering::PanoseProportion::MONO_SPACED
                    : rendering::PanoseProportion::ANYTHING;

            LanguageType aLang = rFont.GetLanguage();
            aFontRequest.Locale = LanguageTag::convertToLocale( aLang, false);

            // setup state-local text transformation,
            // if the font be rotated
            const auto nFontAngle( rFont.GetOrientation() );
            if( nFontAngle )
            {
                // set to unity transform rotated by font angle
                const double nAngle( toRadians(nFontAngle) );
                o_rFontRotation = -nAngle;
            }
            else
            {
                o_rFontRotation = 0.0;
            }

            geometry::Matrix2D aFontMatrix;
            ::canvas::tools::setIdentityMatrix2D( aFontMatrix );

            // TODO(F2): use correct scale direction, font
            // height might be width or anything else

            // TODO(Q3): This code smells of programming by
            // coincidence (the next two if statements)

            ::Size rFontSizeLog( rFont.GetFontSize() );

            if (rFontSizeLog.Height() == 0)
            {
                // guess 16 pixel (as in VCL)
                rFontSizeLog = ::Size(0, 16);

                // convert to target MapUnit if not pixels
                rFontSizeLog = OutputDevice::LogicToLogic(rFontSizeLog, MapMode(MapUnit::MapPixel), rParms.mrVDev.GetMapMode());
            }

            const sal_Int32 nFontWidthLog = rFontSizeLog.Width();
            if( nFontWidthLog != 0 )
            {
                vcl::Font aTestFont = rFont;
                aTestFont.SetAverageFontWidth( 0 );
                sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetAverageFontWidth();
                if( nNormalWidth != nFontWidthLog )
                    if( nNormalWidth )
                        aFontMatrix.m00 = static_cast<double>(nFontWidthLog) / nNormalWidth;
            }

            // #i52608# apply map mode scale also to font matrix - an
            // anisotrophic mapmode must be reflected in an
            // anisotrophic font matrix scale.
            const OutDevState& rState( rParms.mrStates.getState() );
            if( !::basegfx::fTools::equal(
                    rState.mapModeTransform.get(0,0),
                    rState.mapModeTransform.get(1,1)) )
            {
                const double nScaleX( rState.mapModeTransform.get(0,0) );
                const double nScaleY( rState.mapModeTransform.get(1,1) );

                // note: no reason to check for division by zero, we
                // always have the value closer (or equal) to zero as
                // the nominator.
                if( fabs(nScaleX) < fabs(nScaleY) )
                    aFontMatrix.m00 *= nScaleX / nScaleY;
                else
                    aFontMatrix.m11 *= nScaleY / nScaleX;
            }
            aFontRequest.CellSize = (rState.mapModeTransform * vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getHeight();

            if (rFont.GetEmphasisMark() != FontEmphasisMark::NONE)
            {
                uno::Sequence< beans::PropertyValue > aProperties{ comphelper::makePropertyValue(
                    u"EmphasisMark"_ustr, sal_uInt32(rFont.GetEmphasisMark())) };
                return rParms.mrCanvas->getUNOCanvas()->createFont(aFontRequest,
                                                                aProperties,
                                                                aFontMatrix);
            }

            return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest,
                                                                uno::Sequence< beans::PropertyValue >(),
                                                                aFontMatrix );
        }

        // create text effects such as shadow/relief/embossed
        void ImplRenderer::createTextAction( const ::Point&                 rStartPoint,
                                             const OUString&                rString,
                                             int                            nIndex,
                                             int                            nLength,
                                             KernArraySpan                pCharWidths,
                                             std::span<const sal_Bool>     pKashidaArray,
                                             const ActionFactoryParameters& rParms,
                                             bool                           bSubsettableActions )
        {
            ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.getLength() + nIndex,
                              "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );

            if( !nLength )
                return// zero-length text, no visible output

            const OutDevState& rState( rParms.mrStates.getState() );

            // TODO(F2): implement all text effects
            // if( rState.textAlignment );             // TODO(F2): NYI

            ::Color aTextFillColor( COL_AUTO );
            ::Color aShadowColor( COL_AUTO );
            ::Color aReliefColor( COL_AUTO );
            ::Size  aShadowOffset;
            ::Size  aReliefOffset;

            uno::Reference<rendering::XColorSpace> xColorSpace(
                rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );

            if( rState.isTextEffectShadowSet )
            {
                // calculate shadow offset (similar to outdev3.cxx)
                // TODO(F3): better match with outdev3.cxx
                sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetFontHeight()-24.0)/24.0));
                if( nShadowOffset < 1 )
                    nShadowOffset = 1;

                aShadowOffset.setWidth( nShadowOffset );
                aShadowOffset.setHeight( nShadowOffset );

                // determine shadow color (from outdev3.cxx)
                ::Color aTextColor = vcl::unotools::doubleSequenceToColor(
                    rState.textColor, xColorSpace );
                bool bIsDark = (aTextColor == COL_BLACK)
                    || (aTextColor.GetLuminance() < 8);

                aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK;
                aShadowColor.SetAlpha( aTextColor.GetAlpha() );
            }

            if( rState.textReliefStyle != FontRelief::NONE )
            {
                // calculate relief offset (similar to outdev3.cxx)
                sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height();
                nReliefOffset += nReliefOffset/2;
                if( nReliefOffset < 1 )
                    nReliefOffset = 1;

                if( rState.textReliefStyle == FontRelief::Engraved )
                    nReliefOffset = -nReliefOffset;

                aReliefOffset.setWidth( nReliefOffset );
                aReliefOffset.setHeight( nReliefOffset );

                // determine relief color (from outdev3.cxx)
                ::Color aTextColor = vcl::unotools::doubleSequenceToColor(
                    rState.textColor, xColorSpace );

                aReliefColor = COL_LIGHTGRAY;

                // we don't have an automatic color, so black is always
                // drawn on white (literally copied from
                // vcl/source/gdi/outdev3.cxx)
                if( aTextColor == COL_BLACK )
                {
                    aTextColor = COL_WHITE;
                    rParms.mrStates.getState().textColor =
                        vcl::unotools::colorToDoubleSequence(
                            aTextColor, xColorSpace );
                }

                if( aTextColor == COL_WHITE )
                    aReliefColor = COL_BLACK;
                aReliefColor.SetAlpha( aTextColor.GetAlpha() );
            }

            if (rState.isTextFillColorSet)
                aTextFillColor = vcl::unotools::doubleSequenceToColor(rState.textFillColor, xColorSpace);

            // create the actual text action
            std::shared_ptr<Action> pTextAction(
                TextActionFactory::createTextAction(
                    rStartPoint,
                    aReliefOffset,
                    aReliefColor,
                    aShadowOffset,
                    aShadowColor,
                    aTextFillColor,
                    rString,
                    nIndex,
                    nLength,
                    pCharWidths,
                    pKashidaArray,
                    rParms.mrVDev,
                    rParms.mrCanvas,
                    rState,
                    rParms.mrParms,
                    bSubsettableActions ) );

            std::shared_ptr<Action> pStrikeoutTextAction;

            if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH )
            {
                ::tools::Long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength );

                sal_Unicode pChars[4];
                if ( rState.textStrikeoutStyle == STRIKEOUT_X )
                    pChars[0] = 'X';
                else
                    pChars[0] = '/';
                pChars[3]=pChars[2]=pChars[1]=pChars[0];

                ::tools::Long nStrikeoutWidth = (rParms.mrVDev.GetTextWidth(
                    OUString(pChars, std::size(pChars))) + 2) / 4;

                if( nStrikeoutWidth <= 0 )
                    nStrikeoutWidth = 1;

                ::tools::Long nMaxWidth = nStrikeoutWidth/2;
                if ( nMaxWidth < 2 )
                    nMaxWidth = 2;
                nMaxWidth += nWidth + 1;

                ::tools::Long nFullStrikeoutWidth = 0;
                OUStringBuffer aStrikeoutText;
                while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 )
                    aStrikeoutText.append(pChars[0]);

                sal_Int32 nLen = aStrikeoutText.getLength();

                if( nLen )
                {
                    ::tools::Long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen;
                    nStrikeoutWidth += nInterval;
                    KernArray aStrikeoutCharWidths;

                    for ( int i = 0;i< nLen; i++ )
                        aStrikeoutCharWidths.push_back(nStrikeoutWidth * (i + 1));

                    pStrikeoutTextAction =
                        TextActionFactory::createTextAction(
                            rStartPoint,
                            aReliefOffset,
                            aReliefColor,
                            aShadowOffset,
                            aShadowColor,
                            aTextFillColor,
                            aStrikeoutText.makeStringAndClear(),
                            0/*nStartPos*/,
                            nLen,
                            aStrikeoutCharWidths,
                            pKashidaArray,
                            rParms.mrVDev,
                            rParms.mrCanvas,
                            rState,
                            rParms.mrParms,
                            bSubsettableActions ) ;
                }
            }

            if( !pTextAction )
                return;

            maActions.emplace_back(
                    pTextAction,
                    rParms.mrCurrActionIndex );

            if ( pStrikeoutTextAction )
            {
                maActions.emplace_back(
                    pStrikeoutTextAction,
                    rParms.mrCurrActionIndex );
            }

            rParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
        }

        void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon& rClipPoly,
                                           const ActionFactoryParameters&   rParms,
                                           bool                             bIntersect )
        {
            ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );

            const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
            const bool bEmptyClipPoly( rState.clip.count() == 0 );

            ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
                              "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );

            if( !bIntersect ||
                (bEmptyClipRect && bEmptyClipPoly) )
            {
                rState.clip = rClipPoly;
            }
            else
            {
                if( !bEmptyClipRect )
                {
                    // TODO(P3): Use Liang-Barsky polygon clip here,
                    // after all, one object is just a rectangle!

                    // convert rect to polygon beforehand, must revert
                    // to general polygon clipping here.
                    ::tools::Rectangle aRect = rState.clipRect;
                    // #121100# VCL rectangular clips always
                    // include one more pixel to the right
                    // and the bottom
                    aRect.AdjustRight(1);
                    aRect.AdjustBottom(1);
                    rState.clip = ::basegfx::B2DPolyPolygon(
                        ::basegfx::utils::createPolygonFromRect(
                            vcl::unotools::b2DRectangleFromRectangle(aRect) ) );
                }

                // AW: Simplified
                rState.clip = basegfx::utils::clipPolyPolygonOnPolyPolygon(
                    rClipPoly, rState.clip, truefalse);
            }

            // by now, our clip resides in the OutDevState::clip
            // poly-polygon.
            rState.clipRect.SetEmpty();

            if( rState.clip.count() == 0 )
            {
                if( rState.clipRect.IsEmpty() )
                {
                    rState.xClipPoly.clear();
                }
                else
                {
                    ::tools::Rectangle aRect = rState.clipRect;
                    // #121100# VCL rectangular clips
                    // always include one more pixel to
                    // the right and the bottom
                    aRect.AdjustRight(1);
                    aRect.AdjustBottom(1);
                    rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
                        rParms.mrCanvas->getUNOCanvas()->getDevice(),
                        ::basegfx::B2DPolyPolygon(
                            ::basegfx::utils::createPolygonFromRect(
                                vcl::unotools::b2DRectangleFromRectangle(aRect) ) ) );
                }
            }
            else
            {
                rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
                    rParms.mrCanvas->getUNOCanvas()->getDevice(),
                    rState.clip );
            }
        }

        void ImplRenderer::updateClipping( const ::tools::Rectangle&             rClipRect,
                                           const ActionFactoryParameters& rParms,
                                           bool                           bIntersect )
        {
            ::cppcanvas::internal::OutDevState& rState( rParms.mrStates.getState() );

            const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
            const bool bEmptyClipPoly( rState.clip.count() == 0 );

            ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
                              "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );

            if( !bIntersect ||
                (bEmptyClipRect && bEmptyClipPoly) )
            {
                rState.clipRect = rClipRect;
                rState.clip.clear();
            }
            else if( bEmptyClipPoly )
            {
                rState.clipRect.Intersection( rClipRect );
                rState.clip.clear();
            }
            else
            {
                // TODO(P3): Handle a fourth case here, when all clip
                // polygons are rectangular, once B2DMultiRange's
                // sweep line implementation is done.

                // general case: convert to polygon and clip


                // convert rect to polygon beforehand, must revert
                // to general polygon clipping here.
                ::basegfx::B2DPolyPolygon aClipPoly(
                    ::basegfx::utils::createPolygonFromRect(
                        vcl::unotools::b2DRectangleFromRectangle(rClipRect) ) );

                rState.clipRect.SetEmpty();

                // AW: Simplified
                rState.clip = basegfx::utils::clipPolyPolygonOnPolyPolygon(
                    aClipPoly, rState.clip, truefalse);
            }

            if( rState.clip.count() == 0 )
            {
                if( rState.clipRect.IsEmpty() )
                {
                    rState.xClipPoly.clear();
                }
                else
                {
                    // #121100# VCL rectangular clips
                    // always include one more pixel to
                    // the right and the bottom
                    ::tools::Rectangle aRect = rState.clipRect;
                    aRect.AdjustRight(1);
                    aRect.AdjustBottom(1);
                    rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
                        rParms.mrCanvas->getUNOCanvas()->getDevice(),
                        ::basegfx::B2DPolyPolygon(
                            ::basegfx::utils::createPolygonFromRect(
                                vcl::unotools::b2DRectangleFromRectangle(aRect) ) ) );
                }
            }
            else
            {
                rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
                    rParms.mrCanvas->getUNOCanvas()->getDevice(),
                    rState.clip );
            }
        }

        void ImplRenderer::createActions( GDIMetaFile&                   rMtf,
                                          const ActionFactoryParameters& rFactoryParms,
                                          bool                           bSubsettableActions )
        {
            /* TODO(P2): interpret mtf-comments
               ================================

               - gradient fillings (do that via comments)

               - think about mapping. _If_ we do everything in logical
                    coordinates (which would solve the probs for stroke
                 widths and text offsets), then we would have to
                 recalc scaling for every drawing operation. This is
                 because the outdev map mode might change at any time.
                 Also keep in mind, that, although we've double precision
                 float arithmetic now, different offsets might still
                 generate different roundings (aka
                 'OutputDevice::SetPixelOffset())

             */


            // alias common parameters
            VectorOfOutDevStates&  rStates(rFactoryParms.mrStates);
            const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas);
            ::VirtualDevice&       rVDev(rFactoryParms.mrVDev);
            const Parameters&      rParms(rFactoryParms.mrParms);
            sal_Int32&             io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex);


            // Loop over every metaaction
            // ==========================
            MetaAction* pCurrAct;

            // TODO(P1): think about caching
            for( pCurrAct=rMtf.FirstAction();
                 pCurrAct;
                 pCurrAct = rMtf.NextAction() )
            {
                // execute every action, to keep VDev state up-to-date
                // currently used only for
                // - the map mode
                // - the line/fill color when processing a MetaActionType::Transparent
                // - SetFont to process font metric specific actions
                pCurrAct->Execute( &rVDev );

                SAL_INFO("cppcanvas.emf""MTF\trecord type: 0x" << static_cast<sal_uInt16>(pCurrAct->GetType()) << " (" << static_cast<sal_uInt16>(pCurrAct->GetType()) << ")");

                switch( pCurrAct->GetType() )
                {


                    // In the first part of this monster-switch, we
                    // handle all state-changing meta actions. These
                    // are all handled locally.


                    case MetaActionType::PUSH:
                    {
                        MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct);
                        rStates.pushState(pPushAction->GetFlags());
                    }
                    break;

                    case MetaActionType::POP:
                        rStates.popState();
                        break;

                    case MetaActionType::TEXTLANGUAGE:
                    case MetaActionType::REFPOINT:
                        // handled via pCurrAct->Execute( &rVDev )
                        break;

                    case MetaActionType::MAPMODE:
                        // modify current mapModeTransformation
                        // transformation, such that subsequent
                        // coordinates map correctly
                        tools::calcLogic2PixelAffineTransform( rStates.getState().mapModeTransform,
                                                               rVDev );
                        break;

                    // monitor clip regions, to assemble clip polygon on our own
                    case MetaActionType::CLIPREGION:
                    {
                        MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct);

                        if( !pClipAction->IsClipping() )
                        {
                            // clear clipping
                            rStates.getState().clip.clear();
                        }
                        else
                        {
                            if( !pClipAction->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
                            {
                                SAL_INFO( "cppcanvas.emf""ImplRenderer::createActions(): non-polygonal clip "
                                               "region encountered, falling back to bounding box!" );

                                // #121806# explicitly kept integer
                                ::tools::Rectangle aClipRect(
                                    rVDev.LogicToPixel(
                                        pClipAction->GetRegion().GetBoundRect() ) );

                                // intersect current clip with given rect
                                updateClipping(
                                    aClipRect,
                                    rFactoryParms,
                                    false );
                            }
                            else
                            {
                                // set new clip polygon (don't intersect
                                // with old one, just set it)

                                // #121806# explicitly kept integer
                                basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon());

                                aPolyPolygon.transform(rVDev.GetViewTransformation());
                                updateClipping(
                                    aPolyPolygon,
                                    rFactoryParms,
                                    false );
                            }
                        }

                        break;
                    }

                    case MetaActionType::ISECTRECTCLIPREGION:
                    {
                        MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct);

                        // #121806# explicitly kept integer
                        ::tools::Rectangle aClipRect(
                            rVDev.LogicToPixel( pClipAction->GetRect() ) );

                        // intersect current clip with given rect
                        updateClipping(
                            aClipRect,
                            rFactoryParms,
                            true );

                        break;
                    }

                    case MetaActionType::ISECTREGIONCLIPREGION:
                    {
                        MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct);

                        if( !pClipAction->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
                        {
                            SAL_INFO( "cppcanvas.emf""ImplRenderer::createActions(): non-polygonal clip "
                                           "region encountered, falling back to bounding box!" );

                            // #121806# explicitly kept integer
                            ::tools::Rectangle aClipRect(
                                rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) );

                            // intersect current clip with given rect
                            updateClipping(
                                aClipRect,
                                rFactoryParms,
                                true );
                        }
                        else
                        {
                            // intersect current clip with given clip polygon

                            // #121806# explicitly kept integer
                            basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon());

                            aPolyPolygon.transform(rVDev.GetViewTransformation());
                            updateClipping(
                                aPolyPolygon,
                                rFactoryParms,
                                true );
                        }

                        break;
                    }

                    case MetaActionType::MOVECLIPREGION:
                        // TODO(F2): NYI
                        break;

                    case MetaActionType::LINECOLOR:
                        if( !rParms.maLineColor )
                        {
                            setStateColor( static_cast<MetaLineColorAction*>(pCurrAct),
                                           rStates.getState().isLineColorSet,
                                           rStates.getState().lineColor,
                                           rCanvas );
                        }
                        else
                        {
                            // #120994# Do switch on/off LineColor, even when an overriding one is set
                            bool bSetting(static_cast<MetaLineColorAction*>(pCurrAct)->IsSetting());

                            rStates.getState().isLineColorSet = bSetting;
                        }
                        break;

                    case MetaActionType::FILLCOLOR:
                        if( !rParms.maFillColor )
                        {
                            setStateColor( static_cast<MetaFillColorAction*>(pCurrAct),
                                           rStates.getState().isFillColorSet,
                                           rStates.getState().fillColor,
                                           rCanvas );
                        }
                        else
                        {
                            // #120994# Do switch on/off FillColor, even when an overriding one is set
                            bool bSetting(static_cast<MetaFillColorAction*>(pCurrAct)->IsSetting());

                            rStates.getState().isFillColorSet = bSetting;
                        }
                        break;

                    case MetaActionType::TEXTCOLOR:
                    {
                        if( !rParms.maTextColor )
                        {
                            // Text color is set unconditionally, thus, no
                            // use of setStateColor here
                            ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() );

                            // force alpha part of color to
                            // opaque. transparent painting is done
                            // explicitly via MetaActionType::Transparent
                            aColor.SetAlpha(255);

                            rStates.getState().textColor =
                                vcl::unotools::colorToDoubleSequence(
                                    aColor,
                                    rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
                        }
                    }
                    break;

                    case MetaActionType::TEXTFILLCOLOR:
                        if( !rParms.maTextColor )
                        {
                            setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct),
                                           rStates.getState().isTextFillColorSet,
                                           rStates.getState().textFillColor,
                                           rCanvas );
                        }
                        else
                        {
                            // #120994# Do switch on/off TextFillColor, even when an overriding one is set
                            bool bSetting(static_cast<MetaTextFillColorAction*>(pCurrAct)->IsSetting());

                            rStates.getState().isTextFillColorSet = bSetting;
                        }
                        break;

                    case MetaActionType::TEXTLINECOLOR:
                        if( !rParms.maTextColor )
                        {
                            setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct),
                                           rStates.getState().isTextLineColorSet,
                                           rStates.getState().textLineColor,
                                           rCanvas );
                        }
                        else
                        {
                            // #120994# Do switch on/off TextLineColor, even when an overriding one is set
                            bool bSetting(static_cast<MetaTextLineColorAction*>(pCurrAct)->IsSetting());

                            rStates.getState().isTextLineColorSet = bSetting;
                        }
                        break;

                    case MetaActionType::OVERLINECOLOR:
                        if( !rParms.maTextColor )
                        {
                            setStateColor( static_cast<MetaOverlineColorAction*>(pCurrAct),
                                           rStates.getState().isTextOverlineColorSet,
                                           rStates.getState().textOverlineColor,
                                           rCanvas );
                        }
                        else
                        {
                            bool bSetting(static_cast<MetaOverlineColorAction*>(pCurrAct)->IsSetting());

                            rStates.getState().isTextOverlineColorSet = bSetting;
                        }
                        break;

                    case MetaActionType::TEXTALIGN:
                    {
                        ::cppcanvas::internal::OutDevState& rState = rStates.getState();
                        const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() );

                        rState.textReferencePoint = eTextAlign;
                    }
                    break;

                    case MetaActionType::FONT:
                    {
                        ::cppcanvas::internal::OutDevState& rState = rStates.getState();
                        const vcl::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() );

                        rState.xFont = createFont( rState.fontRotation,
                                                   rFont,
--> --------------------

--> maximum size reached

--> --------------------

95%


¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet)  ¤

*© 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 ist noch experimentell.