/* -*- 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 <comphelper/string.hxx>
#include <string.h>
#include <o3tl/string_view.hxx>
#include <rtl/strbuf.hxx>
#include <vcl/outdev.hxx>
#include <vcl/svapp.hxx>
#include <tools/urlobj.hxx>
 
#include <svl/urihelper.hxx>
#include <vcl/imap.hxx>
#include <vcl/imapobj.hxx>
#include <vcl/imaprect.hxx>
#include <vcl/imapcirc.hxx>
#include <vcl/imappoly.hxx>
 
#include <math.h>
 
#define NOTEOL(c) ((c)!='\0')
 
void IMapObject::AppendCERNCoords(OStringBuffer& rBuf, const Point& rPoint100)
{
    const Point aPixPt( Application::GetDefaultDevice()->LogicToPixel( rPoint100, MapMode( MapUnit::Map100thMM ) ) );
 
    rBuf.append('(');
    rBuf.append(static_cast<sal_Int32>(aPixPt.X()));
    rBuf.append(',');
    rBuf.append(static_cast<sal_Int32>(aPixPt.Y()));
    rBuf.append(") ");
}
 
void IMapObject::AppendNCSACoords(OStringBuffer& rBuf, const Point& rPoint100)
{
    const Point aPixPt( Application::GetDefaultDevice()->LogicToPixel( rPoint100, MapMode( MapUnit::Map100thMM ) ) );
 
    rBuf.append(static_cast<sal_Int32>(aPixPt.X()));
    rBuf.append(',');
    rBuf.append(static_cast<sal_Int32>(aPixPt.Y()));
    rBuf.append(' ');
}
 
void IMapObject::AppendCERNURL(OStringBuffer& rBuf) const
{
    rBuf.append(OUStringToOString(URIHelper::simpleNormalizedMakeRelative(u""_ustr, aURL), osl_getThreadTextEncoding()));
}
 
void IMapObject::AppendNCSAURL(OStringBuffer& rBuf) const
{
    rBuf.append(OUStringToOString(URIHelper::simpleNormalizedMakeRelative(u""_ustr, aURL), osl_getThreadTextEncoding()));
    rBuf.append(' ');
}
 
void IMapRectangleObject::WriteCERN( SvStream& rOStm ) const
{
    OStringBuffer aStrBuf("rectangle ");
 
    AppendCERNCoords(aStrBuf, aRect.TopLeft());
    AppendCERNCoords(aStrBuf, aRect.BottomRight());
    AppendCERNURL(aStrBuf);
 
    rOStm.WriteLine(aStrBuf);
}
 
void IMapRectangleObject::WriteNCSA( SvStream& rOStm ) const
{
    OStringBuffer aStrBuf("rect ");
 
    AppendNCSAURL(aStrBuf);
    AppendNCSACoords(aStrBuf, aRect.TopLeft());
    AppendNCSACoords(aStrBuf, aRect.BottomRight());
 
    rOStm.WriteLine(aStrBuf);
}
 
void IMapCircleObject::WriteCERN( SvStream& rOStm ) const
{
    OStringBuffer aStrBuf("circle ");
 
    AppendCERNCoords(aStrBuf, aCenter);
    aStrBuf.append(OString::number(nRadius) + " ");
    AppendCERNURL(aStrBuf);
 
    rOStm.WriteLine(aStrBuf);
}
 
void IMapCircleObject::WriteNCSA( SvStream& rOStm ) const
{
    OStringBuffer aStrBuf("circle ");
 
    AppendNCSAURL(aStrBuf);
    AppendNCSACoords(aStrBuf, aCenter);
    AppendNCSACoords(aStrBuf, aCenter + Point(nRadius, 0));
 
    rOStm.WriteLine(aStrBuf);
}
 
void IMapPolygonObject::WriteCERN( SvStream& rOStm  ) const
{
    OStringBuffer aStrBuf("polygon ");
    const sal_uInt16 nCount = aPoly.GetSize();
 
    for (sal_uInt16 i = 0; i < nCount; ++i)
        AppendCERNCoords(aStrBuf, aPoly[i]);
 
    AppendCERNURL(aStrBuf);
 
    rOStm.WriteLine(aStrBuf);
}
 
void IMapPolygonObject::WriteNCSA( SvStream& rOStm  ) const
{
    OStringBuffer aStrBuf("poly ");
    const sal_uInt16 nCount = std::min( aPoly.GetSize(), sal_uInt16(100) );
 
    AppendNCSAURL(aStrBuf);
 
    for (sal_uInt16 i = 0; i < nCount; ++i)
        AppendNCSACoords(aStrBuf, aPoly[i]);
 
    rOStm.WriteLine(aStrBuf);
}
 
void ImageMap::Write( SvStream& rOStm, IMapFormat nFormat ) const
{
    switch( nFormat )
    {
        case IMapFormat::Binary : Write( rOStm ); break;
        case IMapFormat::CERN : ImpWriteCERN( rOStm ); break;
        case IMapFormat::NCSA : ImpWriteNCSA( rOStm ); break;
 
        default:
        break;
    }
}
 
void ImageMap::ImpWriteCERN( SvStream& rOStm ) const
{
    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 )->WriteCERN( rOStm );
            break;
 
            case IMapObjectType::Circle:
                static_cast<IMapCircleObject*>( pObj )->WriteCERN( rOStm );
            break;
 
            case IMapObjectType::Polygon:
                static_cast<IMapPolygonObject*>( pObj )->WriteCERN( rOStm );
            break;
 
            default:
            break;
        }
    }
}
 
void ImageMap::ImpWriteNCSA( SvStream& rOStm  ) const
{
    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 )->WriteNCSA( rOStm );
            break;
 
            case IMapObjectType::Circle:
                static_cast<IMapCircleObject*>( pObj )->WriteNCSA( rOStm );
            break;
 
            case IMapObjectType::Polygon:
                static_cast<IMapPolygonObject*>( pObj )->WriteNCSA( rOStm );
            break;
 
            default:
            break;
        }
    }
}
 
sal_uLong ImageMap::Read( SvStream& rIStm, IMapFormat nFormat  )
{
    sal_uLong nRet = IMAP_ERR_FORMAT;
 
    if ( nFormat == IMapFormat::Detect )
        nFormat = ImpDetectFormat( rIStm );
 
    switch ( nFormat )
    {
        case IMapFormat::Binary : Read( rIStm ); break;
        case IMapFormat::CERN   : ImpReadCERN( rIStm ); break;
        case IMapFormat::NCSA   : ImpReadNCSA( rIStm ); break;
 
        default:
        break;
    }
 
    if ( !rIStm.GetError() )
        nRet = IMAP_ERR_OK;
 
    return nRet;
}
 
void ImageMap::ImpReadCERN( SvStream& rIStm )
{
    // delete old content
    ClearImageMap();
 
    OStringBuffer aStr;
    while ( rIStm.ReadLine( aStr ) )
        ImpReadCERNLine( aStr );
}
 
void ImageMap::ImpReadCERNLine( std::string_view rLine  )
{
    OString aStr( comphelper::string::stripStart(rLine, ' ') );
    aStr = comphelper::string::stripStart(aStr, '\t');
    aStr = aStr.replaceAll(";"_ostr, ""_ostr);
    aStr = aStr.toAsciiLowerCase();
 
    const char* pStr = aStr.getStr();
    char        cChar = *pStr++;
 
    // find instruction
    OStringBuffer aBuf;
    while ((cChar >= 'a') && (cChar <= 'z'))
    {
        aBuf.append(cChar);
        cChar = *pStr++;
    }
    OString aToken = aBuf.makeStringAndClear();
 
    if ( !(NOTEOL( cChar )) )
        return;
 
    if ( ( aToken == "rectangle" ) || ( aToken == "rect" ) )
    {
        const Point     aTopLeft( ImpReadCERNCoords( &pStr ) );
        const Point     aBottomRight( ImpReadCERNCoords( &pStr ) );
        const OUString  aURL( ImpReadCERNURL( &pStr ) );
        const tools::Rectangle aRect( aTopLeft, aBottomRight );
 
        maList.emplace_back( new IMapRectangleObject( aRect, aURL, OUString(), OUString(), OUString(), OUString() ) );
    }
    else if ( ( aToken == "circle" ) || ( aToken == "circ" ) )
    {
        const Point     aCenter( ImpReadCERNCoords( &pStr ) );
        const tools::Long      nRadius = ImpReadCERNRadius( &pStr );
        const OUString  aURL( ImpReadCERNURL( &pStr ) );
 
        maList.emplace_back( new IMapCircleObject( aCenter, nRadius, aURL, OUString(), OUString(), OUString(), OUString() ) );
    }
    else if ( ( aToken == "polygon" ) || ( aToken == "poly" ) )
    {
        const sal_Int32 nTokenCount = comphelper::string::getTokenCount(aStr, '(');
        tools::Polygon aPoly;
        if (nTokenCount > 0)
        {
            const sal_uInt16 nCount = nTokenCount - 1;
            aPoly.SetSize(nCount);
 
            for (sal_uInt16 i = 0; i < nCount; ++i)
                aPoly[ i ] = ImpReadCERNCoords( &pStr );
        }
 
        const OUString aURL = ImpReadCERNURL( &pStr );
 
        maList.emplace_back( new IMapPolygonObject( aPoly, aURL, OUString(), OUString(), OUString(), OUString() ) );
    }
}
 
Point ImageMap::ImpReadCERNCoords( const char** ppStr )
{
    OUStringBuffer  aStrX;
    OUStringBuffer  aStrY;
    Point           aPt;
    char            cChar = *(*ppStr)++;
 
    while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
        cChar = *(*ppStr)++;
 
    if ( NOTEOL( cChar ) )
    {
        while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
        {
            aStrX.append( cChar );
            cChar = *(*ppStr)++;
        }
 
        if ( NOTEOL( cChar ) )
        {
            while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
                cChar = *(*ppStr)++;
 
            while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
            {
                aStrY.append( cChar );
                cChar = *(*ppStr)++;
            }
 
            if ( NOTEOL( cChar ) )
                while( NOTEOL( cChar ) && ( cChar != ')' ) )
                    cChar = *(*ppStr)++;
 
            aPt = Point( o3tl::toInt32(aStrX), o3tl::toInt32(aStrY) );
        }
    }
 
    return aPt;
}
 
tools::Long ImageMap::ImpReadCERNRadius( const char** ppStr )
{
    OUStringBuffer  aStr;
    char            cChar = *(*ppStr)++;
 
    while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
        cChar = *(*ppStr)++;
 
    if ( NOTEOL( cChar ) )
    {
        while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
        {
            aStr.append( cChar );
            cChar = *(*ppStr)++;
        }
    }
 
    return o3tl::toInt32(aStr);
}
 
OUString ImageMap::ImpReadCERNURL( const char** ppStr )
{
    OUString aStr(OUString::createFromAscii(*ppStr));
 
    aStr = comphelper::string::strip(aStr, ' ');
    aStr = comphelper::string::strip(aStr, '\t');
 
    return INetURLObject::GetAbsURL( u"", aStr );
}
 
void ImageMap::ImpReadNCSA( SvStream& rIStm )
{
    // delete old content
    ClearImageMap();
 
    OStringBuffer aStr;
    while ( rIStm.ReadLine( aStr ) )
        ImpReadNCSALine( aStr );
}
 
void ImageMap::ImpReadNCSALine( std::string_view rLine )
{
    OString aStr( comphelper::string::stripStart(rLine, ' ') );
    aStr = comphelper::string::stripStart(aStr, '\t');
    aStr = aStr.replaceAll(";"_ostr, ""_ostr);
    aStr = aStr.toAsciiLowerCase();
 
    const char* pStr = aStr.getStr();
    char        cChar = *pStr++;
 
    // find instruction
    OStringBuffer aBuf;
    while ((cChar >= 'a') && (cChar <= 'z'))
    {
        aBuf.append(cChar);
        cChar = *pStr++;
    }
    OString aToken = aBuf.makeStringAndClear();
 
    if ( !(NOTEOL( cChar )) )
        return;
 
    if ( aToken == "rect" )
    {
        const OUString  aURL( ImpReadNCSAURL( &pStr ) );
        const Point     aTopLeft( ImpReadNCSACoords( &pStr ) );
        const Point     aBottomRight( ImpReadNCSACoords( &pStr ) );
        const tools::Rectangle aRect( aTopLeft, aBottomRight );
 
        maList.emplace_back( new IMapRectangleObject( aRect, aURL, OUString(), OUString(), OUString(), OUString() ) );
    }
    else if ( aToken == "circle" )
    {
        const OUString  aURL( ImpReadNCSAURL( &pStr ) );
        const Point     aCenter( ImpReadNCSACoords( &pStr ) );
        const Point     aDX( aCenter - ImpReadNCSACoords( &pStr ) );
        tools::Long            nRadius = static_cast<tools::Long>(std::hypot( aDX.X(), aDX.Y()));
 
        maList.emplace_back( new IMapCircleObject( aCenter, nRadius, aURL, OUString(), OUString(), OUString(), OUString() ) );
    }
    else if ( aToken == "poly" )
    {
        const sal_Int32 nTokenCount = comphelper::string::getTokenCount(aStr, ',');
        const OUString aURL( ImpReadNCSAURL( &pStr ) );
        tools::Polygon aPoly;
        if (nTokenCount > 0)
        {
            const sal_uInt16 nCount = nTokenCount - 1;
            aPoly.SetSize(nCount);
 
            for (sal_uInt16 i = 0; i < nCount; ++i)
                aPoly[ i ] = ImpReadNCSACoords( &pStr );
        }
 
        maList.emplace_back( new IMapPolygonObject( aPoly, aURL, OUString(), OUString(), OUString(), OUString() ) );
    }
}
 
OUString ImageMap::ImpReadNCSAURL( const char** ppStr )
{
    OUStringBuffer  aStr;
    char            cChar = *(*ppStr)++;
 
    while( NOTEOL( cChar ) && ( ( cChar == ' ' ) || ( cChar == '\t' ) ) )
        cChar = *(*ppStr)++;
 
    if ( NOTEOL( cChar ) )
    {
        while( NOTEOL( cChar ) && ( cChar != ' ' ) && ( cChar != '\t' ) )
        {
            aStr.append( cChar );
            cChar = *(*ppStr)++;
        }
    }
 
    return INetURLObject::GetAbsURL( u"", aStr.makeStringAndClear() );
}
 
Point ImageMap::ImpReadNCSACoords( const char** ppStr )
{
    OUStringBuffer  aStrX;
    OUStringBuffer  aStrY;
    Point           aPt;
    char            cChar = *(*ppStr)++;
 
    while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
        cChar = *(*ppStr)++;
 
    if ( NOTEOL( cChar ) )
    {
        while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
        {
            aStrX.append( cChar );
            cChar = *(*ppStr)++;
        }
 
        if ( NOTEOL( cChar ) )
        {
            while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
                cChar = *(*ppStr)++;
 
            while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
            {
                aStrY.append( cChar );
                cChar = *(*ppStr)++;
            }
 
            aPt = Point( o3tl::toInt32(aStrX), o3tl::toInt32(aStrY) );
        }
    }
 
    return aPt;
}
 
IMapFormat ImageMap::ImpDetectFormat( SvStream& rIStm )
{
    sal_uInt64  nPos = rIStm.Tell();
    IMapFormat  nRet = IMapFormat::Binary;
    char    cMagic[6];
 
    rIStm.ReadBytes(cMagic, sizeof(cMagic));
 
    // if we do not have an internal formats
    // we check the format
    if ( memcmp( cMagic, IMAPMAGIC, sizeof( cMagic ) ) )
    {
        tools::Long        nCount = 128;
 
        rIStm.Seek( nPos );
        OString aStr;
        while ( rIStm.ReadLine( aStr ) && nCount-- )
        {
            aStr = aStr.toAsciiLowerCase();
 
            if ( (aStr.indexOf("rect") != -1) ||
                 (aStr.indexOf("circ") != -1) ||
                 (aStr.indexOf("poly") != -1) )
            {
                if ( ( aStr.indexOf('(') != -1 ) &&
                     ( aStr.indexOf(')') != -1 ) )
                {
                    nRet = IMapFormat::CERN;
                }
                else
                    nRet = IMapFormat::NCSA;
 
                break;
            }
        }
    }
 
    rIStm.Seek( nPos );
 
    return nRet;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.