    /* -*- 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 <tools/urlobj.hxx>
    #include <tools/fract.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/mapmod.hxx>
    #include <vcl/window.hxx>
    #include <o3tl/numeric.hxx>
    #include <svl/urihelper.hxx>
    #include <svtools/imap.hxx>
    #include <svtools/imapobj.hxx>
    #include <svtools/imapcirc.hxx>
    #include <svtools/imaprect.hxx>
    #include <svtools/imappoly.hxx>

    #include <string.h>
    #include <math.h>
    #include <memory>
    #include <sal/log.hxx>

    #define SCALEPOINT(aPT,aFracX,aFracY) (aPT).setX(long((aPT).X()*aFracX));  \
                                          (aPT).setY(long((aPT).Y()*aFracY));

    /******************************************************************************/

    sal_uInt16 IMapObject::nActualTextEncoding = sal_uInt16(RTL_TEXTENCODING_DONTKNOW);

    /******************************************************************************/

    IMapObject::IMapObject()
        : bActive( false )
        , nReadVersion( 0 )
    {
    }

    IMapObject::IMapObject( const OUString& rURL, const OUString& rAltText, const OUString& rDesc,
                            const OUString& rTarget, const OUString& rName, bool bURLActive )
    : aURL( rURL )
    , aAltText( rAltText )
    , aDesc( rDesc )
    , aTarget( rTarget )
    , aName( rName )
    , bActive( bURLActive )
    , nReadVersion( 0 )
    {
    }

    void IMapObject::Write( SvStream& rOStm ) const
    {
        const rtl_TextEncoding  eEncoding = osl_getThreadTextEncoding();

        rOStm.WriteUInt16( GetType() );
        rOStm.WriteUInt16( IMAP_OBJ_VERSION );
        rOStm.WriteUInt16( eEncoding  );

        const OString aRelURL = OUStringToOString(
            URIHelper::simpleNormalizedMakeRelative("", aURL), eEncoding);
        write_uInt16_lenPrefixed_uInt8s_FromOString(rOStm, aRelURL);
        write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, aAltText, eEncoding);
        rOStm.WriteBool( bActive );
        write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, aTarget, eEncoding);

        std::unique_ptr<IMapCompat> pCompat(new IMapCompat( rOStm, StreamMode::WRITE ));

        WriteIMapObject( rOStm );
        aEventList.Write( rOStm );                                      // V4
        write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, aName, eEncoding); // V5
    }

    /******************************************************************************
    |*
    |*  Binary import
    |*
    \******************************************************************************/

    void IMapObject::Read( SvStream& rIStm )
    {
        rtl_TextEncoding    nTextEncoding;

        // read on type and version
        rIStm.SeekRel( 2 );
        rIStm.ReadUInt16( nReadVersion );
        rIStm.ReadUInt16( nTextEncoding );
        aURL = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIStm, nTextEncoding);
        aAltText = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIStm, nTextEncoding);
        rIStm.ReadCharAsBool( bActive );
        aTarget = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIStm, nTextEncoding);

        // make URL absolute
        aURL = URIHelper::SmartRel2Abs( INetURLObject(""), aURL, URIHelper::GetMaybeFileHdl(), true, false, INetURLObject::EncodeMechanism::WasEncoded, INetURLObject::DecodeMechanism::Unambiguous );
        std::unique_ptr<IMapCompat> pCompat(new IMapCompat( rIStm, StreamMode::READ ));

        ReadIMapObject( rIStm );

        // from version 4 onwards we read a eventlist
        if ( nReadVersion >= 0x0004 )
        {
            aEventList.Read(rIStm);

            // from version 5 onwards an objectname could be available
            if ( nReadVersion >= 0x0005 )
                aName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIStm, nTextEncoding);
        }
    }

    bool IMapObject::IsEqual( const IMapObject& rEqObj )
    {
        return ( ( aURL == rEqObj.aURL ) &&
                 ( aAltText == rEqObj.aAltText ) &&
                 ( aDesc == rEqObj.aDesc ) &&
                 ( aTarget == rEqObj.aTarget ) &&
                 ( aName == rEqObj.aName ) &&
                 ( bActive == rEqObj.bActive ) );
    }

    IMapRectangleObject::IMapRectangleObject( const tools::Rectangle& rRect,
                                              const OUString& rURL,
                                              const OUString& rAltText,
                                              const OUString& rDesc,
                                              const OUString& rTarget,
                                              const OUString& rName,
                                              bool bURLActive,
                                              bool bPixelCoords ) :
                IMapObject  ( rURL, rAltText, rDesc, rTarget, rName, bURLActive )
    {
        ImpConstruct( rRect, bPixelCoords );
    }

    void IMapRectangleObject::ImpConstruct( const tools::Rectangle& rRect, bool bPixel )
    {
        if ( bPixel )
            aRect = Application::GetDefaultDevice()->PixelToLogic( rRect, MapMode( MapUnit::Map100thMM ) );
        else
            aRect = rRect;
    }

    /******************************************************************************
    |*
    |* Binary export
    |*
    \******************************************************************************/

    void IMapRectangleObject::WriteIMapObject( SvStream& rOStm ) const
    {
        WriteRectangle( rOStm, aRect );
    }

    /******************************************************************************
    |*
    |* Binary import
    |*
    \******************************************************************************/

    void IMapRectangleObject::ReadIMapObject( SvStream& rIStm )
    {
        ReadRectangle( rIStm, aRect );
    }

    /******************************************************************************
    |*
    |* return type
    |*
    \******************************************************************************/

    sal_uInt16 IMapRectangleObject::GetType() const
    {
        return IMAP_OBJ_RECTANGLE;
    }

    /******************************************************************************
    |*
    |* Hit test
    |*
    \******************************************************************************/

    bool IMapRectangleObject::IsHit( const Point& rPoint ) const
    {
        return aRect.IsInside( rPoint );
    }

    tools::Rectangle IMapRectangleObject::GetRectangle( bool bPixelCoords ) const
    {
        tools::Rectangle   aNewRect;

        if ( bPixelCoords )
            aNewRect = Application::GetDefaultDevice()->LogicToPixel( aRect, MapMode( MapUnit::Map100thMM ) );
        else
            aNewRect = aRect;

        return aNewRect;
    }

    void IMapRectangleObject::Scale( const Fraction& rFracX, const Fraction& rFracY )
    {
        Point   aTL( aRect.TopLeft() );
        Point   aBR( aRect.BottomRight() );

        if ( rFracX.GetDenominator() && rFracY.GetDenominator() )
        {
            SCALEPOINT( aTL, rFracX, rFracY );
            SCALEPOINT( aBR, rFracX, rFracY );
        }

        aRect = tools::Rectangle( aTL, aBR );
    }

    bool IMapRectangleObject::IsEqual( const IMapRectangleObject& rEqObj )
    {
        return ( IMapObject::IsEqual( rEqObj ) && ( aRect == rEqObj.aRect ) );
    }

    IMapCircleObject::IMapCircleObject( const Point& rCenter, sal_uLong nCircleRadius,
                                        const OUString& rURL,
                                        const OUString& rAltText,
                                        const OUString& rDesc,
                                        const OUString& rTarget,
                                        const OUString& rName,
                                        bool bURLActive,
                                        bool bPixelCoords ) :
                IMapObject  ( rURL, rAltText, rDesc, rTarget, rName, bURLActive )
    {
        ImpConstruct( rCenter, nCircleRadius, bPixelCoords );
    }

    void IMapCircleObject::ImpConstruct( const Point& rCenter, sal_uLong nRad, bool bPixel )
    {
        if ( bPixel )
        {
            MapMode aMap100( MapUnit::Map100thMM );

            aCenter = Application::GetDefaultDevice()->PixelToLogic( rCenter, aMap100 );
            nRadius = Application::GetDefaultDevice()->PixelToLogic( Size( nRad, 0 ), aMap100 ).Width();
        }
        else
        {
            aCenter = rCenter;
            nRadius = nRad;
        }
    }

    /******************************************************************************
    |*
    |* Binary export
    |*
    \******************************************************************************/

    void IMapCircleObject::WriteIMapObject( SvStream& rOStm ) const
    {
        sal_uInt32 nTmp = nRadius;

        WritePair( rOStm, aCenter );
        rOStm.WriteUInt32( nTmp );
    }

    /******************************************************************************
    |*
    |* Binary import
    |*
    \******************************************************************************/

    void IMapCircleObject::ReadIMapObject( SvStream& rIStm )
    {
        sal_uInt32 nTmp;

        ReadPair( rIStm, aCenter );
        rIStm.ReadUInt32( nTmp );

        nRadius = nTmp;
    }

    /******************************************************************************
    |*
    |* return type
    |*
    \******************************************************************************/

    sal_uInt16 IMapCircleObject::GetType() const
    {
        return IMAP_OBJ_CIRCLE;
    }

    /******************************************************************************
    |*
    |* Hit-Test
    |*
    \******************************************************************************/

    bool IMapCircleObject::IsHit( const Point& rPoint ) const
    {
        const Point aPoint( aCenter - rPoint );
        bool        bRet = false;

        if ( static_cast<sal_Int32>(sqrt( static_cast<double>(aPoint.X()) * aPoint.X() +
                           aPoint.Y() * aPoint.Y() )) <= nRadius )
        {
            bRet = true;
        }

        return bRet;
    }

    Point IMapCircleObject::GetCenter( bool bPixelCoords ) const
    {
        Point aNewPoint;

        if ( bPixelCoords )
            aNewPoint = Application::GetDefaultDevice()->LogicToPixel( aCenter, MapMode( MapUnit::Map100thMM ) );
        else
            aNewPoint = aCenter;

        return aNewPoint;
    }

    sal_uLong IMapCircleObject::GetRadius( bool bPixelCoords ) const
    {
        sal_uLong nNewRadius;

        if ( bPixelCoords )
            nNewRadius = Application::GetDefaultDevice()->LogicToPixel( Size( nRadius, 0 ), MapMode( MapUnit::Map100thMM ) ).Width();
        else
            nNewRadius = nRadius;

        return nNewRadius;
    }

    void IMapCircleObject::Scale( const Fraction& rFracX, const Fraction& rFracY )
    {
        Fraction aAverage( rFracX );

        aAverage += rFracY;
        aAverage *= Fraction( 1, 2 );

        if ( rFracX.GetDenominator() && rFracY.GetDenominator() )
        {
            SCALEPOINT( aCenter, rFracX, rFracY );
        }

        if (!aAverage.GetDenominator())
            throw o3tl::divide_by_zero();

        nRadius = double(nRadius * aAverage);
    }

    bool IMapCircleObject::IsEqual( const IMapCircleObject& rEqObj )
    {
        return ( IMapObject::IsEqual( rEqObj ) &&
                 ( aCenter == rEqObj.aCenter ) &&
                 ( nRadius == rEqObj.nRadius ) );
    }

    IMapPolygonObject::IMapPolygonObject( const tools::Polygon& rPoly,
                                          const OUString& rURL,
                                          const OUString& rAltText,
                                          const OUString& rDesc,
                                          const OUString& rTarget,
                                          const OUString& rName,
                                          bool bURLActive,
                                          bool bPixelCoords ) :
                IMapObject  ( rURL, rAltText, rDesc, rTarget, rName, bURLActive ),
                bEllipse    ( false )
    {
        ImpConstruct( rPoly, bPixelCoords );
    }

    void IMapPolygonObject::ImpConstruct( const tools::Polygon& rPoly, bool bPixel )
    {
        if ( bPixel )
            aPoly = Application::GetDefaultDevice()->PixelToLogic( rPoly, MapMode( MapUnit::Map100thMM ) );
        else
            aPoly = rPoly;
    }

    /******************************************************************************
    |*
    |* Binary export
    |*
    \******************************************************************************/

    void IMapPolygonObject::WriteIMapObject( SvStream& rOStm ) const
    {
        WritePolygon( rOStm, aPoly );
        rOStm.WriteBool( bEllipse );  // >= Version 2
        WriteRectangle( rOStm, aEllipse );  // >= Version 2
    }

    /******************************************************************************
    |*
    |* Binary import
    |*
    \******************************************************************************/

    void IMapPolygonObject::ReadIMapObject( SvStream& rIStm )
    {
        ReadPolygon( rIStm, aPoly );

        // Version >= 2 has additional ellipses information
        if ( nReadVersion >= 2 )
        {
            rIStm.ReadCharAsBool( bEllipse );
            ReadRectangle( rIStm, aEllipse );
        }
    }

    /******************************************************************************
    |*
    |* return type
    |*
    \******************************************************************************/

    sal_uInt16 IMapPolygonObject::GetType() const
    {
        return IMAP_OBJ_POLYGON;
    }

    /******************************************************************************
    |*
    |* hit test
    |*
    \******************************************************************************/

    bool IMapPolygonObject::IsHit( const Point& rPoint ) const
    {
        return aPoly.IsInside( rPoint );
    }

    tools::Polygon IMapPolygonObject::GetPolygon( bool bPixelCoords ) const
    {
        tools::Polygon aNewPoly;

        if ( bPixelCoords )
            aNewPoly = Application::GetDefaultDevice()->LogicToPixel( aPoly, MapMode( MapUnit::Map100thMM ) );
        else
            aNewPoly = aPoly;

        return aNewPoly;
    }

    void IMapPolygonObject::SetExtraEllipse( const tools::Rectangle& rEllipse )
    {
        if ( aPoly.GetSize() )
        {
            bEllipse = true;
            aEllipse = rEllipse;
        }
    }

    void IMapPolygonObject::Scale( const Fraction& rFracX, const Fraction& rFracY )
    {
        sal_uInt16 nCount = aPoly.GetSize();

        for ( sal_uInt16 i = 0; i < nCount; i++ )
        {
            Point aScaledPt( aPoly[ i ] );

            if ( rFracX.GetDenominator() && rFracY.GetDenominator() )
            {
                SCALEPOINT( aScaledPt, rFracX, rFracY );
            }

            aPoly[ i ] = aScaledPt;
        }

        if ( !bEllipse )
            return;

        Point   aTL( aEllipse.TopLeft() );
        Point   aBR( aEllipse.BottomRight() );

        if ( rFracX.GetDenominator() && rFracY.GetDenominator() )
        {
            SCALEPOINT( aTL, rFracX, rFracY );
            SCALEPOINT( aBR, rFracX, rFracY );
        }

        aEllipse = tools::Rectangle( aTL, aBR );
    }

    bool IMapPolygonObject::IsEqual( const IMapPolygonObject& rEqObj )
    {
        bool bRet = false;

        if ( IMapObject::IsEqual( rEqObj ) )
        {
            const tools::Polygon&   rEqPoly = rEqObj.aPoly;
            const sal_uInt16 nCount = aPoly.GetSize();
            const sal_uInt16 nEqCount = rEqPoly.GetSize();

            if ( nCount == nEqCount )
            {
                bool bDifferent = false;

                for ( sal_uInt16 i = 0; i < nCount; i++ )
                {
                    if ( aPoly[ i ] != rEqPoly[ i ] )
                    {
                        bDifferent = true;
                        break;
                    }
                }

                if ( !bDifferent )
                    bRet = true;
            }
        }

        return bRet;
    }

    /******************************************************************************
    |*
    |* Ctor
    |*
    \******************************************************************************/

    ImageMap::ImageMap( const OUString& rName )
    :   aName( rName )
    {
    }

    /******************************************************************************
    |*
    |* Copy-Ctor
    |*
    \******************************************************************************/

    ImageMap::ImageMap( const ImageMap& rImageMap )
    {

        size_t nCount = rImageMap.GetIMapObjectCount();

        for ( size_t i = 0; i < nCount; i++ )
        {
            IMapObject* pCopyObj = rImageMap.GetIMapObject( i );

            switch( pCopyObj->GetType() )
            {
                case IMAP_OBJ_RECTANGLE:
                    maList.emplace_back( new IMapRectangleObject( *static_cast<IMapRectangleObject*>( pCopyObj ) ) );
                break;

                case IMAP_OBJ_CIRCLE:
                    maList.emplace_back( new IMapCircleObject( *static_cast<IMapCircleObject*>( pCopyObj ) ) );
                break;

                case IMAP_OBJ_POLYGON:
                    maList.emplace_back( new IMapPolygonObject( *static_cast<IMapPolygonObject*>( pCopyObj ) ) );
                break;

                default:
                break;
            }
        }

        aName = rImageMap.aName;
    }

    /******************************************************************************
    |*
    |* Dtor
    |*
    \******************************************************************************/

    ImageMap::~ImageMap()
    {
    }

    /******************************************************************************
    |*
    |* release internal memory
    |*
    \******************************************************************************/

    void ImageMap::ClearImageMap()
    {
        maList.clear();

        aName.clear();
    }

    /******************************************************************************
    |*
    |* assignment operator
    |*
    \******************************************************************************/

    ImageMap& ImageMap::operator=( const ImageMap& rImageMap )
    {
        size_t nCount = rImageMap.GetIMapObjectCount();

        ClearImageMap();

        for ( size_t i = 0; i < nCount; i++ )
        {
            IMapObject* pCopyObj = rImageMap.GetIMapObject( i );

            switch( pCopyObj->GetType() )
            {
                case IMAP_OBJ_RECTANGLE:
                    maList.emplace_back( new IMapRectangleObject( *static_cast<IMapRectangleObject*>(pCopyObj) ) );
                break;

                case IMAP_OBJ_CIRCLE:
                    maList.emplace_back( new IMapCircleObject( *static_cast<IMapCircleObject*>(pCopyObj) ) );
                break;

                case IMAP_OBJ_POLYGON:
                    maList.emplace_back( new IMapPolygonObject( *static_cast<IMapPolygonObject*>(pCopyObj) ) );
                break;

                default:
                break;
            }
        }

        aName = rImageMap.aName;

        return *this;
    }

    /******************************************************************************
    |*
    |* compare operator I
    |*
    \******************************************************************************/

    bool ImageMap::operator==( const ImageMap& rImageMap )
    {
        const size_t    nCount = maList.size();
        const size_t    nEqCount = rImageMap.GetIMapObjectCount();
        bool            bRet = false;

        if ( nCount == nEqCount )
        {
            bool bDifferent = ( aName != rImageMap.aName );

            for ( size_t i = 0; ( i < nCount ) && !bDifferent; i++ )
            {
                IMapObject* pObj = maList[ i ].get();
                IMapObject* pEqObj = rImageMap.GetIMapObject( i );

                if ( pObj->GetType() == pEqObj->GetType() )
                {
                    switch( pObj->GetType() )
                    {
                        case IMAP_OBJ_RECTANGLE:
                        {
                            if ( ! static_cast<IMapRectangleObject*>(pObj)->IsEqual( *static_cast<IMapRectangleObject*>(pEqObj) ) )
                                bDifferent = true;
                        }
                        break;

                        case IMAP_OBJ_CIRCLE:
                        {
                            if ( ! static_cast<IMapCircleObject*>(pObj)->IsEqual( *static_cast<IMapCircleObject*>(pEqObj) ) )
                                bDifferent = true;
                        }
                        break;

                        case IMAP_OBJ_POLYGON:
                        {
                            if ( ! static_cast<IMapPolygonObject*>(pObj)->IsEqual( *static_cast<IMapPolygonObject*>(pEqObj) ) )
                                bDifferent = true;
                        }
                        break;

                        default:
                        break;
                    }
                }
                else
                    bDifferent = true;
            }

            if ( !bDifferent )
                bRet = true;
        }

        return bRet;
    }

    /******************************************************************************
    |*
    |* compare operator II
    |*
    \******************************************************************************/

    bool ImageMap::operator!=( const ImageMap& rImageMap )
    {
        return !( *this == rImageMap );
    }

    /******************************************************************************
    |*
    |* insert new object
    |*
    \******************************************************************************/

    void ImageMap::InsertIMapObject( const IMapObject& rIMapObject )
    {
        switch( rIMapObject.GetType() )
        {
            case IMAP_OBJ_RECTANGLE:
                maList.emplace_back( new IMapRectangleObject( static_cast<const IMapRectangleObject&>( rIMapObject ) ) );
            break;

            case IMAP_OBJ_CIRCLE:
                maList.emplace_back( new IMapCircleObject( static_cast<const IMapCircleObject&>( rIMapObject ) ) );
            break;

            case IMAP_OBJ_POLYGON:
                maList.emplace_back( new IMapPolygonObject( static_cast<const IMapPolygonObject&>( rIMapObject ) ) );
            break;

            default:
            break;
        }
    }

    void ImageMap::InsertIMapObject( std::unique_ptr<IMapObject> pNewObject )
    {
        maList.emplace_back( std::move(pNewObject) );
    }

    /******************************************************************************
    |*
    |* hit test
    |*
    \******************************************************************************/

    IMapObject* ImageMap::GetHitIMapObject( const Size& rTotalSize,
                                            const Size& rDisplaySize,
                                            const Point& rRelHitPoint,
                                            sal_uLong nFlags )
    {
        Point aRelPoint( rTotalSize.Width() * rRelHitPoint.X() / rDisplaySize.Width(),
                         rTotalSize.Height() * rRelHitPoint.Y() / rDisplaySize.Height() );

        // transform point to check before checking if flags to mirror etc. are set,
        if ( nFlags )
        {
            if ( nFlags & IMAP_MIRROR_HORZ )
                aRelPoint.setX( rTotalSize.Width() - aRelPoint.X() );

            if ( nFlags & IMAP_MIRROR_VERT )
                aRelPoint.setY( rTotalSize.Height() - aRelPoint.Y() );
        }

        // walk over all objects and execute HitTest
        IMapObject* pObj = nullptr;
        for(auto& i : maList) {
            if ( i->IsHit( aRelPoint ) ) {
                pObj = i.get();
                break;
            }
        }

        return( pObj ? ( pObj->IsActive() ? pObj : nullptr ) : nullptr );
    }

    void ImageMap::Scale( const Fraction& rFracX, const Fraction& rFracY )
    {
        size_t nCount = maList.size();

        for ( size_t i = 0; i < nCount; i++ )
        {
            IMapObject* pObj = maList[ i ].get();

            switch( pObj->GetType() )
            {
                case IMAP_OBJ_RECTANGLE:
                    static_cast<IMapRectangleObject*>( pObj )->Scale( rFracX, rFracY );
                break;

                case IMAP_OBJ_CIRCLE:
                    static_cast<IMapCircleObject*>( pObj )->Scale( rFracX, rFracY );
                break;

                case IMAP_OBJ_POLYGON:
                    static_cast<IMapPolygonObject*>( pObj )->Scale( rFracX, rFracY );
                break;

                default:
                break;
            }
        }
    }

    /******************************************************************************
    |*
    |* sequentially write objects
    |*
    \******************************************************************************/

    void ImageMap::ImpWriteImageMap( SvStream& rOStm ) const
    {
        size_t      nCount = maList.size();

        for ( size_t i = 0; i < nCount; i++ )
        {
            auto& pObj = maList[ i ];
            pObj->Write( rOStm );
        }
    }

    /******************************************************************************
    |*
    |* sequentially read objects
    |*
    \******************************************************************************/

    void ImageMap::ImpReadImageMap( SvStream& rIStm, size_t nCount )
    {
        const size_t nMinRecordSize = 12; //circle, three 32bit numbers
        const size_t nMaxRecords = rIStm.remainingSize() / nMinRecordSize;

        if (nCount > nMaxRecords)
        {
            SAL_WARN("svtools.misc", "Parsing error: " << nMaxRecords << " max possible entries, but " <<
                     nCount << " claimed, truncating");
            nCount = nMaxRecords;
        }

        // read new objects
        for (size_t i = 0; i < nCount; ++i)
        {
            sal_uInt16 nType;

            rIStm.ReadUInt16( nType );
            rIStm.SeekRel( -2 );

            switch( nType )
            {
                case IMAP_OBJ_RECTANGLE:
                {
                    IMapRectangleObject* pObj = new IMapRectangleObject;
                    pObj->Read( rIStm );
                    maList.emplace_back( pObj );
                }
                break;

                case IMAP_OBJ_CIRCLE:
                {
                    IMapCircleObject* pObj = new IMapCircleObject;
                    pObj->Read( rIStm );
                    maList.emplace_back( pObj );
                }
                break;

                case IMAP_OBJ_POLYGON:
                {
                    IMapPolygonObject* pObj = new IMapPolygonObject;
                    pObj->Read( rIStm );
                    maList.emplace_back( pObj );
                }
                break;

                default:
                break;
            }
        }
    }

    /******************************************************************************
    |*
    |* store binary
    |*
    \******************************************************************************/

    void ImageMap::Write( SvStream& rOStm ) const
    {
        IMapCompat*             pCompat;
        OUString                aImageName( GetName() );
        SvStreamEndian          nOldFormat = rOStm.GetEndian();
        sal_uInt16              nCount = static_cast<sal_uInt16>(GetIMapObjectCount());
        const rtl_TextEncoding  eEncoding = osl_getThreadTextEncoding(); //vomit!

        rOStm.SetEndian( SvStreamEndian::LITTLE );

        // write MagicCode
        rOStm.WriteCharPtr( IMAPMAGIC );
        rOStm.WriteUInt16( IMAGE_MAP_VERSION );
        write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, aImageName, eEncoding);
        write_uInt16_lenPrefixed_uInt8s_FromOString(rOStm, OString()); //dummy
        rOStm.WriteUInt16( nCount );
        write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, aImageName, eEncoding);

        pCompat = new IMapCompat( rOStm, StreamMode::WRITE );

        // here one can insert in newer versions

        delete pCompat;

        ImpWriteImageMap( rOStm );

        rOStm.SetEndian( nOldFormat );
    }

    /******************************************************************************
    |*
    |* load binary
    |*
    \******************************************************************************/

    void ImageMap::Read( SvStream& rIStm )
    {
        char            cMagic[6];
        SvStreamEndian  nOldFormat = rIStm.GetEndian();
        sal_uInt16      nCount;

        rIStm.SetEndian( SvStreamEndian::LITTLE );
        rIStm.ReadBytes(cMagic, sizeof(cMagic));

        if ( !memcmp( cMagic, IMAPMAGIC, sizeof( cMagic ) ) )
        {
            IMapCompat* pCompat;

            // delete old content
            ClearImageMap();

            // read on version
            rIStm.SeekRel( 2 );

            aName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIStm, osl_getThreadTextEncoding());
            read_uInt16_lenPrefixed_uInt8s_ToOString(rIStm); // Dummy
            rIStm.ReadUInt16( nCount );
            read_uInt16_lenPrefixed_uInt8s_ToOString(rIStm); // Dummy

            pCompat = new IMapCompat( rIStm, StreamMode::READ );

            // here one can read in newer versions

            delete pCompat;
            ImpReadImageMap( rIStm, nCount );

        }
        else
            rIStm.SetError( SVSTREAM_GENERALERROR );

        rIStm.SetEndian( nOldFormat );
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V794 The assignment operator should be protected from the case of 'this == &rImageMap'.

↑ V799 The 'pCompat' variable is not used after memory has been allocated for it. Consider checking the use of this variable.

↑ V1023 A pointer without owner is added to the 'maList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.

↑ V1023 A pointer without owner is added to the 'maList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.

↑ V799 The 'pCompat' variable is not used after memory has been allocated for it. Consider checking the use of this variable.

↑ V1023 A pointer without owner is added to the 'maList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.

↑ V1023 A pointer without owner is added to the 'maList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.

↑ V1023 A pointer without owner is added to the 'maList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.

↑ V1023 A pointer without owner is added to the 'maList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.

↑ V1023 A pointer without owner is added to the 'maList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.

↑ V1023 A pointer without owner is added to the 'maList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.

↑ V1023 A pointer without owner is added to the 'maList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.