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

Quelle  wmfemfhelper.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 <wmfemfhelper.hxx>
#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
#include <vcl/lineinfo.hxx>
#include <vcl/metaact.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/utils/gradienttools.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
#include <vcl/BitmapPalette.hxx>
#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygonclipper.hxx>
#include <drawinglayer/primitive2d/invertprimitive2d.hxx>
#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
#include <primitive2d/wallpaperprimitive2d.hxx>
#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
#include <primitive2d/textlineprimitive2d.hxx>
#include <primitive2d/textstrikeoutprimitive2d.hxx>
#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
#include <sal/log.hxx>
#include <tools/fract.hxx>
#include <tools/stream.hxx>
#include <tools/UnitConversion.hxx>
#include <vcl/canvastools.hxx>
#include <vcl/gradient.hxx>
#include <vcl/hatch.hxx>
#include <vcl/outdev.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <emfplushelper.hxx>
#include <numeric>
#include <toolkit/helper/vclunohelper.hxx>

namespace drawinglayer::primitive2d
{
        namespace {

        /** NonOverlappingFillGradientPrimitive2D class

        This is a special version of the FillGradientPrimitive2D which decomposes
        to a non-overlapping geometry version of the gradient. This needs to be
        used to support the old XOR paint-'trick'.

        It does not need an own identifier since a renderer who wants to interpret
        it itself may do so. It just overrides the decomposition of the C++
        implementation class to do an alternative decomposition.
        */

        class NonOverlappingFillGradientPrimitive2D : public FillGradientPrimitive2D
        {
        protected:
            /// local decomposition.
            virtual Primitive2DReference create2DDecomposition(
                const geometry::ViewInformation2D& rViewInformation) const override;

        public:
            /// constructor
            NonOverlappingFillGradientPrimitive2D(
                const basegfx::B2DRange& rObjectRange,
                const attribute::FillGradientAttribute& rFillGradient)
                : FillGradientPrimitive2D(rObjectRange, rFillGradient)
            {
            }
        };

        }

        Primitive2DReference NonOverlappingFillGradientPrimitive2D::create2DDecomposition(
            const geometry::ViewInformation2D& /*rViewInformation*/) const
        {
            if (!getFillGradient().isDefault())
            {
                return createFill(false);
            }
            return nullptr;
        }

// end of namespace

namespace wmfemfhelper
{
    /** helper class for graphic context

        This class allows to hold a complete representation of classic
        VCL OutputDevice state. This data is needed for correct
        interpretation of the MetaFile action flow.
    */

    PropertyHolder::PropertyHolder()
    :   maMapUnit(MapUnit::Map100thMM),
        maTextColor(sal_uInt32(COL_BLACK)),
        maRasterOp(RasterOp::OverPaint),
        mnLayoutMode(vcl::text::ComplexTextLayoutFlags::Default),
        maLanguageType(0),
        mnPushFlags(vcl::PushFlags::NONE),
        mbLineColor(false),
        mbFillColor(false),
        mbTextColor(true),
        mbTextFillColor(false),
        mbTextLineColor(false),
        mbOverlineColor(false),
        mbClipPolyPolygonActive(false)
    {
    }
}

namespace wmfemfhelper
{
    /** stack for properties

        This class builds a stack based on the PropertyHolder
        class. It encapsulates the pointer/new/delete usage to
        make it safe and implements the push/pop as needed by a
        VCL Metafile interpreter. The critical part here are the
        flag values VCL OutputDevice uses here; not all stuff is
        pushed and thus needs to be copied at pop.
    */

    PropertyHolders::PropertyHolders()
    {
        maPropertyHolders.push_back(new PropertyHolder());
    }

    void PropertyHolders::PushDefault()
    {
        PropertyHolder* pNew = new PropertyHolder();
        maPropertyHolders.push_back(pNew);
    }

    void PropertyHolders::Push(vcl::PushFlags nPushFlags)
    {
        if (bool(nPushFlags))
        {
            OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: PUSH with no property holders (!)");
            if (!maPropertyHolders.empty())
            {
                PropertyHolder* pNew = new PropertyHolder(*maPropertyHolders.back());
                pNew->setPushFlags(nPushFlags);
                maPropertyHolders.push_back(pNew);
            }
        }
    }

    void PropertyHolders::Pop()
    {
        OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: POP with no property holders (!)");
        const sal_uInt32 nSize(maPropertyHolders.size());

        if (!nSize)
            return;

        const PropertyHolder* pTip = maPropertyHolders.back();
        const vcl::PushFlags nPushFlags(pTip->getPushFlags());

        if (nPushFlags != vcl::PushFlags::NONE)
        {
            if (nSize > 1)
            {
                // copy back content for all non-set flags
                PropertyHolder* pLast = maPropertyHolders[nSize - 2];

                if (vcl::PushFlags::ALL != nPushFlags)
                {
                    if (!(nPushFlags & vcl::PushFlags::LINECOLOR))
                    {
                        pLast->setLineColor(pTip->getLineColor());
                        pLast->setLineColorActive(pTip->getLineColorActive());
                    }
                    if (!(nPushFlags & vcl::PushFlags::FILLCOLOR))
                    {
                        pLast->setFillColor(pTip->getFillColor());
                        pLast->setFillColorActive(pTip->getFillColorActive());
                    }
                    if (!(nPushFlags & vcl::PushFlags::FONT))
                    {
                        pLast->setFont(pTip->getFont());
                    }
                    if (!(nPushFlags & vcl::PushFlags::TEXTCOLOR))
                    {
                        pLast->setTextColor(pTip->getTextColor());
                        pLast->setTextColorActive(pTip->getTextColorActive());
                    }
                    if (!(nPushFlags & vcl::PushFlags::MAPMODE))
                    {
                        pLast->setTransformation(pTip->getTransformation());
                        pLast->setMapUnit(pTip->getMapUnit());
                    }
                    if (!(nPushFlags & vcl::PushFlags::CLIPREGION))
                    {
                        pLast->setClipPolyPolygon(pTip->getClipPolyPolygon());
                        pLast->setClipPolyPolygonActive(pTip->getClipPolyPolygonActive());
                    }
                    if (!(nPushFlags & vcl::PushFlags::RASTEROP))
                    {
                        pLast->setRasterOp(pTip->getRasterOp());
                    }
                    if (!(nPushFlags & vcl::PushFlags::TEXTFILLCOLOR))
                    {
                        pLast->setTextFillColor(pTip->getTextFillColor());
                        pLast->setTextFillColorActive(pTip->getTextFillColorActive());
                    }
                    if (!(nPushFlags & vcl::PushFlags::TEXTALIGN))
                    {
                        if (pLast->getFont().GetAlignment() != pTip->getFont().GetAlignment())
                        {
                            vcl::Font aFont(pLast->getFont());
                            aFont.SetAlignment(pTip->getFont().GetAlignment());
                            pLast->setFont(aFont);
                        }
                    }
                    if (!(nPushFlags & vcl::PushFlags::REFPOINT))
                    {
                        // not supported
                    }
                    if (!(nPushFlags & vcl::PushFlags::TEXTLINECOLOR))
                    {
                        pLast->setTextLineColor(pTip->getTextLineColor());
                        pLast->setTextLineColorActive(pTip->getTextLineColorActive());
                    }
                    if (!(nPushFlags & vcl::PushFlags::TEXTLAYOUTMODE))
                    {
                        pLast->setLayoutMode(pTip->getLayoutMode());
                    }
                    if (!(nPushFlags & vcl::PushFlags::TEXTLANGUAGE))
                    {
                        pLast->setLanguageType(pTip->getLanguageType());
                    }
                    if (!(nPushFlags & vcl::PushFlags::OVERLINECOLOR))
                    {
                        pLast->setOverlineColor(pTip->getOverlineColor());
                        pLast->setOverlineColorActive(pTip->getOverlineColorActive());
                    }
                }
            }
        }

        // execute the pop
        delete maPropertyHolders.back();
        maPropertyHolders.pop_back();
    }

    PropertyHolder& PropertyHolders::Current()
    {
        static PropertyHolder aDummy;
        OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: CURRENT with no property holders (!)");
        return maPropertyHolders.empty() ? aDummy : *maPropertyHolders.back();
    }

    PropertyHolders::~PropertyHolders()
    {
        while (!maPropertyHolders.empty())
        {
            delete maPropertyHolders.back();
            maPropertyHolders.pop_back();
        }
    }
}

namespace
{
    /** helper to convert a vcl::Region to a B2DPolyPolygon
        when it does not yet contain one. In the future
        this may be expanded to merge the polygons created
        from rectangles or use a special algo to directly turn
        the spans of regions to a single, already merged
        PolyPolygon.
     */

    basegfx::B2DPolyPolygon getB2DPolyPolygonFromRegion(const vcl::Region& rRegion)
    {
        basegfx::B2DPolyPolygon aRetval;

        if (!rRegion.IsEmpty())
        {
            aRetval = rRegion.GetAsB2DPolyPolygon();
        }

        return aRetval;
    }
}

namespace wmfemfhelper
{
    /** Helper class to buffer and hold a Primitive target vector. It
        encapsulates the new/delete functionality and allows to work
        on pointers of the implementation classes. All data will
        be converted to uno sequences of uno references when accessing the
        data.
    */

    TargetHolder::TargetHolder()
    {
    }

    TargetHolder::~TargetHolder()
    {
    }

    sal_uInt32 TargetHolder::size() const
    {
        return aTargets.size();
    }

    void TargetHolder::append(drawinglayer::primitive2d::BasePrimitive2D* pCandidate)
    {
        if (pCandidate)
        {
            aTargets.push_back(pCandidate);
        }
    }

    drawinglayer::primitive2d::Primitive2DContainer TargetHolder::getPrimitive2DSequence(const PropertyHolder& rPropertyHolder)
    {
        drawinglayer::primitive2d::Primitive2DContainer xRetval = std::move(aTargets);

        if (!xRetval.empty() && rPropertyHolder.getClipPolyPolygonActive())
        {
            const basegfx::B2DPolyPolygon& rClipPolyPolygon = rPropertyHolder.getClipPolyPolygon();

            if (rClipPolyPolygon.count())
            {
                xRetval = drawinglayer::primitive2d::Primitive2DContainer{
                        new drawinglayer::primitive2d::MaskPrimitive2D(
                            rClipPolyPolygon,
                            std::move(xRetval))
                };
            }
        }

        return xRetval;
    }
}

namespace wmfemfhelper
{
    /** Helper class which builds a stack on the TargetHolder class */
    TargetHolders::TargetHolders()
    {
        maTargetHolders.push_back(new TargetHolder());
    }

    sal_uInt32 TargetHolders::size() const
    {
        return maTargetHolders.size();
    }

    void TargetHolders::Push()
    {
        maTargetHolders.push_back(new TargetHolder());
    }

    void TargetHolders::Pop()
    {
        OSL_ENSURE(maTargetHolders.size(), "TargetHolders: POP with no property holders (!)");
        if (!maTargetHolders.empty())
        {
            delete maTargetHolders.back();
            maTargetHolders.pop_back();
        }
    }

    TargetHolder& TargetHolders::Current()
    {
        static TargetHolder aDummy;
        OSL_ENSURE(maTargetHolders.size(), "TargetHolders: CURRENT with no property holders (!)");
        return maTargetHolders.empty() ? aDummy : *maTargetHolders.back();
    }

    TargetHolders::~TargetHolders()
    {
        while (!maTargetHolders.empty())
        {
            delete maTargetHolders.back();
            maTargetHolders.pop_back();
        }
    }
}

namespace
{
    /** helper to convert a MapMode to a transformation */
    basegfx::B2DHomMatrix getTransformFromMapMode(const MapMode& rMapMode)
    {
        basegfx::B2DHomMatrix aMapping;
        const Fraction aNoScale(1, 1);
        const Point& rOrigin(rMapMode.GetOrigin());

        if(0 != rOrigin.X() || 0 != rOrigin.Y())
        {
            aMapping.translate(rOrigin.X(), rOrigin.Y());
        }

        if(rMapMode.GetScaleX() != aNoScale || rMapMode.GetScaleY() != aNoScale)
        {
            aMapping.scale(
                double(rMapMode.GetScaleX()),
                double(rMapMode.GetScaleY()));
        }

        return aMapping;
    }
}

namespace wmfemfhelper
{
    /** helper to create a PointArrayPrimitive2D based on current context */
    static void createPointArrayPrimitive(
        std::vector< basegfx::B2DPoint >&& rPositions,
        TargetHolder& rTarget,
        PropertyHolder const & rProperties,
        const basegfx::BColor& rBColor)
    {
        if(rPositions.empty())
            return;

        if(rProperties.getTransformation().isIdentity())
        {
            rTarget.append(
                new drawinglayer::primitive2d::PointArrayPrimitive2D(
                    std::move(rPositions),
                    rBColor));
        }
        else
        {
            for(basegfx::B2DPoint & aPosition : rPositions)
            {
                aPosition = rProperties.getTransformation() * aPosition;
            }

            rTarget.append(
                new drawinglayer::primitive2d::PointArrayPrimitive2D(
                    std::move(rPositions),
                    rBColor));
        }
    }

    /** helper to create a PolygonHairlinePrimitive2D based on current context */
    static void createHairlinePrimitive(
        const basegfx::B2DPolygon& rLinePolygon,
        TargetHolder& rTarget,
        PropertyHolder const & rProperties)
    {
        if(rLinePolygon.count())
        {
            basegfx::B2DPolygon aLinePolygon(rLinePolygon);
            aLinePolygon.transform(rProperties.getTransformation());
            rTarget.append(
                new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
                    std::move(aLinePolygon),
                    rProperties.getLineColor()));
        }
    }

    /** helper to create a PolyPolygonColorPrimitive2D based on current context */
    static void createFillPrimitive(
        const basegfx::B2DPolyPolygon& rFillPolyPolygon,
        TargetHolder& rTarget,
        PropertyHolder const & rProperties)
    {
        if(rFillPolyPolygon.count())
        {
            basegfx::B2DPolyPolygon aFillPolyPolygon(rFillPolyPolygon);
            aFillPolyPolygon.transform(rProperties.getTransformation());
            rTarget.append(
                new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
                    std::move(aFillPolyPolygon),
                    rProperties.getFillColor()));
        }
    }

    /** helper to create a PolygonStrokePrimitive2D based on current context */
    static void createLinePrimitive(
        const basegfx::B2DPolygon& rLinePolygon,
        const LineInfo& rLineInfo,
        TargetHolder& rTarget,
        PropertyHolder const & rProperties)
    {
        if(!rLinePolygon.count())
            return;

        const bool bDashDotUsed(LineStyle::Dash == rLineInfo.GetStyle());
        const bool bWidthUsed(rLineInfo.GetWidth() > 1);

        if(bDashDotUsed || bWidthUsed)
        {
            basegfx::B2DPolygon aLinePolygon(rLinePolygon);
            aLinePolygon.transform(rProperties.getTransformation());
            drawinglayer::attribute::LineAttribute aLineAttribute(
                rProperties.getLineColor(),
                bWidthUsed ? rLineInfo.GetWidth() : 0.0,
                rLineInfo.GetLineJoin(),
                rLineInfo.GetLineCap());

            if(bDashDotUsed)
            {
                std::vector< double > fDotDashArray = rLineInfo.GetDotDashArray();
                const double fAccumulated(std::accumulate(fDotDashArray.begin(), fDotDashArray.end(), 0.0));
                drawinglayer::attribute::StrokeAttribute aStrokeAttribute(
                    std::move(fDotDashArray),
                    fAccumulated);

                rTarget.append(
                    new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
                        std::move(aLinePolygon),
                        std::move(aLineAttribute),
                        std::move(aStrokeAttribute)));
            }
            else
            {
                rTarget.append(
                    new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
                        std::move(aLinePolygon),
                        aLineAttribute));
            }
        }
        else
        {
            createHairlinePrimitive(rLinePolygon, rTarget, rProperties);
        }
    }

    /** helper to create needed line and fill primitives based on current context */
    static void createHairlineAndFillPrimitive(
        const basegfx::B2DPolygon& rPolygon,
        TargetHolder& rTarget,
        PropertyHolder const & rProperties)
    {
        if(rProperties.getFillColorActive())
        {
            createFillPrimitive(basegfx::B2DPolyPolygon(rPolygon), rTarget, rProperties);
        }

        if(rProperties.getLineColorActive())
        {
            createHairlinePrimitive(rPolygon, rTarget, rProperties);
        }
    }

    /** helper to create needed line and fill primitives based on current context */
    static void createHairlineAndFillPrimitive(
        const basegfx::B2DPolyPolygon& rPolyPolygon,
        TargetHolder& rTarget,
        PropertyHolder const & rProperties)
    {
        if(rProperties.getFillColorActive())
        {
            createFillPrimitive(rPolyPolygon, rTarget, rProperties);
        }

        if(rProperties.getLineColorActive())
        {
            for(sal_uInt32 a(0); a < rPolyPolygon.count(); a++)
            {
                createHairlinePrimitive(rPolyPolygon.getB2DPolygon(a), rTarget, rProperties);
            }
        }
    }

    /** helper to create DiscreteBitmapPrimitive2D based on current context.
        The DiscreteBitmapPrimitive2D is especially created for this usage
        since no other usage defines a bitmap visualisation based on top-left
        position and size in pixels. At the end it will create a view-dependent
        transformed embedding of a BitmapPrimitive2D.
    */

    static void createBitmapExPrimitive(
        const BitmapEx& rBitmapEx,
        const Point& rPoint,
        TargetHolder& rTarget,
        PropertyHolder const & rProperties)
    {
        if(!rBitmapEx.IsEmpty())
        {
            basegfx::B2DPoint aPoint(rPoint.X(), rPoint.Y());
            aPoint = rProperties.getTransformation() * aPoint;

            rTarget.append(
                new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D(
                    rBitmapEx,
                    aPoint));
        }
    }

    /** helper to create BitmapPrimitive2D based on current context */
    static void createBitmapExPrimitive(
        const BitmapEx& rBitmapEx,
        const Point& rPoint,
        const Size& rSize,
        TargetHolder& rTarget,
        PropertyHolder const & rProperties)
    {
        if(rBitmapEx.IsEmpty())
            return;

        basegfx::B2DHomMatrix aObjectTransform;

        aObjectTransform.set(0, 0, rSize.Width());
        aObjectTransform.set(1, 1, rSize.Height());
        aObjectTransform.set(0, 2, rPoint.X());
        aObjectTransform.set(1, 2, rPoint.Y());

        aObjectTransform = rProperties.getTransformation() * aObjectTransform;

        rTarget.append(
            new drawinglayer::primitive2d::BitmapPrimitive2D(
                rBitmapEx,
                aObjectTransform));
    }

    /** helper to create a regular BotmapEx from a MaskAction (definitions
        which use a bitmap without transparence but define one of the colors as
        transparent)
     */

    static 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);
    }

    /** helper to convert from a VCL Gradient definition to the corresponding
        data for primitive representation
     */

    static drawinglayer::attribute::FillGradientAttribute createFillGradientAttribute(const Gradient& rGradient)
    {
        const Color aStartColor(rGradient.GetStartColor());
        const sal_uInt16 nStartIntens(rGradient.GetStartIntensity());
        basegfx::BColor aStart(aStartColor.getBColor());

        if(nStartIntens != 100)
        {
            const basegfx::BColor aBlack;
            aStart = interpolate(aBlack, aStart, static_cast<double>(nStartIntens) * 0.01);
        }

        const Color aEndColor(rGradient.GetEndColor());
        const sal_uInt16 nEndIntens(rGradient.GetEndIntensity());
        basegfx::BColor aEnd(aEndColor.getBColor());

        if(nEndIntens != 100)
        {
            const basegfx::BColor aBlack;
            aEnd = interpolate(aBlack, aEnd, static_cast<double>(nEndIntens) * 0.01);
        }

        return drawinglayer::attribute::FillGradientAttribute(
            rGradient.GetStyle(),
            static_cast<double>(rGradient.GetBorder()) * 0.01,
            static_cast<double>(rGradient.GetOfsX()) * 0.01,
            static_cast<double>(rGradient.GetOfsY()) * 0.01,
            toRadians(rGradient.GetAngle()),
            basegfx::BColorStops(aStart, aEnd),
            rGradient.GetSteps());
    }

    /** helper to convert from a VCL Hatch definition to the corresponding
        data for primitive representation
     */

    static drawinglayer::attribute::FillHatchAttribute createFillHatchAttribute(const Hatch& rHatch)
    {
        drawinglayer::attribute::HatchStyle aHatchStyle(drawinglayer::attribute::HatchStyle::Single);

        switch(rHatch.GetStyle())
        {
            default : // case HatchStyle::Single :
            {
                aHatchStyle = drawinglayer::attribute::HatchStyle::Single;
                break;
            }
            case HatchStyle::Double :
            {
                aHatchStyle = drawinglayer::attribute::HatchStyle::Double;
                break;
            }
            case HatchStyle::Triple :
            {
                aHatchStyle = drawinglayer::attribute::HatchStyle::Triple;
                break;
            }
        }

        return drawinglayer::attribute::FillHatchAttribute(
            aHatchStyle,
            static_cast<double>(rHatch.GetDistance()),
            toRadians(rHatch.GetAngle()),
            rHatch.GetColor().getBColor(),
            3, // same default as VCL, a minimum of three discrete units (pixels) offset
            false);
    }

    /** helper to take needed action on ClipRegion change. This method needs to be called
        on any vcl::Region change, e.g. at the obvious actions doing this, but also at pop-calls
        which change the vcl::Region of the current context. It takes care of creating the
        current embedded context, set the new vcl::Region at the context and possibly prepare
        a new target for including new geometry into the current region
     */

    void HandleNewClipRegion(
        const basegfx::B2DPolyPolygon& rClipPolyPolygon,
        TargetHolders& rTargetHolders,
        PropertyHolders& rPropertyHolders)
    {
        const bool bNewActive(rClipPolyPolygon.count());

        // #i108636# The handling of new ClipPolyPolygons was not done as good as possible
        // in the first version of this interpreter; e.g. when a ClipPolyPolygon was set
        // initially and then using a lot of push/pop actions, the pop always leads
        // to setting a 'new' ClipPolyPolygon which indeed is the return to the ClipPolyPolygon
        // of the properties next on the stack.

        // This ClipPolyPolygon is identical to the current one, so there is no need to
        // create a MaskPrimitive2D containing the up-to-now created primitives, but
        // this was done before. While this does not lead to wrong primitive
        // representations of the metafile data, it creates unnecessarily expensive
        // representations. Just detecting when no really 'new' ClipPolyPolygon gets set
        // solves the problem.

        if(!rPropertyHolders.Current().getClipPolyPolygonActive() && !bNewActive)
        {
            // no active ClipPolyPolygon exchanged by no new one, done
            return;
        }

        if(rPropertyHolders.Current().getClipPolyPolygonActive() && bNewActive)
        {
            // active ClipPolyPolygon and new active ClipPolyPolygon
            if(rPropertyHolders.Current().getClipPolyPolygon() == rClipPolyPolygon)
            {
                // new is the same as old, done
                return;
            }
        }

        // Here the old and the new are definitively different, maybe
        // old one and/or new one is not active.

        // Handle deletion of old ClipPolyPolygon. The process evtl. created primitives which
        // belong to this active ClipPolyPolygon. These need to be embedded to a
        // MaskPrimitive2D accordingly.
        if(rPropertyHolders.Current().getClipPolyPolygonActive() && rTargetHolders.size() > 1)
        {
            drawinglayer::primitive2d::Primitive2DContainer aSubContent;

            if(rPropertyHolders.Current().getClipPolyPolygon().count()
                && rTargetHolders.Current().size())
            {
                aSubContent = rTargetHolders.Current().getPrimitive2DSequence(
                    rPropertyHolders.Current());
            }

            rTargetHolders.Pop();

            if(!aSubContent.empty())
            {
                rTargetHolders.Current().append(
                    new drawinglayer::primitive2d::GroupPrimitive2D(
                        std::move(aSubContent)));
            }
        }

        // apply new settings to current properties by setting
        // the new region now
        rPropertyHolders.Current().setClipPolyPolygonActive(bNewActive);

        if(bNewActive)
        {
            rPropertyHolders.Current().setClipPolyPolygon(rClipPolyPolygon);

            // prepare new content holder for new active region
            rTargetHolders.Push();
        }
    }

    /** helper to handle the change of RasterOp. It takes care of encapsulating all current
        geometry to the current RasterOp (if changed) and needs to be called on any RasterOp
        change. It will also start a new geometry target to embrace to the new RasterOp if
        a changing RasterOp is used. Currently, RasterOp::Xor and RasterOp::Invert are supported using
        InvertPrimitive2D, and RasterOp::N0 by using a ModifiedColorPrimitive2D to force to black paint
     */

    static void HandleNewRasterOp(
        RasterOp aRasterOp,
        TargetHolders& rTargetHolders,
        PropertyHolders& rPropertyHolders)
    {
        // check if currently active
        if(rPropertyHolders.Current().isRasterOpActive() && rTargetHolders.size() > 1)
        {
            drawinglayer::primitive2d::Primitive2DContainer aSubContent;

            if(rTargetHolders.Current().size())
            {
                aSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
            }

            rTargetHolders.Pop();

            if(!aSubContent.empty())
            {
                if(rPropertyHolders.Current().isRasterOpForceBlack())
                {
                    // force content to black
                    rTargetHolders.Current().append(
                        new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
                            std::move(aSubContent),
                            std::make_shared<basegfx::BColorModifier_replace>(
                                    basegfx::BColor(0.0, 0.0, 0.0))));
                }
                else // if(rPropertyHolders.Current().isRasterOpInvert())
                {
                    // invert content
                    rTargetHolders.Current().append(
                        new drawinglayer::primitive2d::InvertPrimitive2D(
                            std::move(aSubContent)));
                }
            }
        }

        // apply new settings
        rPropertyHolders.Current().setRasterOp(aRasterOp);

        // check if now active
        if(rPropertyHolders.Current().isRasterOpActive())
        {
            // prepare new content holder for new invert
            rTargetHolders.Push();
        }
    }

    /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
        It is a quite mighty action. This helper is for simple color filled background.
     */

    static rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> CreateColorWallpaper(
        const basegfx::B2DRange& rRange,
        const basegfx::BColor& rColor,
        PropertyHolder const & rPropertyHolder)
    {
        basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(rRange));
        aOutline.transform(rPropertyHolder.getTransformation());

        return new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
            basegfx::B2DPolyPolygon(aOutline),
            rColor);
    }

    /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
        It is a quite mighty action. This helper is for gradient filled background.
     */

    static rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> CreateGradientWallpaper(
        const basegfx::B2DRange& rRange,
        const Gradient& rGradient,
        PropertyHolder const & rPropertyHolder)
    {
        drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
        basegfx::BColor aSingleColor;

        if (aAttribute.getColorStops().isSingleColor(aSingleColor))
        {
            // not really a gradient. Create filled rectangle
            return CreateColorWallpaper(rRange, aSingleColor, rPropertyHolder);
        }
        else
        {
            // really a gradient
            rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pRetval(
                new drawinglayer::primitive2d::FillGradientPrimitive2D(
                    rRange,
                    std::move(aAttribute)));

            if(!rPropertyHolder.getTransformation().isIdentity())
            {
                const drawinglayer::primitive2d::Primitive2DReference xPrim(pRetval);
                drawinglayer::primitive2d::Primitive2DContainer xSeq { xPrim };

                pRetval = new drawinglayer::primitive2d::TransformPrimitive2D(
                    rPropertyHolder.getTransformation(),
                    std::move(xSeq));
            }

            return pRetval;
        }
    }

    /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
        It is a quite mighty action. This helper decides if color and/or gradient
        background is needed for the wanted bitmap fill and then creates the needed
        WallpaperBitmapPrimitive2D. This primitive was created for this purpose and
        takes over all needed logic of orientations and tiling.
     */

    static void CreateAndAppendBitmapWallpaper(
        basegfx::B2DRange aWallpaperRange,
        const Wallpaper& rWallpaper,
        TargetHolder& rTarget,
        PropertyHolder const & rProperty)
    {
        const BitmapEx& aBitmapEx(rWallpaper.GetBitmap());
        const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle());

        // if bitmap visualisation is transparent, maybe background
        // needs to be filled. Create background
        if(aBitmapEx.IsAlpha()
            || (WallpaperStyle::Tile != eWallpaperStyle && WallpaperStyle::Scale != eWallpaperStyle))
        {
            if(rWallpaper.IsGradient())
            {
                rTarget.append(
                    CreateGradientWallpaper(
                        aWallpaperRange,
                        rWallpaper.GetGradient(),
                        rProperty));
            }
            else if(!rWallpaper.GetColor().IsTransparent())
            {
                rTarget.append(
                    CreateColorWallpaper(
                        aWallpaperRange,
                        rWallpaper.GetColor().getBColor(),
                        rProperty));
            }
        }

        // use wallpaper rect if set
        if(rWallpaper.IsRect() && !rWallpaper.GetRect().IsEmpty())
        {
            aWallpaperRange = vcl::unotools::b2DRectangleFromRectangle(rWallpaper.GetRect());
        }

        rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBitmapWallpaperFill =
            new drawinglayer::primitive2d::WallpaperBitmapPrimitive2D(
                aWallpaperRange,
                aBitmapEx,
                eWallpaperStyle);

        if(rProperty.getTransformation().isIdentity())
        {
            // add directly
            rTarget.append(pBitmapWallpaperFill);
        }
        else
        {
            // when a transformation is set, embed to it
            rTarget.append(
                new drawinglayer::primitive2d::TransformPrimitive2D(
                    rProperty.getTransformation(),
                    drawinglayer::primitive2d::Primitive2DContainer { pBitmapWallpaperFill }));
        }
    }

    /** helper to decide UnderlineAbove for text primitives */
    static bool isUnderlineAbove(const vcl::Font& rFont)
    {
        if(!rFont.IsVertical())
        {
            return false;
        }

        // the underline is right for Japanese only
        return (LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage());
    }

    static void createFontAttributeTransformAndAlignment(
        drawinglayer::attribute::FontAttribute& rFontAttribute,
        basegfx::B2DHomMatrix& rTextTransform,
        basegfx::B2DVector& rAlignmentOffset,
        PropertyHolder const & rProperty)
    {
        const vcl::Font& rFont = rProperty.getFont();
        basegfx::B2DVector aFontScaling;

        rFontAttribute = drawinglayer::primitive2d::getFontAttributeFromVclFont(
                            aFontScaling,
                            rFont,
                            bool(rProperty.getLayoutMode() & vcl::text::ComplexTextLayoutFlags::BiDiRtl),
                            bool(rProperty.getLayoutMode() & vcl::text::ComplexTextLayoutFlags::BiDiStrong));

        // add FontScaling
        rTextTransform.scale(aFontScaling.getX(), aFontScaling.getY());

        // take text align into account
        if(ALIGN_BASELINE != rFont.GetAlignment())
        {
            drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
            aTextLayouterDevice.setFont(rFont);

            if(ALIGN_TOP == rFont.GetAlignment())
            {
                rAlignmentOffset.setY(aTextLayouterDevice.getFontAscent());
            }
            else // ALIGN_BOTTOM
            {
                rAlignmentOffset.setY(-aTextLayouterDevice.getFontDescent());
            }

            rTextTransform.translate(rAlignmentOffset.getX(), rAlignmentOffset.getY());
        }

        // add FontRotation (if used)
        if(rFont.GetOrientation())
        {
            rTextTransform.rotate(-toRadians(rFont.GetOrientation()));
        }
    }

    /** helper which takes complete care for creating the needed text primitives. It
        takes care of decorated stuff and all the geometry adaptations needed
     */

    static void processMetaTextAction(
        const Point& rTextStartPosition,
        const OUString& rText,
        sal_uInt16 nTextStart,
        sal_uInt16 nTextLength,
        std::vector< double >&& rDXArray,
        std::vector< sal_Bool >&& rKashidaArray,
        TargetHolder& rTarget,
        PropertyHolder const & rProperty)
    {
        rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pResult;
        const vcl::Font& rFont = rProperty.getFont();
        basegfx::B2DVector aAlignmentOffset(0.0, 0.0);

        if(nTextLength)
        {
            drawinglayer::attribute::FontAttribute aFontAttribute;
            basegfx::B2DHomMatrix aTextTransform;

            // fill parameters derived from current font
            createFontAttributeTransformAndAlignment(
                aFontAttribute,
                aTextTransform,
                aAlignmentOffset,
                rProperty);

            // add TextStartPosition
            aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y());

            // prepare FontColor and Locale
            const basegfx::BColor aFontColor(rProperty.getTextColor());
            const Color aFillColor(rFont.GetFillColor());
            css::lang::Locale aLocale(LanguageTag(rProperty.getLanguageType()).getLocale());
            const bool bWordLineMode(rFont.IsWordLineMode());

            const bool bDecoratedIsNeeded(
                   LINESTYLE_NONE != rFont.GetOverline()
                || LINESTYLE_NONE != rFont.GetUnderline()
                || STRIKEOUT_NONE != rFont.GetStrikeout()
                || FontEmphasisMark::NONE != (rFont.GetEmphasisMark() & FontEmphasisMark::Style)
                || FontRelief::NONE != rFont.GetRelief()
                || rFont.IsShadow()
                || bWordLineMode);

            if(bDecoratedIsNeeded)
            {
                // prepare overline, underline and strikeout data
                const drawinglayer::primitive2d::TextLine eFontOverline(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rFont.GetOverline()));
                const drawinglayer::primitive2d::TextLine eFontLineStyle(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rFont.GetUnderline()));
                const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rFont.GetStrikeout()));

                // check UndelineAbove
                const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE != eFontLineStyle && isUnderlineAbove(rFont));

                // prepare emphasis mark data
                drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE);

                switch(rFont.GetEmphasisMark() & FontEmphasisMark::Style)
                {
                    case FontEmphasisMark::Dot : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT; break;
                    case FontEmphasisMark::Circle : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE; break;
                    case FontEmphasisMark::Disc : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC; break;
                    case FontEmphasisMark::Accent : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT; break;
                    defaultbreak;
                }

                const bool bEmphasisMarkAbove(rFont.GetEmphasisMark() & FontEmphasisMark::PosAbove);
                const bool bEmphasisMarkBelow(rFont.GetEmphasisMark() & FontEmphasisMark::PosBelow);

                // prepare font relief data
                drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);

                switch(rFont.GetRelief())
                {
                    case FontRelief::Embossed : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
                    case FontRelief::Engraved : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
                    default : break// RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
                }

                // prepare shadow/outline data
                const bool bShadow(rFont.IsShadow());

                // TextDecoratedPortionPrimitive2D is needed, create one
                pResult = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(

                    // attributes for TextSimplePortionPrimitive2D
                    aTextTransform,
                    rText,
                    nTextStart,
                    nTextLength,
                    std::vector(rDXArray),
                    std::move(rKashidaArray),
                    aFontAttribute,
                    aLocale,
                    aFontColor,
                    aFillColor,
                    rFont.GetFixKerning(),

                    // attributes for TextDecoratedPortionPrimitive2D
                    rProperty.getOverlineColorActive() ? rProperty.getOverlineColor() : aFontColor,
                    rProperty.getTextLineColorActive() ? rProperty.getTextLineColor() : aFontColor,
                    eFontOverline,
                    eFontLineStyle,
                    bUnderlineAbove,
                    eTextStrikeout,
                    bWordLineMode,
                    eTextEmphasisMark,
                    bEmphasisMarkAbove,
                    bEmphasisMarkBelow,
                    eTextRelief,
                    bShadow);
            }
            else
            {
                // TextSimplePortionPrimitive2D is enough
                pResult = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
                    aTextTransform,
                    rText,
                    nTextStart,
                    nTextLength,
                    std::vector(rDXArray),
                    std::move(rKashidaArray),
                    std::move(aFontAttribute),
                    std::move(aLocale),
                    aFontColor,
                    COL_TRANSPARENT,
                    rFont.GetFixKerning());
            }
        }

        if(pResult && rProperty.getTextFillColorActive())
        {
            // text background is requested, add and encapsulate both to new primitive
            drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
            aTextLayouterDevice.setFont(rFont);

            // get text width
            double fTextWidth(0.0);

            if (rDXArray.empty())
            {
                fTextWidth = aTextLayouterDevice.getTextWidth(rText, nTextStart, nTextLength);
            }
            else
            {
                fTextWidth = rDXArray.back();
            }

            if (fTextWidth > 0.0 && !basegfx::fTools::equalZero(fTextWidth))
            {
                // build text range
                const basegfx::B2DRange aTextRange(
                    0.0, -aTextLayouterDevice.getFontAscent(),
                    fTextWidth, aTextLayouterDevice.getFontDescent());

                // create Transform
                basegfx::B2DHomMatrix aTextTransform;

                aTextTransform.translate(aAlignmentOffset.getX(), aAlignmentOffset.getY());

                if(rFont.GetOrientation())
                {
                    aTextTransform.rotate(-toRadians(rFont.GetOrientation()));
                }

                aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y());

                // prepare Primitive2DSequence, put text in foreground
                drawinglayer::primitive2d::Primitive2DContainer aSequence(2);
                aSequence[1] = pResult;

                // prepare filled polygon
                basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aTextRange));
                aOutline.transform(aTextTransform);

                aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(
                    new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
                        basegfx::B2DPolyPolygon(aOutline),
                        rProperty.getTextFillColor()));

                // set as group at pResult
                pResult = new drawinglayer::primitive2d::GroupPrimitive2D(std::move(aSequence));
            }
        }

        if(!pResult)
            return;

        // add created text primitive to target
        if(rProperty.getTransformation().isIdentity())
        {
            rTarget.append(pResult);
        }
        else
        {
            // when a transformation is set, embed to it
            rTarget.append(
                new drawinglayer::primitive2d::TransformPrimitive2D(
                    rProperty.getTransformation(),
                    drawinglayer::primitive2d::Primitive2DContainer { pResult }));
        }
    }

    /** helper which takes complete care for creating the needed textLine primitives */
    static void processMetaTextLineAction(
        const MetaTextLineAction& rAction,
        TargetHolder& rTarget,
        PropertyHolder const & rProperty)
    {
        const double fLineWidth(fabs(static_cast<double>(rAction.GetWidth())));

        if(fLineWidth <= 0.0)
            return;

        const drawinglayer::primitive2d::TextLine aOverlineMode(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rAction.GetOverline()));
        const drawinglayer::primitive2d::TextLine aUnderlineMode(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rAction.GetUnderline()));
        const drawinglayer::primitive2d::TextStrikeout aTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rAction.GetStrikeout()));

        const bool bOverlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aOverlineMode);
        const bool bUnderlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aUnderlineMode);
        const bool bStrikeoutUsed(drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE != aTextStrikeout);

        if(!(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed))
            return;

        drawinglayer::primitive2d::Primitive2DContainer aTargets;
        basegfx::B2DVector aAlignmentOffset(0.0, 0.0);
        drawinglayer::attribute::FontAttribute aFontAttribute;
        basegfx::B2DHomMatrix aTextTransform;

        // fill parameters derived from current font
        createFontAttributeTransformAndAlignment(
            aFontAttribute,
            aTextTransform,
            aAlignmentOffset,
            rProperty);

        // add TextStartPosition
        aTextTransform.translate(rAction.GetStartPoint().X(), rAction.GetStartPoint().Y());

        // prepare TextLayouter (used in most cases)
        drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
        aTextLayouter.setFont(rProperty.getFont());

        if(bOverlineUsed)
        {
            // create primitive geometry for overline
            aTargets.push_back(
                new drawinglayer::primitive2d::TextLinePrimitive2D(
                    aTextTransform,
                    fLineWidth,
                    aTextLayouter.getOverlineOffset(),
                    aTextLayouter.getOverlineHeight(),
                    aOverlineMode,
                    rProperty.getOverlineColor()));
        }

        if(bUnderlineUsed)
        {
            // create primitive geometry for underline
            aTargets.push_back(
                new drawinglayer::primitive2d::TextLinePrimitive2D(
                    aTextTransform,
                    fLineWidth,
                    aTextLayouter.getUnderlineOffset(),
                    aTextLayouter.getUnderlineHeight(),
                    aUnderlineMode,
                    rProperty.getTextLineColor()));
        }

        if(bStrikeoutUsed)
        {
            // create primitive geometry for strikeout
            if(drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout
                || drawinglayer::primitive2d::TEXT_STRIKEOUT_X == aTextStrikeout)
            {
                // strikeout with character
                const sal_Unicode aStrikeoutChar(
                    drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout ? '/' : 'X');
                css::lang::Locale aLocale(LanguageTag(
                    rProperty.getLanguageType()).getLocale());

                aTargets.push_back(
                    new drawinglayer::primitive2d::TextCharacterStrikeoutPrimitive2D(
                        aTextTransform,
                        fLineWidth,
                        rProperty.getTextColor(),
                        aStrikeoutChar,
                        std::move(aFontAttribute),
                        std::move(aLocale)));
            }
            else
            {
                // strikeout with geometry
                aTargets.push_back(
                    new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D(
                        aTextTransform,
                        fLineWidth,
                        rProperty.getTextColor(),
                        aTextLayouter.getUnderlineHeight(),
                        aTextLayouter.getStrikeoutOffset(),
                        aTextStrikeout));
            }
        }

        if(aTargets.empty())
            return;

        // add created text primitive to target
        if(rProperty.getTransformation().isIdentity())
        {
            rTarget.append(std::move(aTargets));
        }
        else
        {
            // when a transformation is set, embed to it
            rTarget.append(
                new drawinglayer::primitive2d::TransformPrimitive2D(
                    rProperty.getTransformation(),
                    std::move(aTargets)));
        }
    }

    /** This is the main interpreter method. It is designed to handle the given Metafile
        completely inside the given context and target. It may use and modify the context and
        target. This design allows to call itself recursively which adapted contexts and
        targets as e.g. needed for the MetaActionType::FLOATTRANSPARENT where the content is expressed
        as a metafile as sub-content.

        This interpreter is as free of VCL functionality as possible. It uses VCL data classes
        (else reading the data would not be possible), but e.g. does NOT use a local OutputDevice
        as most other MetaFile interpreters/exporters do to hold and work with the current context.
        This is necessary to be able to get away from the strong internal VCL-binding.

        It tries to combine e.g. pixel and/or point actions and to stitch together single line primitives
        where possible (which is not trivial with the possible line geometry definitions).

        It tries to handle clipping no longer as Regions and spans of Rectangles, but as PolyPolygon
        ClipRegions with (where possible) high precision by using the best possible data quality
        from the Region. The vcl::Region is unavoidable as data container, but nowadays allows the transport
        of Polygon-based clip regions. Where this is not used, a Polygon is constructed from the
        vcl::Region ranges. All primitive clipping uses the MaskPrimitive2D with Polygon-based clipping.

        I have marked the single MetaActions with:

        SIMPLE, DONE:
        Simple, e.g nothing to do or value setting in the context

        CHECKED, WORKS WELL:
        Thoroughly tested with extra written test code which created a replacement
        Metafile just to test this action in various combinations

        NEEDS IMPLEMENTATION:
        Not implemented and asserted, but also no usage found, neither in own Metafile
        creations, nor in EMF/WMF imports (checked with a whole bunch of critical EMF/WMF
        bugdocs)

        For more comments, see the single action implementations.
    */

    static void implInterpretMetafile(
        const GDIMetaFile& rMetaFile,
        TargetHolders& rTargetHolders,
        PropertyHolders& rPropertyHolders,
        const drawinglayer::geometry::ViewInformation2D& rViewInformation)
    {
        const size_t nCount(rMetaFile.GetActionSize());
        std::unique_ptr<emfplushelper::EmfPlusHelper> aEMFPlus;

        for(size_t nAction(0); nAction < nCount; nAction++)
        {
            MetaAction* pAction = rMetaFile.GetAction(nAction);

            switch(pAction->GetType())
            {
                case MetaActionType::NONE :
                {
                    /** SIMPLE, DONE */
                    break;
                }
                case MetaActionType::PIXEL :
                {
                    /** CHECKED, WORKS WELL */
                    std::vector< basegfx::B2DPoint > aPositions;
                    Color aLastColor(COL_BLACK);

                    while(MetaActionType::PIXEL == pAction->GetType() && nAction < nCount)
                    {
                        const MetaPixelAction* pA = static_cast<const MetaPixelAction*>(pAction);

                        if(pA->GetColor() != aLastColor)
                        {
                            if(!aPositions.empty())
                            {
                                createPointArrayPrimitive(std::move(aPositions), rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
                                aPositions.clear();
                            }

                            aLastColor = pA->GetColor();
                        }

                        const Point& rPoint = pA->GetPoint();
                        aPositions.emplace_back(rPoint.X(), rPoint.Y());
                        nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
                    }

                    nAction--;

                    if(!aPositions.empty())
                    {
                        createPointArrayPrimitive(std::move(aPositions), rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
                    }

                    break;
                }
                case MetaActionType::POINT :
                {
                    /** CHECKED, WORKS WELL */
                    if(rPropertyHolders.Current().getLineColorActive())
                    {
                        std::vector< basegfx::B2DPoint > aPositions;

                        while(MetaActionType::POINT == pAction->GetType() && nAction < nCount)
                        {
                            const MetaPointAction* pA = static_cast<const MetaPointAction*>(pAction);
                            const Point& rPoint = pA->GetPoint();
                            aPositions.emplace_back(rPoint.X(), rPoint.Y());
                            nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
                        }

                        nAction--;

                        if(!aPositions.empty())
                        {
                            createPointArrayPrimitive(std::move(aPositions), rTargetHolders.Current(), rPropertyHolders.Current(), rPropertyHolders.Current().getLineColor());
                        }
                    }

                    break;
                }
                case MetaActionType::LINE :
                {
                    /** CHECKED, WORKS WELL */
                    if(rPropertyHolders.Current().getLineColorActive())
                    {
                        basegfx::B2DPolygon aLinePolygon;
                        LineInfo aLineInfo;

                        while(MetaActionType::LINE == pAction->GetType() && nAction < nCount)
                        {
                            const MetaLineAction* pA = static_cast<const MetaLineAction*>(pAction);
                            const Point& rStartPoint = pA->GetStartPoint();
                            const Point& rEndPoint = pA->GetEndPoint();
                            const basegfx::B2DPoint aStart(rStartPoint.X(), rStartPoint.Y());
                            const basegfx::B2DPoint aEnd(rEndPoint.X(), rEndPoint.Y());

                            if(aLinePolygon.count())
                            {
                                if(pA->GetLineInfo() == aLineInfo
                                    && aStart == aLinePolygon.getB2DPoint(aLinePolygon.count() - 1))
                                {
                                    aLinePolygon.append(aEnd);
                                }
                                else
                                {
                                    createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
                                    aLinePolygon.clear();
                                    aLineInfo = pA->GetLineInfo();
                                    aLinePolygon.append(aStart);
                                    aLinePolygon.append(aEnd);
                                }
                            }
                            else
                            {
                                aLineInfo = pA->GetLineInfo();
                                aLinePolygon.append(aStart);
                                aLinePolygon.append(aEnd);
                            }

                            nAction++;
                            if (nAction < nCount)
                                pAction = rMetaFile.GetAction(nAction);
                        }

                        nAction--;
                        if (aLinePolygon.count())
                            createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
                    }

                    break;
                }
                case MetaActionType::RECT :
                {
                    /** CHECKED, WORKS WELL */
                    if(rPropertyHolders.Current().getLineOrFillActive())
                    {
                        const MetaRectAction* pA = static_cast<const MetaRectAction*>(pAction);
                        const tools::Rectangle& rRectangle = pA->GetRect();

                        if(!rRectangle.IsEmpty())
                        {
                            const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRectangle);

                            if(!aRange.isEmpty())
                            {
                                const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange));
                                createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
                            }
                        }
                    }

                    break;
                }
                case MetaActionType::ROUNDRECT :
                {
                    /** CHECKED, WORKS WELL */
                    /** The original OutputDevice::DrawRect paints nothing when nHor or nVer is zero; but just
                        because the tools::Polygon operator creating the rounding does produce nonsense. I assume
                        this an error and create an unrounded rectangle in that case (implicit in
                        createPolygonFromRect)
                     */

                    if(rPropertyHolders.Current().getLineOrFillActive())
                    {
                        const MetaRoundRectAction* pA = static_cast<const MetaRoundRectAction*>(pAction);
                        const tools::Rectangle& rRectangle = pA->GetRect();

                        if(!rRectangle.IsEmpty())
                        {
                            const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRectangle);

                            if(!aRange.isEmpty())
                            {
                                const sal_uInt32 nHor(pA->GetHorzRound());
                                const sal_uInt32 nVer(pA->GetVertRound());
                                basegfx::B2DPolygon aOutline;

                                if(nHor || nVer)
                                {
                                    double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0));
                                    double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0));
                                    fRadiusX = std::clamp(fRadiusX, 0.0, 1.0);
                                    fRadiusY = std::clamp(fRadiusY, 0.0, 1.0);

                                    aOutline = basegfx::utils::createPolygonFromRect(aRange, fRadiusX, fRadiusY);
                                }
                                else
                                {
                                    aOutline = basegfx::utils::createPolygonFromRect(aRange);
                                }

                                createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
                            }
                        }
                    }

                    break;
                }
                case MetaActionType::ELLIPSE :
                {
                    /** CHECKED, WORKS WELL */
                    if(rPropertyHolders.Current().getLineOrFillActive())
                    {
                        const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pAction);
                        const tools::Rectangle& rRectangle = pA->GetRect();

                        if(!rRectangle.IsEmpty())
                        {
                            const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRectangle);

                            if(!aRange.isEmpty())
                            {
                                const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromEllipse(
                                    aRange.getCenter(), aRange.getWidth() * 0.5, aRange.getHeight() * 0.5));

                                createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
                            }
                        }
                    }

                    break;
                }
                case MetaActionType::ARC :
                {
                    /** CHECKED, WORKS WELL */
                    if(rPropertyHolders.Current().getLineColorActive())
                    {
                        const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
                        const tools::Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Arc);
                        const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());

                        createHairlinePrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
                    }

                    break;
                }
                case MetaActionType::PIE :
                {
                    /** CHECKED, WORKS WELL */
                    if(rPropertyHolders.Current().getLineOrFillActive())
                    {
                        const MetaPieAction* pA = static_cast<const MetaPieAction*>(pAction);
--> --------------------

--> maximum size reached

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

Messung V0.5
C=93 H=98 G=95

¤ Dauer der Verarbeitung: 0.18 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.