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 115 kB image not shown  

Quelle  emfphelperdata.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 "emfpcustomlinecap.hxx"
#include "emfphelperdata.hxx"
#include "emfpbrush.hxx"
#include "emfppen.hxx"
#include "emfppath.hxx"
#include "emfpregion.hxx"
#include "emfpimage.hxx"
#include "emfpimageattributes.hxx"
#include "emfpfont.hxx"
#include "emfpstringformat.hxx"
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <wmfemfhelper.hxx>
#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/attribute/fontattribute.hxx>
#include <basegfx/color/bcolor.hxx>
#include <basegfx/color/bcolormodifier.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/polygon/b2dpolygonclipper.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
#include <sal/log.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <toolkit/helper/vclunohelper.hxx>

#include <algorithm>

namespace emfplushelper
{

    enum
    {
        WrapModeTile = 0x00000000,
        WrapModeTileFlipX = 0x00000001,
        WrapModeTileFlipY = 0x00000002,
        WrapModeTileFlipXY = 0x00000003,
        WrapModeClamp = 0x00000004
    };

    const char* emfTypeToName(sal_uInt16 type)
    {
        switch (type)
        {
            case EmfPlusRecordTypeHeader: return "EmfPlusRecordTypeHeader";
            case EmfPlusRecordTypeEndOfFile: return "EmfPlusRecordTypeEndOfFile";
            case EmfPlusRecordTypeComment: return "EmfPlusRecordTypeComment";
            case EmfPlusRecordTypeGetDC: return "EmfPlusRecordTypeGetDC";
            case EmfPlusRecordTypeObject: return "EmfPlusRecordTypeObject";
            case EmfPlusRecordTypeFillRects: return "EmfPlusRecordTypeFillRects";
            case EmfPlusRecordTypeDrawRects: return "EmfPlusRecordTypeDrawRects";
            case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon";
            case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines";
            case EmfPlusRecordTypeFillClosedCurve: return "EmfPlusRecordTypeFillClosedCurve";
            case EmfPlusRecordTypeDrawClosedCurve: return "EmfPlusRecordTypeDrawClosedCurve";
            case EmfPlusRecordTypeDrawCurve: return "EmfPlusRecordTypeDrawCurve";
            case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse";
            case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse";
            case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie";
            case EmfPlusRecordTypeDrawPie: return "EmfPlusRecordTypeDrawPie";
            case EmfPlusRecordTypeDrawArc: return "EmfPlusRecordTypeDrawArc";
            case EmfPlusRecordTypeFillRegion: return "EmfPlusRecordTypeFillRegion";
            case EmfPlusRecordTypeFillPath: return "EmfPlusRecordTypeFillPath";
            case EmfPlusRecordTypeDrawPath: return "EmfPlusRecordTypeDrawPath";
            case EmfPlusRecordTypeDrawBeziers: return "EmfPlusRecordTypeDrawBeziers";
            case EmfPlusRecordTypeDrawImage: return "EmfPlusRecordTypeDrawImage";
            case EmfPlusRecordTypeDrawImagePoints: return "EmfPlusRecordTypeDrawImagePoints";
            case EmfPlusRecordTypeDrawString: return "EmfPlusRecordTypeDrawString";
            case EmfPlusRecordTypeSetRenderingOrigin: return "EmfPlusRecordTypeSetRenderingOrigin";
            case EmfPlusRecordTypeSetAntiAliasMode: return "EmfPlusRecordTypeSetAntiAliasMode";
            case EmfPlusRecordTypeSetTextRenderingHint: return "EmfPlusRecordTypeSetTextRenderingHint";
            case EmfPlusRecordTypeSetTextContrast: return "EmfPlusRecordTypeSetTextContrast";
            case EmfPlusRecordTypeSetInterpolationMode: return "EmfPlusRecordTypeSetInterpolationMode";
            case EmfPlusRecordTypeSetPixelOffsetMode: return "EmfPlusRecordTypeSetPixelOffsetMode";
            case EmfPlusRecordTypeSetCompositingQuality: return "EmfPlusRecordTypeSetCompositingQuality";
            case EmfPlusRecordTypeSave: return "EmfPlusRecordTypeSave";
            case EmfPlusRecordTypeRestore: return "EmfPlusRecordTypeRestore";
            case EmfPlusRecordTypeBeginContainer: return "EmfPlusRecordTypeBeginContainer";
            case EmfPlusRecordTypeBeginContainerNoParams: return "EmfPlusRecordTypeBeginContainerNoParams";
            case EmfPlusRecordTypeEndContainer: return "EmfPlusRecordTypeEndContainer";
            case EmfPlusRecordTypeSetWorldTransform: return "EmfPlusRecordTypeSetWorldTransform";
            case EmfPlusRecordTypeResetWorldTransform: return "EmfPlusRecordTypeResetWorldTransform";
            case EmfPlusRecordTypeMultiplyWorldTransform: return "EmfPlusRecordTypeMultiplyWorldTransform";
            case EmfPlusRecordTypeTranslateWorldTransform: return "EmfPlusRecordTypeTranslateWorldTransform";
            case EmfPlusRecordTypeScaleWorldTransform: return "EmfPlusRecordTypeScaleWorldTransform";
            case EmfPlusRecordTypeSetPageTransform: return "EmfPlusRecordTypeSetPageTransform";
            case EmfPlusRecordTypeResetClip: return "EmfPlusRecordTypeResetClip";
            case EmfPlusRecordTypeSetClipRect: return "EmfPlusRecordTypeSetClipRect";
            case EmfPlusRecordTypeSetClipPath: return "EmfPlusRecordTypeSetClipPath";
            case EmfPlusRecordTypeSetClipRegion: return "EmfPlusRecordTypeSetClipRegion";
            case EmfPlusRecordTypeOffsetClip: return "EmfPlusRecordTypeOffsetClip";
            case EmfPlusRecordTypeDrawDriverString: return "EmfPlusRecordTypeDrawDriverString";
        }
        return "";
    }

    static OUString emfObjectToName(sal_uInt16 type)
    {
        switch (type)
        {
            case EmfPlusObjectTypeBrush: return u"EmfPlusObjectTypeBrush"_ustr;
            case EmfPlusObjectTypePen: return u"EmfPlusObjectTypePen"_ustr;
            case EmfPlusObjectTypePath: return u"EmfPlusObjectTypePath"_ustr;
            case EmfPlusObjectTypeRegion: return u"EmfPlusObjectTypeRegion"_ustr;
            case EmfPlusObjectTypeImage: return u"EmfPlusObjectTypeImage"_ustr;
            case EmfPlusObjectTypeFont: return u"EmfPlusObjectTypeFont"_ustr;
            case EmfPlusObjectTypeStringFormat: return u"EmfPlusObjectTypeStringFormat"_ustr;
            case EmfPlusObjectTypeImageAttributes: return u"EmfPlusObjectTypeImageAttributes"_ustr;
            case EmfPlusObjectTypeCustomLineCap: return u"EmfPlusObjectTypeCustomLineCap"_ustr;
        }
        return u""_ustr;
    }

    static OUString PixelOffsetModeToString(sal_uInt16 nPixelOffset)
    {
        switch (nPixelOffset)
        {
            case PixelOffsetMode::PixelOffsetModeDefault: return u"PixelOffsetModeDefault"_ustr;
            case PixelOffsetMode::PixelOffsetModeHighSpeed: return u"PixelOffsetModeHighSpeed"_ustr;
            case PixelOffsetMode::PixelOffsetModeHighQuality: return u"PixelOffsetModeHighQuality"_ustr;
            case PixelOffsetMode::PixelOffsetModeNone: return u"PixelOffsetModeNone"_ustr;
            case PixelOffsetMode::PixelOffsetModeHalf: return u"PixelOffsetModeHalf"_ustr;
        }
        return u""_ustr;
    }

    static OUString SmoothingModeToString(sal_uInt16 nSmoothMode)
    {
        switch (nSmoothMode)
        {
            case SmoothingMode::SmoothingModeDefault: return u"SmoothingModeDefault"_ustr;
            case SmoothingMode::SmoothingModeHighSpeed: return u"SmoothModeHighSpeed"_ustr;
            case SmoothingMode::SmoothingModeHighQuality: return u"SmoothingModeHighQuality"_ustr;
            case SmoothingMode::SmoothingModeNone: return u"SmoothingModeNone"_ustr;
            case SmoothingMode::SmoothingModeAntiAlias8x4: return u"SmoothingModeAntiAlias8x4"_ustr;
            case SmoothingMode::SmoothingModeAntiAlias8x8: return u"SmoothingModeAntiAlias8x8"_ustr;
        }
        return u""_ustr;
    }

    static OUString TextRenderingHintToString(sal_uInt16 nHint)
    {
        switch (nHint)
        {
            case TextRenderingHint::TextRenderingHintSystemDefault: return u"TextRenderingHintSystemDefault"_ustr;
            case TextRenderingHint::TextRenderingHintSingleBitPerPixelGridFit: return u"TextRenderingHintSingleBitPerPixelGridFit"_ustr;
            case TextRenderingHint::TextRenderingHintSingleBitPerPixel: return u"TextRenderingHintSingleBitPerPixel"_ustr;
            case TextRenderingHint::TextRenderingHintAntialiasGridFit: return u"TextRenderingHintAntialiasGridFit"_ustr;
            case TextRenderingHint::TextRenderingHintAntialias: return u"TextRenderingHintAntialias"_ustr;
            case TextRenderingHint::TextRenderingHintClearTypeGridFit: return u"TextRenderingHintClearTypeGridFit"_ustr;
        }
        return u""_ustr;
    }

    static OUString InterpolationModeToString(sal_uInt16 nMode)
    {
        switch (nMode)
        {
            case InterpolationMode::InterpolationModeDefault: return u"InterpolationModeDefault"_ustr;
            case InterpolationMode::InterpolationModeLowQuality: return u"InterpolationModeLowQuality"_ustr;
            case InterpolationMode::InterpolationModeHighQuality: return u"InterpolationModeHighQuality"_ustr;
            case InterpolationMode::InterpolationModeBilinear: return u"InterpolationModeBilinear"_ustr;
            case InterpolationMode::InterpolationModeBicubic: return u"InterpolationModeBicubic"_ustr;
            case InterpolationMode::InterpolationModeNearestNeighbor: return u"InterpolationModeNearestNeighbor"_ustr;
            case InterpolationMode::InterpolationModeHighQualityBilinear: return u"InterpolationModeHighQualityBilinear"_ustr;
            case InterpolationMode::InterpolationModeHighQualityBicubic: return u"InterpolationModeHighQualityBicubic"_ustr;
        }
        return u""_ustr;
    }

    OUString UnitTypeToString(sal_uInt16 nType)
    {
        switch (nType)
        {
            case UnitTypeWorld: return u"UnitTypeWorld"_ustr;
            case UnitTypeDisplay: return u"UnitTypeDisplay"_ustr;
            case UnitTypePixel: return u"UnitTypePixel"_ustr;
            case UnitTypePoint: return u"UnitTypePoint"_ustr;
            case UnitTypeInch: return u"UnitTypeInch"_ustr;
            case UnitTypeDocument: return u"UnitTypeDocument"_ustr;
            case UnitTypeMillimeter: return u"UnitTypeMillimeter"_ustr;
        }
        return u""_ustr;
    }

    static bool IsBrush(sal_uInt16 flags)
    {
        return (!((flags >> 15) & 0x0001));
    }

    static OUString BrushIDToString(sal_uInt16 flags, sal_uInt32 brushid)
    {
        if (IsBrush(flags))
            return "EmfPlusBrush ID: " + OUString::number(brushid);
        else
            return "ARGB: 0x" + OUString::number(brushid, 16);
    }

    EMFPObject::~EMFPObject()
    {
    }

    double EmfPlusHelperData::unitToPixel(double n, sal_uInt32 aUnitType, Direction d)
    {
        switch (static_cast<UnitType>(aUnitType))
        {
            case UnitTypePixel:
                return n;

            case UnitTypePoint:
                return o3tl::convert(n, o3tl::Length::pt, o3tl::Length::in) * DPI(d);

            case UnitTypeInch:
                return n * DPI(d);

            case UnitTypeMillimeter:
                return o3tl::convert(n, o3tl::Length::mm, o3tl::Length::in) * DPI(d);

            case UnitTypeDocument:
                return n * DPI(d) / 300.0;

            case UnitTypeWorld:
            case UnitTypeDisplay:
                SAL_WARN("drawinglayer.emf""EMF+\t Converting to World/Display.");
                return n;

            default:
                SAL_WARN("drawinglayer.emf""EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType);
                return n;
        }
    }

    void EmfPlusHelperData::processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream)
    {
        sal_uInt16 objecttype = flags & 0x7f00;
        sal_uInt16 index = flags & 0xff;
        SAL_INFO("drawinglayer.emf""EMF+ Object: " << emfObjectToName(objecttype) << " (0x" << objecttype << ")");
        SAL_INFO("drawinglayer.emf""EMF+\tObject slot: " << index);
        SAL_INFO("drawinglayer.emf""EMF+\tFlags: " << (flags & 0xff00));

        switch (objecttype)
        {
            case EmfPlusObjectTypeBrush:
            {
                EMFPBrush *brush = new EMFPBrush();
                maEMFPObjects[index].reset(brush);
                brush->Read(rObjectStream, *this);
                break;
            }
            case EmfPlusObjectTypePen:
            {
                EMFPPen *pen = new EMFPPen();
                maEMFPObjects[index].reset(pen);
                pen->Read(rObjectStream, *this);
                pen->penWidth = unitToPixel(pen->penWidth, pen->penUnit, Direction::horizontal);
                break;
            }
            case EmfPlusObjectTypePath:
            {
                sal_uInt32 aVersion, aPathPointCount, aPathPointFlags;

                rObjectStream.ReadUInt32(aVersion).ReadUInt32(aPathPointCount).ReadUInt32(aPathPointFlags);
                SAL_INFO("drawinglayer.emf""EMF+\t\tVersion: 0x" << std::hex << aVersion);
                SAL_INFO("drawinglayer.emf""EMF+\t\tNumber of points: " << std::dec << aPathPointCount);
                SAL_INFO("drawinglayer.emf""EMF+\t\tPath point flags: 0x" << std::hex << aPathPointFlags << std::dec);
                EMFPPath *path = new EMFPPath(aPathPointCount);
                maEMFPObjects[index].reset(path);
                path->Read(rObjectStream, aPathPointFlags);
                break;
            }
            case EmfPlusObjectTypeRegion:
            {
                EMFPRegion *region = new EMFPRegion();
                maEMFPObjects[index].reset(region);
                region->ReadRegion(rObjectStream, *this);
                break;
            }
            case EmfPlusObjectTypeImage:
            {
                EMFPImage *image = new EMFPImage;
                maEMFPObjects[index].reset(image);
                image->type = 0;
                image->width = 0;
                image->height = 0;
                image->stride = 0;
                image->pixelFormat = 0;
                image->Read(rObjectStream, dataSize, bUseWholeStream);
                break;
            }
            case EmfPlusObjectTypeFont:
            {
                EMFPFont *font = new EMFPFont;
                maEMFPObjects[index].reset(font);
                font->emSize = 0;
                font->sizeUnit = 0;
                font->fontFlags = 0;
                font->Read(rObjectStream);
                // tdf#113624 Convert unit to Pixels
                font->emSize = unitToPixel(font->emSize, font->sizeUnit, Direction::horizontal);

                break;
            }
            case EmfPlusObjectTypeStringFormat:
            {
                EMFPStringFormat *stringFormat = new EMFPStringFormat();
                maEMFPObjects[index].reset(stringFormat);
                stringFormat->Read(rObjectStream);
                break;
            }
            case EmfPlusObjectTypeImageAttributes:
            {
                EMFPImageAttributes *imageAttributes = new EMFPImageAttributes();
                maEMFPObjects[index].reset(imageAttributes);
                imageAttributes->Read(rObjectStream);
                break;
            }
            case EmfPlusObjectTypeCustomLineCap:
            {
                SAL_WARN("drawinglayer.emf""EMF+\t TODO Object type 'custom line cap' not yet implemented");
                break;
            }
            default:
            {
                SAL_WARN("drawinglayer.emf""EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec);
            }
        }
    }

    void EmfPlusHelperData::ReadPoint(SvStream& s, float& x, float& y, sal_uInt32 flags)
    {
        if (flags & 0x800)
        {
            // specifies a location in the coordinate space that is relative to
            // the location specified by the previous element in the array. In the case of the first element in
            // PointData, a previous location at coordinates (0,0) is assumed.
            SAL_WARN("drawinglayer.emf""EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR");
        }

        if (flags & 0x4000)
        {
            sal_Int16 ix, iy;

            s.ReadInt16(ix).ReadInt16(iy);

            x = ix;
            y = iy;
        }
        else
        {
            s.ReadFloat(x).ReadFloat(y);
        }
    }

    void EmfPlusHelperData::ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed)
    {
        if (bCompressed)
        {
            sal_Int16 ix, iy, iw, ih;

            s.ReadInt16(ix).ReadInt16(iy).ReadInt16(iw).ReadInt16(ih);

            x = ix;
            y = iy;
            width = iw;
            height = ih;
        }
        else
        {
            s.ReadFloat(x).ReadFloat(y).ReadFloat(width).ReadFloat(height);
        }
    }

    bool EmfPlusHelperData::readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget)
    {
        rTarget.identity();

        if (sizeof(float) != 4)
        {
            OSL_FAIL("EnhWMFReader::sizeof( float ) != 4");
            return false;
        }
        else
        {
            float eM11(0.0);
            float eM12(0.0);
            float eM21(0.0);
            float eM22(0.0);
            float eDx(0.0);
            float eDy(0.0);
            rIn.ReadFloat(eM11).ReadFloat(eM12).ReadFloat(eM21).ReadFloat(eM22).ReadFloat(eDx).ReadFloat(eDy);
            rTarget = basegfx::B2DHomMatrix(
                eM11, eM21, eDx,
                eM12, eM22, eDy);
        }

        return true;
    }

    void EmfPlusHelperData::mappingChanged()
    {
        if (mnPixX == 0 || mnPixY == 0)
        {
            SAL_WARN("drawinglayer.emf""dimensions in pixels is 0");
            return;
        }
        // Call when mnMmX/mnMmY/mnPixX/mnPixY/mnFrameLeft/mnFrameTop/maWorldTransform/ changes.
        // Currently not used are mnHDPI/mnVDPI/mnFrameRight/mnFrameBottom. *If* these should
        // be used in the future, this method will need to be called.
        //
        // Re-calculate maMapTransform to contain the complete former transformation so that
        // it can be applied by a single matrix multiplication or be added to an encapsulated
        // primitive later
        //
        // To evtl. correct and see where this came from, please compare with the implementations
        // of EmfPlusHelperData::MapToDevice and EmfPlusHelperData::Map* in prev versions
        maMapTransform = maWorldTransform;
        maMapTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(100.0 * mnMmX / mnPixX, 100.0 * mnMmY / mnPixY,
                                                                           double(-mnFrameLeft), double(-mnFrameTop));
        maMapTransform *= maBaseTransform;

        // Used only for performance optimization, to do not calculate it every line draw
        mdExtractedXScale = std::hypot(maMapTransform.a(), maMapTransform.b());
        mdExtractedYScale = std::hypot(maMapTransform.c(), maMapTransform.d());
    }

    ::basegfx::B2DPoint EmfPlusHelperData::Map(double ix, double iy) const
    {
        // map in one step using complete MapTransform (see mappingChanged)
        return maMapTransform * ::basegfx::B2DPoint(ix, iy);
    }

    Color EmfPlusHelperData::EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags, const sal_uInt32 brushIndexOrColor) const {
        Color color;
        if (flags & 0x8000) // we use a color
        {
            color = Color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff,
                          (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
        }
        else // we use a brush
        {
            const EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
            if (brush)
            {
                color = brush->GetColor();
                if (brush->type != BrushTypeSolidColor)
                    SAL_WARN("drawinglayer.emf""EMF+\t\t TODO Brush other than solid color is not supported");
            }
        }
        return color;
    }

    void EmfPlusHelperData::GraphicStatePush(GraphicStateMap& map, sal_Int32 index)
    {
        GraphicStateMap::iterator iter = map.find( index );

        if ( iter != map.end() )
        {
            map.erase( iter );
            SAL_INFO("drawinglayer.emf""EMF+\t\tStack index: " << index << " found and erased");
        }

        wmfemfhelper::PropertyHolder state = mrPropertyHolders.Current();
        // tdf#112500 We need to save world transform somehow, during graphic state push
        state.setTransformation(maWorldTransform);
        map[ index ] = std::move(state);
    }

    void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index)
    {
        GraphicStateMap::iterator iter = map.find(index);

        if (iter != map.end())
        {
            wmfemfhelper::PropertyHolder state = iter->second;

            maWorldTransform = state.getTransformation();
            if (state.getClipPolyPolygonActive())
            {
                SAL_INFO("drawinglayer.emf",
                        "EMF+\t Restore clipping region to saved in index: " << index);
                wmfemfhelper::HandleNewClipRegion(state.getClipPolyPolygon(), mrTargetHolders,
                                                  mrPropertyHolders);
            }
            else
            {
                SAL_INFO("drawinglayer.emf""EMF+\t Disable clipping");
                wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
                                                  mrPropertyHolders);
            }
            mappingChanged();
            SAL_INFO("drawinglayer.emf",
                    "EMF+\t\tStack index: " << index
                                            << " found, maWorldTransform: " << maWorldTransform);
        }
    }

    drawinglayer::attribute::LineStartEndAttribute
    EmfPlusHelperData::CreateLineEnd(const sal_Int32 aCap, const float aPenWidth) const
    {
        const double pw = mdExtractedYScale * aPenWidth;
        if (aCap == LineCapTypeSquare)
        {
            basegfx::B2DPolygon aCapPolygon(
                { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
            aCapPolygon.setClosed(true);
            return drawinglayer::attribute::LineStartEndAttribute(
                pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
        }
        else if (aCap == LineCapTypeRound)
        {
            basegfx::B2DPolygon aCapPolygon(
                { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.9236, -0.3827},
                  {0.7071, -0.7071}, {0.3827, -0.9236}, {0.0, -1.0}, {-0.3827, -0.9236},
                  {-0.7071, -0.7071}, {-0.9236, -0.3827}, {-1.0, 0.0} });
            aCapPolygon.setClosed(true);
            return drawinglayer::attribute::LineStartEndAttribute(
                pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
        }
        else if (aCap == LineCapTypeTriangle)
        {
            basegfx::B2DPolygon aCapPolygon(
                { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, -1.0}, {-1.0, 0.0} });
            aCapPolygon.setClosed(true);
            return drawinglayer::attribute::LineStartEndAttribute(
                pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
        }
        else if (aCap == LineCapTypeSquareAnchor)
        {
            basegfx::B2DPolygon aCapPolygon(
                { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
            aCapPolygon.setClosed(true);
            return drawinglayer::attribute::LineStartEndAttribute(
                1.5 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
        }
        else if (aCap == LineCapTypeRoundAnchor)
        {
            const basegfx::B2DPolygon aCapPolygon
                = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 1.0, 1.0);
            return drawinglayer::attribute::LineStartEndAttribute(
                2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
        }
        else if (aCap == LineCapTypeDiamondAnchor)
        {
            basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 0.0}, {0.5, 0.5},
                                              {0.5, 1.0}, {-0.5, 1.0}, {-0.5, 0.5},
                                              {-1.0, 0.0} });
            aCapPolygon.setClosed(true);
            return drawinglayer::attribute::LineStartEndAttribute(
                2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
        }
        else if (aCap == LineCapTypeArrowAnchor)
        {
            basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
            aCapPolygon.setClosed(true);
            return drawinglayer::attribute::LineStartEndAttribute(
                2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
        }
        return drawinglayer::attribute::LineStartEndAttribute();
    }

    void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon,
                                                sal_uInt32 penIndex)
    {
        const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get());
        SAL_WARN_IF(!pen, "drawinglayer.emf""emf+ missing pen");

        if (!(pen && polygon.count()))
            return;

        const double transformedPenWidth = mdExtractedYScale * pen->penWidth;
        drawinglayer::attribute::LineAttribute lineAttribute(
            pen->GetColor().getBColor(), transformedPenWidth, pen->maLineJoin,
            css::drawing::LineCap_BUTT, //TODO implement PenDataDashedLineCap support here
            pen->fMiterMinimumAngle);

        drawinglayer::attribute::LineStartEndAttribute aStart;
        if (pen->penDataFlags & EmfPlusPenDataStartCap)
        {
            if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap)
                && (pen->customStartCap->polygon.begin()->count() > 1))
                aStart = drawinglayer::attribute::LineStartEndAttribute(
                    pen->customStartCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
                        * pen->customStartCap->widthScale * pen->penWidth,
                    pen->customStartCap->polygon, false);
            else
                aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, pen->penWidth);
        }

        drawinglayer::attribute::LineStartEndAttribute aEnd;
        if (pen->penDataFlags & EmfPlusPenDataEndCap)
        {
            if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap)
                && (pen->customEndCap->polygon.begin()->count() > 1))
                aEnd = drawinglayer::attribute::LineStartEndAttribute(
                    pen->customEndCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
                        * pen->customEndCap->widthScale * pen->penWidth,
                    pen->customEndCap->polygon, false);
            else
                aEnd = EmfPlusHelperData::CreateLineEnd(pen->endCap, pen->penWidth);
        }

        if (pen->GetColor().IsTransparent())
        {
            drawinglayer::primitive2d::Primitive2DContainer aContainer;
            if (aStart.isDefault() && aEnd.isDefault())
                aContainer.append(
                    new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
                        polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale)));
            else
            {
                aContainer.resize(polygon.count());
                for (sal_uInt32 i = 0; i < polygon.count(); i++)
                    aContainer[i] =
                        new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
                            polygon.getB2DPolygon(i), lineAttribute,
                            pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd);
            }
            mrTargetHolders.Current().append(
                new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
                    std::move(aContainer), (255 - pen->GetColor().GetAlpha()) / 255.0));
        }
        else
        {
            if (aStart.isDefault() && aEnd.isDefault())
                mrTargetHolders.Current().append(
                    new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
                        polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale)));
            else
                for (sal_uInt32 i = 0; i < polygon.count(); i++)
                {
                    mrTargetHolders.Current().append(
                        new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
                            polygon.getB2DPolygon(i), lineAttribute,
                            pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd));
                }
        }
        mrPropertyHolders.Current().setLineColor(pen->GetColor().getBColor());
        mrPropertyHolders.Current().setLineColorActive(true);
        mrPropertyHolders.Current().setFillColorActive(false);
    }

    void EmfPlusHelperData::EMFPPlusFillPolygonSolidColor(const ::basegfx::B2DPolyPolygon& polygon, Color const& color)
    {
        if (color.GetAlpha() == 0)
            return;

        if (!color.IsTransparent())
        {
            // not transparent
            mrTargetHolders.Current().append(
                        new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
                            polygon,
                            color.getBColor()));
        }
        else
        {
            // transparent
            mrTargetHolders.Current().append(
                        new drawinglayer::primitive2d::PolyPolygonRGBAPrimitive2D(
                            polygon,
                            color.getBColor(),
                            (255 - color.GetAlpha()) / 255.0));
        }
    }

    void EmfPlusHelperData::EMFPPlusFillPolygon(const ::basegfx::B2DPolyPolygon& polygon, const bool isColor, const sal_uInt32 brushIndexOrColor)
    {
        if (!polygon.count())
          return;

        if (isColor) // use Color
        {
            SAL_INFO("drawinglayer.emf""EMF+\t\t Fill polygon, ARGB color: 0x" << std::hex << brushIndexOrColor << std::dec);

            // EMF Alpha (1 byte): An 8-bit unsigned integer that specifies the transparency of the background,
            // ranging from 0 for completely transparent to 0xFF for completely opaque.
            const Color color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
            EMFPPlusFillPolygonSolidColor(polygon, color);

            mrPropertyHolders.Current().setFillColor(color.getBColor());
            mrPropertyHolders.Current().setFillColorActive(true);
            mrPropertyHolders.Current().setLineColorActive(false);
        }
        else // use Brush
        {
            EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
            SAL_INFO("drawinglayer.emf""EMF+\t\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")");

            // give up in case something wrong happened
            if( !brush )
                return;

            mrPropertyHolders.Current().setFillColorActive(false);
            mrPropertyHolders.Current().setLineColorActive(false);

            if (brush->type == BrushTypeSolidColor)
            {
                Color fillColor = brush->solidColor;
                EMFPPlusFillPolygonSolidColor(polygon, fillColor);
            }
            else if (brush->type == BrushTypeHatchFill)
            {
                // EMF+ like hatching is currently not supported. These are just color blends which serve as an approximation for some of them
                // for the others the hatch "background" color (secondColor in brush) is used.

                bool isHatchBlend = true;
                double blendFactor = 0.0;

                switch (brush->hatchStyle)
                {
                    case HatchStyle05Percent: blendFactor = 0.05; break;
                    case HatchStyle10Percent: blendFactor = 0.10; break;
                    case HatchStyle20Percent: blendFactor = 0.20; break;
                    case HatchStyle25Percent: blendFactor = 0.25; break;
                    case HatchStyle30Percent: blendFactor = 0.30; break;
                    case HatchStyle40Percent: blendFactor = 0.40; break;
                    case HatchStyle50Percent: blendFactor = 0.50; break;
                    case HatchStyle60Percent: blendFactor = 0.60; break;
                    case HatchStyle70Percent: blendFactor = 0.70; break;
                    case HatchStyle75Percent: blendFactor = 0.75; break;
                    case HatchStyle80Percent: blendFactor = 0.80; break;
                    case HatchStyle90Percent: blendFactor = 0.90; break;
                    default:
                        isHatchBlend = false;
                        break;
                }
                Color fillColor;
                if (isHatchBlend)
                {
                    fillColor = brush->solidColor;
                    fillColor.Merge(brush->secondColor, static_cast<sal_uInt8>(255 * blendFactor));
                }
                else
                {
                    fillColor = brush->secondColor;
                }
                // temporal solution: create a solid colored polygon
                // TODO create a 'real' hatching primitive
                mrTargetHolders.Current().append(
                    new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
                        polygon,
                        fillColor.getBColor()));
            }
            else if (brush->type == BrushTypeTextureFill)
            {
                SAL_WARN("drawinglayer.emf""EMF+\tTODO: implement BrushTypeTextureFill brush");
            }
            else if (brush->type == BrushTypePathGradient || brush->type == BrushTypeLinearGradient)

            {
                if (brush->type == BrushTypePathGradient && !(brush->additionalFlags & 0x1))
                {
                    SAL_WARN("drawinglayer.emf""EMF+\t TODO Implement displaying BrushTypePathGradient with Boundary: ");
                }
                ::basegfx::B2DHomMatrix aTextureTransformation;

                if (brush->hasTransformation) {
                   aTextureTransformation = brush->brush_transformation;

                   // adjust aTextureTransformation for our world space:
                   // -> revert the mapping -> apply the transformation -> map back
                   basegfx::B2DHomMatrix aInvertedMapTrasform(maMapTransform);
                   aInvertedMapTrasform.invert();
                   aTextureTransformation =  maMapTransform * aTextureTransformation * aInvertedMapTrasform;
                }

                // select the stored colors
                const basegfx::BColor aStartColor = brush->solidColor.getBColor();
                const basegfx::BColor aEndColor = brush->secondColor.getBColor();
                drawinglayer::primitive2d::SvgGradientEntryVector aVector;

                if (brush->blendPositions)
                {
                    SAL_INFO("drawinglayer.emf""EMF+\t\tUse blend");

                    // store the blendpoints in the vector
                    for (sal_uInt32 i = 0; i < brush->blendPoints; i++)
                    {
                        const double aBlendPoint = brush->blendPositions[i];
                        basegfx::BColor aColor;
                        aColor.setGreen(aStartColor.getGreen() + brush->blendFactors[i] * (aEndColor.getGreen() - aStartColor.getGreen()));
                        aColor.setBlue (aStartColor.getBlue()  + brush->blendFactors[i] * (aEndColor.getBlue() - aStartColor.getBlue()));
                        aColor.setRed  (aStartColor.getRed()   + brush->blendFactors[i] * (aEndColor.getRed() - aStartColor.getRed()));
                        const double aAlpha = brush->solidColor.GetAlpha() + brush->blendFactors[i] * (brush->secondColor.GetAlpha() - brush->solidColor.GetAlpha());
                        aVector.emplace_back(aBlendPoint, aColor, aAlpha / 255.0);
                    }
                }
                else if (brush->colorblendPositions)
                {
                    SAL_INFO("drawinglayer.emf""EMF+\t\tUse color blend");

                    // store the colorBlends in the vector
                    for (sal_uInt32 i = 0; i < brush->colorblendPoints; i++)
                    {
                        const double aBlendPoint = brush->colorblendPositions[i];
                        const basegfx::BColor aColor = brush->colorblendColors[i].getBColor();
                        aVector.emplace_back(aBlendPoint, aColor, brush->colorblendColors[i].GetAlpha() / 255.0);
                    }
                }
                else // ok, no extra points: just start and end
                {
                    aVector.emplace_back(0.0, aStartColor, brush->solidColor.GetAlpha() / 255.0);
                    aVector.emplace_back(1.0, aEndColor, brush->secondColor.GetAlpha() / 255.0);
                }

                // get the polygon range to be able to map the start/end/center point correctly
                // therefore, create a mapping and invert it
                basegfx::B2DRange aPolygonRange= polygon.getB2DRange();
                basegfx::B2DHomMatrix aPolygonTransformation = basegfx::utils::createScaleTranslateB2DHomMatrix(
                    aPolygonRange.getWidth(),aPolygonRange.getHeight(),
                    aPolygonRange.getMinX(), aPolygonRange.getMinY());
                aPolygonTransformation.invert();

                if (brush->type == BrushTypeLinearGradient)
                {
                    // support for public enum EmfPlusWrapMode
                    basegfx::B2DPoint aStartPoint = Map(brush->firstPointX, 0.0);
                    aStartPoint = aPolygonTransformation * aStartPoint;
                    basegfx::B2DPoint aEndPoint = Map(brush->firstPointX + brush->aWidth, 0.0);
                    aEndPoint = aPolygonTransformation * aEndPoint;

                    // support for public enum EmfPlusWrapMode
                    drawinglayer::primitive2d::SpreadMethod aSpreadMethod(drawinglayer::primitive2d::SpreadMethod::Pad);
                    switch(brush->wrapMode)
                    {
                        case WrapModeTile:
                        case WrapModeTileFlipY:
                        {
                            aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Repeat;
                            break;
                        }
                        case WrapModeTileFlipX:
                        case WrapModeTileFlipXY:
                        {
                            aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Reflect;
                            break;
                        }
                        default:
                            break;
                    }

                    // create the same one used for SVG
                    mrTargetHolders.Current().append(
                        new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
                            aTextureTransformation,
                            polygon,
                            std::move(aVector),
                            aStartPoint,
                            aEndPoint,
                            false,                  // do not use UnitCoordinates
                            aSpreadMethod));
                }
                else // BrushTypePathGradient
                { // TODO The PathGradient is not implemented, and Radial Gradient is used instead
                    basegfx::B2DPoint aCenterPoint = Map(brush->firstPointX, brush->firstPointY);
                    aCenterPoint = aPolygonTransformation * aCenterPoint;

                    // create the same one used for SVG
                    mrTargetHolders.Current().append(
                        new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D(
                            aTextureTransformation,
                            polygon,
                            std::move(aVector),
                            aCenterPoint,
                            0.7, // relative radius little bigger to cover all elements
                            true// use UnitCoordinates to stretch the gradient
                            drawinglayer::primitive2d::SpreadMethod::Pad,
                            nullptr));
                }
            }
        }
    }

    EmfPlusHelperData::EmfPlusHelperData(
        SvMemoryStream& rMS,
        wmfemfhelper::TargetHolders& rTargetHolders,
        wmfemfhelper::PropertyHolders& rPropertyHolders)
    :   mfPageScale(0.0),
        mnOriginX(0),
        mnOriginY(0),
        mnHDPI(0),
        mnVDPI(0),
        mbSetTextContrast(false),
        mnTextContrast(0),
        mnFrameLeft(0),
        mnFrameTop(0),
        mnFrameRight(0),
        mnFrameBottom(0),
        mnPixX(0),
        mnPixY(0),
        mnMmX(0),
        mnMmY(0),
        mbMultipart(false),
        mMFlags(0),
        mdExtractedXScale(1.0),
        mdExtractedYScale(1.0),
        mrTargetHolders(rTargetHolders),
        mrPropertyHolders(rPropertyHolders),
        bIsGetDCProcessing(false)
    {
        rMS.ReadInt32(mnFrameLeft).ReadInt32(mnFrameTop).ReadInt32(mnFrameRight).ReadInt32(mnFrameBottom);
        SAL_INFO("drawinglayer.emf""EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << mnFrameBottom);
        rMS.ReadInt32(mnPixX).ReadInt32(mnPixY).ReadInt32(mnMmX).ReadInt32(mnMmY);
        SAL_INFO("drawinglayer.emf""EMF+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << mnMmY);
        readXForm(rMS, maBaseTransform);
        SAL_INFO("drawinglayer.emf""EMF+ base transform: " << maBaseTransform);
        mappingChanged();
    }

    EmfPlusHelperData::~EmfPlusHelperData()
    {
    }

    ::basegfx::B2DPolyPolygon EmfPlusHelperData::combineClip(::basegfx::B2DPolyPolygoconst & leftPolygon, int combineMode, ::basegfx::B2DPolyPolygon const & rightPolygon)
    {
        basegfx::B2DPolyPolygon aClippedPolyPolygon;
        switch (combineMode)
        {
        case EmfPlusCombineModeReplace:
        {
            aClippedPolyPolygon = rightPolygon;
            break;
        }
        case EmfPlusCombineModeIntersect:
        {
            aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon(
                leftPolygon, rightPolygon, truefalse);
            break;
        }
        case EmfPlusCombineModeUnion:
        {
            aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationOr(leftPolygon, rightPolygon);
            break;
        }
        case EmfPlusCombineModeXOR:
        {
            aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationXor(leftPolygon, rightPolygon);
            break;
        }
        case EmfPlusCombineModeExclude:
        {
            // Replaces the existing region with the part of itself that is not in the new region.
            aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(leftPolygon, rightPolygon);
            break;
        }
        case EmfPlusCombineModeComplement:
        {
            // Replaces the existing region with the part of the new region that is not in the existing region.
            aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(rightPolygon, leftPolygon);
            break;
        }
        }
        return aClippedPolyPolygon;
    }

    void EmfPlusHelperData::processEmfPlusData(
        SvMemoryStream& rMS,
        const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/)
    {
        sal_uInt64 length = rMS.GetSize();

        if (length < 12)
            SAL_WARN("drawinglayer.emf""length is less than required header size");

        // 12 is minimal valid EMF+ record size; remaining bytes are padding
        while (length >= 12)
        {
            sal_uInt16 type, flags;
            sal_uInt32 size, dataSize;
            sal_uInt64 next;

            rMS.ReadUInt16(type).ReadUInt16(flags).ReadUInt32(size).ReadUInt32(dataSize);

            next = rMS.Tell() + (size - 12);

            if (size < 12)
            {
                SAL_WARN("drawinglayer.emf""Size field is less than 12 bytes");
                break;
            }
            else if (size > length)
            {
                SAL_WARN("drawinglayer.emf""Size field is greater than bytes left");
                break;
            }

            if (dataSize > (size - 12))
            {
                SAL_WARN("drawinglayer.emf""DataSize field is greater than Size-12");
                break;
            }

            SAL_INFO("drawinglayer.emf""EMF+ " << emfTypeToName(type) << " (0x" << std::hex << type << ")" << std::dec);
            SAL_INFO("drawinglayer.emf""EMF+\t record size: " << size);
            SAL_INFO("drawinglayer.emf""EMF+\t flags: 0x" << std::hex << flags << std::dec);
            SAL_INFO("drawinglayer.emf""EMF+\t data size: " << dataSize);

            if (bIsGetDCProcessing)
            {
                if (aGetDCState.getClipPolyPolygonActive())
                {
                    SAL_INFO("drawinglayer.emf""EMF+\t Restore region to GetDC saved");
                    wmfemfhelper::HandleNewClipRegion(aGetDCState.getClipPolyPolygon(), mrTargetHolders,
                                                      mrPropertyHolders);
                }
                else
                {
                    SAL_INFO("drawinglayer.emf""EMF+\t Disable clipping");
                    wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
                                                      mrPropertyHolders);
                }
                bIsGetDCProcessing = false;
            }
            if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000)))
            {
                if (!mbMultipart)
                {
                    mbMultipart = true;
                    mMFlags = flags;
                    mMStream.Seek(0);
                }

                OSL_ENSURE(dataSize >= 4, "No room for TotalObjectSize in EmfPlusContinuedObjectRecord");

                // 1st 4 bytes are TotalObjectSize
                mMStream.WriteBytes(static_cast<const char *>(rMS.GetData()) + rMS.Tell() + 4, dataSize - 4);
                SAL_INFO("drawinglayer.emf""EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize);
            }
            else
            {
                if (mbMultipart)
                {
                    SAL_INFO("drawinglayer.emf""EMF+ multipart record flags: " << mMFlags);
                    mMStream.Seek(0);
                    processObjectRecord(mMStream, mMFlags, 0, true);
                }

                mbMultipart = false;
            }

            if (type != EmfPlusRecordTypeObject || !(flags & 0x8000))
            {
                switch (type)
                {
                    case EmfPlusRecordTypeHeader:
                    {
                        sal_uInt32 version, emfPlusFlags;
                        SAL_INFO("drawinglayer.emf""EMF+\tDual: " << ((flags & 1) ? "true" : "false"));

                        rMS.ReadUInt32(version).ReadUInt32(emfPlusFlags).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI);
                        SAL_INFO("drawinglayer.emf""EMF+\tVersion: 0x" << std::hex << version);
                        SAL_INFO("drawinglayer.emf""EMF+\tEmf+ Flags: 0x"  << emfPlusFlags << std::dec);
                        SAL_INFO("drawinglayer.emf""EMF+\tMetafile was recorded with a reference device context for " << ((emfPlusFlags & 1) ? "video display" : "printer"));
                        SAL_INFO("drawinglayer.emf""EMF+\tHorizontal DPI: " << mnHDPI);
                        SAL_INFO("drawinglayer.emf""EMF+\tVertical DPI: " << mnVDPI);
                        break;
                    }
                    case EmfPlusRecordTypeEndOfFile:
                    {
                        break;
                    }
                    case EmfPlusRecordTypeComment:
                    {
#if OSL_DEBUG_LEVEL > 1
                        unsigned char data;
                        OUString hexdata;

                        SAL_INFO("drawinglayer.emf""EMF+\tDatasize: 0x" << std::hex << dataSize << std::dec);

                        for (sal_uInt32 i=0; i<dataSize; i++)
                        {
                            rMS.ReadUChar(data);

                            if (i % 16 == 0)
                                hexdata += "\n";

                            OUString padding;
                            if ((data & 0xF0) == 0)
                                padding = "0";

                            hexdata += "0x" + padding + OUString::number(data, 16) + " ";
                        }

                        SAL_INFO("drawinglayer.emf""EMF+\t" << hexdata);
#endif
                        break;
                    }
                    case EmfPlusRecordTypeGetDC:
                    {
                        bIsGetDCProcessing = true;
                        aGetDCState = mrPropertyHolders.Current();
                        SAL_INFO("drawinglayer.emf""EMF+\tAlready used in svtools wmf/emf filter parser");
                        break;
                    }
                    case EmfPlusRecordTypeObject:
                    {
                        processObjectRecord(rMS, flags, dataSize);
                        break;
                    }
                    case EmfPlusRecordTypeFillPie:
                    case EmfPlusRecordTypeDrawPie:
                    case EmfPlusRecordTypeDrawArc:
                    {
                        float startAngle, sweepAngle;

                        // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
                        sal_uInt32 brushIndexOrColor = 999;

                        if (type == EmfPlusRecordTypeFillPie)
                        {
                            rMS.ReadUInt32(brushIndexOrColor);
                            SAL_INFO("drawinglayer.emf""EMF+\t FillPie colorOrIndex: " << brushIndexOrColor);
                        }
                        else if (type == EmfPlusRecordTypeDrawPie)
                        {
                            SAL_INFO("drawinglayer.emf""EMF+\t DrawPie");
                        }
                        else
                        {
                            SAL_INFO("drawinglayer.emf""EMF+\t DrawArc");
                        }

                        rMS.ReadFloat(startAngle).ReadFloat(sweepAngle);
                        float dx, dy, dw, dh;
                        ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
                        SAL_INFO("drawinglayer.emf""EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
                        startAngle = basegfx::deg2rad(startAngle);
                        sweepAngle = basegfx::deg2rad(sweepAngle);
                        float endAngle = startAngle + sweepAngle;
                        startAngle = fmodf(startAngle, static_cast<float>(M_PI * 2));

                        if (startAngle < 0.0)
                        {
                            startAngle += static_cast<float>(M_PI * 2.0);
                        }
                        endAngle = fmodf(endAngle, static_cast<float>(M_PI * 2.0));

                        if (endAngle < 0.0)
                        {
                            endAngle += static_cast<float>(M_PI * 2.0);
                        }
                        if (sweepAngle < 0)
                        {
                            std::swap(endAngle, startAngle);
                        }

                        SAL_INFO("drawinglayer.emf""EMF+\t Adjusted angles: start " <<
                            basegfx::rad2deg(startAngle) << ", end: " << basegfx::rad2deg(endAngle) <<
                            " startAngle: " << startAngle << " sweepAngle: " << sweepAngle);
                        const ::basegfx::B2DPoint centerPoint(dx + 0.5 * dw, dy + 0.5 * dh);
                        ::basegfx::B2DPolygon polygon(
                            ::basegfx::utils::createPolygonFromEllipseSegment(centerPoint,
                                                                              0.5 * dw, 0.5 * dh,
                                                                              startAngle, endAngle));
                        if (type != EmfPlusRecordTypeDrawArc)
                        {
                            polygon.append(centerPoint);
                            polygon.setClosed(true);
                        }
                        ::basegfx::B2DPolyPolygon polyPolygon(polygon);
                        polyPolygon.transform(maMapTransform);
                        if (type == EmfPlusRecordTypeFillPie)
                            EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
                        else
                            EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
                    }
                    break;
                    case EmfPlusRecordTypeFillPath:
                    {
                        sal_uInt32 index = flags & 0xff;
                        sal_uInt32 brushIndexOrColor;
                        rMS.ReadUInt32(brushIndexOrColor);
                        SAL_INFO("drawinglayer.emf""EMF+ FillPath slot: " << index);

                        EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[index].get());
                        if (path)
                            EMFPPlusFillPolygon(path->GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
                        else
                            SAL_WARN("drawinglayer.emf""EMF+\tEmfPlusRecordTypeFillPath missing path");
                    }
                    break;
                    case EmfPlusRecordTypeFillRegion:
                    {
                        sal_uInt32 index = flags & 0xff;
                        sal_uInt32 brushIndexOrColor;
                        rMS.ReadUInt32(brushIndexOrColor);
                        SAL_INFO("drawinglayer.emf""EMF+\t FillRegion slot: " << index);

                        EMFPRegion* region = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
                        if (region)
                            EMFPPlusFillPolygon(region->regionPolyPolygon, flags & 0x8000, brushIndexOrColor);
                        else
                            SAL_WARN("drawinglayer.emf""EMF+\tEmfPlusRecordTypeFillRegion missing region");
                    }
                    break;
                    case EmfPlusRecordTypeDrawEllipse:
                    case EmfPlusRecordTypeFillEllipse:
                    {
                        // Intentionally very bogus initial value to avoid MSVC complaining about potentially uninitialized local
                        // variable. As long as the code stays as intended, this variable will be assigned a (real) value in the case
                        // when it is later used.
                        sal_uInt32 brushIndexOrColor = 1234567;

                        if (type == EmfPlusRecordTypeFillEllipse)
                        {
                            rMS.ReadUInt32(brushIndexOrColor);
                        }

                        SAL_INFO("drawinglayer.emf""EMF+\t " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff));
                        float dx, dy, dw, dh;
                        ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
                        SAL_INFO("drawinglayer.emf""EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
                        ::basegfx::B2DPolyPolygon polyPolygon(
                            ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(dx + 0.5 * dw, dy + 0.5 * dh),
                                                                       0.5 * dw, 0.5 * dh));
                        polyPolygon.transform(maMapTransform);
                        if (type == EmfPlusRecordTypeFillEllipse)
                            EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
                        else
                            EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
                    }
                    break;
                    case EmfPlusRecordTypeFillRects:
                    case EmfPlusRecordTypeDrawRects:
                    {
                        // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
                        sal_uInt32 brushIndexOrColor = 999;
                        ::basegfx::B2DPolyPolygon polyPolygon;
                        sal_uInt32 rectangles;
                        float x, y, width, height;
                        const bool isColor = (flags & 0x8000);
                        ::basegfx::B2DPolygon polygon;

                        if (EmfPlusRecordTypeFillRects == type)
                        {
                            SAL_INFO("drawinglayer.emf""EMF+\t FillRects");
                            rMS.ReadUInt32(brushIndexOrColor);
                            SAL_INFO("drawinglayer.emf""EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
                        }
                        else
                        {
                            SAL_INFO("drawinglayer.emf""EMF+\t DrawRects");
                        }

                        rMS.ReadUInt32(rectangles);
                        for (sal_uInt32 i = 0; i < rectangles; i++)
                        {
                            ReadRectangle(rMS, x, y, width, height, bool(flags & 0x4000));
                            polygon.clear();
                            polygon.append(Map(x, y));
                            polygon.append(Map(x + width, y));
                            polygon.append(Map(x + width, y + height));
                            polygon.append(Map(x, y + height));
                            polygon.setClosed(true);

                            SAL_INFO("drawinglayer.emf""EMF+\t\t rectangle: " << x << ", "<< y << " " << width << "x" << height);
                            polyPolygon.append(polygon);
                        }
                        if (type == EmfPlusRecordTypeFillRects)
                            EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor);
                        else
                            EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
                        break;
                    }
                    case EmfPlusRecordTypeFillPolygon:
                    {
                        sal_uInt32 brushIndexOrColor, points;

                        rMS.ReadUInt32(brushIndexOrColor);
                        rMS.ReadUInt32(points);
                        SAL_INFO("drawinglayer.emf""EMF+\t Points: " << points);
                        SAL_INFO("drawinglayer.emf""EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << " : 0x" << std::hex << brushIndexOrColor << std::dec);

                        EMFPPath path(points, true);
                        path.Read(rMS, flags);

                        EMFPPlusFillPolygon(path.GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
                        break;
                    }
                    case EmfPlusRecordTypeDrawLines:
                    {
                        sal_uInt32 points;
                        rMS.ReadUInt32(points);
                        SAL_INFO("drawinglayer.emf""EMF+\t Points: " << points);
                        EMFPPath path(points, true);
                        path.Read(rMS, flags);

                        // 0x2000 bit indicates whether to draw an extra line between the last point
                        // and the first point, to close the shape.
                        EMFPPlusDrawPolygon(path.GetPolygon(*thistrue, (flags & 0x2000)), flags);

                        break;
                    }
                    case EmfPlusRecordTypeDrawPath:
                    {
                        sal_uInt32 penIndex;
                        rMS.ReadUInt32(penIndex);
                        SAL_INFO("drawinglayer.emf""EMF+\t Pen: " << penIndex);

                        EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
                        if (path)
                            EMFPPlusDrawPolygon(path->GetPolygon(*this), penIndex);
                        else
                            SAL_WARN("drawinglayer.emf""\t\tEmfPlusRecordTypeDrawPath missing path");

                        break;
                    }
                    case EmfPlusRecordTypeDrawBeziers:
                    {
                        sal_uInt32 aCount;
                        float x1, y1, x2, y2, x3, y3, x4, y4;
                        ::basegfx::B2DPolygon aPolygon;
                        rMS.ReadUInt32(aCount);
                        SAL_INFO("drawinglayer.emf""EMF+\t DrawBeziers slot: " << (flags & 0xff));
                        SAL_INFO("drawinglayer.emf""EMF+\t Number of points: " << aCount);
                        SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf",
                                    "EMF+\t Bezier Draw not support number of points other than 4, 7, "
                                    "10, 13, 16...");

                        if (aCount < 4)
                        {
                            SAL_WARN("drawinglayer.emf""EMF+\t Bezier Draw does not support less "
                                                         "than 4 points. Number of points: "
                                                             << aCount);
                            break;
                        }

                        ReadPoint(rMS, x1, y1, flags);
                        // We need to add first starting point
                        aPolygon.append(Map(x1, y1));
                        SAL_INFO("drawinglayer.emf",
                                 "EMF+\t Bezier starting point: " << x1 << "," << y1);
                        for (sal_uInt32 i = 4; i <= aCount; i += 3)
                        {
                            ReadPoint(rMS, x2, y2, flags);
                            ReadPoint(rMS, x3, y3, flags);
                            ReadPoint(rMS, x4, y4, flags);

                            SAL_INFO("drawinglayer.emf",
                                     "EMF+\t Bezier points: " << x2 << "," << y2 << " " << x3 << ","
                                                              << y3 << " " << x4 << "," << y4);
                            aPolygon.appendBezierSegment(Map(x2, y2), Map(x3, y3), Map(x4, y4));
                        }
                        EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff);
                        break;
                    }
                    case EmfPlusRecordTypeDrawCurve:
                    {
                        sal_uInt32 aOffset, aNumSegments, points;
                        float aTension;
                        rMS.ReadFloat(aTension);
--> --------------------

--> maximum size reached

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

Messung V0.5
C=85 H=95 G=90

¤ Dauer der Verarbeitung: 0.23 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 und die Messung sind noch experimentell.