/* -*- 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 <tools/GenericTypeSerializer.hxx>
#include <utility>
#include <vcl/outdev.hxx>
#include <vcl/svapp.hxx>
#include <vcl/mapmod.hxx>
#include <o3tl/numeric.hxx>
#include <svl/urihelper.hxx>
#include <vcl/imap.hxx>
#include <vcl/imapobj.hxx>
#include <vcl/imapcirc.hxx>
#include <vcl/imaprect.hxx>
#include <vcl/imappoly.hxx>
 
#include <string.h>
#include <math.h>
#include <memory>
#include <sal/log.hxx>
 
 
#define SCALEPOINT(aPT,aFracX,aFracY) (aPT).setX(tools::Long((aPT).X()*aFracX));  \
                                      (aPT).setY(tools::Long((aPT).Y()*aFracY));
 
 
/******************************************************************************/
 
 
IMapObject::IMapObject()
    : bActive( false )
    , nReadVersion( 0 )
{
}
 
IMapObject::IMapObject( OUString _aURL, OUString _aAltText, OUString _aDesc,
                        OUString _aTarget, OUString _aName, bool bURLActive )
: aURL(std::move( _aURL ))
, aAltText(std::move( _aAltText ))
, aDesc(std::move( _aDesc ))
, aTarget(std::move( _aTarget ))
, aName(std::move( _aName ))
, bActive( bURLActive )
, nReadVersion( 0 )
{
}
 
 
void IMapObject::Write( SvStream& rOStm ) const
{
    const rtl_TextEncoding  eEncoding = osl_getThreadTextEncoding();
 
    rOStm.WriteUInt16( static_cast<sal_uInt16>(GetType()) );
    rOStm.WriteUInt16( IMAP_OBJ_VERSION );
    rOStm.WriteUInt16( eEncoding  );
 
    const OString aRelURL = OUStringToOString(
        URIHelper::simpleNormalizedMakeRelative(u""_ustr, 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);
 
    IMapCompat aCompat( 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(u""), aURL, URIHelper::GetMaybeFileHdl(), true, false, INetURLObject::EncodeMechanism::WasEncoded, INetURLObject::DecodeMechanism::Unambiguous );
    IMapCompat aCompat( rIStm, StreamMode::READ );
 
    ReadIMapObject( rIStm );
 
    // from version 4 onwards we read an 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 ) const
{
    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
{
    tools::GenericTypeSerializer aSerializer(rOStm);
    aSerializer.writeRectangle(aRect);
}
 
 
/******************************************************************************
|*
|* Binary import
|*
\******************************************************************************/
 
void IMapRectangleObject::ReadIMapObject( SvStream& rIStm )
{
    tools::GenericTypeSerializer aSerializer(rIStm);
    aSerializer.readRectangle(aRect);
}
 
 
/******************************************************************************
|*
|* return type
|*
\******************************************************************************/
 
IMapObjectType IMapRectangleObject::GetType() const
{
    return IMapObjectType::Rectangle;
}
 
 
/******************************************************************************
|*
|* Hit test
|*
\******************************************************************************/
 
bool IMapRectangleObject::IsHit( const Point& rPoint ) const
{
    return aRect.Contains( 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 ) const
{
    return ( IMapObject::IsEqual( rEqObj ) && ( aRect == rEqObj.aRect ) );
}
 
IMapCircleObject::IMapCircleObject( const Point& rCenter, sal_Int32 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_Int32 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;
    tools::GenericTypeSerializer aSerializer(rOStm);
    aSerializer.writePoint(aCenter);
    rOStm.WriteUInt32( nTmp );
}
 
 
/******************************************************************************
|*
|* Binary import
|*
\******************************************************************************/
 
void IMapCircleObject::ReadIMapObject( SvStream& rIStm )
{
    sal_uInt32 nTmp;
 
    tools::GenericTypeSerializer aSerializer(rIStm);
    aSerializer.readPoint(aCenter);
    rIStm.ReadUInt32( nTmp );
 
    nRadius = nTmp;
}
 
 
/******************************************************************************
|*
|* return type
|*
\******************************************************************************/
 
IMapObjectType IMapCircleObject::GetType() const
{
    return IMapObjectType::Circle;
}
 
 
/******************************************************************************
|*
|* Hit-Test
|*
\******************************************************************************/
 
bool IMapCircleObject::IsHit( const Point& rPoint ) const
{
    const Point aPoint( aCenter - rPoint );
 
    return static_cast<sal_Int32>( std::hypot( aPoint.X(), aPoint.Y() ) ) <= nRadius;
}
 
Point IMapCircleObject::GetCenter( bool bPixelCoords ) const
{
    Point aNewPoint;
 
    if ( bPixelCoords )
        aNewPoint = Application::GetDefaultDevice()->LogicToPixel( aCenter, MapMode( MapUnit::Map100thMM ) );
    else
        aNewPoint = aCenter;
 
    return aNewPoint;
}
 
sal_Int32 IMapCircleObject::GetRadius( bool bPixelCoords ) const
{
    sal_Int32 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 ) const
{
    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
{
    tools::GenericTypeSerializer aSerializer(rOStm);
    WritePolygon( rOStm, aPoly );
    // Version 2
    rOStm.WriteBool( bEllipse );
    aSerializer.writeRectangle(aEllipse);
}
 
 
/******************************************************************************
|*
|* Binary import
|*
\******************************************************************************/
 
void IMapPolygonObject::ReadIMapObject( SvStream& rIStm )
{
    ReadPolygon( rIStm, aPoly );
 
    // Version >= 2 has additional ellipses information
    if ( nReadVersion >= 2 )
    {
        rIStm.ReadCharAsBool( bEllipse );
        tools::GenericTypeSerializer aSerializer(rIStm);
        aSerializer.readRectangle(aEllipse);
    }
}
 
 
/******************************************************************************
|*
|* return type
|*
\******************************************************************************/
 
IMapObjectType IMapPolygonObject::GetType() const
{
    return IMapObjectType::Polygon;
}
 
 
/******************************************************************************
|*
|* hit test
|*
\******************************************************************************/
 
bool IMapPolygonObject::IsHit( const Point& rPoint ) const
{
    return aPoly.Contains( 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( OUString _aName )
:   aName(std::move( _aName ))
{
}
 
 
/******************************************************************************
|*
|* 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 IMapObjectType::Rectangle:
                maList.emplace_back( new IMapRectangleObject( *static_cast<IMapRectangleObject*>( pCopyObj ) ) );
            break;
 
            case IMapObjectType::Circle:
                maList.emplace_back( new IMapCircleObject( *static_cast<IMapCircleObject*>( pCopyObj ) ) );
            break;
 
            case IMapObjectType::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 )
{
    if (this != &rImageMap)
    {
        size_t nCount = rImageMap.GetIMapObjectCount();
 
        ClearImageMap();
 
        for ( size_t i = 0; i < nCount; i++ )
        {
            IMapObject* pCopyObj = rImageMap.GetIMapObject( i );
 
            switch( pCopyObj->GetType() )
            {
                case IMapObjectType::Rectangle:
                    maList.emplace_back( new IMapRectangleObject( *static_cast<IMapRectangleObject*>(pCopyObj) ) );
                break;
 
                case IMapObjectType::Circle:
                    maList.emplace_back( new IMapCircleObject( *static_cast<IMapCircleObject*>(pCopyObj) ) );
                break;
 
                case IMapObjectType::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 IMapObjectType::Rectangle:
                    {
                        if ( ! static_cast<IMapRectangleObject*>(pObj)->IsEqual( *static_cast<IMapRectangleObject*>(pEqObj) ) )
                            bDifferent = true;
                    }
                    break;
 
                    case IMapObjectType::Circle:
                    {
                        if ( ! static_cast<IMapCircleObject*>(pObj)->IsEqual( *static_cast<IMapCircleObject*>(pEqObj) ) )
                            bDifferent = true;
                    }
                    break;
 
                    case IMapObjectType::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 IMapObjectType::Rectangle:
            maList.emplace_back( new IMapRectangleObject( static_cast<const IMapRectangleObject&>( rIMapObject ) ) );
        break;
 
        case IMapObjectType::Circle:
            maList.emplace_back( new IMapCircleObject( static_cast<const IMapCircleObject&>( rIMapObject ) ) );
        break;
 
        case IMapObjectType::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 ) const
{
    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(const 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 IMapObjectType::Rectangle:
                static_cast<IMapRectangleObject*>( pObj )->Scale( rFracX, rFracY );
            break;
 
            case IMapObjectType::Circle:
                static_cast<IMapCircleObject*>( pObj )->Scale( rFracX, rFracY );
            break;
 
            case IMapObjectType::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( static_cast<IMapObjectType>(nType) )
        {
            case IMapObjectType::Rectangle:
            {
                IMapRectangleObject* pObj = new IMapRectangleObject;
                pObj->Read( rIStm );
                maList.emplace_back( pObj );
            }
            break;
 
            case IMapObjectType::Circle:
            {
                IMapCircleObject* pObj = new IMapCircleObject;
                pObj->Read( rIStm );
                maList.emplace_back( pObj );
            }
            break;
 
            case IMapObjectType::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.WriteOString( IMAPMAGIC );
    rOStm.WriteUInt16( IMAGE_MAP_VERSION );
    write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, aImageName, eEncoding);
    write_uInt16_lenPrefixed_uInt8s_FromOString(rOStm, ""); //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();
 
    rIStm.SetEndian( SvStreamEndian::LITTLE );
    rIStm.ReadBytes(cMagic, sizeof(cMagic));
 
    if ( !memcmp( cMagic, IMAPMAGIC, sizeof( cMagic ) ) )
    {
        IMapCompat* pCompat;
        sal_uInt16  nCount;
 
        // 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: */

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

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

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

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.

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.