/* -*- 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 <wmfreader.hxx>
#include <emfreader.hxx>
 
#include <cstdlib>
#include <memory>
#include <optional>
#include <o3tl/safeint.hxx>
#include <o3tl/sprintf.hxx>
#include <o3tl/unit_conversion.hxx>
#include <rtl/crc.h>
#include <rtl/tencinfo.h>
#include <sal/log.hxx>
#include <osl/endian.h>
#include <vcl/gdimtf.hxx>
#include <vcl/svapp.hxx>
#include <vcl/dibtools.hxx>
#include <vcl/outdev.hxx>
#include <vcl/wmfexternal.hxx>
#include <tools/fract.hxx>
#include <vcl/BitmapReadAccess.hxx>
#include <vcl/BitmapTools.hxx>
#include <osl/thread.h>
 
namespace
{
    // MS Windows defines
    enum WMFRecords
    {
        W_META_EOF                  = 0x0000,
        W_META_SETBKCOLOR           = 0x0201,
        W_META_SETBKMODE            = 0x0102,
        W_META_SETMAPMODE           = 0x0103,
        W_META_SETROP2              = 0x0104,
        W_META_SETRELABS            = 0x0105,
        W_META_SETPOLYFILLMODE      = 0x0106,
        W_META_SETSTRETCHBLTMODE    = 0x0107,
        W_META_SETTEXTCHAREXTRA     = 0x0108,
        W_META_SETTEXTCOLOR         = 0x0209,
        W_META_SETTEXTJUSTIFICATION = 0x020A,
        W_META_SETWINDOWORG         = 0x020B,
        W_META_SETWINDOWEXT         = 0x020C,
        W_META_SETVIEWPORTORG       = 0x020D,
        W_META_SETVIEWPORTEXT       = 0x020E,
        W_META_OFFSETWINDOWORG      = 0x020F,
        W_META_SCALEWINDOWEXT       = 0x0410,
        W_META_OFFSETVIEWPORTORG    = 0x0211,
        W_META_SCALEVIEWPORTEXT     = 0x0412,
        W_META_LINETO               = 0x0213,
        W_META_MOVETO               = 0x0214,
        W_META_EXCLUDECLIPRECT      = 0x0415,
        W_META_INTERSECTCLIPRECT    = 0x0416,
        W_META_ARC                  = 0x0817,
        W_META_ELLIPSE              = 0x0418,
        W_META_FLOODFILL            = 0x0419,
        W_META_PIE                  = 0x081A,
        W_META_RECTANGLE            = 0x041B,
        W_META_ROUNDRECT            = 0x061C,
        W_META_PATBLT               = 0x061D,
        W_META_SAVEDC               = 0x001E,
        W_META_SETPIXEL             = 0x041F,
        W_META_OFFSETCLIPRGN        = 0x0220,
        W_META_TEXTOUT              = 0x0521,
        W_META_BITBLT               = 0x0922,
        W_META_STRETCHBLT           = 0x0B23,
        W_META_POLYGON              = 0x0324,
        W_META_POLYLINE             = 0x0325,
        W_META_ESCAPE               = 0x0626,
        W_META_RESTOREDC            = 0x0127,
        W_META_FILLREGION           = 0x0228,
        W_META_FRAMEREGION          = 0x0429,
        W_META_INVERTREGION         = 0x012A,
        W_META_PAINTREGION          = 0x012B,
        W_META_SELECTCLIPREGION     = 0x012C,
        W_META_SELECTOBJECT         = 0x012D,
        W_META_SETTEXTALIGN         = 0x012E,
        W_META_DRAWTEXT             = 0x062F,
        W_META_CHORD                = 0x0830,
        W_META_SETMAPPERFLAGS       = 0x0231,
        W_META_EXTTEXTOUT           = 0x0a32,
        W_META_SETDIBTODEV          = 0x0d33,
        W_META_SELECTPALETTE        = 0x0234,
        W_META_REALIZEPALETTE       = 0x0035,
        W_META_ANIMATEPALETTE       = 0x0436,
        W_META_SETPALENTRIES        = 0x0037,
        W_META_POLYPOLYGON          = 0x0538,
        W_META_RESIZEPALETTE        = 0x0139,
        W_META_DIBBITBLT            = 0x0940,
        W_META_DIBSTRETCHBLT        = 0x0b41,
        W_META_DIBCREATEPATTERNBRUSH = 0x0142,
        W_META_STRETCHDIB           = 0x0f43,
        W_META_EXTFLOODFILL         = 0x0548,
        W_META_RESETDC              = 0x014C,
        W_META_STARTDOC             = 0x014D,
        W_META_STARTPAGE            = 0x004F,
        W_META_ENDPAGE              = 0x0050,
        W_META_ABORTDOC             = 0x0052,
        W_META_ENDDOC               = 0x005E,
        W_META_DELETEOBJECT         = 0x01f0,
        W_META_CREATEPALETTE        = 0x00f7,
        W_META_CREATEBRUSH          = 0x00F8,
        W_META_CREATEPATTERNBRUSH   = 0x01F9,
        W_META_CREATEPENINDIRECT    = 0x02FA,
        W_META_CREATEFONTINDIRECT   = 0x02FB,
        W_META_CREATEBRUSHINDIRECT  = 0x02FC,
        W_META_CREATEBITMAPINDIRECT = 0x02FD,
        W_META_CREATEBITMAP         = 0x06FE,
        W_META_CREATEREGION         = 0x06FF
    };
 
    void GetWinExtMax(const Point& rSource, tools::Rectangle& rPlaceableBound, emfio::MappingMode eMapMode)
    {
        Point aSource(rSource);
        if (eMapMode == emfio::MappingMode::MM_HIMETRIC)
            aSource.setY( -rSource.Y() );
        if (aSource.X() < rPlaceableBound.Left())
            rPlaceableBound.SetLeft( aSource.X() );
        if (aSource.X() > rPlaceableBound.Right())
            rPlaceableBound.SetRight( aSource.X() );
        if (aSource.Y() < rPlaceableBound.Top())
            rPlaceableBound.SetTop( aSource.Y() );
        if (aSource.Y() > rPlaceableBound.Bottom())
            rPlaceableBound.SetBottom( aSource.Y() );
    }
 
    void GetWinExtMax(const tools::Rectangle& rSource, tools::Rectangle& rPlaceableBound, emfio::MappingMode nMapMode)
    {
        GetWinExtMax(rSource.TopLeft(), rPlaceableBound, nMapMode);
        GetWinExtMax(rSource.BottomRight(), rPlaceableBound, nMapMode);
    }
 
    const char *
    record_type_name(sal_uInt16 nRecType)
    {
    #ifndef SAL_LOG_INFO
        (void) nRecType;
        return "";
    #else
        switch( nRecType )
        {
        case W_META_EOF: return "W_META_EOF";
        case W_META_SETBKCOLOR: return "META_SETBKCOLOR";
        case W_META_SETBKMODE: return "META_SETBKMODE";
        case W_META_SETMAPMODE: return "META_SETMAPMODE";
        case W_META_SETROP2: return "META_SETROP2";
        case W_META_SETRELABS: return "META_SETRELABS";
        case W_META_SETPOLYFILLMODE: return "META_SETPOLYFILLMODE";
        case W_META_SETSTRETCHBLTMODE: return "META_SETSTRETCHBLTMODE";
        case W_META_SETTEXTCHAREXTRA: return "META_SETTEXTCHAREXTRA";
        case W_META_SETTEXTCOLOR: return "META_SETTEXTCOLOR";
        case W_META_SETTEXTJUSTIFICATION: return "META_SETTEXTJUSTIFICATION";
        case W_META_SETWINDOWORG: return "META_SETWINDOWORG";
        case W_META_SETWINDOWEXT: return "META_SETWINDOWEXT";
        case W_META_SETVIEWPORTORG: return "META_SETVIEWPORTORG";
        case W_META_SETVIEWPORTEXT: return "META_SETVIEWPORTEXT";
        case W_META_OFFSETWINDOWORG: return "META_OFFSETWINDOWORG";
        case W_META_SCALEWINDOWEXT: return "META_SCALEWINDOWEXT";
        case W_META_OFFSETVIEWPORTORG: return "META_OFFSETVIEWPORTORG";
        case W_META_SCALEVIEWPORTEXT: return "META_SCALEVIEWPORTEXT";
        case W_META_LINETO: return "META_LINETO";
        case W_META_MOVETO: return "META_MOVETO";
        case W_META_EXCLUDECLIPRECT: return "META_EXCLUDECLIPRECT";
        case W_META_INTERSECTCLIPRECT: return "META_INTERSECTCLIPRECT";
        case W_META_ARC: return "META_ARC";
        case W_META_ELLIPSE: return "META_ELLIPSE";
        case W_META_FLOODFILL: return "META_FLOODFILL";
        case W_META_PIE: return "META_PIE";
        case W_META_RECTANGLE: return "META_RECTANGLE";
        case W_META_ROUNDRECT: return "META_ROUNDRECT";
        case W_META_PATBLT: return "META_PATBLT";
        case W_META_SAVEDC: return "META_SAVEDC";
        case W_META_SETPIXEL: return "META_SETPIXEL";
        case W_META_OFFSETCLIPRGN: return "META_OFFSETCLIPRGN";
        case W_META_TEXTOUT: return "META_TEXTOUT";
        case W_META_BITBLT: return "META_BITBLT";
        case W_META_STRETCHBLT: return "META_STRETCHBLT";
        case W_META_POLYGON: return "META_POLYGON";
        case W_META_POLYLINE: return "META_POLYLINE";
        case W_META_ESCAPE: return "META_ESCAPE";
        case W_META_RESTOREDC: return "META_RESTOREDC";
        case W_META_FILLREGION: return "META_FILLREGION";
        case W_META_FRAMEREGION: return "META_FRAMEREGION";
        case W_META_INVERTREGION: return "META_INVERTREGION";
        case W_META_PAINTREGION: return "META_PAINTREGION";
        case W_META_SELECTCLIPREGION: return "META_SELECTCLIPREGION";
        case W_META_SELECTOBJECT: return "META_SELECTOBJECT";
        case W_META_SETTEXTALIGN: return "META_SETTEXTALIGN";
        case W_META_DRAWTEXT: return "META_DRAWTEXT";
        case W_META_CHORD: return "META_CHORD";
        case W_META_SETMAPPERFLAGS: return "META_SETMAPPERFLAGS";
        case W_META_EXTTEXTOUT: return "META_EXTTEXTOUT";
        case W_META_SETDIBTODEV: return "META_SETDIBTODEV";
        case W_META_SELECTPALETTE: return "META_SELECTPALETTE";
        case W_META_REALIZEPALETTE: return "META_REALIZEPALETTE";
        case W_META_ANIMATEPALETTE: return "META_ANIMATEPALETTE";
        case W_META_SETPALENTRIES: return "META_SETPALENTRIES";
        case W_META_POLYPOLYGON: return "META_POLYPOLYGON";
        case W_META_RESIZEPALETTE: return "META_RESIZEPALETTE";
        case W_META_DIBBITBLT: return "META_DIBBITBLT";
        case W_META_DIBSTRETCHBLT: return "META_DIBSTRETCHBLT";
        case W_META_DIBCREATEPATTERNBRUSH: return "META_DIBCREATEPATTERNBRUSH";
        case W_META_STRETCHDIB: return "META_STRETCHDIB";
        case W_META_EXTFLOODFILL: return "META_EXTFLOODFILL";
        case W_META_RESETDC: return "META_RESETDC";
        case W_META_STARTDOC: return "META_STARTDOC";
        case W_META_STARTPAGE: return "META_STARTPAGE";
        case W_META_ENDPAGE: return "META_ENDPAGE";
        case W_META_ABORTDOC: return "META_ABORTDOC";
        case W_META_ENDDOC: return "META_ENDDOC";
        case W_META_DELETEOBJECT: return "META_DELETEOBJECT";
        case W_META_CREATEPALETTE: return "META_CREATEPALETTE";
        case W_META_CREATEBRUSH: return "META_CREATEBRUSH";
        case W_META_CREATEPATTERNBRUSH: return "META_CREATEPATTERNBRUSH";
        case W_META_CREATEPENINDIRECT: return "META_CREATEPENINDIRECT";
        case W_META_CREATEFONTINDIRECT: return "META_CREATEFONTINDIRECT";
        case W_META_CREATEBRUSHINDIRECT: return "META_CREATEBRUSHINDIRECT";
        case W_META_CREATEBITMAPINDIRECT: return "META_CREATEBITMAPINDIRECT";
        case W_META_CREATEBITMAP: return "META_CREATEBITMAP";
        case W_META_CREATEREGION: return "META_CREATEREGION";
        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, sal_uInt32(nRecType));
            return buffer;
        }
    #endif
    }
 
}
 
namespace emfio
{
    inline Point WmfReader::ReadPoint()
    {
        short nX = 0, nY = 0;
        mpInputStream->ReadInt16( nX ).ReadInt16( nY );
        return Point( nX, nY );
    }
 
    inline Point WmfReader::ReadYX()
    {
        short nX = 0, nY = 0;
        mpInputStream->ReadInt16( nY ).ReadInt16( nX );
        return Point( nX, nY );
    }
 
    tools::Rectangle WmfReader::ReadRectangle()
    {
        Point aBR, aTL;
        aBR = ReadYX();
        aTL = ReadYX();
        aBR.AdjustX( -1 );
        aBR.AdjustY( -1 );
        if (aTL.X() > aBR.X() || aTL.Y() > aBR.Y())
        {
            SAL_WARN("emfio", "broken rectangle");
            return tools::Rectangle::Normalize(aTL, aBR);
        }
        return tools::Rectangle( aTL, aBR );
    }
 
    Size WmfReader::ReadYXExt()
    {
        short nW=0, nH=0;
        mpInputStream->ReadInt16( nH ).ReadInt16( nW );
        return Size( nW, nH );
    }
 
    void WmfReader::ReadRecordParams( sal_uInt32 nRecordSize, sal_uInt16 nFunc )
    {
        bool bRecordOk = true;
        SAL_INFO("emfio", "\t" << record_type_name(nFunc));
        switch(nFunc)
        {
            case W_META_SETBKCOLOR:
            {
                if (nRecordSize != 5)
                    bRecordOk = false;
                SetBkColor(ReadColor());
            }
            break;
 
            case W_META_SETBKMODE:
            {
                // It could have Reserved values. Both 4 and 5 sizes are allowed
                if ((nRecordSize != 4) && (nRecordSize != 5))
                    bRecordOk = false;
                sal_uInt16 nDat = 0;
                mpInputStream->ReadUInt16( nDat );
                SetBkMode( static_cast<BackgroundMode>(nDat) );
            }
            break;
 
            case W_META_SETMAPMODE:
            {
                if (nRecordSize != 4)
                    bRecordOk = false;
                sal_uInt16 nMapMode = 0;
                mpInputStream->ReadUInt16(nMapMode);
                SetMapMode(static_cast<MappingMode>(nMapMode));
            }
            break;
 
            case W_META_SETROP2:
            {
                // It could have Reserved values. Both 4 and 5 sizes are allowed
                if ((nRecordSize != 4) && (nRecordSize != 5))
                    bRecordOk = false;
                sal_uInt16 nROP2 = 0;
                mpInputStream->ReadUInt16(nROP2);
                SetRasterOp(static_cast<WMFRasterOp>(nROP2));
                mpInputStream->SeekRel(2); // reserved data
            }
            break;
 
            case W_META_SETTEXTCOLOR:
            {
                if (nRecordSize != 5)
                    bRecordOk = false;
                SetTextColor( ReadColor() );
            }
            break;
 
            case W_META_SETWINDOWORG:
            {
                if (nRecordSize != 5)
                    bRecordOk = false;
                SetWinOrg( ReadYX() );
            }
            break;
 
            case W_META_SETWINDOWEXT:
            {
                if (nRecordSize != 5)
                    bRecordOk = false;
                short nWidth = 0, nHeight = 0;
                mpInputStream->ReadInt16( nHeight ).ReadInt16( nWidth );
                SetWinExt( Size( nWidth, nHeight ) );
            }
            break;
 
            case W_META_OFFSETWINDOWORG:
            {
                if (nRecordSize != 5)
                    bRecordOk = false;
                short nXAdd = 0, nYAdd = 0;
                mpInputStream->ReadInt16( nYAdd ).ReadInt16( nXAdd );
                SetWinOrgOffset( nXAdd, nYAdd );
            }
            break;
 
            case W_META_SCALEWINDOWEXT:
            {
                if (nRecordSize != 7)
                    bRecordOk = false;
                short nXNum = 0, nXDenom = 0, nYNum = 0, nYDenom = 0;
                mpInputStream->ReadInt16( nYDenom ).ReadInt16( nYNum ).ReadInt16( nXDenom ).ReadInt16( nXNum );
                if (!nYDenom || !nXDenom)
                {
                    mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
                    break;
                }
                ScaleWinExt( static_cast<double>(nXNum) / nXDenom, static_cast<double>(nYNum) / nYDenom );
            }
            break;
 
            case W_META_SETVIEWPORTORG:
            case W_META_SETVIEWPORTEXT:
            {
                if (nRecordSize != 5)
                    bRecordOk = false;
            }
            break;
 
            case W_META_OFFSETVIEWPORTORG:
            {
                if (nRecordSize != 5)
                    bRecordOk = false;
                short nXAdd = 0, nYAdd = 0;
                mpInputStream->ReadInt16( nYAdd ).ReadInt16( nXAdd );
                SetDevOrgOffset( nXAdd, nYAdd );
            }
            break;
 
            case W_META_SCALEVIEWPORTEXT:
            {
                if (nRecordSize != 7)
                    bRecordOk = false;
                short nXNum = 0, nXDenom = 0, nYNum = 0, nYDenom = 0;
                mpInputStream->ReadInt16( nYDenom ).ReadInt16( nYNum ).ReadInt16( nXDenom ).ReadInt16( nXNum );
                if (!nYDenom || !nXDenom)
                {
                    mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
                    break;
                }
                ScaleDevExt( static_cast<double>(nXNum) / nXDenom, static_cast<double>(nYNum) / nYDenom );
            }
            break;
 
            case W_META_LINETO:
            {
                if (nRecordSize != 5)
                    bRecordOk = false;
                LineTo( ReadYX() );
            }
            break;
 
            case W_META_MOVETO:
            {
                if (nRecordSize != 5)
                    bRecordOk = false;
                MoveTo( ReadYX() );
            }
            break;
 
            case W_META_INTERSECTCLIPRECT:
            {
                if (nRecordSize != 7)
                    bRecordOk = false;
                IntersectClipRect(ReadRectangle());
            }
            break;
 
            case W_META_RECTANGLE:
            {
                if (nRecordSize != 7)
                    bRecordOk = false;
                DrawRect(ReadRectangle());
            }
            break;
 
            case W_META_ROUNDRECT:
            {
                if (nRecordSize != 9)
                    bRecordOk = false;
                Size aSize( ReadYXExt() );
                DrawRoundRect( ReadRectangle(), Size( aSize.Width() / 2, aSize.Height() / 2 ) );
            }
            break;
 
            case W_META_ELLIPSE:
            {
                if (nRecordSize != 7)
                    bRecordOk = false;
                DrawEllipse(ReadRectangle());
            }
            break;
 
            case W_META_ARC:
            {
                if (nRecordSize != 11)
                    bRecordOk = false;
                Point aEnd( ReadYX() );
                Point aStart( ReadYX() );
                tools::Rectangle aRect( ReadRectangle() );
                aRect.Normalize();
                DrawArc( aRect, aStart, aEnd );
            }
            break;
 
            case W_META_PIE:
            {
                if (nRecordSize != 11)
                    bRecordOk = false;
                Point     aEnd( ReadYX() );
                Point     aStart( ReadYX() );
                tools::Rectangle aRect( ReadRectangle() );
                aRect.Normalize();
 
                // #i73608# OutputDevice deviates from WMF
                // semantics. start==end means full ellipse here.
                if( aStart == aEnd )
                    DrawEllipse( aRect );
                else
                    DrawPie( aRect, aStart, aEnd );
            }
            break;
 
            case W_META_CHORD:
            {
                if (nRecordSize != 11)
                    bRecordOk = false;
                Point aEnd( ReadYX() );
                Point aStart( ReadYX() );
                tools::Rectangle aRect( ReadRectangle() );
                aRect.Normalize();
                DrawChord( aRect, aStart, aEnd );
            }
            break;
 
            case W_META_POLYGON:
            {
                sal_uInt16 nPoints(0);
                mpInputStream->ReadUInt16(nPoints);
 
                if (nPoints > mpInputStream->remainingSize() / (2 * sizeof(sal_uInt16)))
                {
                    bRecordOk = false;
                }
                else
                {
                    tools::Polygon aPoly(nPoints);
                    for (sal_uInt16 i(0); i < nPoints && mpInputStream->good(); ++i)
                        aPoly[ i ] = ReadPoint();
                    DrawPolygon(std::move(aPoly), false/*bRecordPath*/);
                }
 
                SAL_WARN_IF(!bRecordOk, "emfio", "polygon record has more points than we can handle");
 
                bRecordOk &= mpInputStream->good();
            }
            break;
 
            case W_META_POLYPOLYGON:
            {
                sal_uInt16 nPolyCount(0);
                // Number of polygons:
                mpInputStream->ReadUInt16( nPolyCount );
                if (nPolyCount && mpInputStream->good())
                {
                    if (nPolyCount > mpInputStream->remainingSize() / sizeof(sal_uInt16))
                        break;
 
                    // Number of points of each polygon. Determine total number of points
                    std::unique_ptr<sal_uInt16[]> xPolygonPointCounts(new sal_uInt16[nPolyCount]);
                    sal_uInt16* pnPoints = xPolygonPointCounts.get();
                    tools::PolyPolygon aPolyPoly(nPolyCount);
                    sal_uInt16 nPoints = 0;
                    for (sal_uInt16 a = 0; a < nPolyCount && mpInputStream->good(); ++a)
                    {
                        mpInputStream->ReadUInt16( pnPoints[a] );
 
                        if (pnPoints[a] > SAL_MAX_UINT16 - nPoints)
                        {
                            bRecordOk = false;
                            break;
                        }
 
                        nPoints += pnPoints[a];
                    }
 
                    SAL_WARN_IF(!bRecordOk, "emfio", "polypolygon record has more polygons than we can handle");
 
                    bRecordOk &= mpInputStream->good();
 
                    if (!bRecordOk)
                        break;
 
                    // Polygon points are:
                    for (sal_uInt16 a = 0; a < nPolyCount && mpInputStream->good(); ++a)
                    {
                        const sal_uInt16 nPointCount(pnPoints[a]);
 
                        if (nPointCount > mpInputStream->remainingSize() / (2 * sizeof(sal_uInt16)))
                        {
                            bRecordOk = false;
                            break;
                        }
 
                        std::unique_ptr<Point[]> xPolygonPoints(new Point[nPointCount]);
                        Point* pPtAry = xPolygonPoints.get();
 
                        for(sal_uInt16 b(0); b < nPointCount && mpInputStream->good(); ++b)
                        {
                            pPtAry[b] = ReadPoint();
                        }
 
                        aPolyPoly.Insert( tools::Polygon(nPointCount, pPtAry) );
                    }
 
                    bRecordOk &= mpInputStream->good();
 
                    if (!bRecordOk)
                        break;
 
                    DrawPolyPolygon( aPolyPoly );
                }
            }
            break;
 
            case W_META_POLYLINE:
            {
                sal_uInt16 nPoints(0);
                mpInputStream->ReadUInt16(nPoints);
 
                if (nPoints > mpInputStream->remainingSize() / (2 * sizeof(sal_uInt16)))
                {
                    bRecordOk = false;
                }
                else
                {
                    tools::Polygon aPoly(nPoints);
                    for (sal_uInt16 i(0); i < nPoints && mpInputStream->good(); ++i)
                        aPoly[ i ] = ReadPoint();
                    DrawPolyLine( std::move(aPoly) );
                }
 
                SAL_WARN_IF(!bRecordOk, "emfio", "polyline record has more points than we can handle");
 
                bRecordOk &= mpInputStream->good();
            }
            break;
 
            case W_META_SAVEDC:
            {
                if (nRecordSize != 3)
                    bRecordOk = false;
                Push();
            }
            break;
 
            case W_META_RESTOREDC:
            {
                sal_Int16 nSavedDC(0);
                if (nRecordSize != 4)
                    bRecordOk = false;
                mpInputStream->ReadInt16(nSavedDC);
                SAL_INFO("emfio", "\t\t SavedDC: " << nSavedDC);
                SAL_WARN_IF(nSavedDC < 0,  "emfio", "TODO implement relative to the current state");
                Pop(nSavedDC);
            }
            break;
 
            case W_META_SETPIXEL:
            {
                if (nRecordSize != 7)
                    bRecordOk = false;
                const Color aColor = ReadColor();
                DrawPixel( ReadYX(), aColor );
            }
            break;
 
            case W_META_OFFSETCLIPRGN:
            {
                if (nRecordSize != 5)
                    bRecordOk = false;
                MoveClipRegion( ReadYXExt() );
            }
            break;
 
            case W_META_TEXTOUT:
            {
                //record is Recordsize, RecordFunction, StringLength, <String>, YStart, XStart
                const sal_uInt32 nNonStringLen = sizeof(sal_uInt32) + 4 * sizeof(sal_uInt16);
                const sal_uInt32 nRecSize = nRecordSize * 2;
 
                if (nRecSize < nNonStringLen)
                {
                    bRecordOk = false;
                    break;
                }
 
                sal_uInt16 nLength = 0;
                mpInputStream->ReadUInt16(nLength);
                sal_uInt16 nStoredLength = (nLength + 1) &~ 1;
 
                if (nRecSize - nNonStringLen < nStoredLength)
                {
                    SAL_WARN("emfio", "W_META_TEXTOUT too short, truncating string");
                    nLength = nStoredLength = nRecSize - nNonStringLen;
                }
 
                if (nLength)
                {
                    std::vector<char> aChars(nStoredLength);
                    nLength = std::min<sal_uInt16>(nLength, mpInputStream->ReadBytes(aChars.data(), aChars.size()));
                    OUString aText(aChars.data(), nLength, GetCharSet());
                    Point aPosition( ReadYX() );
                    DrawText( aPosition, aText );
                }
            }
            break;
 
            case W_META_EXTTEXTOUT:
            {
                //record is Recordsize, RecordFunction, Y, X, StringLength, options, maybe rectangle, <String>
                sal_uInt32 nNonStringLen = sizeof(sal_uInt32) + 5 * sizeof(sal_uInt16);
                const sal_uInt32 nRecSize = nRecordSize * 2;
 
                if (nRecSize < nNonStringLen)
                {
                    bRecordOk = false;
                    break;
                }
 
                auto nRecordPos = mpInputStream->Tell() - 6;
                Point aPosition = ReadYX();
                sal_uInt16 nLen = 0, nOptions = 0;
                mpInputStream->ReadUInt16( nLen ).ReadUInt16( nOptions );
                SAL_INFO( "emfio", "\t\t\t Pos: " << aPosition.getX() << ":" << aPosition.getY() << " String Length: " << nLen << " Options: " << nOptions );
                tools::Rectangle aRect;
                if ( ( nOptions & ETO_CLIPPED ) || ( nOptions & ETO_OPAQUE ) )
                {
                    nNonStringLen += 2 * sizeof(sal_uInt16);
 
                    if (nRecSize < nNonStringLen)
                    {
                        bRecordOk = false;
                        break;
                    }
                    const Point aTopLeft = ReadPoint();
                    const Point aBottomRight = ReadPoint();
                    aRect = tools::Rectangle( aTopLeft, aBottomRight );
                    if ( nOptions & ETO_OPAQUE )
                        DrawRectWithBGColor( aRect );
                    SAL_INFO( "emfio", "\t\t\t Rectangle : " << aTopLeft.getX() << ":" << aTopLeft.getY() << ", " << aBottomRight.getX() << ":" << aBottomRight.getY() );
                }
 
                vcl::text::ComplexTextLayoutFlags nTextLayoutMode = vcl::text::ComplexTextLayoutFlags::Default;
                if ( nOptions & ETO_RTLREADING )
                    nTextLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
                SetTextLayoutMode( nTextLayoutMode );
                SAL_WARN_IF( ( nOptions & ( ETO_PDY | ETO_GLYPH_INDEX ) ) != 0, "emfio", "SJ: ETO_PDY || ETO_GLYPH_INDEX in WMF" );
 
                // output only makes sense if the text contains characters
                if (nLen)
                {
                    sal_Int32 nOriginalTextLen = nLen;
                    sal_Int32 nOriginalBlockLen = ( nOriginalTextLen + 1 ) &~ 1;
 
                    auto nMaxStreamPos = nRecordPos + nRecSize;
                    auto nRemainingSize = std::min(mpInputStream->remainingSize(), nMaxStreamPos - mpInputStream->Tell());
                    if (nRemainingSize < o3tl::make_unsigned(nOriginalBlockLen))
                    {
                        SAL_WARN("emfio", "exttextout record claimed more data than the stream can provide");
                        nOriginalTextLen = nOriginalBlockLen = nRemainingSize;
                    }
 
                    std::vector<char> pChar(nOriginalBlockLen);
                    mpInputStream->ReadBytes(pChar.data(), nOriginalBlockLen);
                    OUString aText(pChar.data(), nOriginalTextLen, GetCharSet()); // after this conversion the text may contain
                    sal_Int32 nNewTextLen = aText.getLength();                         // less character (japanese version), so the
                                                                                       // dxAry will not fit
                    if ( nNewTextLen )
                    {
                        if ( nOptions & ETO_CLIPPED )
                        {
                            Push(); // Save the current clip. It will be restored after text drawing
                            IntersectClipRect( aRect );
                        }
                        SAL_INFO( "emfio", "\t\t\t Text : " << aText );
                        KernArray aDXAry;
                        std::unique_ptr<tools::Long[]> pDYAry;
                        auto nDxArySize =  nMaxStreamPos - mpInputStream->Tell();
                        auto nDxAryEntries = nDxArySize >> 1;
                        bool        bUseDXAry = false;
 
                        if ( ( ( nDxAryEntries % nOriginalTextLen ) == 0 ) && ( nNewTextLen <= nOriginalTextLen ) )
                        {
                            sal_Int32 i; // needed just outside the for
                            aDXAry.resize( nNewTextLen );
                            if ( nOptions & ETO_PDY )
                            {
                                pDYAry.reset(new tools::Long[ nNewTextLen ]);
                            }
                            for (i = 0; i < nNewTextLen; i++ )
                            {
                                if ( mpInputStream->Tell() >= nMaxStreamPos )
                                    break;
                                sal_Int32 nDxCount = 1;
                                if ( nNewTextLen != nOriginalTextLen )
                                {
                                    sal_Unicode cUniChar = aText[i];
                                    OString aTmp(&cUniChar, 1, GetCharSet());
                                    if ( aTmp.getLength() > 1 )
                                    {
                                        nDxCount = aTmp.getLength();
                                    }
                                }
 
                                sal_Int16 nDx = 0, nDy = 0;
                                while ( nDxCount-- )
                                {
                                    if ( ( mpInputStream->Tell() + 2 ) > nMaxStreamPos )
                                        break;
                                    sal_Int16 nDxTmp = 0;
                                    mpInputStream->ReadInt16(nDxTmp);
                                    nDx += nDxTmp;
                                    if ( nOptions & ETO_PDY )
                                    {
                                        if ( ( mpInputStream->Tell() + 2 ) > nMaxStreamPos )
                                            break;
                                        sal_Int16 nDyTmp = 0;
                                        mpInputStream->ReadInt16(nDyTmp);
                                        nDy += nDyTmp;
                                    }
                                }
 
                                aDXAry[i] = nDx;
                                if ( nOptions & ETO_PDY )
                                {
                                    pDYAry[i] = nDy;
                                }
                            }
                            if ( i == nNewTextLen )
                                bUseDXAry = true;
                        }
                        if ( bUseDXAry )
                            DrawText( aPosition, aText, &aDXAry, pDYAry.get() );
                        else
                            DrawText( aPosition, aText );
                        if ( nOptions & ETO_CLIPPED )
                            Pop();
                    }
                }
            }
            break;
 
            case W_META_SELECTOBJECT:
            case W_META_SELECTPALETTE:
            {
                if (nRecordSize != 4)
                    bRecordOk = false;
                sal_uInt16   nObjIndex = 0;
                mpInputStream->ReadUInt16( nObjIndex );
                SelectObject( nObjIndex );
            }
            break;
 
            case W_META_SETTEXTALIGN:
            {
                // It could have Reserved values. Both 4 and 5 sizes are allowed
                if ((nRecordSize != 4) && (nRecordSize != 5))
                    bRecordOk = false;
                sal_uInt16  nAlign = 0;
                mpInputStream->ReadUInt16( nAlign );
                SetTextAlign( nAlign );
            }
            break;
 
            case W_META_BITBLT:
            case W_META_STRETCHBLT:
            {
                sal_uInt32  nRasterOperation = 0;
                sal_Int16   nSrcHeight = 0, nSrcWidth = 0, nYSrc, nXSrc, nSye, nSxe, nBitmapType, nWidth, nHeight, nBytesPerScan;
                sal_uInt8   nPlanes, nBitCount;
                const bool bNoSourceBitmap = ( nRecordSize == ( static_cast< sal_uInt32 >( nFunc ) >> 8 ) + 3 );
 
                mpInputStream->ReadUInt32( nRasterOperation );
                SAL_INFO("emfio", "\t\t Raster operation: 0x" << std::hex << nRasterOperation << std::dec << ", No source bitmap: " << bNoSourceBitmap);
 
                if (nFunc == W_META_STRETCHBLT)
                {
                    if (nRecordSize < 18)
                        bRecordOk = false;
                    mpInputStream->ReadInt16(nSrcHeight).ReadInt16(nSrcWidth);
                }
                else
                    if (nRecordSize < 16)
                        bRecordOk = false;
                mpInputStream->ReadInt16( nYSrc ).ReadInt16( nXSrc );
                if ( bNoSourceBitmap )
                    mpInputStream->SeekRel( 2 ); // Skip Reserved 2 bytes (it must be ignored)
                mpInputStream->ReadInt16( nSye ).ReadInt16( nSxe );
                Point aPoint( ReadYX() ); // The upper-left corner of the destination rectangle.
                mpInputStream->ReadInt16( nBitmapType ).ReadInt16( nWidth ).ReadInt16( nHeight ).ReadInt16( nBytesPerScan ).ReadUChar( nPlanes ).ReadUChar( nBitCount );
 
                SAL_INFO("emfio", "\t\t Bitmap type:" << nBitmapType << " Width:" << nWidth << " Height:" << nHeight << " WidthBytes:" << nBytesPerScan << " Planes: " << static_cast< sal_uInt16 >( nPlanes ) << " BitCount: " << static_cast< sal_uInt16 >( nBitCount ) );
                if (!mpInputStream->good() || bNoSourceBitmap || nBitCount == 4 || nBitCount == 8 || nPlanes != 1)
                {
                    SAL_WARN("emfio", "\t\t TODO The unsupported Bitmap record. Please fill a bug.");
                    break;
                }
                bool bOk = nWidth > 0 && nHeight > 0 && nBytesPerScan > 0
                    && (nBitCount == 1 || nBitCount == 8 || nBitCount == 24 || nBitCount == 32);
                if (bOk)
                {
                    // must be enough data to fulfil the request
                    bOk = o3tl::make_unsigned( nBytesPerScan ) <= mpInputStream->remainingSize() / nHeight;
                }
                if (bOk)
                {
                    // scanline must be large enough to provide all pixels
                    bOk = nBytesPerScan >= nWidth * nBitCount / 8;
                }
                if (bOk)
                {
                    std::unique_ptr< sal_uInt8[] > pData;
                    pData.reset( new sal_uInt8[ nHeight * nBytesPerScan ] );
                    mpInputStream->ReadBytes( pData.get(), nHeight * nBytesPerScan );
                    BitmapEx aBitmap = vcl::bitmap::CreateFromData( pData.get(), nWidth, nHeight, nBytesPerScan, nBitCount, true );
                    if ( nSye && nSxe &&
                         ( nXSrc + nSxe <= nWidth ) &&
                         ( nYSrc + nSye <= nHeight ) )
                    {
                        tools::Rectangle aCropRect( Point( nXSrc, nYSrc ), Size( nSxe, nSye ) );
                        aBitmap.Crop( aCropRect );
                    }
                    tools::Rectangle aDestRect( aPoint, Size( nSxe, nSye ) );
                    maBmpSaveList.emplace_back(aBitmap, aDestRect, nRasterOperation);
                }
            }
            break;
 
            case W_META_DIBBITBLT:
            case W_META_DIBSTRETCHBLT:
            case W_META_STRETCHDIB:
            {
                sal_uInt32  nRasterOperation = 0;
                sal_uInt16  nColorUsage = 0;
                sal_Int16   nSrcHeight = 0, nSrcWidth = 0, nYSrc = 0, nXSrc = 0;
                Bitmap      aBmp;
                const bool bNoSourceBitmap = ( nFunc != W_META_STRETCHDIB ) && ( nRecordSize == ( ( static_cast< sal_uInt32 >( nFunc ) >> 8 ) + 3 ) );
 
                if (nRecordSize < 12)
                    bRecordOk = false;
                mpInputStream->ReadUInt32( nRasterOperation );
                SAL_INFO("emfio", "\t\t Raster operation: 0x" << std::hex << nRasterOperation << std::dec << ", No source bitmap: " << bNoSourceBitmap);
                if (nFunc == W_META_STRETCHDIB)
                {
                    if (nRecordSize < 15)
                        bRecordOk = false;
                    mpInputStream->ReadUInt16(nColorUsage);
                }
 
                // nSrcHeight and nSrcWidth is the number of pixels that has to been used
                // If they are set to zero, it is as indicator not to scale the bitmap later
                if( nFunc == W_META_DIBSTRETCHBLT ||
                    nFunc == W_META_STRETCHDIB )
                    mpInputStream->ReadInt16( nSrcHeight ).ReadInt16( nSrcWidth );
 
                // nYSrc and nXSrc is the offset of the first pixel
                mpInputStream->ReadInt16( nYSrc ).ReadInt16( nXSrc );
 
                if ( bNoSourceBitmap )
                    mpInputStream->SeekRel( 2 ); // Skip Reserved 2 bytes (it must be ignored)
 
                Size aDestSize( ReadYXExt() );
                if ( aDestSize.Width() && aDestSize.Height() )  // #92623# do not try to read buggy bitmaps
                {
                    tools::Rectangle aDestRect( ReadYX(), aDestSize );
                    if ( !bNoSourceBitmap )
                    {
                        // tdf#142625 Read the DIBHeader and check if bitmap is supported
                        // If bitmap is not supported don't run ReadDIB, as it will interrupt image processing
                        const auto nOldPos(mpInputStream->Tell());
                        sal_uInt32 nHeaderSize(0);
                        mpInputStream->ReadUInt32( nHeaderSize );
                        if ( nHeaderSize == 0xC ) // BitmapCoreHeader
                            mpInputStream->SeekRel( 6 ); // skip Width (16), Height (16), Planes (16)
                        else
                            mpInputStream->SeekRel( 10 ); // skip Width (32), Height (32), Planes (16)
                        sal_uInt16 nBitCount(0);
                        mpInputStream->ReadUInt16( nBitCount );
                        if ( nBitCount == 0 ) // TODO Undefined BitCount (JPEG/PNG), which are not supported
                            break;
                        mpInputStream->Seek(nOldPos);
 
                        if ( !ReadDIB( aBmp, *mpInputStream, false ) )
                            SAL_WARN( "emfio", "\tTODO Read DIB failed. Interrupting processing whole image. Please report bug report." );
                    }
                    // test if it is sensible to crop
                    if ( nSrcHeight && nSrcWidth &&
                            ( nXSrc + nSrcWidth <= aBmp.GetSizePixel().Width() ) &&
                            ( nYSrc + nSrcHeight <= aBmp.GetSizePixel().Height() ) )
                    {
                        tools::Rectangle aCropRect( Point( nXSrc, nYSrc ), Size( nSrcWidth, nSrcHeight ) );
                        aBmp.Crop( aCropRect );
                    }
 
                    maBmpSaveList.emplace_back(aBmp, aDestRect, nRasterOperation);
                }
            }
            break;
 
            case W_META_DIBCREATEPATTERNBRUSH:
            {
                Bitmap  aBmp;
                sal_uInt32 nRed(0), nGreen(0), nBlue(0), nCount(1);
                sal_uInt16 nStyle(0), nColorUsage(0);
 
                if (nRecordSize < 5)
                    bRecordOk = false;
                mpInputStream->ReadUInt16( nStyle ).ReadUInt16( nColorUsage );
                BrushStyle eStyle = static_cast<BrushStyle>(nStyle);
                SAL_INFO( "emfio", "\t\t Style:" << nStyle << ", ColorUsage: " << nColorUsage );
                if ( eStyle == BrushStyle::BS_PATTERN ) // TODO tdf#142625 Add support for pattern
                {
                    SAL_WARN( "emfio", "\tTODO: Pattern brush style is not supported." );
                    CreateObject();
                    break;
                }
                if ( !ReadDIB( aBmp, *mpInputStream, false ) )
                    SAL_WARN( "emfio", "\tTODO Read DIB failed. Interrupting processing whole image. Please report bug report." );
                if ( !aBmp.IsEmpty() )
                {
                    BitmapScopedReadAccess pBmp(aBmp);
                    for ( tools::Long y = 0; y < pBmp->Height(); y++ )
                    {
                        for ( tools::Long x = 0; x < pBmp->Width(); x++ )
                        {
                            const BitmapColor aColor( pBmp->GetColor( y, x ) );
 
                            nRed += aColor.GetRed();
                            nGreen += aColor.GetGreen();
                            nBlue += aColor.GetBlue();
                        }
                    }
                    nCount = pBmp->Height() * pBmp->Width();
                    if ( !nCount )
                        nCount++;
                }
                Color aColor( static_cast<sal_uInt8>( nRed / nCount ), static_cast<sal_uInt8>( nGreen / nCount ), static_cast<sal_uInt8>( nBlue / nCount ) );
                CreateObject(std::make_unique<WinMtfFillStyle>( aColor, false ));
            }
            break;
 
            case W_META_DELETEOBJECT:
            {
                if (nRecordSize != 4)
                    bRecordOk = false;
                sal_uInt16 nIndex = 0;
                mpInputStream->ReadUInt16( nIndex );
                DeleteObject( nIndex );
            }
            break;
 
            case W_META_CREATEPALETTE:
            {
                sal_uInt16 nStart = 0;
                sal_uInt16 nNumberOfEntries = 0;
                mpInputStream->ReadUInt16(nStart);
                mpInputStream->ReadUInt16(nNumberOfEntries);
 
                if (nRecordSize != 2u * nNumberOfEntries + 5u)
                    bRecordOk = false;
                SAL_INFO("emfio", "\t\t Start 0x" << std::hex << nStart << std::dec << ", Number of entries: " << nNumberOfEntries);
                sal_uInt32 nPalleteEntry;
                std::vector< Color > aPaletteColors;
                for (sal_uInt16 i = 0; i < nNumberOfEntries; ++i)
                {
                    //PALETTEENTRY: Values, Blue, Green, Red
                    mpInputStream->ReadUInt32( nPalleteEntry );
                    SAL_INFO("emfio", "\t\t " << i << ". Palette entry: " << std::setw(10) << std::showbase <<std::hex << nPalleteEntry << std::dec );
                    aPaletteColors.emplace_back(static_cast<sal_uInt8>(nPalleteEntry), static_cast<sal_uInt8>(nPalleteEntry >> 8), static_cast<sal_uInt8>(nPalleteEntry >> 16));
                }
                CreateObject(std::make_unique<WinMtfPalette>( aPaletteColors ));
            }
            break;
 
            case W_META_CREATEBRUSH:
            {
                SAL_WARN( "emfio", "TODO: Not implemented. Please fill the bug report" );
                CreateObject(std::make_unique<WinMtfFillStyle>( COL_WHITE, false ));
            }
            break;
 
            case W_META_CREATEPATTERNBRUSH:
            {
                SAL_WARN( "emfio", "TODO: Not implemented. Please fill the bug report" );
                CreateObject(std::make_unique<WinMtfFillStyle>( COL_WHITE, false ));
            }
            break;
 
            case W_META_CREATEPENINDIRECT:
            {
                // FIXME For some WMF correct size is 8 and for some 9
                if ((nRecordSize != 8) && (nRecordSize != 9))
                    bRecordOk = false;
                LineInfo aLineInfo;
                sal_uInt16 nStyle = 0;
                sal_uInt16 nWidth = 0;
                sal_uInt16 nHeight = 0;
 
                mpInputStream->ReadUInt16(nStyle);
                mpInputStream->ReadUInt16(nWidth);
                mpInputStream->ReadUInt16(nHeight);
                CreateObject(std::make_unique<WinMtfLineStyle>(ReadColor(), nStyle, nWidth));
            }
            break;
 
            case W_META_CREATEBRUSHINDIRECT:
            {
                if (nRecordSize != 7)
                    bRecordOk = false;
                sal_uInt16  nBrushStyle = 0;
                mpInputStream->ReadUInt16( nBrushStyle );
                BrushStyle eBrushStyle = static_cast<BrushStyle>(nBrushStyle);
                CreateObject(std::make_unique<WinMtfFillStyle>( ReadColor(), ( eBrushStyle == BrushStyle::BS_NULL ) ));
                SAL_WARN_IF( (eBrushStyle != BrushStyle::BS_SOLID) && (eBrushStyle != BrushStyle::BS_NULL), "emfio", "TODO: Brush style not implemented. Please fill the bug report" );
            }
            break;
 
            case W_META_CREATEFONTINDIRECT:
            {
                Size aFontSize;
                char lfFaceName[LF_FACESIZE+1];
                sal_Int16 lfEscapement = 0;
                sal_Int16 lfOrientation = 0;
                sal_Int16 lfWeight = 0;
 
                LOGFONTW aLogFont;
                aFontSize = ReadYXExt();
                mpInputStream->ReadInt16( lfEscapement );
                mpInputStream->ReadInt16( lfOrientation );
                mpInputStream->ReadInt16( lfWeight );
                mpInputStream->ReadUChar( aLogFont.lfItalic );
                mpInputStream->ReadUChar( aLogFont.lfUnderline );
                mpInputStream->ReadUChar( aLogFont.lfStrikeOut );
                mpInputStream->ReadUChar( aLogFont.lfCharSet );
                mpInputStream->ReadUChar( aLogFont.lfOutPrecision );
                mpInputStream->ReadUChar( aLogFont.lfClipPrecision );
                mpInputStream->ReadUChar( aLogFont.lfQuality );
                mpInputStream->ReadUChar( aLogFont.lfPitchAndFamily );
                size_t nRet = mpInputStream->ReadBytes( lfFaceName, LF_FACESIZE );
                lfFaceName[nRet] = 0;
                aLogFont.lfWidth = aFontSize.Width();
                aLogFont.lfHeight = aFontSize.Height();
                aLogFont.lfEscapement = lfEscapement;
                aLogFont.lfOrientation = lfOrientation;
                aLogFont.lfWeight = lfWeight;
 
                rtl_TextEncoding eCharSet;
                if ( ( aLogFont.lfCharSet == OEM_CHARSET ) || ( aLogFont.lfCharSet == DEFAULT_CHARSET ) )
                    eCharSet = osl_getThreadTextEncoding();
                else
                    eCharSet = rtl_getTextEncodingFromWindowsCharset( aLogFont.lfCharSet );
                if ( eCharSet == RTL_TEXTENCODING_DONTKNOW )
                    eCharSet = osl_getThreadTextEncoding();
                if ( eCharSet == RTL_TEXTENCODING_SYMBOL )
                    eCharSet = RTL_TEXTENCODING_MS_1252;
                size_t nLength = strlen(lfFaceName);
                aLogFont.alfFaceName = OUString( lfFaceName, nLength, eCharSet );
                SAL_INFO("emfio", "\tFacename: " << lfFaceName);
 
                if ((nRecordSize < 12) || (nRecordSize > 28))
                    bRecordOk = false;
                else
                    CreateObject(std::make_unique<WinMtfFontStyle>(aLogFont));
            }
            break;
 
            case W_META_CREATEBITMAPINDIRECT:
            {
                SAL_WARN( "emfio", "TODO: W_META_CREATEBITMAPINDIRECT is not implemented. Please fill the bug report" );
                CreateObject();
            }
            break;
 
            case W_META_CREATEBITMAP:
            {
                SAL_WARN( "emfio", "TODO: W_META_CREATEBITMAP is not implemented. Please fill the bug report" );
                CreateObject();
            }
            break;
 
            case W_META_CREATEREGION:
            {
                SAL_WARN( "emfio", "TODO: W_META_CREATEREGION is not implemented. Please fill the bug report" );
                CreateObject();
            }
            break;
 
            case W_META_EXCLUDECLIPRECT:
            {
                if (nRecordSize != 7)
                    bRecordOk = false;
                SAL_WARN("emfio", "TODO:  Not working correctly. Please fill the bug report");
                ExcludeClipRect(ReadRectangle());
            }
            break;
 
            case W_META_PATBLT:
            {
                if (nRecordSize != 9)
                    bRecordOk = false;
                sal_uInt32 nROP = 0;
                mpInputStream->ReadUInt32( nROP );
                Size aSize = ReadYXExt();
                WMFRasterOp nOldROP = SetRasterOp( static_cast<WMFRasterOp>(nROP) );
                DrawRect( tools::Rectangle( ReadYX(), aSize ), false );
                SetRasterOp( nOldROP );
            }
            break;
 
            case W_META_SELECTCLIPREGION:
            {
                if (nRecordSize != 4)
                    bRecordOk = false;
                sal_uInt16 nObjIndex = 0;
                mpInputStream->ReadUInt16( nObjIndex );
                SAL_WARN( "emfio", "TODO: W_META_SELECTCLIPREGION is not implemented. Please fill the bug report" );
                if ( !nObjIndex )
                {
                    tools::PolyPolygon aEmptyPolyPoly;
                    SetClipPath( aEmptyPolyPoly, RegionMode::RGN_COPY, true );
                }
            }
            break;
 
            case W_META_ESCAPE:
            {
                sal_uInt64 nMetaRecSize = static_cast<sal_uInt64>(nRecordSize - 2) * 2;
                sal_uInt64 nMetaRecEndPos = mpInputStream->Tell() + nMetaRecSize;
 
                // taking care that nRecordSize does not exceed the maximal stream position
                if (nMetaRecEndPos > mnEndPos)
                {
                    mpInputStream->SetError(SVSTREAM_FILEFORMAT_ERROR);
                    break;
                }
                sal_uInt16 nMode = 0, nLen = 0;
                mpInputStream->ReadUInt16(nMode).ReadUInt16(nLen);
                if (nRecordSize != ((nLen + 1u) >> 1u) + 5u)
                {
                    bRecordOk = false;
                    break;
                }
                if ((nMode == W_MFCOMMENT) && (nLen >= 4))
                {
                    sal_uInt32 nNewMagic = 0; // we have to read int32 for
                    // META_ESCAPE_ENHANCED_METAFILE CommentIdentifier
                    mpInputStream->ReadUInt32(nNewMagic);
 
                    if (nNewMagic == 0x2c2a4f4f && nLen >= 14)
                    {
                        sal_uInt16 nMagic2 = 0;
                        mpInputStream->ReadUInt16(nMagic2);
                        if (nMagic2 == 0x0a) // 2nd half of magic
                        { // continue with private escape
                            sal_uInt32 nCheck = 0, nEsc = 0;
                            mpInputStream->ReadUInt32(nCheck).ReadUInt32(nEsc);
 
                            sal_uInt32 nEscLen = nLen - 14;
                            if (nEscLen <= (nRecordSize * 2))
                            {
#ifdef OSL_BIGENDIAN
                                sal_uInt32 nTmp = OSL_SWAPDWORD(nEsc);
                                sal_uInt32 nCheckSum = rtl_crc32(0, &nTmp, 4);
#else
                                sal_uInt32 nCheckSum = rtl_crc32(0, &nEsc, 4);
#endif
                                std::unique_ptr<sal_Int8[]> pData;
 
                                if ((static_cast<sal_uInt64>(nEscLen) + mpInputStream->Tell())
                                    > nMetaRecEndPos)
                                {
                                    mpInputStream->SetError(SVSTREAM_FILEFORMAT_ERROR);
                                    break;
                                }
                                if (nEscLen > 0)
                                {
                                    pData.reset(new sal_Int8[nEscLen]);
                                    mpInputStream->ReadBytes(pData.get(), nEscLen);
                                    nCheckSum = rtl_crc32(nCheckSum, pData.get(), nEscLen);
                                }
                                if (nCheck == nCheckSum)
                                {
                                    switch (nEsc)
                                    {
                                        case PRIVATE_ESCAPE_UNICODE:
                                        {
                                            // we will use text instead of polygons only if we have the correct font
                                            if (Application::GetDefaultDevice()->IsFontAvailable(
                                                    GetFont().GetFamilyName()))
                                            {
                                                Point aPt;
                                                sal_uInt32 nStringLen, nDXCount;
                                                KernArray aDXAry;
                                                SvMemoryStream aMemoryStream(nEscLen);
                                                aMemoryStream.WriteBytes(pData.get(), nEscLen);
                                                aMemoryStream.Seek(STREAM_SEEK_TO_BEGIN);
                                                sal_Int32 nTmpX(0), nTmpY(0);
                                                aMemoryStream.ReadInt32(nTmpX)
                                                    .ReadInt32(nTmpY)
                                                    .ReadUInt32(nStringLen);
                                                aPt.setX(nTmpX);
                                                aPt.setY(nTmpY);
 
                                                if ((static_cast<sal_uInt64>(nStringLen)
                                                    * sizeof(sal_Unicode))
                                                    < (nEscLen - aMemoryStream.Tell()))
                                                {
                                                    OUString aString = read_uInt16s_ToOUString(
                                                        aMemoryStream, nStringLen);
                                                    aMemoryStream.ReadUInt32(nDXCount);
                                                    if ((static_cast<sal_uInt64>(nDXCount)
                                                        * sizeof(sal_Int32))
                                                        >= (nEscLen - aMemoryStream.Tell()))
                                                        nDXCount = 0;
                                                    if (nDXCount)
                                                        aDXAry.resize(nDXCount);
                                                    for (sal_uInt32 i = 0; i < nDXCount; i++)
                                                    {
                                                        sal_Int32 val;
                                                        aMemoryStream.ReadInt32(val);
                                                        aDXAry[i] = val;
                                                    }
                                                    aMemoryStream.ReadUInt32(mnSkipActions);
                                                    DrawText(aPt, aString,
                                                             aDXAry.empty() ? nullptr : &aDXAry);
                                                }
                                            }
                                        }
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    else if ((nNewMagic == static_cast<sal_uInt32>(0x43464D57)) && (nLen >= 34)
                            && (static_cast<sal_Int32>(nLen + 10)
                                <= static_cast<sal_Int32>(nRecordSize * 2)))
                    {
                        sal_uInt32 nComType = 0, nVersion = 0, nFlags = 0, nComRecCount = 0,
                                nCurRecSize = 0, nRemainingSize = 0, nEMFTotalSize = 0;
                        sal_uInt16 nCheck = 0;
 
                        mpInputStream->ReadUInt32(nComType)
                            .ReadUInt32(nVersion)
                            .ReadUInt16(nCheck)
                            .ReadUInt32(nFlags)
                            .ReadUInt32(nComRecCount)
                            .ReadUInt32(nCurRecSize)
                            .ReadUInt32(nRemainingSize)
                            .ReadUInt32(
                                nEMFTotalSize); // the nRemainingSize is not mentioned in MSDN documentation
                            // but it seems to be required to read in data produced by OLE
 
                        if (nComType == 0x01 && nVersion == 0x10000 && nComRecCount)
                        {
                            if (!mnEMFRec)
                            { // first EMF comment
                                mnEMFRecCount = nComRecCount;
                                mnEMFSize = nEMFTotalSize;
                                if (mnEMFSize > mpInputStream->remainingSize())
                                {
                                    SAL_WARN("emfio",
                                            "emf size claims to be larger than remaining data");
                                    mpEMFStream.reset();
                                }
                                else
                                    mpEMFStream = std::vector<sal_uInt8>();
                            }
                            else if ((mnEMFRecCount != nComRecCount)
                                    || (mnEMFSize != nEMFTotalSize)) // add additional checks here
                            {
                                // total records should be the same as in previous comments
                                mnEMFRecCount = 0xFFFFFFFF;
                                mpEMFStream.reset();
                            }
                            mnEMFRec++;
 
                            if (mpEMFStream && nCurRecSize + 34 > nLen)
                            {
                                mnEMFRecCount = 0xFFFFFFFF;
                                mpEMFStream.reset();
                            }
 
                            if (mpEMFStream && nCurRecSize > mpInputStream->remainingSize())
                            {
                                SAL_WARN("emfio",
                                        "emf record size claims to be larger than remaining data");
                                mnEMFRecCount = 0xFFFFFFFF;
                                mpEMFStream.reset();
                            }
 
                            if (mpEMFStream)
                            {
                                std::vector<sal_Int8> aBuf(nCurRecSize);
                                sal_uInt32 nCount = mpInputStream->ReadBytes(aBuf.data(), nCurRecSize);
                                if (nCount == nCurRecSize)
                                {
                                    mpEMFStream->insert(mpEMFStream->end(), aBuf.begin(), aBuf.end());
                                }
                            }
                        }
                    }
                }
            }
            break;
 
            case W_META_SETRELABS:
            case W_META_SETPOLYFILLMODE:
            case W_META_SETSTRETCHBLTMODE:
            case W_META_SETTEXTCHAREXTRA:
            case W_META_SETTEXTJUSTIFICATION:
            case W_META_FLOODFILL:
            case W_META_FILLREGION:
            case W_META_FRAMEREGION:
            case W_META_INVERTREGION:
            case W_META_PAINTREGION:
            case W_META_DRAWTEXT:
            case W_META_SETMAPPERFLAGS:
            case W_META_SETDIBTODEV:
            case W_META_REALIZEPALETTE:
            case W_META_ANIMATEPALETTE:
            case W_META_SETPALENTRIES:
            case W_META_RESIZEPALETTE:
            case W_META_EXTFLOODFILL:
            case W_META_RESETDC:
            case W_META_STARTDOC:
            case W_META_STARTPAGE:
            case W_META_ENDPAGE:
            case W_META_ABORTDOC:
            case W_META_ENDDOC:
            {
                SAL_WARN("emfio", "TODO: WMF record not implemented: " << record_type_name(nFunc));
            }
            break;
 
            default:
            {
                SAL_WARN("emfio", "Unknown Meta Action: 0x" << std::hex << nFunc << std::dec);
            }
        }
 
        if (!bRecordOk)
        {
            SAL_WARN("emfio", "WMF validation failed, record: " <<
                              record_type_name(nFunc) << ", size: " << nRecordSize);
            mpInputStream->SetError(SVSTREAM_FILEFORMAT_ERROR);
        }
 
        // tdf#127471
        maScaledFontHelper.applyAlternativeFontScale();
    }
 
    const tools::Long   aMaxWidth = 1024;
 
    bool WmfReader::ReadHeader()
    {
        sal_uInt64 const nStrmPos = mpInputStream->Tell();
 
        sal_uInt32 nPlaceableMetaKey(0);
        // if available read the METAFILEHEADER
        mpInputStream->ReadUInt32( nPlaceableMetaKey );
        if (!mpInputStream->good())
            return false;
 
        tools::Rectangle aPlaceableBound;
 
        mbPlaceable = nPlaceableMetaKey == 0x9ac6cdd7L;
 
        SAL_INFO("emfio", "Placeable: \"" << (mbPlaceable ? "yes" : "no") << "\"");
 
        if (mbPlaceable)
        {
            //TODO do some real error handling here
            sal_Int16 nVal(0);
 
            // Skip reserved bytes
            mpInputStream->SeekRel(2);
 
            // BoundRect
            // These are simply ignored for now
            mpInputStream->ReadInt16( nVal );
            aPlaceableBound.SetLeft( nVal );
            mpInputStream->ReadInt16( nVal );
            aPlaceableBound.SetTop( nVal );
            mpInputStream->ReadInt16( nVal );
            aPlaceableBound.SetRight( nVal );
            mpInputStream->ReadInt16( nVal );
            aPlaceableBound.SetBottom( nVal );
 
            // inch
            mpInputStream->ReadUInt16( mnUnitsPerInch );
 
            // reserved
            mpInputStream->SeekRel( 4 );
 
            // Skip and don't check the checksum
            mpInputStream->SeekRel( 2 );
 
            // Skip wmf header
            mpInputStream->Seek( nStrmPos + 40 );    // set the streampos to the start of the metaactions
            GetPlaceableBound( aPlaceableBound, mpInputStream );
            // Go back to the place after placeable header
            mpInputStream->Seek( nStrmPos + 22);
        }
        else
        {
            // Default is 1440, but it is set to 96 to show the wmf larger
            mnUnitsPerInch = 96;
 
            if (mpExternalHeader != nullptr
                && mpExternalHeader->xExt > 0
                && mpExternalHeader->yExt > 0
                && (mpExternalHeader->mapMode == MappingMode::MM_ISOTROPIC || mpExternalHeader->mapMode == MappingMode::MM_ANISOTROPIC))
            {
                // #n417818#: If we have an external header then overwrite the bounds!
                tools::Rectangle aExtRect(0, 0,
                    o3tl::convert(mpExternalHeader->xExt, o3tl::Length::mm100, o3tl::Length::px),
                    o3tl::convert(mpExternalHeader->yExt, o3tl::Length::mm100, o3tl::Length::px));
                aPlaceableBound = aExtRect;
 
                SAL_INFO("emfio", "External header size "
                    " left: " << aPlaceableBound.Left() << " top: " << aPlaceableBound.Top()
                    << " right: " << aPlaceableBound.Right() << " bottom: " << aPlaceableBound.Bottom());
 
                SetMapMode(static_cast<MappingMode>(mpExternalHeader->mapMode));;
            }
            else
            {
                mpInputStream->Seek(nStrmPos + 18);    // set the streampos to the start of the metaactions
                GetPlaceableBound(aPlaceableBound, mpInputStream);
 
                // The image size is not known so normalize the calculated bounds so that the
                // resulting image is not too big
                if (aPlaceableBound.GetWidth() > aMaxWidth)
                {
                    const double fMaxWidth = static_cast<double>(aMaxWidth);
                    double fRatio = aPlaceableBound.GetWidth() / fMaxWidth;
 
                    // changing mnUnitsPerInch as a tool to scale wmf
                    mnUnitsPerInch *= fRatio;
 
                }
                SAL_INFO("emfio", "Placeable bounds "
                                  " left: " << aPlaceableBound.Left() << " top: " << aPlaceableBound.Top() <<
                                  " right: " << aPlaceableBound.Right() << " bottom: " << aPlaceableBound.Bottom());
            }
 
            mpInputStream->Seek( nStrmPos );
        }
 
        SetWinOrg( aPlaceableBound.TopLeft() );
        Size aWMFSize(
            std::abs( aPlaceableBound.GetWidth() ), std::abs( aPlaceableBound.GetHeight() ) );
        SetWinExt( aWMFSize );
 
        SAL_INFO("emfio", "WMF size  w: " << aWMFSize.Width()    << " h: " << aWMFSize.Height());
 
        Size aDevExt( 10000, 10000 );
        if( ( std::abs( aWMFSize.Width() ) > 1 ) && ( std::abs( aWMFSize.Height() ) > 1 ) )
        {
            const Fraction  aFrac( 1, mnUnitsPerInch);
            MapMode         aWMFMap( MapUnit::MapInch, Point(), aFrac, aFrac );
            Size            aSize100(OutputDevice::LogicToLogic(aWMFSize, aWMFMap, MapMode(MapUnit::Map100thMM)));
            aDevExt = Size( std::abs( aSize100.Width() ), std::abs( aSize100.Height() ) );
        }
        SetDevExt( aDevExt );
 
        SAL_INFO("emfio", "Dev size  w: " << aDevExt.Width()    << " h: " << aDevExt.Height());
 
        // read the METAHEADER
        sal_uInt32 nMetaKey(0);
        mpInputStream->ReadUInt32( nMetaKey ); // type and headersize
        if (!mpInputStream->good())
            return false;
        if (nMetaKey != 0x00090001)
        {
            sal_uInt16 aNextWord(0);
            mpInputStream->ReadUInt16( aNextWord );
            if (nMetaKey != 0x10000 || aNextWord != 0x09)
            {
                mpInputStream->SetError( SVSTREAM_FILEFORMAT_ERROR );
                return false;
            }
        }
 
        mpInputStream->SeekRel( 2 ); // Version (of Windows)
        mpInputStream->SeekRel( 4 ); // Size (of file in words)
        mpInputStream->SeekRel( 2 ); // NoObjects (maximum number of simultaneous objects)
        mpInputStream->SeekRel( 4 ); // MaxRecord (size of largest record in words)
        mpInputStream->SeekRel( 2 ); // NoParameters (Unused
 
        return mpInputStream->good();
    }
 
    void WmfReader::ReadWMF()
    {
        sal_uInt16  nFunction;
 
        mnSkipActions = 0;
 
        mpEMFStream.reset();
        mnEMFRecCount = 0;
        mnEMFRec = 0;
        mnEMFSize = 0;
 
        SetMapMode( MappingMode::MM_ANISOTROPIC );
        SetWinOrg( Point() );
        SetWinExt( Size( 1, 1 ) );
        SetDevExt( Size( 10000, 10000 ) );
 
        mnEndPos=mpInputStream->TellEnd();
        mpInputStream->Seek( mnStartPos );
 
        if ( ReadHeader( ) )
        {
            auto nPos = mpInputStream->Tell();
 
            if( mnEndPos - mnStartPos )
            {
                bool bEMFAvailable = false;
                while( !mpInputStream->eof() )
                {
                    mpInputStream->ReadUInt32(mnRecSize).ReadUInt16( nFunction );
 
                    if (!mpInputStream->good() || (mnRecSize < 3) || (nFunction == W_META_EOF))
                    {
                        if (mpInputStream->eof())
                            mpInputStream->SetError(SVSTREAM_FILEFORMAT_ERROR);
 
                        break;
                    }
 
                    const sal_uInt32 nAvailableBytes = mnEndPos - nPos;
                    const sal_uInt32 nMaxPossibleRecordSize = nAvailableBytes/2;
                    if (mnRecSize > nMaxPossibleRecordSize)
                    {
                        mpInputStream->SetError(SVSTREAM_FILEFORMAT_ERROR);
                        break;
                    }
 
                    if ( !bEMFAvailable )
                    {
                        if(   !maBmpSaveList.empty()
                          && ( nFunction != W_META_STRETCHDIB    )
                          && ( nFunction != W_META_DIBBITBLT     )
                          && ( nFunction != W_META_DIBSTRETCHBLT )
                          )
                        {
                            ResolveBitmapActions( maBmpSaveList );
                        }
 
                        if ( !mnSkipActions)
                            ReadRecordParams( mnRecSize, nFunction );
                        else
                            mnSkipActions--;
 
                        if(mpEMFStream && mnEMFRecCount == mnEMFRec)
                        {
                            GDIMetaFile aMeta;
                            SvMemoryStream aStream(mpEMFStream->data(), mpEMFStream->size(), StreamMode::STD_READ);
                            std::unique_ptr<EmfReader> pEMFReader(std::make_unique<EmfReader>(aStream, aMeta));
                            pEMFReader->SetEnableEMFPlus(mbEnableEMFPlus);
                            bEMFAvailable = pEMFReader->ReadEnhWMF();
                            pEMFReader.reset(); // destroy first!!!
 
                            if( bEMFAvailable )
                            {
                                AddFromGDIMetaFile( aMeta );
                                SetrclFrame( tools::Rectangle( Point(0, 0), aMeta.GetPrefSize()));
 
                                // the stream needs to be set to the wmf end position,
                                // otherwise the GfxLink that is created will be incorrect
                                // (leading to graphic loss after swapout/swapin).
                                // so we will proceed normally, but are ignoring further wmf
                                // records
                            }
                            else
                            {
                                // something went wrong
                                // continue with WMF, don't try this again
                                mpEMFStream.reset();
                            }
                        }
                    }
 
                    nPos += mnRecSize * 2;
                    mpInputStream->Seek(nPos);
                }
            }
            else
                mpInputStream->SetError( SVSTREAM_GENERALERROR );
 
            if( !mpInputStream->GetError() && !maBmpSaveList.empty() )
                ResolveBitmapActions( maBmpSaveList );
        }
        if ( mpInputStream->GetError() )
            mpInputStream->Seek( mnStartPos );
    }
 
    void WmfReader::GetPlaceableBound( tools::Rectangle& rPlaceableBound, SvStream* pStm )
    {
        bool bRet = true;
 
        tools::Rectangle aBound;
        aBound.SetLeft( RECT_MAX );
        aBound.SetTop( RECT_MAX );
        aBound.SetRight( RECT_MIN );
        aBound.SetBottom( RECT_MIN );
        bool bBoundsDetermined = false;
 
        auto nPos = pStm->Tell();
        auto nEnd = nPos + pStm->remainingSize();
 
        Point aWinOrg(0,0);
        std::optional<Size>  aWinExt;
 
        Point aViewportOrg(0,0);
        std::optional<Size>  aViewportExt;
 
        MappingMode eMapMode = MappingMode::MM_ANISOTROPIC;
 
        if (nEnd - nPos)
        {
            sal_uInt16 nFunction;
            sal_uInt32 nRSize;
 
            while( bRet )
            {
                pStm->ReadUInt32( nRSize ).ReadUInt16( nFunction );
 
                if( pStm->GetError() )
                {
                    bRet = false;
                    break;
                }
                else if (pStm->eof() || nRSize < 3)
                {
                    pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
                    bRet = false;
                    break;
                }
                else if (nFunction == W_META_EOF)
                {
                    break;
                }
 
                switch( nFunction )
                {
                    case W_META_SETWINDOWORG:
                    {
                        aWinOrg = ReadYX();
                    }
                    break;
 
                    case W_META_SETWINDOWEXT:
                    {
                        sal_Int16 nWidth(0), nHeight(0);
                        pStm->ReadInt16(nHeight);
                        pStm->ReadInt16(nWidth);
                        aWinExt = Size(nWidth, nHeight);
                    }
                    break;
 
                    case W_META_SETVIEWPORTORG:
                    {
                        aViewportOrg = ReadYX();
                    }
                    break;
 
                    case W_META_SETVIEWPORTEXT:
                    {
                        sal_Int16 nWidth(0), nHeight(0);
                        pStm->ReadInt16(nHeight);
                        pStm->ReadInt16(nWidth);
                        aViewportExt = Size(nWidth, nHeight);
                    }
                    break;
 
                    case W_META_SETMAPMODE:
                    {
                        sal_uInt16 nMapMode(0);
                        pStm->ReadUInt16(nMapMode);
                        eMapMode = static_cast<MappingMode>(nMapMode);
                    }
                    break;
 
                    case W_META_MOVETO:
                    case W_META_LINETO:
                    {
                        GetWinExtMax( ReadYX(), aBound, eMapMode );
                        bBoundsDetermined = true;
                    }
                    break;
 
                    case W_META_RECTANGLE:
                    case W_META_INTERSECTCLIPRECT:
                    case W_META_EXCLUDECLIPRECT:
                    case W_META_ELLIPSE:
                    {
                        GetWinExtMax( ReadRectangle(), aBound, eMapMode );
                        bBoundsDetermined = true;
                    }
                    break;
 
                    case W_META_ROUNDRECT:
                    {
                        ReadYXExt(); // size
                        GetWinExtMax( ReadRectangle(), aBound, eMapMode );
                        bBoundsDetermined = true;
                    }
                    break;
 
                    case W_META_ARC:
                    case W_META_PIE:
                    case W_META_CHORD:
                    {
                        ReadYX(); // end
                        ReadYX(); // start
                        GetWinExtMax( ReadRectangle(), aBound, eMapMode );
                        bBoundsDetermined = true;
                    }
                    break;
 
                    case W_META_POLYGON:
                    {
                        bool bRecordOk = true;
 
                        sal_uInt16 nPoints(0);
                        pStm->ReadUInt16( nPoints );
 
                        if (nPoints > pStm->remainingSize() / (2 * sizeof(sal_uInt16)))
                        {
                            bRecordOk = false;
                        }
                        else
                        {
                            for(sal_uInt16 i = 0; i < nPoints; i++ )
                            {
                                GetWinExtMax( ReadPoint(), aBound, eMapMode );
                                bBoundsDetermined = true;
                            }
                        }
 
                        bRecordOk &= pStm->good();
 
                        SAL_WARN_IF(!bRecordOk, "emfio", "polyline record claimed more points than the stream can provide");
 
                        if (!bRecordOk)
                        {
                            pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
                            bRet = false;
                            break;
                        }
                    }
                    break;
 
                    case W_META_POLYPOLYGON:
                    {
                        bool bRecordOk = true;
                        sal_uInt16 nPoly(0), nPoints(0);
                        pStm->ReadUInt16(nPoly);
                        if (nPoly > pStm->remainingSize() / sizeof(sal_uInt16))
                        {
                            bRecordOk = false;
                        }
                        else
                        {
                            for(sal_uInt16 i = 0; i < nPoly; i++ )
                            {
                                sal_uInt16 nP = 0;
                                pStm->ReadUInt16( nP );
                                if (nP > SAL_MAX_UINT16 - nPoints)
                                {
                                    bRecordOk = false;
                                    break;
                                }
                                nPoints += nP;
                            }
                        }
 
                        SAL_WARN_IF(!bRecordOk, "emfio", "polypolygon record has more polygons than we can handle");
 
                        bRecordOk &= pStm->good();
 
                        if (!bRecordOk)
                        {
                            pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
                            bRet = false;
                            break;
                        }
 
                        if (nPoints > pStm->remainingSize() / (2 * sizeof(sal_uInt16)))
                        {
                            bRecordOk = false;
                        }
                        else
                        {
                            for (sal_uInt16 i = 0; i < nPoints; i++ )
                            {
                                GetWinExtMax( ReadPoint(), aBound, eMapMode );
                                bBoundsDetermined = true;
                            }
                        }
 
                        SAL_WARN_IF(!bRecordOk, "emfio", "polypolygon record claimed more points than the stream can provide");
 
                        bRecordOk &= pStm->good();
 
                        if (!bRecordOk)
                        {
                            pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
                            bRet = false;
                            break;
                        }
                    }
                    break;
 
                    case W_META_POLYLINE:
                    {
                        bool bRecordOk = true;
 
                        sal_uInt16 nPoints(0);
                        pStm->ReadUInt16(nPoints);
                        if (nPoints > pStm->remainingSize() / (2 * sizeof(sal_uInt16)))
                        {
                            bRecordOk = false;
                        }
                        else
                        {
                            for (sal_uInt16 i = 0; i < nPoints; ++i)
                            {
                                GetWinExtMax( ReadPoint(), aBound, eMapMode );
                                bBoundsDetermined = true;
                            }
                        }
 
                        SAL_WARN_IF(!bRecordOk, "emfio", "polyline record claimed more points than the stream can provide");
 
                        bRecordOk &= pStm->good();
 
                        if (!bRecordOk)
                        {
                            pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
                            bRet = false;
                            break;
                        }
                    }
                    break;
 
                    case W_META_SETPIXEL:
                    {
                        ReadColor();
                        GetWinExtMax( ReadYX(), aBound, eMapMode );
                        bBoundsDetermined = true;
                    }
                    break;
 
                    case W_META_TEXTOUT:
                    {
                        sal_uInt16 nLength(0);
                        pStm->ReadUInt16( nLength );
                        // todo: we also have to take care of the text width
                        if ( nLength )
                        {
                            pStm->SeekRel( ( nLength + 1 ) &~ 1 );
                            GetWinExtMax( ReadYX(), aBound, eMapMode );
                            bBoundsDetermined = true;
                        }
                    }
                    break;
 
                    case W_META_EXTTEXTOUT:
                    {
                        sal_uInt16 nLen(0), nOptions;
                        Point aPosition = ReadYX();
                        pStm->ReadUInt16( nLen ).ReadUInt16( nOptions );
                        // todo: we also have to take care of the text width
                        if( nLen )
                        {
                            GetWinExtMax( aPosition, aBound, eMapMode );
                            bBoundsDetermined = true;
                        }
                    }
                    break;
                    case W_META_BITBLT:
                    case W_META_DIBBITBLT:
                    case W_META_DIBSTRETCHBLT:
                    case W_META_STRETCHBLT:
                    case W_META_STRETCHDIB:
                    {
                        sal_uInt32 nRasterOperation;
                        sal_Int16 nYSrc, nXSrc;
                        sal_uInt16 nColorUsage;
                        pStm->ReadUInt32( nRasterOperation );
 
                        if( nFunction == W_META_STRETCHDIB )
                            pStm->ReadUInt16( nColorUsage );
 
                        if( nFunction == W_META_DIBSTRETCHBLT ||
                            nFunction == W_META_STRETCHBLT ||
                            nFunction == W_META_STRETCHDIB )
                        {
                            sal_Int16 nSrcHeight, nSrcWidth;
                            pStm->ReadInt16( nSrcHeight ).ReadInt16( nSrcWidth );
                        }
 
                        // nYSrc and nXSrc is the offset of the first pixel
                        pStm->ReadInt16( nYSrc ).ReadInt16( nXSrc );
 
                        const bool bNoSourceBitmap = ( nFunction != W_META_STRETCHDIB ) && ( nRSize == ( ( static_cast< sal_uInt32 >( nFunction ) >> 8 ) + 3 ) );
                        if ( bNoSourceBitmap )
                            mpInputStream->SeekRel( 2 ); // Skip Reserved 2 bytes (it must be ignored)
 
                        Size aDestSize( ReadYXExt() );
                        if ( aDestSize.Width() && aDestSize.Height() )  // #92623# do not try to read buggy bitmaps
                        {
                            tools::Rectangle aDestRect( ReadYX(), aDestSize );
                            GetWinExtMax( aDestRect, aBound, eMapMode );
                            bBoundsDetermined = true;
                        }
                    }
                    break;
 
                    case W_META_PATBLT:
                    {
                        sal_uInt32 nROP(0);
                        pStm->ReadUInt32( nROP );
                        Size aSize = ReadYXExt();
                        GetWinExtMax( tools::Rectangle( ReadYX(), aSize ), aBound, eMapMode );
                        bBoundsDetermined = true;
                    }
                    break;
                }
 
                const auto nAvailableBytes = nEnd - nPos;
                const auto nMaxPossibleRecordSize = nAvailableBytes/2;
                if (nRSize <= nMaxPossibleRecordSize)
                {
                    nPos += nRSize * 2;
                    pStm->Seek(nPos);
                }
                else
                {
                    pStm->SetError( SVSTREAM_FILEFORMAT_ERROR );
                    bRet = false;
                }
            }
        }
        else
        {
            pStm->SetError( SVSTREAM_GENERALERROR );
            bRet = false;
        }
        if (!bRet)
        {
            SAL_WARN("emfio", "Unable to calculate Placeable Bounds");
            return;
        }
 
        if (aWinExt)
        {
            rPlaceableBound = tools::Rectangle(aWinOrg, *aWinExt);
            if (mbPlaceable && eMapMode == MM_ANISOTROPIC)
            {
                // It seems that (in MM_ANISOTROPIC WMFs) the "inch" field (PPI) in META_PLACEABLE is
                // ignored and instead competitor office suites decide what it should be arbitrarily
                // Could have to do with MM_ANISOTROPICs definition:
                // Logical units are mapped to arbitrary units with arbitrarily scaled axes.
                // The issue is that when PPI is bigger than the window size, the image appears
                // tiny (smaller than an inch squared).
                // A solution is to scale PPI down in such images to an arbitrary amount that makes
                // the image visible:
                auto nWidth = rPlaceableBound.GetWidth();
                auto nHeight = rPlaceableBound.GetHeight();
                if (mnUnitsPerInch > nWidth && mnUnitsPerInch > nHeight)
                    mnUnitsPerInch = std::max(nWidth, nHeight);
            }
            SAL_INFO("emfio", "Window dimension "
                       " left: " << rPlaceableBound.Left()  << " top: " << rPlaceableBound.Top()
                    << " right: " << rPlaceableBound.Right() << " bottom: " << rPlaceableBound.Bottom());
        }
        else if (aViewportExt)
        {
            rPlaceableBound = tools::Rectangle(aViewportOrg, *aViewportExt);
            SAL_INFO("emfio", "Viewport dimension "
                       " left: " << rPlaceableBound.Left()  << " top: " << rPlaceableBound.Top()
                    << " right: " << rPlaceableBound.Right() << " bottom: " << rPlaceableBound.Bottom());
        }
        else if (bBoundsDetermined)
        {
            rPlaceableBound = aBound;
            SAL_INFO("emfio", "Determined dimension "
                       " left: " << rPlaceableBound.Left()  << " top: " << rPlaceableBound.Top()
                    << " right: " << rPlaceableBound.Right() << " bottom: " << rPlaceableBound.Bottom());
        }
        else
        {
            rPlaceableBound.SetLeft( 0 );
            rPlaceableBound.SetTop( 0 );
            rPlaceableBound.SetRight( aMaxWidth );
            rPlaceableBound.SetBottom( aMaxWidth );
            SAL_INFO("emfio", "Default dimension "
                       " left: " << rPlaceableBound.Left()  << " top: " << rPlaceableBound.Top()
                    << " right: " << rPlaceableBound.Right() << " bottom: " << rPlaceableBound.Bottom());
        }
    }
 
    WmfReader::WmfReader(SvStream& rStreamWMF, GDIMetaFile& rGDIMetaFile, const WmfExternal* pExternalHeader)
        : MtfTools(rGDIMetaFile, rStreamWMF)
        , mnUnitsPerInch(96)
        , mnRecSize(0)
        , mbPlaceable(false)
        , mnEMFRecCount(0)
        , mnEMFRec(0)
        , mnEMFSize(0)
        , mnSkipActions(0)
        , mpExternalHeader(pExternalHeader)
    {
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'ReadYXExt' is required to be utilized.

V530 The return value of function 'ReadYX' is required to be utilized.

V530 The return value of function 'ReadYX' is required to be utilized.

V530 The return value of function 'ReadColor' is required to be utilized.

V560 A part of conditional expression is always false: nBitCount == 8.

V1051 Consider checking for misprints. It's possible that the 'nPos' should be checked here.