/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: IBM Corporation
 *
 *  Copyright: 2008 by IBM Corporation
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
 
/**
 * @file
 *  For LWP filter architecture prototype
*/
#include <memory>
#include <string_view>
 
#include <lwpfilehdr.hxx>
#include "lwpgrfobj.hxx"
#include "lwpsdwfileloader.hxx"
#include "bento.hxx"
 
#include <lwpglobalmgr.hxx>
#include <o3tl/numeric.hxx>
#include <o3tl/sprintf.hxx>
#include <rtl/string.hxx>
#include "lwpframelayout.hxx"
 
#include <xfilter/xfframe.hxx>
#include <xfilter/xfimage.hxx>
#include <xfilter/xfimagestyle.hxx>
#include <xfilter/xfstylemanager.hxx>
#include <xfilter/xfparagraph.hxx>
#include <xfilter/xfannotation.hxx>
 
//For chart
#include <string.h>
 
#include <osl/thread.h>
#include <sal/log.hxx>
 
#define EF_NONE 0x0000
#define EF_ODMA 0x0002
 
LwpGraphicObject::LwpGraphicObject(LwpObjectHeader const &objHdr, LwpSvStream* pStrm)
    : LwpGraphicOleObject(objHdr, pStrm)
    , m_nCachedBaseLine(0)
    , m_bIsLinked(0)
    , m_bCompressed(0)
{
}
 
LwpGraphicObject::~LwpGraphicObject()
{
}
 
void LwpGraphicObject::Read()
{
    LwpGraphicOleObject::Read();
    m_pObjStrm->QuickReaduInt16(); //disksize
    sal_uInt16 strsize = m_pObjStrm->QuickReaduInt16();
    if (strsize<AFID_MAX_FILE_FORMAT_SIZE)
    {
        m_pObjStrm->QuickRead(m_sDataFormat,strsize);
        m_sDataFormat[strsize] = '\0';
    }
    sal_uInt32 nServerContextSize = m_pObjStrm->QuickReaduInt32();
    if (nServerContextSize > 0)
    {
        sal_uInt16 nMaxPossibleSize = m_pObjStrm->remainingSize();
 
        if (nServerContextSize > nMaxPossibleSize)
        {
            SAL_WARN("lwp", "stream too short for claimed no of records");
            nServerContextSize = nMaxPossibleSize;
        }
 
        std::vector<unsigned char> aServerContext(nServerContextSize);
        m_pObjStrm->QuickRead(aServerContext.data(), static_cast<sal_uInt16>(nServerContextSize));
        if (nServerContextSize > 44)
        {
            m_aIPData.nBrightness = aServerContext[14];
            m_aIPData.nContrast = aServerContext[19];
            m_aIPData.nEdgeEnhancement = aServerContext[24];
            m_aIPData.nSmoothing = aServerContext[29];
            m_aIPData.bInvertImage = (aServerContext[34] == 0x01);
            m_aIPData.bAutoContrast = (aServerContext[44] == 0x00);
        }
    }
    m_pObjStrm->QuickReaduInt16(); //disksize
    strsize = m_pObjStrm->QuickReaduInt16();
    if (strsize<AFID_MAX_FILE_FORMAT_SIZE)
    {
        m_pObjStrm->QuickRead(m_sServerContextFormat,strsize);
        m_sServerContextFormat[strsize] = '\0';
    }
    if (nServerContextSize == 0)
    {
        if (strcmp(reinterpret_cast<char *>(m_sServerContextFormat), ".cht") == 0 &&
            strcmp(reinterpret_cast<char *>(m_sDataFormat), ".sdw") == 0)
        {
            strcpy(reinterpret_cast<char *>(m_sServerContextFormat), ".lch");
            strcpy(reinterpret_cast<char *>(m_sDataFormat), ".lch");
        }
    }
    m_nCachedBaseLine = m_pObjStrm->QuickReadInt32();
    m_bIsLinked = m_pObjStrm->QuickReadInt16();
 
    if (m_bIsLinked)
    {
        m_LinkedFilePath = m_pObjStrm->QuickReadStringPtr();
 
        sal_uInt32 nFilterContextSize = m_pObjStrm->QuickReaduInt32();
        if (nFilterContextSize > 0)
        {
            sal_uInt16 nMaxPossibleSize = m_pObjStrm->remainingSize();
 
            if (nFilterContextSize > nMaxPossibleSize)
            {
                SAL_WARN("lwp", "stream too short for claimed no of records");
                nFilterContextSize = nMaxPossibleSize;
            }
 
            std::vector<unsigned char> aFilterContext(nFilterContextSize);
            m_pObjStrm->QuickRead(aFilterContext.data(), static_cast<sal_uInt16>(nFilterContextSize));
        }
        if (LwpFileHeader::m_nFileRevision >= 0x000b)
        {
 
            // read external file object stuff
            sal_uInt16 type = m_pObjStrm->QuickReaduInt16();
            if ((EF_ODMA != type) && (EF_NONE != type)) // don't know about this
            {
                sal_uInt32 size = m_pObjStrm->QuickReaduInt32();
                m_pObjStrm->SeekRel(static_cast<sal_uInt16>(size));
            }
            // else no external file object
        }
    }
 
    if (LwpFileHeader::m_nFileRevision >= 0x000b)
    {
        m_bCompressed = m_pObjStrm->QuickReadInt16();
        m_Cache.LinkedFileSize = m_pObjStrm->QuickReaduInt32();
        m_Cache.LinkedFileTime = m_pObjStrm->QuickReaduInt32();
        m_Cache.Width = m_pObjStrm->QuickReadInt32();
        m_Cache.Height = m_pObjStrm->QuickReadInt32();
    }
 
    if(LwpFileHeader::m_nFileRevision >= 0x000c)
    {
        m_WatermarkName = m_pObjStrm->QuickReadStringPtr();
    }
}
 
void LwpGraphicObject::XFConvert (XFContentContainer* pCont)
{
    if (m_sServerContextFormat[1]=='s'&&m_sServerContextFormat[2]=='d'&&m_sServerContextFormat[3]=='w')
    {
        for (auto const& vXFDrawObject : m_vXFDrawObjects)
        {
            pCont->Add(vXFDrawObject.get());
        }
    }
    else if (IsGrafFormatValid() && !m_vXFDrawObjects.empty())
    {
        XFImage* pImage = static_cast<XFImage*>(m_vXFDrawObjects.front().get());
 
        if (m_bIsLinked)
        {
            OUString fileURL = LwpTools::convertToFileUrl(OUStringToOString(m_LinkedFilePath, osl_getThreadTextEncoding()));
            pImage->SetFileURL(fileURL);
        }
        else
        {
            std::vector<sal_uInt8> aGrafData = GetRawGrafData();
            pImage->SetImageData(aGrafData.data(), aGrafData.size());
        }
 
        pCont->Add(pImage);
    }
    else if(m_sServerContextFormat[1]=='t'&&m_sServerContextFormat[2]=='e'&&m_sServerContextFormat[3]=='x')
    {
        XFConvertEquation(pCont);
    }
}
 
/**
 * @descr   judge if the graphic format is what we can support: bmp, jpg, wmf, gif, tgf(tif). other format will be filtered to
 *  these formats by Word Pro.
 * @return  sal_True if yes sal_False if not.
 */
bool LwpGraphicObject::IsGrafFormatValid() const
{
    return (m_sServerContextFormat[1]=='b'&& m_sServerContextFormat[2]=='m' && m_sServerContextFormat[3]=='p')
    || (m_sServerContextFormat[1]=='j' && m_sServerContextFormat[2]=='p' && m_sServerContextFormat[3]=='g')
    || (m_sServerContextFormat[1]=='w' && m_sServerContextFormat[2]=='m' && m_sServerContextFormat[3]=='f')
    || (m_sServerContextFormat[1]=='g' && m_sServerContextFormat[2]=='i' && m_sServerContextFormat[3]=='f')
    || (m_sServerContextFormat[1]=='t' && m_sServerContextFormat[2]=='g' && m_sServerContextFormat[3]=='f')
    || (m_sServerContextFormat[1]=='p' && m_sServerContextFormat[2]=='n' && m_sServerContextFormat[3]=='g')
    || (m_sServerContextFormat[1]=='e' && m_sServerContextFormat[2]=='p' && m_sServerContextFormat[3]=='s');
}
 
/**
 * @descr   create drawing object and image object.
 */
void LwpGraphicObject::RegisterStyle()
{
    if (m_sServerContextFormat[1]=='s'&&m_sServerContextFormat[2]=='d'&&m_sServerContextFormat[3]=='w')
    {
        CreateDrawObjects();
    }
    // test codes for importing pictures
    else if(IsGrafFormatValid())
    {
        CreateGrafObject();
    }
 
    if (m_sServerContextFormat[1]=='l'&&m_sServerContextFormat[2]=='c'&&m_sServerContextFormat[3]=='h')
    {
        rtl::Reference<LwpVirtualLayout> xMyLayout(GetLayout(nullptr));
        if (xMyLayout.is() && xMyLayout->IsFrame())
        {
            std::unique_ptr<XFFrameStyle> pXFFrameStyle(new XFFrameStyle());
            pXFFrameStyle->SetXPosType(enumXFFrameXPosFromLeft, enumXFFrameXRelFrame);
            pXFFrameStyle->SetYPosType(enumXFFrameYPosFromTop, enumXFFrameYRelPara);
            XFStyleManager* pXFStyleManager = LwpGlobalMgr::GetInstance()->GetXFStyleManager();
            m_strStyleName = pXFStyleManager->AddStyle(std::move(pXFFrameStyle)).m_pStyle->GetStyleName();
        }
    }
 
}
 
/**
 * @descr   create drawing object.
 */
void LwpGraphicObject::CreateDrawObjects()
{
    // if small file, use the compressed stream for BENTO
    LwpSvStream* pStream = m_pStrm->GetCompressedStream() ?  m_pStrm->GetCompressedStream(): m_pStrm;
 
    std::unique_ptr<OpenStormBento::LtcBenContainer> pBentoContainer;
    OpenStormBento::BenError ulRet = OpenStormBento::BenOpenContainer(pStream, &pBentoContainer);
    if (ulRet != OpenStormBento::BenErr_OK)
        return;
 
    // get graphic object's bento object name
    LwpObjectID& rMyID = GetObjectID();
    std::string aGrfObjName;
    GetBentoNamebyID(rMyID,  aGrfObjName);
 
    // get bento stream by the name
    std::vector<sal_uInt8> aData = pBentoContainer->GetGraphicData(aGrfObjName.c_str());
    if (!aData.empty())
    {
        SvMemoryStream aDrawObjStream(aData.data(), aData.size(), StreamMode::READ);
 
        LwpSdwFileLoader fileLoader(&aDrawObjStream, this);
        fileLoader.CreateDrawObjects(&m_vXFDrawObjects);
    }
}
 
/**
 * @descr   create drawing object.
 */
void LwpGraphicObject::GetBentoNamebyID(LwpObjectID const & rMyID, std::string& rName)
{
    sal_uInt16 nHigh = rMyID.GetHigh();
    sal_uInt32 nLow = rMyID.GetLow();
    char pTempStr[32];
    rName = std::string("Gr");
    o3tl::sprintf(pTempStr, "%X,%" SAL_PRIXUINT32, nHigh, nLow);
    rName.append(pTempStr);
}
 
/**
 * @descr   get the image data read from bento stream according to the VO_GRAPHIC ID.
 * @return  the image data.
 */
std::vector<sal_uInt8> LwpGraphicObject::GetRawGrafData()
{
    std::vector<sal_uInt8> aGrafData;
 
    // create graphic object
    // if small file, use the compressed stream for BENTO
    LwpSvStream* pStream = m_pStrm->GetCompressedStream() ?  m_pStrm->GetCompressedStream(): m_pStrm;
 
    std::unique_ptr<OpenStormBento::LtcBenContainer> pBentoContainer;
    {
        OpenStormBento::BenError ulRet = OpenStormBento::BenOpenContainer(pStream, &pBentoContainer);
        if (ulRet != OpenStormBento::BenErr_OK)
            return aGrafData;
    }
 
    // get graphic object's bento object name
    LwpObjectID& rMyID = GetObjectID();
    std::string aGrfObjName;
    GetBentoNamebyID(rMyID,  aGrfObjName);
 
    // get bento stream by the name and read image data
    return pBentoContainer->GetGraphicData(aGrfObjName.c_str());
}
 
/**
 * @descr   get the image data (only -D data) read from bento stream according to the VO_GRAPHIC ID.
 * @param   pGrafData   the array to store the image data. the pointer need to be deleted outside.
 * @return  the length of the image data.
 */
sal_uInt32 LwpGraphicObject::GetGrafData(std::unique_ptr<sal_uInt8[]>& pGrafData)
{
    // create graphic object
    // if small file, use the compressed stream for BENTO
    LwpSvStream* pStream = m_pStrm->GetCompressedStream() ?  m_pStrm->GetCompressedStream(): m_pStrm;
 
    std::unique_ptr<OpenStormBento::LtcBenContainer> pBentoContainer;
    OpenStormBento::BenError ulRet = OpenStormBento::BenOpenContainer(pStream, &pBentoContainer);
    if (ulRet != OpenStormBento::BenErr_OK)
        return 0;
 
    SvStream* pGrafStream = nullptr;
 
    // get graphic object's bento object name
    LwpObjectID& rMyID = GetObjectID();
    std::string aGrfObjName;
    GetBentoNamebyID(rMyID,  aGrfObjName);
 
    OString sDName=OString::Concat(std::string_view(aGrfObjName)) + "-D";
 
    // get bento stream by the name
    pGrafStream = pBentoContainer->FindValueStreamWithPropertyName(sDName.getStr());
 
    std::unique_ptr<SvMemoryStream> pMemGrafStream(static_cast<SvMemoryStream*>(pGrafStream));
 
    if (pMemGrafStream)
    {
        // read image data
        sal_uInt32 nDataLen = pGrafStream->TellEnd();
 
        pGrafData.reset(new sal_uInt8 [nDataLen]);
        pMemGrafStream->ReadBytes(pGrafData.get(), nDataLen);
 
        return nDataLen;
    }
 
    return 0;
}
 
/**
 * @descr   create xf-image object and save it in the container: m_vXFDrawObjects.
 */
void LwpGraphicObject::CreateGrafObject()
{
    rtl::Reference<XFImage> pImage = new XFImage();
 
    // set image processing styles
    std::unique_ptr<XFImageStyle> xImageStyle(new XFImageStyle);
    if (m_sServerContextFormat[1]!='w' || m_sServerContextFormat[2]!='m' || m_sServerContextFormat[3]!='f')
    {
        if (m_aIPData.nBrightness != 50)
        {
            sal_Int32 nSODCBrightness = static_cast<sal_Int32>(m_aIPData.nBrightness)*2 - 100;
            xImageStyle->SetBrightness(nSODCBrightness);
        }
        if (m_aIPData.nContrast != 50)
        {
            sal_Int32 nSODCContrast = static_cast<sal_Int32>(80 - static_cast<double>(m_aIPData.nContrast)*1.6);
            xImageStyle->SetContrast(nSODCContrast);
        }
    }
 
    // set scale and crop styles
    LwpAssociatedLayouts& rLayoutWithMe = GetLayoutsWithMe();
    LwpFrameLayout* pMyFrameLayout =
        static_cast<LwpFrameLayout*>(rLayoutWithMe.GetOnlyLayout().obj(VO_FRAMELAYOUT).get());
    if (pMyFrameLayout)
    {
        LwpLayoutScale* pMyScale = pMyFrameLayout->GetLayoutScale();
        LwpLayoutGeometry* pFrameGeo = pMyFrameLayout->GetGeometry();
 
        // original image size
        double fOrgGrafWidth = LwpTools::ConvertFromTwips(m_Cache.Width);
        double fOrgGrafHeight = LwpTools::ConvertFromTwips(m_Cache.Height);
 
        // get margin values
        double fLeftMargin = pMyFrameLayout->GetMarginsValue(MARGIN_LEFT);
        double fRightMargin = pMyFrameLayout->GetMarginsValue(MARGIN_RIGHT);
        double fTopMargin = pMyFrameLayout->GetMarginsValue(MARGIN_TOP);
        double fBottomMargin = pMyFrameLayout->GetMarginsValue(MARGIN_BOTTOM);
 
        if (pMyScale && pFrameGeo)
        {
            if (fOrgGrafHeight == 0.0 || fOrgGrafWidth == 0.0)
                throw o3tl::divide_by_zero();
 
            // frame size
            double fFrameWidth = LwpTools::ConvertFromUnits(pFrameGeo->GetWidth());
            double fFrameHeight = LwpTools::ConvertFromUnits(pFrameGeo->GetHeight());
 
            // calculate the displayed size of the frame
            double fDisFrameWidth = fFrameWidth - (fLeftMargin+fRightMargin);
            double fDisFrameHeight = fFrameHeight - (fTopMargin+fBottomMargin);
 
            // scaled image size
            double fSclGrafWidth = fOrgGrafWidth;
            double fSclGrafHeight = fOrgGrafHeight;
 
            // get scale mode
            sal_uInt16 nScalemode = pMyScale->GetScaleMode();
            if (nScalemode & LwpLayoutScale::CUSTOM)
            {
                fSclGrafWidth = LwpTools::ConvertFromUnits(pMyScale->GetScaleWidth());
                fSclGrafHeight = LwpTools::ConvertFromUnits(pMyScale->GetScaleHeight());
            }
            else if (nScalemode & LwpLayoutScale::PERCENTAGE)
            {
                double fScalePercentage = static_cast<double>(pMyScale->GetScalePercentage()) / 1000;
                fSclGrafWidth = fScalePercentage * fOrgGrafWidth;
                fSclGrafHeight = fScalePercentage * fOrgGrafHeight;
            }
            else if (nScalemode & LwpLayoutScale::FIT_IN_FRAME)
            {
                if (pMyFrameLayout->IsFitGraphic())
                {
                    fSclGrafWidth = fOrgGrafWidth;
                    fSclGrafHeight = fOrgGrafHeight;
                }
                else if (nScalemode & LwpLayoutScale::MAINTAIN_ASPECT_RATIO)
                {
                    if (fDisFrameHeight == 0.0)
                        throw o3tl::divide_by_zero();
                    if (fOrgGrafWidth/fOrgGrafHeight >= fDisFrameWidth/fDisFrameHeight)
                    {
                        fSclGrafWidth = fDisFrameWidth;
                        fSclGrafHeight = (fDisFrameWidth/fOrgGrafWidth) * fOrgGrafHeight;
                    }
                    else
                    {
                        fSclGrafHeight = fDisFrameHeight;
                        fSclGrafWidth = (fDisFrameHeight/fOrgGrafHeight) * fOrgGrafWidth;
                    }
                }
                else
                {
                    fSclGrafWidth = fDisFrameWidth;
                    fSclGrafHeight = fDisFrameHeight;
                }
            }
 
            // scaled ratio
            double fXRatio = fSclGrafWidth / fOrgGrafWidth;
            double fYRatio = fSclGrafHeight / fOrgGrafHeight;
 
            // set image to scaled size.
            pImage->SetWidth(fSclGrafWidth);
            pImage->SetHeight(fSclGrafHeight);
 
            // placement:centered or tiled. tiled style is not supported so it's processed together with centered.
            if (pMyFrameLayout->GetScaleCenter() || pMyFrameLayout->GetScaleTile())
            {
                // set center alignment
                xImageStyle->SetXPosType(enumXFFrameXPosCenter, enumXFFrameXRelFrame);
                xImageStyle->SetYPosType(enumXFFrameYPosMiddle, enumXFFrameYRelFrame);
 
                // need horizontal crop?
                double fClipWidth = 0;
                double fClipHeight = 0;
                bool sal_bCropped = false;
                if (fSclGrafWidth > fDisFrameWidth)
                {
                    if (fXRatio == 0.0)
                        throw o3tl::divide_by_zero();
                    fClipWidth = (fSclGrafWidth-fDisFrameWidth ) / 2 / fXRatio;
                    sal_bCropped = true;
                }
 
                // need vertical crop?
                if (fSclGrafHeight > fDisFrameHeight)
                {
                    if (fYRatio == 0.0)
                        throw o3tl::divide_by_zero();
                    fClipHeight = (fSclGrafHeight-fDisFrameHeight ) / 2 / fYRatio;
                    sal_bCropped = true;
                }
 
                if (sal_bCropped)
                {
                    xImageStyle->SetClip(fClipWidth, fClipWidth, fClipHeight, fClipHeight);
                    pImage->SetWidth(fDisFrameWidth);
                    pImage->SetHeight(fDisFrameHeight);
                }
            }
            // placement:automatic
            else
            {
                // set left-top alignment
                xImageStyle->SetYPosType(enumXFFrameYPosFromTop, enumXFFrameYRelFrame);
                xImageStyle->SetXPosType(enumXFFrameXPosFromLeft, enumXFFrameXRelFrame);
 
                // get image position offset
                LwpPoint& rOffset = pMyScale->GetOffset();
                double fOffsetX = LwpTools::ConvertFromUnits(rOffset.GetX());
                double fOffsetY = LwpTools::ConvertFromUnits(rOffset.GetY());
 
                struct LwpRect
                {
                    double fLeft;
                    double fRight;
                    double fTop;
                    double fBottom;
 
                    LwpRect()
                    {
                        fLeft = 0.00;
                        fRight = 0.00;
                        fTop = 0.00;
                        fBottom = 0.00;
                    }
                    LwpRect(double fL, double fR, double fT, double fB)
                    {
                        fLeft = fL;
                        fRight = fR;
                        fTop = fT;
                        fBottom = fB;
                    }
                };
                LwpRect aFrameRect(-fOffsetX, (fDisFrameWidth-fOffsetX), (-fOffsetY), (fDisFrameHeight-fOffsetY));
                LwpRect aImageRect(0, fSclGrafWidth, 0, fSclGrafHeight);
 
                if (aFrameRect.fRight <= aImageRect.fLeft || aFrameRect.fLeft >= aImageRect.fRight
                    ||aFrameRect.fBottom <= aImageRect.fTop|| aFrameRect.fTop >= aImageRect.fBottom)
                {
                    // display blank
                }
                else// need cropped
                {
                    // horizontal crop
                    LwpRect aCropRect;
                    if (aFrameRect.fLeft > aImageRect.fLeft)
                    {
                        aCropRect.fLeft = (aFrameRect.fLeft - aImageRect.fLeft) / fXRatio;
                    }
 
                    if (aFrameRect.fRight < aImageRect.fRight)
                    {
                        aCropRect.fRight = (aImageRect.fRight - aFrameRect.fRight) / fXRatio;
                    }
 
                    // vertical crop
                    if (aFrameRect.fTop > aImageRect.fTop)
                    {
                        aCropRect.fTop = (aFrameRect.fTop - aImageRect.fTop) / fYRatio;
                    }
                    if (aFrameRect.fBottom < aImageRect.fBottom)
                    {
                        aCropRect.fBottom = (aImageRect.fBottom - aFrameRect.fBottom) / fYRatio;
                    }
 
                    xImageStyle->SetClip(aCropRect.fLeft, aCropRect.fRight, aCropRect.fTop, aCropRect.fBottom);
                    double fPicWidth = fSclGrafWidth - (aCropRect.fLeft+aCropRect.fRight)*fXRatio;
                    double fPicHeight = fSclGrafHeight- (aCropRect.fTop+aCropRect.fBottom)*fYRatio;
                    double fX = fOffsetX > 0 ? fOffsetX : 0.00;
                    double fY = fOffsetY > 0 ? fOffsetY : 0.00;
                    pImage->SetPosition((fX+fLeftMargin), (fY+fTopMargin), fPicWidth, fPicHeight);
                }
            }
        }
    }
 
    // set style for the image
    XFStyleManager* pXFStyleManager = LwpGlobalMgr::GetInstance()->GetXFStyleManager();
    pImage->SetStyleName(pXFStyleManager->AddStyle(std::move(xImageStyle)).m_pStyle->GetStyleName());
 
    // set anchor to frame
    pImage->SetAnchorType(enumXFAnchorFrame);
 
    // set object name
    LwpAtomHolder& rHolder = GetName();
    if ( !rHolder.str().isEmpty() )
    {
        pImage->SetName(rHolder.str());
    }
 
    // insert image object into array
    m_vXFDrawObjects.emplace_back(pImage.get());
 
}
 
/**
 * @descr   Reserve the equation text in a note in the context.
 */
void LwpGraphicObject::XFConvertEquation(XFContentContainer * pCont)
{
    std::unique_ptr<sal_uInt8[]> pGrafData;
    sal_uInt32 nDataLen = GetGrafData(pGrafData);
    if(!pGrafData)
        return;
 
    //convert equation
    XFParagraph* pXFPara = new XFParagraph;
    pXFPara->Add(u"Formula:"_ustr);
    //add notes
    XFAnnotation* pXFNote = new XFAnnotation;
    //add equation to comment notes
    XFParagraph* pXFNotePara = new XFParagraph;
    //equation header text: Times New Roman,
    //                                18,12,0,0,0,0,0.
    //                                 .TCIformat{2}
    //total head length = 45
    bool bOk = true;
    sal_uInt32 nBegin = 45;
    sal_uInt32 nEnd = 0;
    if (nDataLen >= 1)
        nEnd = nDataLen - 1;
    else
        bOk = false;
 
    if (bOk && pGrafData[nEnd] == '$' && nEnd > 0 && pGrafData[nEnd-1] != '\\')
    {
        //equation body is contained by '$';
        nBegin++;
        nEnd--;
    }
 
    bOk &= nEnd >= nBegin;
    if (bOk)
    {
        std::unique_ptr<sal_uInt8[]> pEquData( new sal_uInt8[nEnd - nBegin + 1] );
        for(sal_uInt32 nIndex = 0; nIndex < nEnd - nBegin +1 ; nIndex++)
        {
            pEquData[nIndex] = pGrafData[nBegin + nIndex];
        }
        pXFNotePara->Add(OUString(reinterpret_cast<char*>(pEquData.get()), (nEnd - nBegin + 1), osl_getThreadTextEncoding()));
    }
    pXFNote->Add(pXFNotePara);
 
    pXFPara->Add(pXFNote);
    pCont->Add(pXFPara);
}
 
void LwpGraphicObject::GetGrafOrgSize(double & rWidth, double & rHeight)
{
    // original image size
    rWidth = LwpTools::ConvertFromTwips(m_Cache.Width);
    rHeight = LwpTools::ConvertFromTwips(m_Cache.Height);
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V781 The value of the 'nEnd' index is checked after it was used. Perhaps there is a mistake in program logic.