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

Quelle  emfreader.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 <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
#include <emfreader.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <vcl/dibtools.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/sprintf.hxx>
#include <tools/stream.hxx>
#include <memory>
#include <comphelper/configuration.hxx>
#include <vcl/graph.hxx>
#include <vcl/pdfread.hxx>
#include <rtl/bootstrap.hxx>

#ifdef DBG_UTIL
#include <vcl/filter/PngImageWriter.hxx>
#endif

// GDI-Array

#define EMR_HEADER                      1
#define EMR_POLYBEZIER                  2
#define EMR_POLYGON                     3
#define EMR_POLYLINE                    4
#define EMR_POLYBEZIERTO                5
#define EMR_POLYLINETO                  6
#define EMR_POLYPOLYLINE                7
#define EMR_POLYPOLYGON                 8
#define EMR_SETWINDOWEXTEX              9
#define EMR_SETWINDOWORGEX              10
#define EMR_SETVIEWPORTEXTEX            11
#define EMR_SETVIEWPORTORGEX            12
#define EMR_SETBRUSHORGEX               13
#define EMR_EOF                         14
#define EMR_SETPIXELV                   15
#define EMR_SETMAPPERFLAGS              16
#define EMR_SETMAPMODE                  17
#define EMR_SETBKMODE                   18
#define EMR_SETPOLYFILLMODE             19
#define EMR_SETROP2                     20
#define EMR_SETSTRETCHBLTMODE           21
#define EMR_SETTEXTALIGN                22
#define EMR_SETCOLORADJUSTMENT          23
#define EMR_SETTEXTCOLOR                24
#define EMR_SETBKCOLOR                  25
#define EMR_OFFSETCLIPRGN               26
#define EMR_MOVETOEX                    27
#define EMR_SETMETARGN                  28
#define EMR_EXCLUDECLIPRECT             29
#define EMR_INTERSECTCLIPRECT           30
#define EMR_SCALEVIEWPORTEXTEX          31
#define EMR_SCALEWINDOWEXTEX            32
#define EMR_SAVEDC                      33
#define EMR_RESTOREDC                   34
#define EMR_SETWORLDTRANSFORM           35
#define EMR_MODIFYWORLDTRANSFORM        36
#define EMR_SELECTOBJECT                37
#define EMR_CREATEPEN                   38
#define EMR_CREATEBRUSHINDIRECT         39
#define EMR_DELETEOBJECT                40
#define EMR_ANGLEARC                    41
#define EMR_ELLIPSE                     42
#define EMR_RECTANGLE                   43
#define EMR_ROUNDRECT                   44
#define EMR_ARC                         45
#define EMR_CHORD                       46
#define EMR_PIE                         47
#define EMR_SELECTPALETTE               48
#define EMR_CREATEPALETTE               49
#define EMR_SETPALETTEENTRIES           50
#define EMR_RESIZEPALETTE               51
#define EMR_REALIZEPALETTE              52
#define EMR_EXTFLOODFILL                53
#define EMR_LINETO                      54
#define EMR_ARCTO                       55
#define EMR_POLYDRAW                    56
#define EMR_SETARCDIRECTION             57
#define EMR_SETMITERLIMIT               58
#define EMR_BEGINPATH                   59
#define EMR_ENDPATH                     60
#define EMR_CLOSEFIGURE                 61
#define EMR_FILLPATH                    62
#define EMR_STROKEANDFILLPATH           63
#define EMR_STROKEPATH                  64
#define EMR_FLATTENPATH                 65
#define EMR_WIDENPATH                   66
#define EMR_SELECTCLIPPATH              67
#define EMR_ABORTPATH                   68

#define EMR_COMMENT                     70          // Contains arbitrary private data.
// Comment Identifiers:
#define EMR_COMMENT_EMFPLUS             0x2B464D45  // Contains embedded EMF+ records.
#define EMR_COMMENT_EMFSPOOL            0x00000000  // Contains embedded EMFSPOOL records.
#define EMR_COMMENT_PUBLIC              0x43494447  // Specify extensions to EMF processing.

#define EMR_FILLRGN                     71
#define EMR_FRAMERGN                    72
#define EMR_INVERTRGN                   73
#define EMR_PAINTRGN                    74
#define EMR_EXTSELECTCLIPRGN            75
#define EMR_BITBLT                      76
#define EMR_STRETCHBLT                  77
#define EMR_MASKBLT                     78
#define EMR_PLGBLT                      79
#define EMR_SETDIBITSTODEVICE           80
#define EMR_STRETCHDIBITS               81
#define EMR_EXTCREATEFONTINDIRECTW      82
#define EMR_EXTTEXTOUTA                 83
#define EMR_EXTTEXTOUTW                 84
#define EMR_POLYBEZIER16                85
#define EMR_POLYGON16                   86
#define EMR_POLYLINE16                  87
#define EMR_POLYBEZIERTO16              88
#define EMR_POLYLINETO16                89
#define EMR_POLYPOLYLINE16              90
#define EMR_POLYPOLYGON16               91
#define EMR_POLYDRAW16                  92
#define EMR_CREATEMONOBRUSH             93
#define EMR_CREATEDIBPATTERNBRUSHPT     94
#define EMR_EXTCREATEPEN                95
#define EMR_POLYTEXTOUTA                96
#define EMR_POLYTEXTOUTW                97

// WINDOWS VERSION >= 0x400
#define EMR_SETICMMODE                  98
#define EMR_CREATECOLORSPACE            99
#define EMR_SETCOLORSPACE              100
#define EMR_DELETECOLORSPACE           101
#define EMR_GLSRECORD                  102
#define EMR_GLSBOUNDEDRECORD           103
#define EMR_PIXELFORMAT                104

// WINDOWS VERSION >= 0x500
#define EMR_DRAWESCAPE                 105
#define EMR_EXTESCAPE                  106
#define EMR_STARTDOC                   107
#define EMR_SMALLTEXTOUT               108
#define EMR_FORCEUFIMAPPING            109
#define EMR_NAMEDESCAPE                110
#define EMR_COLORCORRECTPALETTE        111
#define EMR_SETICMPROFILEA             112
#define EMR_SETICMPROFILEW             113
#define EMR_ALPHABLEND                 114
#define EMR_ALPHADIBBLEND              115
#define EMR_TRANSPARENTBLT             116
#define EMR_TRANSPARENTDIB             117
#define EMR_GRADIENTFILL               118
#define EMR_SETLINKEDUFIS              119
#define EMR_SETTEXTJUSTIFICATION       120

#define PDF_SIGNATURE 0x50444620 // "PDF "

/* [MS-EMF] - v20210625 - page 28 */
constexpr sal_Int32 ARCDIRECTION_CLOCKWISE = 0x00000002;

namespace
{

/* [MS-EMF] - v20210625 - page 41 */
/* 2.1.26 Point Enumeration */
enum EMFPointTypes
{
    PT_CLOSEFIGURE = 0x01,
    PT_LINETO = 0x02,
    PT_BEZIERTO = 0x04,
    PT_MOVETO = 0x06
};

const char *
record_type_name(sal_uInt32 nRecType)
{
#ifndef SAL_LOG_INFO
    (void) nRecType;
    return "";
#else
    switch( nRecType )
    {
    case EMR_HEADER: return "HEADER";
    case EMR_POLYBEZIER: return "POLYBEZIER";
    case EMR_POLYGON: return "POLYGON";
    case EMR_POLYLINE: return "POLYLINE";
    case EMR_POLYBEZIERTO: return "POLYBEZIERTO";
    case EMR_POLYLINETO: return "POLYLINETO";
    case EMR_POLYPOLYLINE: return "POLYPOLYLINE";
    case EMR_POLYPOLYGON: return "POLYPOLYGON";
    case EMR_SETWINDOWEXTEX: return "SETWINDOWEXTEX";
    case EMR_SETWINDOWORGEX: return "SETWINDOWORGEX";
    case EMR_SETVIEWPORTEXTEX: return "SETVIEWPORTEXTEX";
    case EMR_SETVIEWPORTORGEX: return "SETVIEWPORTORGEX";
    case EMR_SETBRUSHORGEX: return "SETBRUSHORGEX";
    case EMR_EOF: return "EOF";
    case EMR_SETPIXELV: return "SETPIXELV";
    case EMR_SETMAPPERFLAGS: return "SETMAPPERFLAGS";
    case EMR_SETMAPMODE: return "SETMAPMODE";
    case EMR_SETBKMODE: return "SETBKMODE";
    case EMR_SETPOLYFILLMODE: return "SETPOLYFILLMODE";
    case EMR_SETROP2: return "SETROP2";
    case EMR_SETSTRETCHBLTMODE: return "SETSTRETCHBLTMODE";
    case EMR_SETTEXTALIGN: return "SETTEXTALIGN";
    case EMR_SETCOLORADJUSTMENT: return "SETCOLORADJUSTMENT";
    case EMR_SETTEXTCOLOR: return "SETTEXTCOLOR";
    case EMR_SETBKCOLOR: return "SETBKCOLOR";
    case EMR_OFFSETCLIPRGN: return "OFFSETCLIPRGN";
    case EMR_MOVETOEX: return "MOVETOEX";
    case EMR_SETMETARGN: return "SETMETARGN";
    case EMR_EXCLUDECLIPRECT: return "EXCLUDECLIPRECT";
    case EMR_INTERSECTCLIPRECT: return "INTERSECTCLIPRECT";
    case EMR_SCALEVIEWPORTEXTEX: return "SCALEVIEWPORTEXTEX";
    case EMR_SCALEWINDOWEXTEX: return "SCALEWINDOWEXTEX";
    case EMR_SAVEDC: return "SAVEDC";
    case EMR_RESTOREDC: return "RESTOREDC";
    case EMR_SETWORLDTRANSFORM: return "SETWORLDTRANSFORM";
    case EMR_MODIFYWORLDTRANSFORM: return "MODIFYWORLDTRANSFORM";
    case EMR_SELECTOBJECT: return "SELECTOBJECT";
    case EMR_CREATEPEN: return "CREATEPEN";
    case EMR_CREATEBRUSHINDIRECT: return "CREATEBRUSHINDIRECT";
    case EMR_DELETEOBJECT: return "DELETEOBJECT";
    case EMR_ANGLEARC: return "ANGLEARC";
    case EMR_ELLIPSE: return "ELLIPSE";
    case EMR_RECTANGLE: return "RECTANGLE";
    case EMR_ROUNDRECT: return "ROUNDRECT";
    case EMR_ARC: return "ARC";
    case EMR_CHORD: return "CHORD";
    case EMR_PIE: return "PIE";
    case EMR_SELECTPALETTE: return "SELECTPALETTE";
    case EMR_CREATEPALETTE: return "CREATEPALETTE";
    case EMR_SETPALETTEENTRIES: return "SETPALETTEENTRIES";
    case EMR_RESIZEPALETTE: return "RESIZEPALETTE";
    case EMR_REALIZEPALETTE: return "REALIZEPALETTE";
    case EMR_EXTFLOODFILL: return "EXTFLOODFILL";
    case EMR_LINETO: return "LINETO";
    case EMR_ARCTO: return "ARCTO";
    case EMR_POLYDRAW: return "POLYDRAW";
    case EMR_SETARCDIRECTION: return "SETARCDIRECTION";
    case EMR_SETMITERLIMIT: return "SETMITERLIMIT";
    case EMR_BEGINPATH: return "BEGINPATH";
    case EMR_ENDPATH: return "ENDPATH";
    case EMR_CLOSEFIGURE: return "CLOSEFIGURE";
    case EMR_FILLPATH: return "FILLPATH";
    case EMR_STROKEANDFILLPATH: return "STROKEANDFILLPATH";
    case EMR_STROKEPATH: return "STROKEPATH";
    case EMR_FLATTENPATH: return "FLATTENPATH";
    case EMR_WIDENPATH: return "WIDENPATH";
    case EMR_SELECTCLIPPATH: return "SELECTCLIPPATH";
    case EMR_ABORTPATH: return "ABORTPATH";
    case EMR_COMMENT: return "COMMENT";
    case EMR_FILLRGN: return "FILLRGN";
    case EMR_FRAMERGN: return "FRAMERGN";
    case EMR_INVERTRGN: return "INVERTRGN";
    case EMR_PAINTRGN: return "PAINTRGN";
    case EMR_EXTSELECTCLIPRGN: return "EXTSELECTCLIPRGN";
    case EMR_BITBLT: return "BITBLT";
    case EMR_STRETCHBLT: return "STRETCHBLT";
    case EMR_MASKBLT: return "MASKBLT";
    case EMR_PLGBLT: return "PLGBLT";
    case EMR_SETDIBITSTODEVICE: return "SETDIBITSTODEVICE";
    case EMR_STRETCHDIBITS: return "STRETCHDIBITS";
    case EMR_EXTCREATEFONTINDIRECTW: return "EXTCREATEFONTINDIRECTW";
    case EMR_EXTTEXTOUTA: return "EXTTEXTOUTA";
    case EMR_EXTTEXTOUTW: return "EXTTEXTOUTW";
    case EMR_POLYBEZIER16: return "POLYBEZIER16";
    case EMR_POLYGON16: return "POLYGON16";
    case EMR_POLYLINE16: return "POLYLINE16";
    case EMR_POLYBEZIERTO16: return "POLYBEZIERTO16";
    case EMR_POLYLINETO16: return "POLYLINETO16";
    case EMR_POLYPOLYLINE16: return "POLYPOLYLINE16";
    case EMR_POLYPOLYGON16: return "POLYPOLYGON16";
    case EMR_POLYDRAW16: return "POLYDRAW16";
    case EMR_CREATEMONOBRUSH: return "CREATEMONOBRUSH";
    case EMR_CREATEDIBPATTERNBRUSHPT: return "CREATEDIBPATTERNBRUSHPT";
    case EMR_EXTCREATEPEN: return "EXTCREATEPEN";
    case EMR_POLYTEXTOUTA: return "POLYTEXTOUTA";
    case EMR_POLYTEXTOUTW: return "POLYTEXTOUTW";
    case EMR_SETICMMODE: return "SETICMMODE";
    case EMR_CREATECOLORSPACE: return "CREATECOLORSPACE";
    case EMR_SETCOLORSPACE: return "SETCOLORSPACE";
    case EMR_DELETECOLORSPACE: return "DELETECOLORSPACE";
    case EMR_GLSRECORD: return "GLSRECORD";
    case EMR_GLSBOUNDEDRECORD: return "GLSBOUNDEDRECORD";
    case EMR_PIXELFORMAT: return "PIXELFORMAT";
    case EMR_DRAWESCAPE: return "DRAWESCAPE";
    case EMR_EXTESCAPE: return "EXTESCAPE";
    case EMR_STARTDOC: return "STARTDOC";
    case EMR_SMALLTEXTOUT: return "SMALLTEXTOUT";
    case EMR_FORCEUFIMAPPING: return "FORCEUFIMAPPING";
    case EMR_NAMEDESCAPE: return "NAMEDESCAPE";
    case EMR_COLORCORRECTPALETTE: return "COLORCORRECTPALETTE";
    case EMR_SETICMPROFILEA: return "SETICMPROFILEA";
    case EMR_SETICMPROFILEW: return "SETICMPROFILEW";
    case EMR_ALPHABLEND: return "ALPHABLEND";
    case EMR_ALPHADIBBLEND: return "ALPHADIBBLEND";
    case EMR_TRANSPARENTBLT: return "TRANSPARENTBLT";
    case EMR_TRANSPARENTDIB: return "TRANSPARENTDIB";
    case EMR_GRADIENTFILL: return "GRADIENTFILL";
    case EMR_SETLINKEDUFIS: return "SETLINKEDUFIS";
    case EMR_SETTEXTJUSTIFICATION: return "SETTEXTJUSTIFICATION";
    default:
        // Yes, return a pointer to a static buffer. This is a very
        // local debugging output function, so no big deal.
        static char buffer[11];
        o3tl::sprintf(buffer, "0x%08" SAL_PRIxUINT32, nRecType);
        return buffer;
    }
#endif
}

struct BLENDFUNCTION
{
    unsigned char aBlendOperation;
    unsigned char aBlendFlags;
    unsigned char aSrcConstantAlpha;
    unsigned char aAlphaFormat;

    friend SvStream& operator>>(SvStream& rInStream, BLENDFUNCTION& rBlendFun);
};

SvStream& operator>>(SvStream& rInStream, BLENDFUNCTION& rBlendFun)
{
    rInStream.ReadUChar(rBlendFun.aBlendOperation);
    rInStream.ReadUChar(rBlendFun.aBlendFlags);
    rInStream.ReadUChar(rBlendFun.aSrcConstantAlpha);
    rInStream.ReadUChar(rBlendFun.aAlphaFormat);
    return rInStream;
}

bool ImplReadRegion( basegfx::B2DPolyPolygon& rPolyPoly, SvStream& rStream, sal_uInt32 nLen, Point aWinOrg )
{
    if (nLen < 32) // 32 bytes - Size of RegionDataHeader
        return false;

    sal_uInt32 nHdSize, nType, nCountRects, nRgnSize;
    rStream.ReadUInt32(nHdSize);
    rStream.ReadUInt32(nType);
    rStream.ReadUInt32(nCountRects);
    rStream.ReadUInt32(nRgnSize);

    sal_Int32 nLeft, nTop, nRight, nBottom;
    //bounds of the region
    rStream.ReadInt32(nLeft);
    rStream.ReadInt32(nTop);
    rStream.ReadInt32(nRight);
    rStream.ReadInt32(nBottom);

    if (!rStream.good() || nCountRects == 0 || nType != emfio::RDH_RECTANGLES)
        return false;

    SAL_INFO("emfio""\t\tBounds Left: " << nLeft << ", top: " << nTop << ", right: " << nRight << ", bottom: " << nBottom);

    nLen -= 32;

    sal_uInt32 nSize;
    if (o3tl::checked_multiply<sal_uInt32>(nCountRects, 16, nSize))
        return false;
    if (nLen < nSize)
        return false;

    for (sal_uInt32 i = 0; i < nCountRects; ++i)
    {
        rStream.ReadInt32(nLeft);
        rStream.ReadInt32(nTop);
        rStream.ReadInt32(nRight);
        rStream.ReadInt32(nBottom);
        nLeft += aWinOrg.X();
        nRight += aWinOrg.X();
        nTop += aWinOrg.Y();
        nBottom += aWinOrg.Y();
        rPolyPoly.append( basegfx::utils::createPolygonFromRect( ::basegfx::B2DRectangle( nLeft, nTop, nRight, nBottom ) ) );
        SAL_INFO("emfio""\t\t" << i << " Left: " << nLeft << ", top: " << nTop << ", right: " << nRight << ", bottom: " << nBottom);
    }
    if (!comphelper::IsFuzzing())
    {
        rPolyPoly = basegfx::utils::solveCrossovers(rPolyPoly);
        rPolyPoly = basegfx::utils::stripNeutralPolygons(rPolyPoly);
        rPolyPoly = basegfx::utils::stripDispensablePolygons(rPolyPoly);
    }
    return true;
}

// anonymous namespace

namespace emfio
{
    EmfReader::EmfReader(SvStream& rStream,GDIMetaFile& rGDIMetaFile)
        : MtfTools(rGDIMetaFile, rStream)
        , mnRecordCount(0)
        , mbRecordPath(false)
        , mbEMFPlus(false)
        , mbEMFPlusDualMode(false)
    {
    }

    EmfReader::~EmfReader()
    {
    }

    const sal_uInt32 EMR_COMMENT_BEGINGROUP = 0x00000002;
    const sal_uInt32 EMR_COMMENT_ENDGROUP = 0x00000003;
    const sal_uInt32 EMR_COMMENT_MULTIFORMATS = 0x40000004;
    const sal_uInt32 EMR_COMMENT_WINDOWS_METAFILE = 0x80000001;

    void EmfReader::ReadGDIComment(sal_uInt32 nCommentId)
    {
        sal_uInt32 nPublicCommentIdentifier(0);
        mpInputStream->ReadUInt32(nPublicCommentIdentifier);

        SAL_INFO("emfio""\t\tEMR_COMMENT_PUBLIC, id: 0x" << std::hex << nCommentId << std::dec);
        switch (nPublicCommentIdentifier)
        {
            case EMR_COMMENT_BEGINGROUP:
            {
                SAL_INFO("emfio""\t\t\tEMR_COMMENT_BEGINGROUP");
                sal_uInt32 left, top, right, bottom;
                mpInputStream->ReadUInt32(left).ReadUInt32(top).ReadUInt32(right).ReadUInt32(bottom);

                SAL_INFO("emfio""\t\t\t\tBounding rect");
                SAL_INFO("emfio""\t\t\t\t\tLeft: " << left);
                SAL_INFO("emfio""\t\t\t\t\tTop: " << top);
                SAL_INFO("emfio""\t\t\t\t\tRight: " << right);
                SAL_INFO("emfio""\t\t\t\t\tBottom: " << bottom);

                sal_uInt32 nDescChars(0);
                mpInputStream->ReadUInt32(nDescChars);

                OUString aDesc;
                for (sal_uInt32 i=0; i < nDescChars; i++)
                {
                    sal_uInt16 cChar(0);
                    mpInputStream->ReadUInt16(cChar);
                    if (cChar == '\0')
                        break;

                    sal_Unicode cUniChar = static_cast<sal_Unicode>(cChar);
                    aDesc = aDesc + OUStringChar(cUniChar);
                }

                SAL_INFO("emfio""\t\tDescription: " << aDesc);
            }
            break;

            case EMR_COMMENT_ENDGROUP:
                SAL_INFO("emfio""\t\t\tEMR_COMMENT_ENDGROUP");
                break;

            case EMR_COMMENT_MULTIFORMATS:
                ReadMultiformatsComment();
                break;

            case EMR_COMMENT_WINDOWS_METAFILE:
                SAL_WARN("emfio""\t\tEMR_COMMENT_WINDOWS_METAFILE not implemented");
                break;

            default:
                SAL_WARN("emfio""\t\tEMR_COMMENT_PUBLIC not implemented, id: 0x" << std::hex << nCommentId << std::dec);
                break;
        }
    }

    void EmfReader::ReadMultiformatsComment()
    {
        tools::Rectangle aOutputRect = EmfReader::ReadRectangle();

        sal_uInt32 nCountFormats(0);
        mpInputStream->ReadUInt32(nCountFormats);
        if (nCountFormats < 1)
        {
            return;
        }

        // Read the first EmrFormat.
        sal_uInt32 nSignature(0);
        mpInputStream->ReadUInt32(nSignature);
        if (nSignature != PDF_SIGNATURE)
        {
            return;
        }

        sal_uInt32 nVersion(0);
        mpInputStream->ReadUInt32(nVersion);
        if (nVersion != 1)
        {
            return;
        }

        sal_uInt32 nSizeData(0);
        mpInputStream->ReadUInt32(nSizeData);
        if (!nSizeData || nSizeData > mpInputStream->remainingSize())
        {
            return;
        }

        sal_uInt32 nOffData(0);
        mpInputStream->ReadUInt32(nOffData);
        if (!nOffData)
        {
            return;
        }

        std::vector<char> aPdfData(nSizeData);
        mpInputStream->ReadBytes(aPdfData.data(), aPdfData.size());
        if (!mpInputStream->good())
        {
            return;
        }

        SvMemoryStream aPdfStream;
        aPdfStream.WriteBytes(aPdfData.data(), aPdfData.size());
        aPdfStream.Seek(0);
        Graphic aGraphic;
        if (!vcl::ImportPDF(aPdfStream, aGraphic))
        {
            return;
        }

        // aGraphic will be the only output of the EMF parser, so its size hint can be the same as
        // ours.
        aGraphic.getVectorGraphicData()->setSizeHint(maSizeHint);

        maBmpSaveList.emplace_back(
            aGraphic.GetBitmapEx(), aOutputRect, SRCCOPY, /*bForceAlpha=*/true);
        const std::shared_ptr<VectorGraphicData> pVectorGraphicData
            = aGraphic.getVectorGraphicData();
        if (!pVectorGraphicData)
        {
            return;
        }

        if (pVectorGraphicData->getType() != VectorGraphicDataType::Pdf)
        {
            return;
        }

        mbReadOtherGraphicFormat = true;
    }

    void EmfReader::ReadEMFPlusComment(sal_uInt32 length, bool& bHaveDC)
    {
        if (!mbEMFPlus)
        {
            PassEMFPlusHeaderInfo();

    #if OSL_DEBUG_LEVEL > 1
            // debug code - write the stream to debug file /tmp/emf-stream.emf
            sal_uInt64 const pos = mpInputStream->Tell();
            mpInputStream->Seek(0);
            SvFileStream file( OUString( "/tmp/emf-stream.emf" ), StreamMode::WRITE | StreamMode::TRUNC );

            mpInputStream->WriteStream(file);
            file.Flush();
            file.Close();

            mpInputStream->Seek( pos );
    #endif

        }

        mbEMFPlus = true;
        sal_uInt64 const pos = mpInputStream->Tell();
        auto buffer = std::make_unique<char[]>( length );
        PassEMFPlus( buffer.get(), mpInputStream->ReadBytes(buffer.get(), length) );
        buffer.reset();
        mpInputStream->Seek( pos );

        bHaveDC = false;

        // skip in SeekRel if impossibly unavailable
        sal_uInt32 nRemainder = length;

        const size_t nRequiredHeaderSize = 12;
        while (nRemainder >= nRequiredHeaderSize)
        {
            sal_uInt16 type(0), flags(0);
            sal_uInt32 size(0), dataSize(0);

            mpInputStream->ReadUInt16( type ).ReadUInt16( flags ).ReadUInt32( size ).ReadUInt32( dataSize );
            nRemainder -= nRequiredHeaderSize;

            SAL_INFO("emfio""\t\tEMF+ record type: 0x" << std::hex << type << std::dec);

            // Get Device Context
            // TODO We should use  EmfPlusRecordType::GetDC instead
            if( type == 0x4004 )
            {
                bHaveDC = true;
                SAL_INFO("emfio""\t\tEMF+ lock DC (device context)");
            }

            // look for the "dual mode" in header
            // it indicates that either EMF or EMF+ records should be processed
            // 0x4001    = EMF+ header
            // flags & 1 = dual mode active
            if ( type == 0x4001 && flags & 1 )
            {
                mbEMFPlusDualMode = true;
                SAL_INFO ("emfio""\t\tEMF+ dual mode detected");
            }

            // Get the length of the remaining data of this record based
            // on the alleged size
            sal_uInt32 nRemainingRecordData = size >= nRequiredHeaderSize ?
                size-nRequiredHeaderSize : 0;
            // clip to available size
            nRemainingRecordData = std::min(nRemainingRecordData, nRemainder);
            mpInputStream->SeekRel(nRemainingRecordData);
            nRemainder -= nRemainingRecordData;
        }
        mpInputStream->SeekRel(nRemainder);
    }

    // these are referenced from inside the templates
    static SvStream& operator >> (SvStream& rStream, sal_Int16 &n)
    {
        return rStream.ReadInt16(n);
    }

    static SvStream& operator >> (SvStream& rStream, sal_Int32 &n)
    {
        return rStream.ReadInt32(n);
    }

    /**
     * Reads polygons from the stream.
     * The \<class T> parameter is for the type of the points (sal_uInt32 or sal_uInt16).
     * skipFirst: if the first point read is the 0th point or the 1st point in the array.
     * */

    template <class T>
    tools::Polygon EmfReader::ReadPolygonWithSkip(const bool skipFirst, sal_uInt32 nNextPos)
    {
        sal_uInt32 nPoints(0), nStartIndex(0);
        mpInputStream->SeekRel( 16 );
        mpInputStream->ReadUInt32( nPoints );
        if (skipFirst)
        {
            nPoints ++;
            nStartIndex ++;
        }

        return ReadPolygon<T>(nStartIndex, nPoints, nNextPos);
    }

    /**
     * Reads polygons from the stream.
     * The \<class T> parameter is for the type of the points
     * nStartIndex: which is the starting index in the polygon of the first point read
     * nPoints: number of points
     * mpInputStream: the stream containing the polygons
     * */

    template <class T>
    tools::Polygon EmfReader::ReadPolygon(sal_uInt32 nStartIndex, sal_uInt32 nPoints, sal_uInt32 nNextPos)
    {
        SAL_INFO ("emfio""\t\tPolygon:");

        bool bRecordOk = nPoints <= SAL_MAX_UINT16;
        SAL_WARN_IF(!bRecordOk, "emfio""polygon record has more polygons than we can handle");
        if (!bRecordOk || !nPoints)
            return tools::Polygon();

        auto nRemainingSize = std::min(nNextPos - mpInputStream->Tell(), mpInputStream->remainingSize());
        auto nMaxPossiblePoints = nRemainingSize / (sizeof(T) * 2);
        auto nPointCount = nPoints - nStartIndex;
        if (nPointCount > nMaxPossiblePoints)
        {
            SAL_WARN("emfio""polygon claims more points than record can provide, truncating");
            nPoints = nMaxPossiblePoints + nStartIndex;
        }

        tools::Polygon aPolygon(nPoints);
        for (sal_uInt32 i = nStartIndex ; i < nPoints && mpInputStream->good(); i++ )
        {
            T nX, nY;
            *mpInputStream >> nX >> nY;

            SAL_INFO("emfio""\t\t\tPoint " << i << " of " << nPoints - 1 << ": " << nX << ", " << nY);

            if (!mpInputStream->good())
            {
                SAL_WARN("emfio""short read on polygon, truncating");
                aPolygon.SetSize(i);
                break;
            }
            aPolygon[ i ] = Point( nX, nY );
        }

        return aPolygon;
    }

    /**
     * Reads a polyline from the WMF file and draws it
     * The \<class T> parameter refers to the type of the points. (e.g. sal_uInt16 or sal_uInt32)
     * */

    template <class T>
    void EmfReader::ReadAndDrawPolyLine(sal_uInt32 nNextPos)
    {
        SAL_INFO("emfio""\t\tPolyline: ");

        mpInputStream->SeekRel( 0x10 ); // TODO Skipping Bounds. A 128-bit WMF RectL object (specifies the bounding rectangle in device units.)

        sal_uInt32 nNumberOfPolylines = 0;
        mpInputStream->ReadUInt32( nNumberOfPolylines );
        SAL_INFO("emfio""\t\t\tPolylines: " << nNumberOfPolylines);

        sal_uInt32 nCount = 0;
        mpInputStream->ReadUInt32( nCount ); // total number of points in all polylines
        SAL_INFO("emfio""\t\t\tPoints: " << nCount);

        const auto nEndPos = std::min(nNextPos, mnEndPos);
        if (mpInputStream->Tell() >= nEndPos)
            return;

        // taking the amount of points of each polygon, retrieving the total number of points
        if ( !(mpInputStream->good() &&
             ( nNumberOfPolylines < SAL_MAX_UINT32 / sizeof( sal_uInt16 ) ) &&
             ( nNumberOfPolylines * sizeof( sal_uInt16 ) ) <= ( nEndPos - mpInputStream->Tell() ))
           )
            return;

        std::unique_ptr< sal_uInt32[] > pnPolylinePointCount( new sal_uInt32[ nNumberOfPolylines ] );
        for ( sal_uInt32 i = 0; i < nNumberOfPolylines && mpInputStream->good(); i++ )
        {
            sal_uInt32 nPoints;
            mpInputStream->ReadUInt32( nPoints );
            SAL_INFO("emfio""\t\t\tPoint " << i << " of " << nNumberOfPolylines << ": " << nPoints);
            pnPolylinePointCount[ i ] = nPoints;
        }

        // Get polyline points:
        for ( sal_uInt32 i = 0; ( i < nNumberOfPolylines ) && mpInputStream->good(); i++ )
        {
            tools::Polygon aPolygon = ReadPolygon<T>(0, pnPolylinePointCount[i], nNextPos);
            DrawPolyLine(std::move(aPolygon), false, mbRecordPath);
        }
    }

    /**
     * Reads a poly polygon from the WMF file and draws it.
     * The \<class T> parameter refers to the type of the points. (e.g. sal_uInt16 or sal_uInt32)
     * */

    template <class T>
    void EmfReader::ReadAndDrawPolyPolygon(sal_uInt32 nNextPos)
    {
        SAL_INFO("emfio""\t\tPolygon: ");
        mpInputStream->SeekRel( 0x10 ); // RectL bounds

        sal_uInt32 nPoly(0), nGesPoints(0), nReadPoints(0);
        // Number of polygons
        mpInputStream->ReadUInt32( nPoly ).ReadUInt32( nGesPoints );
        SAL_INFO("emfio""\t\t\tPolygons: " << nPoly);
        SAL_INFO("emfio""\t\t\tPoints: " << nGesPoints);

        const auto nEndPos = std::min(nNextPos, mnEndPos);
        if (mpInputStream->Tell() >= nEndPos)
            return;
        if (!mpInputStream->good())
            return;
        //check against numeric overflowing
        if (nGesPoints >= SAL_MAX_UINT32 / sizeof(Point))
            return;
        if (nPoly >= SAL_MAX_UINT32 / sizeof(sal_uInt16))
            return;
        if (nPoly * sizeof(sal_uInt16) > nEndPos - mpInputStream->Tell())
            return;

        // Get number of points in each polygon
        std::vector<sal_uInt16> aPoints(nPoly);
        for (sal_uInt32 i = 0; i < nPoly && mpInputStream->good(); ++i)
        {
            sal_uInt32 nPoints(0);
            mpInputStream->ReadUInt32( nPoints );

            SAL_INFO("emfio""\t\t\t\tPolygon " << i << " points: " << nPoints);

            aPoints[i] = static_cast<sal_uInt16>(nPoints);
        }

        if ( mpInputStream->good() && ( nGesPoints * (sizeof(T)+sizeof(T)) ) <= ( nEndPos - mpInputStream->Tell() ) )
        {
            // Get polygon points
            tools::PolyPolygon aPolyPoly(nPoly);
            for (sal_uInt32 i = 0; i < nPoly && mpInputStream->good(); ++i)
            {
                const sal_uInt16 nPointCount(aPoints[i]);
                std::vector<Point> aPtAry(nPointCount);
                for (sal_uInt16 j = 0; j < nPointCount && mpInputStream->good(); ++j)
                {
                    T nX(0), nY(0);
                    *mpInputStream >> nX >> nY;
                    aPtAry[j] = Point( nX, nY );
                    ++nReadPoints;
                }

                aPolyPoly.Insert(tools::Polygon(aPtAry.size(), aPtAry.data()));
            }

            DrawPolyPolygon(aPolyPoly, mbRecordPath);
        }

        OSL_ENSURE(nReadPoints == nGesPoints, "The number Points processed from EMR_POLYPOLYGON is unequal imported number (!)");
    }

    bool EmfReader::ReadEnhWMF()
    {
        sal_uInt32  nStretchBltMode = 0;
        sal_uInt32  nNextPos(0),
                    nW(0), nH(0), nColor(0), nIndex(0),
                    nDat32(0), nNom1(0), nDen1(0), nNom2(0), nDen2(0);
        sal_Int32   nX32(0), nY32(0), nx32(0), ny32(0);

        bool    bStatus = ReadHeader();
        bool    bHaveDC = false;

        OUString aEMFPlusDisable;
        rtl::Bootstrap::get(u"EMF_PLUS_DISABLE"_ustr, aEMFPlusDisable);
        bool bEnableEMFPlus = aEMFPlusDisable.isEmpty();
        if (!mbEnableEMFPlus)
        {
            // EMF+ is enabled if neither the bootstrap variable, not the member variable disables
            // it.
            bEnableEMFPlus = mbEnableEMFPlus;
        }

        SAL_INFO("emfio""EMF+ reading is " << (bEnableEMFPlus ? "enabled" : "disabled"));

        while (bStatus && mnRecordCount-- && mpInputStream->good() && !mbReadOtherGraphicFormat)
        {
            sal_uInt32  nRecType(0), nRecSize(0);
            mpInputStream->ReadUInt32(nRecType).ReadUInt32(nRecSize);

            if ( !mpInputStream->good() || ( nRecSize < 8 ) || ( nRecSize & 3 ) )     // Parameters are always divisible by 4
            {
                bStatus = false;
                break;
            }

            auto nCurPos = mpInputStream->Tell();

            if (mnEndPos < nCurPos - 8)
            {
                bStatus = false;
                break;
            }

            const sal_uInt32 nMaxPossibleRecSize = mnEndPos - (nCurPos - 8);
            if (nRecSize > nMaxPossibleRecSize)
            {
                bStatus = false;
                break;
            }

            nNextPos = nCurPos + (nRecSize - 8);

            if(  !maBmpSaveList.empty()
              && ( nRecType != EMR_STRETCHBLT )
              && ( nRecType != EMR_STRETCHDIBITS )
              ) {
                ResolveBitmapActions( maBmpSaveList );
            }

            bool bFlag = false;

            SAL_INFO("emfio""0x" << std::hex << (nNextPos - nRecSize) <<  "-0x" << nNextPos << " " << record_type_name(nRecType) << " size: "
                                    << std::dec << nRecSize);

            if( bEnableEMFPlus && nRecType == EMR_COMMENT )
            {
                sal_uInt32 length;

                mpInputStream->ReadUInt32( length );

                SAL_INFO("emfio""\tGDI comment, length: " << length);

                if( mpInputStream->good() && length >= 4 && length <= mpInputStream->remainingSize() ) {
                    sal_uInt32 nCommentId;

                    mpInputStream->ReadUInt32( nCommentId );

                    SAL_INFO("emfio""\t\tbegin " << static_cast<char>(nCommentId & 0xff) << static_cast<char>((nCommentId & 0xff00) >> 8) << static_cast<char>((nCommentId & 0xff0000) >> 16) << static_cast<char>((nCommentId & 0xff000000) >> 24) << " id: 0x" << std::hex << nCommentId << std::dec);

                    if( nCommentId == EMR_COMMENT_EMFPLUS && nRecSize >= 12 )
                    {
                        // [MS-EMF] 2.3.3: DataSize includes both CommentIdentifier and CommentRecordParm fields.
                        // We have already read 4-byte CommentIdentifier, so reduce length appropriately
                        ReadEMFPlusComment( length-4, bHaveDC );
                    }
                    else if( nCommentId == EMR_COMMENT_PUBLIC && nRecSize >= 12 )
                    {
                        ReadGDIComment(nCommentId);
                    }
                    else if( nCommentId == EMR_COMMENT_EMFSPOOL && nRecSize >= 12 )
                    {
                        SAL_WARN("emfio""\t\tEMFSPOOL not implemented, id: 0x" << std::hex << nCommentId << std::dec);
                        // TODO Implement reading EMFSPOOL comment

                    }
                    else
                    {
                        SAL_WARN("emfio""\t\tunknown id: 0x" << std::hex << nCommentId << std::dec);
                    }
                }
            }
            else if ( !bHaveDC && mbEMFPlusDualMode && nRecType != EMR_HEADER && nRecType != EMR_EOF )
            {
                // skip content (EMF record) in dual mode
                // we process only EMR_COMMENT (see above) to access EMF+ data
                // with 2 exceptions, according to EMF+ specification:
                // EMR_HEADER and EMR_EOF
                // if a device context is given (bHaveDC) process the following EMF record, too.
            }
            else if( !mbEMFPlus || bHaveDC || nRecType == EMR_EOF )
            {
                switch( nRecType )
                {
                    case EMR_POLYBEZIERTO :
                        DrawPolyBezier(ReadPolygonWithSkip<sal_Int32>(true, nNextPos), true, mbRecordPath);
                    break;
                    case EMR_POLYBEZIER :
                        DrawPolyBezier(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), false, mbRecordPath);
                    break;

                    case EMR_POLYGON :
                        DrawPolygon(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), mbRecordPath);
                    break;

                    case EMR_POLYLINETO :
                        DrawPolyLine(ReadPolygonWithSkip<sal_Int32>(true, nNextPos), true, mbRecordPath);
                    break;

                    case EMR_POLYDRAW:
                    {
                        sal_uInt32 nPointsCount(0), nBezierCount(0);
                        std::vector<Point> aPoints;
                        bool wrongFile = false;
                        std::vector<unsigned char> aPointTypes;
                        mpInputStream->ReadInt32(nX32)
                            .ReadInt32(nY32)
                            .ReadInt32(nx32)
                            .ReadInt32(ny32)
                            .ReadUInt32(nPointsCount);

                        aPoints.reserve(std::min<size_t>(nPointsCount, mpInputStream->remainingSize() / (sizeof(sal_Int32) * 2)));
                        for (sal_uInt32 i = 0; i < nPointsCount && mpInputStream->good(); i++)
                        {
                            sal_Int32 nX, nY;
                            *mpInputStream >> nX >> nY;
                            aPoints.emplace_back(nX, nY);
                        }
                        aPointTypes.reserve(std::min<size_t>(nPointsCount, mpInputStream->remainingSize()));
                        for (sal_uInt32 i = 0; i < nPointsCount && mpInputStream->good(); i++)
                        {
                            unsigned char nPointType(0);
                            mpInputStream->ReadUChar(nPointType);
                            aPointTypes.push_back(nPointType);
                        }
                        nPointsCount = std::min(aPoints.size(), aPointTypes.size());
                        for (sal_uInt32 i = 0; i < nPointsCount; i++)
                        {
                            SAL_INFO_IF(aPointTypes[i] == PT_MOVETO, "emfio",
                                        "\t\t" << i << "/" << nPointsCount - 1 << " PT_MOVETO, "
                                               << aPoints[i].getX() << ", " << aPoints[i].getY());
                            SAL_INFO_IF((aPointTypes[i] != PT_MOVETO) && (aPointTypes[i] & PT_LINETO), "emfio",
                                        "\t\t" << i << "/" << nPointsCount - 1 << " PT_LINETO, "
                                               << aPoints[i].getX() << ", " << aPoints[i].getY());
                            SAL_INFO_IF((aPointTypes[i] != PT_MOVETO) && (aPointTypes[i] & PT_CLOSEFIGURE), "emfio",
                                        "\t\t" << i << "/" << nPointsCount - 1 << " PT_CLOSEFIGURE, "
                                               << aPoints[i].getX() << ", " << aPoints[i].getY());
                            SAL_INFO_IF((aPointTypes[i] != PT_MOVETO) && (aPointTypes[i] & PT_BEZIERTO), "emfio",
                                        "\t\t" << i << "/" << nPointsCount - 1 << " PT_BEZIERTO, "
                                               << aPoints[i].getX() << ", " << aPoints[i].getY());

                            if ((aPointTypes[i] != PT_MOVETO) && (aPointTypes[i] & PT_BEZIERTO))
                                nBezierCount++;
                            else if (nBezierCount % 3 == 0)
                                nBezierCount = 0;
                            else
                            {
                                SAL_WARN(
                                    "emfio",
                                    "EMF file error: Number of Bezier points is not set of three.");
                                wrongFile = true;
                            }
                        }
                        if (wrongFile) break;
                        for (sal_uInt32 i = 0; i < nPointsCount; i++)
                        {
                            if (aPointTypes[i] == PT_MOVETO)
                                MoveTo(aPoints[i], true);
                            else if (aPointTypes[i] & PT_LINETO)
                            {
                                LineTo(aPoints[i], true);
                                if (aPointTypes[i] & PT_CLOSEFIGURE)
                                    ClosePath();
                            }
                            else if (aPointTypes[i] & PT_BEZIERTO)
                            {
                                if (nPointsCount - i < 3)
                                {
                                    SAL_WARN("emfio""EMF file error: Not enough Bezier points.");
                                    break;
                                }
                                tools::Polygon aPolygon(4);
                                aPolygon[0] = maActPos;
                                aPolygon[1] = aPoints[i++];
                                aPolygon[2] = aPoints[i++];
                                aPolygon[3] = aPoints[i];
                                DrawPolyBezier(std::move(aPolygon), truetrue);
                                if (aPointTypes[i] & PT_CLOSEFIGURE)
                                    ClosePath();
                            }
                        }
                        StrokeAndFillPath(truefalse);
                    }
                    break;

                    case EMR_POLYLINE :
                        DrawPolyLine(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), false, mbRecordPath);
                    break;

                    case EMR_POLYPOLYLINE :
                        ReadAndDrawPolyLine<sal_Int32>(nNextPos);
                    break;

                    case EMR_POLYPOLYGON :
                        ReadAndDrawPolyPolygon<sal_Int32>(nNextPos);
                    break;

                    case EMR_SETWINDOWEXTEX :
                    {
                        sal_Int32 w = 0, h = 0;
                        mpInputStream->ReadInt32( w ).ReadInt32( h );
                        SAL_INFO("emfio""\t\tWidth: " << w);
                        SAL_INFO("emfio""\t\tHeight: " << h);
                        SetWinExt( Size( w, h ), true);
                    }
                    break;

                    case EMR_SETWINDOWORGEX :
                    {
                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
                        SAL_INFO("emfio""\t\tPoint: (" << nX32 << ", " << nY32 << ")");
                        SetWinOrg( Point( nX32, nY32 ), true);
                    }
                    break;

                    case EMR_SCALEWINDOWEXTEX :
                    {
                        mpInputStream->ReadUInt32( nNom1 ).ReadUInt32( nDen1 ).ReadUInt32( nNom2 ).ReadUInt32( nDen2 );
                        SAL_INFO("emfio""\t\tHorizontal scale: " << nNom1 << " / " << nDen1);
                        SAL_INFO("emfio""\t\tVertical scale: " << nNom2 << " / " << nDen2);

                        if (nDen1 != 0 && nDen2 != 0)
                            ScaleWinExt( static_cast<double>(nNom1) / nDen1, static_cast<double>(nNom2) / nDen2 );
                        else
                            SAL_WARN("vcl.emf""ignoring bogus divide by zero");
                    }
                    break;

                    case EMR_SETVIEWPORTORGEX :
                    {
                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
                        SAL_INFO("emfio""\t\tPoint: (" << nX32 << ", " << nY32 << ")");
                        SetDevOrg( Point( nX32, nY32 ) );
                    }
                    break;

                    case EMR_SCALEVIEWPORTEXTEX :
                    {
                        mpInputStream->ReadUInt32( nNom1 ).ReadUInt32( nDen1 ).ReadUInt32( nNom2 ).ReadUInt32( nDen2 );
                        SAL_INFO("emfio""\t\tHorizontal scale: " << nNom1 << " / " << nDen1);
                        SAL_INFO("emfio""\t\tVertical scale: " << nNom2 << " / " << nDen2);

                        if (nDen1 != 0 && nDen2 != 0)
                            ScaleDevExt( static_cast<double>(nNom1) / nDen1, static_cast<double>(nNom2) / nDen2 );
                        else
                            SAL_WARN("vcl.emf""ignoring bogus divide by zero");
                    }
                    break;

                    case EMR_SETVIEWPORTEXTEX :
                    {
                        sal_Int32 w = 0, h = 0;
                        mpInputStream->ReadInt32( w ).ReadInt32( h );
                        SAL_INFO("emfio""\t\tWidth: " << w);
                        SAL_INFO("emfio""\t\tHeight: " << h);

                        SetDevExt( Size( w, h ) );
                    }
                    break;

                    case EMR_EOF :
                        mnRecordCount = 0;
                    break;

                    case EMR_SETPIXELV :
                    {
                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
                        SAL_INFO("emfio""\t\tPoint: (" << nX32 << ", " << nY32 << ")");
                        DrawPixel( Point( nX32, nY32 ), ReadColor() );
                    }
                    break;

                    case EMR_SETMAPMODE :
                    {
                        sal_uInt32 nMapMode(0);
                        mpInputStream->ReadUInt32( nMapMode );
                        SAL_INFO("emfio""\t\tMapMode: 0x" << std::hex << nMapMode << std::dec);
                        SetMapMode( static_cast<MappingMode>(nMapMode) );
                    }
                    break;

                    case EMR_SETBKMODE :
                    {
                        mpInputStream->ReadUInt32( nDat32 );
                        SAL_INFO("emfio""\t\tBkMode: 0x" << std::hex << nDat32 << std::dec);
                        SetBkMode( static_cast<BackgroundMode>(nDat32) );
                    }
                    break;

                    case EMR_SETPOLYFILLMODE :
                    break;

                    case EMR_SETROP2 :
                    {
                        mpInputStream->ReadUInt32( nDat32 );
                        SAL_INFO("emfio""\t\tROP2: 0x" << std::hex << nDat32 << std::dec);
                        SetRasterOp( static_cast<WMFRasterOp>(nDat32) );
                    }
                    break;

                    case EMR_SETSTRETCHBLTMODE :
                    {
                        mpInputStream->ReadUInt32( nStretchBltMode );
                        SAL_INFO("emfio""\t\tStretchBltMode: 0x" << std::hex << nDat32 << std::dec);
                    }
                    break;

                    case EMR_SETTEXTALIGN :
                    {
                        mpInputStream->ReadUInt32( nDat32 );
                        SAL_INFO("emfio""\t\tTextAlign: 0x" << std::hex << nDat32 << std::dec);
                        SetTextAlign( nDat32 );
                    }
                    break;

                    case EMR_SETTEXTCOLOR :
                    {
                        SetTextColor( ReadColor() );
                    }
                    break;

                    case EMR_SETARCDIRECTION:
                    {
                        mpInputStream->ReadUInt32(nIndex);
                        SetArcDirection(nIndex == ARCDIRECTION_CLOCKWISE);
                    }
                    break;

                    case EMR_SETBKCOLOR :
                    {
                        SetBkColor( ReadColor() );
                    }
                    break;

                    case EMR_OFFSETCLIPRGN :
                    {
                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
                        SAL_INFO("emfio""\t\tPoint: (" << nX32 << ", " << nY32 << ")");
                        MoveClipRegion( Size( nX32, nY32 ) );
                    }
                    break;

                    case EMR_MOVETOEX :
                    {
                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
                        SAL_INFO("emfio""\t\tPoint: (" << nX32 << ", " << nY32 << ")");
                        MoveTo( Point( nX32, nY32 ), mbRecordPath);
                    }
                    break;

                    case EMR_INTERSECTCLIPRECT :
                    {
                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
                        IntersectClipRect( ReadRectangle( nX32, nY32, nx32, ny32 ) );
                        SAL_INFO("emfio""\t\tPoint: (" << nX32 << ", " << nY32 << ")");
                        SAL_INFO("emfio""\t\tPoint: (" << nx32 << ", " << ny32 << ")");
                    }
                    break;

                    case EMR_SAVEDC :
                    {
                        Push();
                    }
                    break;

                    case EMR_RESTOREDC :
                    {
                        sal_Int32 nSavedDC(0);
                        mpInputStream->ReadInt32( nSavedDC );
                        SAL_INFO( "emfio""\t\t SavedDC Index: " << nSavedDC );
                        if ( nSavedDC < 0 )
                            Pop( nSavedDC );
                        else
                            Pop( -1 ); // For RestoreDC values above -1, treat as get last element
                    }
                    break;

                    case EMR_SETWORLDTRANSFORM :
                    {
                        XForm aTempXForm;
                        *mpInputStream >> aTempXForm;
                        SetWorldTransform( aTempXForm );
                    }
                    break;

                    case EMR_MODIFYWORLDTRANSFORM :
                    {
                        sal_uInt32 nMode(0);
                        XForm aTempXForm;
                        *mpInputStream >> aTempXForm;
                        mpInputStream->ReadUInt32( nMode );
                        ModifyWorldTransform( aTempXForm, static_cast<ModifyWorldTransformMode>(nMode) );
                    }
                    break;

                    case EMR_SELECTOBJECT :
                    {
                        mpInputStream->ReadUInt32( nIndex );
                        SelectObject( nIndex );
                    }
                    break;

                    case EMR_CREATEPEN:
                    {
                        mpInputStream->ReadUInt32(nIndex);
                        if ((nIndex & ENHMETA_STOCK_OBJECT) == 0)
                        {
                            sal_uInt32 nPenStyle(0);
                            sal_Int32 nPenWidth(0), nIgnored;
                            mpInputStream->ReadUInt32(nPenStyle).ReadInt32(nPenWidth).ReadInt32(nIgnored);
                            SAL_INFO("emfio""\t\tIndex: " << nIndex << " Style: 0x" << std::hex
                                                            << nPenStyle << std::dec
                                                            << " PenWidth: " << nPenWidth);
                            // PS_COSMETIC width is always fixed at one logical unit
                            // and is not affected by any geometric transformations like scaling
                            if (nPenStyle == PS_COSMETIC)
                                nPenWidth = 1;
                            CreateObjectIndexed(nIndex, std::make_unique<WinMtfLineStyle>(ReadColor(), nPenStyle, nPenWidth));
                        }
                    }
                    break;

                    case EMR_EXTCREATEPEN:
                    {
                        mpInputStream->ReadUInt32(nIndex);
                        if ((nIndex & ENHMETA_STOCK_OBJECT) == 0)
                        {
                            sal_uInt32 offBmi, cbBmi, offBits, cbBits, nPenStyle, nWidth, nBrushStyle, elpNumEntries;
                            sal_Int32 elpHatch;
                            mpInputStream->ReadUInt32(offBmi).ReadUInt32(cbBmi).ReadUInt32(offBits).ReadUInt32(cbBits);
                            mpInputStream->ReadUInt32(nPenStyle).ReadUInt32(nWidth).ReadUInt32(nBrushStyle);
                            Color aColorRef = ReadColor();
                            mpInputStream->ReadInt32(elpHatch).ReadUInt32(elpNumEntries);

                            if (!mpInputStream->good())
                                bStatus = false;
                            else
                            {
                                SAL_INFO("emfio""\t\tStyle: 0x" << std::hex << nPenStyle << std::dec);
                                // PS_COSMETIC width is always fixed at one logical unit
                                // and is not affected by any geometric transformations like scaling
                                if (nPenStyle == PS_COSMETIC)
                                    nWidth = 1;
                                SAL_INFO("emfio""\t\tWidth: " << nWidth);
                                CreateObjectIndexed(nIndex, std::make_unique<WinMtfLineStyle>(aColorRef, nPenStyle, nWidth));
                            }
                        }
                    }
                    break;

                    case EMR_CREATEBRUSHINDIRECT :
                    {
                        mpInputStream->ReadUInt32( nIndex );
                        if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
                        {
                            sal_uInt32  nStyle;
                            mpInputStream->ReadUInt32( nStyle );
                            BrushStyle eStyle = static_cast<BrushStyle>(nStyle);
                            CreateObjectIndexed(nIndex, std::make_unique<WinMtfFillStyle>( ReadColor(), ( eStyle == BrushStyle::BS_HOLLOW ) ));
                        }
                    }
                    break;

                    case EMR_DELETEOBJECT :
                    {
                        mpInputStream->ReadUInt32( nIndex );
                        if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
                            DeleteObject( nIndex );
                    }
                    break;

                    case EMR_ELLIPSE:
                    {
                        mpInputStream->ReadInt32(nX32).ReadInt32(nY32).ReadInt32(nx32).ReadInt32(ny32);
                        SAL_INFO("emfio""\t\t Rectangle, left: " << nX32 << ", top: " << nY32 << ", right: " << nx32 << ", bottom: " << ny32);

                        sal_Int32 w(0), h(0);
                        if (o3tl::checked_sub(nx32, nX32, w) || o3tl::checked_sub(ny32, nY32, h))
                            SAL_WARN("emfio""broken ellipse");
                        else
                        {
                            tools::Long dw = w / 2;
                            tools::Long dh = h / 2;
                            Point aCenter(nX32 + dw, nY32 + dh);
                            tools::Polygon aPoly(aCenter, dw, dh);
                            DrawPolygon(std::move(aPoly), mbRecordPath);
                        }
                    }
                    break;

                    case EMR_RECTANGLE :
                    {
                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
                        SAL_INFO("emfio""\t\t Rectangle, left: " << nX32 << ", top: " << nY32 << ", right: " << nx32 << ", bottom: " << ny32);
                        Point aPoints[] { Point(nX32, nY32),
                                          Point(nx32, nY32),
                                          Point(nx32, ny32),
                                          Point(nX32, ny32) };
                        tools::Polygon aPoly(4, aPoints);
                        aPoly.Optimize( PolyOptimizeFlags::CLOSE );
                        DrawPolygon( std::move(aPoly), mbRecordPath );
                    }
                    break;

                    case EMR_ROUNDRECT:
                    {
                        mpInputStream->ReadInt32(nX32).ReadInt32(nY32).ReadInt32(nx32).ReadInt32(ny32).ReadUInt32(nW).ReadUInt32(nH);
                        SAL_INFO("emfio""\t\t Rectangle position: " << nX32 << ":" << nY32 << ", " << nx32 << ":" << ny32);
                        SAL_INFO("emfio""\t\t Ellipse Width: " << nW << ", Height" << nH);
                        // Convert from ellipse width and height to ellipse vertical and horizontal radius
                        tools::Polygon aRoundRectPoly(ReadRectangle(nX32, nY32, nx32, ny32), nW >> 1, nH >> 1);
                        DrawPolygon(std::move(aRoundRectPoly), mbRecordPath);
                    }
                    break;

                    case EMR_ANGLEARC:
                    {
                        sal_Int32 nCenterX, nCenterY;
                        sal_uInt32 nRadius;
                        float fStartAngle, fSweepAngle;
                        mpInputStream->ReadInt32(nCenterX).ReadInt32(nCenterY).ReadUInt32(nRadius);
                        mpInputStream->ReadFloat(fStartAngle).ReadFloat(fSweepAngle);
                        SAL_INFO("emfio""\t\t Center: " << nCenterX << ":" << nCenterY << ", Radius: " << nRadius);
                        SAL_INFO("emfio""\t\t Start Angle: " << fStartAngle << ", Sweep Angle: " << fSweepAngle);

                        if (!mpInputStream->good())
                            bStatus = false;
                        else
                        {
                            // Convert from degrees to radians and start angle to draw from x-axis
                            fStartAngle = basegfx::deg2rad(fStartAngle);
                            fSweepAngle = basegfx::deg2rad(fSweepAngle);

                            tools::Polygon aPoly(Point(nCenterX, nCenterY), nRadius, fStartAngle, fSweepAngle, IsArcDirectionClockWise());

                            if (!aPoly.GetSize())
                            {
                                SAL_WARN("emfio""EMF file error: 0 points");
                            }
                            else
                            {
                                // Before drawing the arc, AngleArc draws the line segment from the current position to the beginning of the arc
                                LineTo(aPoly[0], mbRecordPath);
                                DrawPolyLine(std::move(aPoly), true, mbRecordPath);
                            }
                        }
                    }
                    break;

                    case EMR_ARC :
                    case EMR_ARCTO :
                    case EMR_CHORD :
                    {
                        sal_Int32 nStartX, nStartY, nEndX, nEndY;
                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadInt32( nStartX ).ReadInt32( nStartY ).ReadInt32( nEndX ).ReadInt32( nEndY );
                        if (!mpInputStream->good())
                            bStatus = false;
                        else
                        {
                            SAL_INFO( "emfio""\t\t Bounds: " << nX32 << ":" << nY32 << ", " << nx32 << ":" << ny32 << ", Start: " << nStartX << ":" << nStartY << ", End: " << nEndX << ":" << nEndY );
                            tools::Polygon aPoly(ReadRectangle(nX32, nY32, nx32, ny32), Point(nStartX, nStartY), Point(nEndX, nEndY), PolyStyle::Arc, IsArcDirectionClockWise());

                            if ( nRecType == EMR_CHORD )
                                DrawPolygon( std::move(aPoly), mbRecordPath );
                            else
                                DrawPolyLine( std::move(aPoly), nRecType == EMR_ARCTO, mbRecordPath );
                        }
                    }
                    break;

                    case EMR_PIE :
                    {
                        sal_Int32 nStartX, nStartY, nEndX, nEndY;
                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadInt32( nStartX ).ReadInt32( nStartY ).ReadInt32( nEndX ).ReadInt32( nEndY );
                        if (!mpInputStream->good())
                            bStatus = false;
                        else
                        {
                            tools::Polygon aPoly(ReadRectangle(nX32, nY32, nx32, ny32), Point(nStartX, nStartY), Point(nEndX, nEndY), PolyStyle::Pie, IsArcDirectionClockWise());
                            DrawPolygon( std::move(aPoly), mbRecordPath );
                        }
                    }
                    break;

                    case EMR_LINETO :
                    {
                        mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
                        LineTo( Point( nX32, nY32 ), mbRecordPath);
                    }
                    break;

                    case EMR_BEGINPATH :
                    {
                        ClearPath();
                        mbRecordPath = true;
                    }
                    break;

                    case EMR_ABORTPATH :
                        ClearPath();
                        [[fallthrough]];
                    case EMR_ENDPATH :
                        mbRecordPath = false;
                    break;

                    case EMR_CLOSEFIGURE :
                        ClosePath();
                    break;

                    case EMR_FILLPATH :
                        StrokeAndFillPath( falsetrue );
                    break;

                    case EMR_STROKEANDFILLPATH :
                        StrokeAndFillPath( truetrue );
                    break;

                    case EMR_STROKEPATH :
                        StrokeAndFillPath( truefalse );
                    break;

                    case EMR_SELECTCLIPPATH :
                    {
                        sal_Int32 nClippingMode(0);
                        mpInputStream->ReadInt32(nClippingMode);
                        SetClipPath(GetPathObj(), static_cast<RegionMode>(nClippingMode), true);
                    }
                    break;

                    case EMR_EXTSELECTCLIPRGN :
                    {
                        sal_uInt32 nRemainingRecSize = nRecSize - 8;
                        if (nRemainingRecSize < 8)
                            bStatus = false;
                        else
                        {
                            sal_Int32 nClippingMode(0), cbRgnData(0);
                            mpInputStream->ReadInt32(cbRgnData);
                            mpInputStream->ReadInt32(nClippingMode);
                            nRemainingRecSize -= 8;

                            // This record's region data should be ignored if mode
                            // is RGN_COPY - see EMF spec section 2.3.2.2
                            if (static_cast<RegionMode>(nClippingMode) == RegionMode::RGN_COPY)
                            {
                                SetDefaultClipPath();
                            }
                            else
                            {
                                basegfx::B2DPolyPolygon aPolyPoly;
                                if (cbRgnData)
                                    ImplReadRegion(aPolyPoly, *mpInputStream, nRemainingRecSize, GetWinOrg());
                                const tools::PolyPolygon aPolyPolygon(aPolyPoly);
                                SetClipPath(aPolyPolygon, static_cast<RegionMode>(nClippingMode), false);
                            }
                        }
                    }
                    break;

                    case EMR_ALPHABLEND:
                    {
                        sal_Int32 xDest(0), yDest(0), cxDest(0), cyDest(0);

                        BLENDFUNCTION aFunc;
                        sal_Int32 xSrc(0), ySrc(0), cxSrc(0), cySrc(0);
                        XForm xformSrc;
                        sal_uInt32 BkColorSrc(0), iUsageSrc(0), offBmiSrc(0);
                        sal_uInt32 cbBmiSrc(0), offBitsSrc(0), cbBitsSrc(0);

                        sal_uInt64   nStart = mpInputStream->Tell() - 8;
                        mpInputStream->SeekRel( 0x10 );

                        mpInputStream->ReadInt32( xDest ).ReadInt32( yDest ).ReadInt32( cxDest ).ReadInt32( cyDest );
                        *mpInputStream >> aFunc;
                        mpInputStream->ReadInt32( xSrc ).ReadInt32( ySrc );
                        *mpInputStream >> xformSrc;
                        mpInputStream->ReadUInt32( BkColorSrc ).ReadUInt32( iUsageSrc ).ReadUInt32( offBmiSrc ).ReadUInt32( cbBmiSrc )
                                   .ReadUInt32( offBitsSrc ).ReadUInt32( cbBitsSrc ).ReadInt32( cxSrc ).ReadInt32( cySrc ) ;

                        if ( !mpInputStream->good() ||
                             (cbBitsSrc > (SAL_MAX_UINT32 - 14)) ||
                             ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc) ||
                             cxDest == SAL_MAX_INT32 || cyDest == SAL_MAX_INT32 )
                        {
                            bStatus = false;
                        }
                        else
                        {
                            tools::Rectangle aRect(Point(xDest, yDest), Size(cxDest + 1, cyDest + 1));

                            const sal_uInt32 nSourceSize = cbBmiSrc + cbBitsSrc + 14;
                            bool bSafeRead = nSourceSize <= (mnEndPos - mnStartPos);
                            sal_uInt32 nDeltaToDIB5HeaderSize(0);
                            const bool bReadAlpha(0x01 == aFunc.aAlphaFormat);
                            if (bSafeRead && bReadAlpha)
                            {
                                // we need to read alpha channel data if AlphaFormat of BLENDFUNCTION is
                                // AC_SRC_ALPHA (==0x01). To read it, create a temp DIB-File which is ready
                                // for DIB-5 format
--> --------------------

--> maximum size reached

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

100%


¤ Dauer der Verarbeitung: 0.46 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 ist noch experimentell.