/* -*- 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/.
 */
 
#include "htmlreqifreader.hxx"
 
#include <comphelper/scopeguard.hxx>
#include <filter/msfilter/rtfutil.hxx>
#include <rtl/strbuf.hxx>
#include <sot/storage.hxx>
#include <svtools/parrtf.hxx>
#include <svtools/rtfkeywd.hxx>
#include <svtools/rtftoken.h>
#include <tools/stream.hxx>
#include <filter/msfilter/msdffimp.hxx>
#include <vcl/cvtgrf.hxx>
#include <ndole.hxx>
#include <sal/log.hxx>
#include <vcl/FilterConfigItem.hxx>
#include <vcl/wmf.hxx>
#include <comphelper/propertyvalue.hxx>
#include <fmtfsize.hxx>
#include <frmfmt.hxx>
 
using namespace com::sun::star;
 
namespace
{
/// RTF parser that just extracts a single OLE2 object from a file.
class ReqIfRtfReader : public SvRTFParser
{
public:
    ReqIfRtfReader(SvStream& rStream);
    void NextToken(int nToken) override;
    bool WriteObjectData(SvStream& rOLE);
 
private:
    bool m_bInObjData = false;
    OStringBuffer m_aHex;
};
 
ReqIfRtfReader::ReqIfRtfReader(SvStream& rStream)
    : SvRTFParser(rStream)
{
}
 
void ReqIfRtfReader::NextToken(int nToken)
{
    switch (nToken)
    {
        case '}':
            m_bInObjData = false;
            break;
        case RTF_TEXTTOKEN:
            if (m_bInObjData)
                m_aHex.append(OUStringToOString(aToken, RTL_TEXTENCODING_ASCII_US));
            break;
        case RTF_OBJDATA:
            m_bInObjData = true;
            break;
    }
}
 
bool ReqIfRtfReader::WriteObjectData(SvStream& rOLE)
{
    return msfilter::rtfutil::ExtractOLE2FromObjdata(m_aHex.makeStringAndClear(), rOLE);
}
 
/// Looks up what OLE1 calls the ClassName, see [MS-OLEDS] 2.3.8 CompObjStream.
OString ExtractOLEClassName(const rtl::Reference<SotStorage>& xStorage)
{
    OString aRet;
 
    rtl::Reference<SotStorageStream> pCompObj = xStorage->OpenSotStream(u"\1CompObj"_ustr);
    if (!pCompObj)
        return aRet;
 
    pCompObj->Seek(0);
    pCompObj->SeekRel(28); // Header
    if (!pCompObj->good())
        return aRet;
 
    sal_uInt32 nData;
    pCompObj->ReadUInt32(nData); // AnsiUserType
    pCompObj->SeekRel(nData);
    if (!pCompObj->good())
        return aRet;
 
    pCompObj->ReadUInt32(nData); // AnsiClipboardFormat
    pCompObj->SeekRel(nData);
    if (!pCompObj->good())
        return aRet;
 
    pCompObj->ReadUInt32(nData); // Reserved1
    return read_uInt8s_ToOString(*pCompObj, nData - 1); // -1 because it is null-terminated
}
 
/// Parses the presentation stream of an OLE2 storage.
bool ParseOLE2Presentation(SvStream& rOle2, sal_uInt32& nWidth, sal_uInt32& nHeight,
                           SvStream& rPresentationData)
{
    // See [MS-OLEDS] 2.3.4, OLEPresentationStream
    rOle2.Seek(0);
    rtl::Reference<SotStorage> pStorage = new SotStorage(rOle2);
    rtl::Reference<SotStorageStream> xOle2Presentation
        = pStorage->OpenSotStream(u"\002OlePres000"_ustr, StreamMode::STD_READ);
 
    // Read AnsiClipboardFormat.
    sal_uInt32 nMarkerOrLength = 0;
    xOle2Presentation->ReadUInt32(nMarkerOrLength);
    if (nMarkerOrLength != 0xffffffff)
        // FormatOrAnsiString is not present
        return false;
    sal_uInt32 nFormatOrAnsiLength = 0;
    xOle2Presentation->ReadUInt32(nFormatOrAnsiLength);
    if (nFormatOrAnsiLength != 0x00000003) // CF_METAFILEPICT
        return false;
 
    // Read TargetDeviceSize.
    sal_uInt32 nTargetDeviceSize = 0;
    xOle2Presentation->ReadUInt32(nTargetDeviceSize);
    if (nTargetDeviceSize != 0x00000004)
        // TargetDevice is present
        return false;
 
    sal_uInt32 nAspect = 0;
    xOle2Presentation->ReadUInt32(nAspect);
    sal_uInt32 nLindex = 0;
    xOle2Presentation->ReadUInt32(nLindex);
    sal_uInt32 nAdvf = 0;
    xOle2Presentation->ReadUInt32(nAdvf);
    sal_uInt32 nReserved1 = 0;
    xOle2Presentation->ReadUInt32(nReserved1);
    xOle2Presentation->ReadUInt32(nWidth);
    xOle2Presentation->ReadUInt32(nHeight);
    sal_uInt32 nSize = 0;
    xOle2Presentation->ReadUInt32(nSize);
 
    // Read Data.
    if (nSize > xOle2Presentation->remainingSize())
        return false;
 
    if (nSize <= 64)
    {
        SAL_WARN("sw.html",
                 "ParseOLE2Presentation: ignoring potentially broken small preview: size is "
                     << nSize);
        return false;
    }
 
    std::vector<char> aBuffer(nSize);
    xOle2Presentation->ReadBytes(aBuffer.data(), aBuffer.size());
    rPresentationData.WriteBytes(aBuffer.data(), aBuffer.size());
 
    return true;
}
 
/**
 * Inserts an OLE1 header before an OLE2 storage, assuming that the storage has an Ole10Native
 * stream.
 */
OString InsertOLE1HeaderFromOle10NativeStream(const rtl::Reference<SotStorage>& xStorage,
                                              SwOLENode& rOLENode, SvStream& rOle1)
{
    rtl::Reference<SotStorageStream> xOle1Stream
        = xStorage->OpenSotStream(u"\1Ole10Native"_ustr, StreamMode::STD_READ);
    sal_uInt32 nOle1Size = 0;
    xOle1Stream->ReadUInt32(nOle1Size);
 
    OString aClassName;
    if (xStorage->GetClassName() == SvGlobalName(0x0003000A, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46))
    {
        aClassName = "PBrush"_ostr;
    }
    else
    {
        if (xStorage->GetClassName()
            != SvGlobalName(0x0003000C, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46))
        {
            SAL_WARN("sw.html", "InsertOLE1HeaderFromOle10NativeStream: unexpected class id: "
                                    << xStorage->GetClassName().GetHexName());
        }
        aClassName = "Package"_ostr;
    }
 
    // Write ObjectHeader, see [MS-OLEDS] 2.2.4.
    rOle1.Seek(0);
    // OLEVersion.
    rOle1.WriteUInt32(0x00000501);
 
    // FormatID is EmbeddedObject.
    rOle1.WriteUInt32(0x00000002);
 
    // ClassName
    rOle1.WriteUInt32(aClassName.isEmpty() ? 0 : aClassName.getLength() + 1);
    if (!aClassName.isEmpty())
    {
        rOle1.WriteOString(aClassName);
        // Null terminated pascal string.
        rOle1.WriteChar(0);
    }
 
    // TopicName.
    rOle1.WriteUInt32(0);
 
    // ItemName.
    rOle1.WriteUInt32(0);
 
    // NativeDataSize
    rOle1.WriteUInt32(nOle1Size);
 
    // Write the actual native data.
    rOle1.WriteStream(*xOle1Stream, nOle1Size);
 
    // Write Presentation.
    if (!rOLENode.GetGraphic())
    {
        return aClassName;
    }
 
    const Graphic* pGraphic = rOLENode.GetGraphic();
    const Graphic rGraphic = pGraphic ? *pGraphic : Graphic();
    Size aSize = rOLENode.GetTwipSize();
    SvMemoryStream aGraphicStream;
    if (GraphicConverter::Export(aGraphicStream, rGraphic, ConvertDataFormat::WMF) != ERRCODE_NONE)
    {
        return aClassName;
    }
 
    auto pGraphicAry = static_cast<const sal_uInt8*>(aGraphicStream.GetData());
    sal_uInt64 nPresentationData = aGraphicStream.TellEnd();
    msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nPresentationData);
 
    // OLEVersion.
    rOle1.WriteUInt32(0x00000501);
    // FormatID: constant means the ClassName field is present.
    rOle1.WriteUInt32(0x00000005);
    // ClassName: null terminated pascal string.
    OString aPresentationClassName("METAFILEPICT"_ostr);
    rOle1.WriteUInt32(aPresentationClassName.getLength() + 1);
    rOle1.WriteOString(aPresentationClassName);
    rOle1.WriteChar(0);
    // Width.
    rOle1.WriteUInt32(aSize.getWidth());
    // Height.
    rOle1.WriteUInt32(aSize.getHeight() * -1);
    // PresentationDataSize
    rOle1.WriteUInt32(8 + nPresentationData);
    // Reserved1-4.
    rOle1.WriteUInt16(0x0008);
    rOle1.WriteUInt16(0x31b1);
    rOle1.WriteUInt16(0x1dd9);
    rOle1.WriteUInt16(0x0000);
    rOle1.WriteBytes(pGraphicAry, nPresentationData);
 
    return aClassName;
}
 
/**
 * Writes an OLE1 header and data from rOle2 to rOle1.
 *
 * In case rOle2 has presentation data, then its size is written to nWidth/nHeight.  Otherwise
 * nWidth/nHeight/pPresentationData/nPresentationData is used for the presentation data.
 */
OString InsertOLE1Header(SvStream& rOle2, SvStream& rOle1, sal_uInt32& nWidth, sal_uInt32& nHeight,
                         SwOLENode& rOLENode, const sal_uInt8* pPresentationData,
                         sal_uInt64 nPresentationData)
{
    rOle2.Seek(0);
    rtl::Reference<SotStorage> xStorage(new SotStorage(rOle2));
    if (xStorage->GetError() != ERRCODE_NONE)
        return {};
 
    if (xStorage->IsStream(u"\1Ole10Native"_ustr))
    {
        return InsertOLE1HeaderFromOle10NativeStream(xStorage, rOLENode, rOle1);
    }
 
    OString aClassName = ExtractOLEClassName(xStorage);
 
    // Write ObjectHeader, see [MS-OLEDS] 2.2.4.
    rOle1.Seek(0);
    // OLEVersion.
    rOle1.WriteUInt32(0x00000501);
 
    // FormatID is EmbeddedObject.
    rOle1.WriteUInt32(0x00000002);
 
    // ClassName
    rOle1.WriteUInt32(aClassName.isEmpty() ? 0 : aClassName.getLength() + 1);
    if (!aClassName.isEmpty())
    {
        rOle1.WriteOString(aClassName);
        // Null terminated pascal string.
        rOle1.WriteChar(0);
    }
 
    // TopicName.
    rOle1.WriteUInt32(0);
 
    // ItemName.
    rOle1.WriteUInt32(0);
 
    // NativeDataSize
    rOle1.WriteUInt32(rOle2.TellEnd());
 
    // Write the actual native data.
    rOle2.Seek(0);
    rOle1.WriteStream(rOle2);
 
    // Write Presentation.
    SvMemoryStream aPresentationData;
    // OLEVersion.
    rOle1.WriteUInt32(0x00000501);
    // FormatID: constant means the ClassName field is present.
    rOle1.WriteUInt32(0x00000005);
    // ClassName: null terminated pascal string.
    OString aPresentationClassName("METAFILEPICT"_ostr);
    rOle1.WriteUInt32(aPresentationClassName.getLength() + 1);
    rOle1.WriteOString(aPresentationClassName);
    rOle1.WriteChar(0);
    const sal_uInt8* pBytes = nullptr;
    sal_uInt64 nBytes = 0;
    if (ParseOLE2Presentation(rOle2, nWidth, nHeight, aPresentationData))
    {
        // Take presentation data for OLE1 from OLE2.
        pBytes = static_cast<const sal_uInt8*>(aPresentationData.GetData());
        nBytes = aPresentationData.Tell();
    }
    else
    {
        // Take presentation data for OLE1 from RTF.
        pBytes = pPresentationData;
        nBytes = nPresentationData;
    }
    // Width.
    rOle1.WriteUInt32(nWidth);
    // Height.
    rOle1.WriteUInt32(nHeight * -1);
    // PresentationDataSize: size of (reserved fields + pBytes).
    rOle1.WriteUInt32(8 + nBytes);
    // Reserved1-4.
    rOle1.WriteUInt16(0x0008);
    rOle1.WriteUInt16(0x31b1);
    rOle1.WriteUInt16(0x1dd9);
    rOle1.WriteUInt16(0x0000);
    rOle1.WriteBytes(pBytes, nBytes);
 
    return aClassName;
}
 
/// Writes presentation data with the specified size to rRtf as an RTF hexdump.
void WrapOleGraphicInRtf(SvStream& rRtf, sal_uInt32 nWidth, sal_uInt32 nHeight,
                         const sal_uInt8* pPresentationData, sal_uInt64 nPresentationData)
{
    // Start result.
    rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_RESULT);
 
    // Start pict.
    rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_PICT);
 
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_WMETAFILE "8");
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICW);
    rRtf.WriteOString(OString::number(nWidth));
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICH);
    rRtf.WriteOString(OString::number(nHeight));
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICWGOAL);
    rRtf.WriteOString(OString::number(nWidth));
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICHGOAL);
    rRtf.WriteOString(OString::number(nHeight));
    if (pPresentationData)
    {
        rRtf.WriteOString(SAL_NEWLINE_STRING);
        msfilter::rtfutil::WriteHex(pPresentationData, nPresentationData, &rRtf);
    }
 
    // End pict.
    rRtf.WriteOString("}");
 
    // End result.
    rRtf.WriteOString("}");
}
}
 
namespace SwReqIfReader
{
bool ExtractOleFromRtf(SvStream& rRtf, SvStream& rOle, bool& bOwnFormat)
{
    // Add missing header/footer.
    SvMemoryStream aRtf;
    aRtf.WriteOString("{\\rtf1");
    aRtf.WriteStream(rRtf);
    aRtf.WriteOString("}");
    aRtf.Seek(0);
 
    // Read the RTF markup.
    tools::SvRef<ReqIfRtfReader> xReader(new ReqIfRtfReader(aRtf));
    SvParserState eState = xReader->CallParser();
    if (eState == SvParserState::Error)
        return false;
 
    // Write the OLE2 data.
    if (!xReader->WriteObjectData(rOle))
        return false;
 
    rtl::Reference<SotStorage> pStorage = new SotStorage(rOle);
    OUString aFilterName = SvxMSDffManager::GetFilterNameFromClassID(pStorage->GetClassName());
    bOwnFormat = !aFilterName.isEmpty();
    if (!bOwnFormat)
    {
        // Real OLE2 data, we're done.
        rOle.Seek(0);
        return true;
    }
 
    // ODF-in-OLE2 case, extract actual data.
    SvMemoryStream aMemory;
    SvxMSDffManager::ExtractOwnStream(*pStorage, aMemory);
    rOle.Seek(0);
    aMemory.Seek(0);
    rOle.WriteStream(aMemory);
    // Stream length is current position + 1.
    rOle.SetStreamSize(aMemory.GetSize() + 1);
    rOle.Seek(0);
    return true;
}
 
bool WrapOleInRtf(SvStream& rOle2, SvStream& rRtf, SwOLENode& rOLENode,
                  const SwFrameFormat& rFormat)
{
    sal_uInt64 nPos = rOle2.Tell();
    comphelper::ScopeGuard g([&rOle2, nPos] { rOle2.Seek(nPos); });
 
    // Write OLE1 header, then the RTF wrapper.
    SvMemoryStream aOLE1;
 
    // Prepare presentation data early, so it's available to both OLE1 and RTF.
    Size aSize = rFormat.GetFrameSize().GetSize();
    sal_uInt32 nWidth = aSize.getWidth();
    sal_uInt32 nHeight = aSize.getHeight();
    const Graphic* pGraphic = rOLENode.GetGraphic();
    const sal_uInt8* pPresentationData = nullptr;
    sal_uInt64 nPresentationData = 0;
    SvMemoryStream aGraphicStream;
    if (pGraphic)
    {
        uno::Sequence<beans::PropertyValue> aFilterData
            = { comphelper::makePropertyValue(u"EmbedEMF"_ustr, false) };
        FilterConfigItem aConfigItem(&aFilterData);
        if (ConvertGraphicToWMF(*pGraphic, aGraphicStream, &aConfigItem))
        {
            pPresentationData = static_cast<const sal_uInt8*>(aGraphicStream.GetData());
            nPresentationData = aGraphicStream.TellEnd();
            msfilter::rtfutil::StripMetafileHeader(pPresentationData, nPresentationData);
        }
    }
    OString aClassName = InsertOLE1Header(rOle2, aOLE1, nWidth, nHeight, rOLENode,
                                          pPresentationData, nPresentationData);
 
    // Start object.
    rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_OBJECT);
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJEMB);
 
    // Start objclass.
    rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJCLASS " ");
    rRtf.WriteOString(aClassName);
    // End objclass.
    rRtf.WriteOString("}");
 
    // Object size.
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJW);
    rRtf.WriteOString(OString::number(nWidth));
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJH);
    rRtf.WriteOString(OString::number(nHeight));
 
    // Start objdata.
    rRtf.WriteOString(
        "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJDATA SAL_NEWLINE_STRING);
    msfilter::rtfutil::WriteHex(static_cast<const sal_uInt8*>(aOLE1.GetData()), aOLE1.GetSize(),
                                &rRtf);
    // End objdata.
    rRtf.WriteOString("}");
 
    if (pPresentationData)
    {
        WrapOleGraphicInRtf(rRtf, nWidth, nHeight, pPresentationData, nPresentationData);
    }
 
    // End object.
    rRtf.WriteOString("}");
 
    return true;
}
 
bool WrapGraphicInRtf(const Graphic& rGraphic, const SwFrameFormat& rFormat, SvStream& rRtf)
{
    // Start object.
    rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_OBJECT);
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJEMB);
 
    // Object size: as used in the document model (not pixel size)
    Size aSize = rFormat.GetFrameSize().GetSize();
    sal_uInt32 nWidth = aSize.getWidth();
    sal_uInt32 nHeight = aSize.getHeight();
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJW);
    rRtf.WriteOString(OString::number(nWidth));
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJH);
    rRtf.WriteOString(OString::number(nHeight));
    rRtf.WriteOString(SAL_NEWLINE_STRING);
 
    // Start objclass.
    rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJCLASS " ");
    OString aClassName("PBrush"_ostr);
    rRtf.WriteOString(aClassName);
    // End objclass.
    rRtf.WriteOString("}");
    rRtf.WriteOString(SAL_NEWLINE_STRING);
 
    // Start objdata.
    rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJDATA " ");
 
    SvMemoryStream aOle1;
    // Write ObjectHeader, see [MS-OLEDS] 2.2.4.
    // OLEVersion.
    aOle1.WriteUInt32(0x00000501);
 
    // FormatID is EmbeddedObject.
    aOle1.WriteUInt32(0x00000002);
 
    // ClassName
    aOle1.WriteUInt32(aClassName.getLength() + 1);
    aOle1.WriteOString(aClassName);
    // Null terminated pascal string.
    aOle1.WriteChar(0);
 
    // TopicName.
    aOle1.WriteUInt32(0);
 
    // ItemName.
    aOle1.WriteUInt32(0);
 
    // NativeDataSize
    SvMemoryStream aNativeData;
 
    // Set white background for the semi-transparent pixels.
    BitmapEx aBitmapEx = rGraphic.GetBitmapEx();
    Bitmap aBitmap = aBitmapEx.GetBitmap(/*aTransparentReplaceColor=*/COL_WHITE);
 
    if (aBitmap.getPixelFormat() != vcl::PixelFormat::N24_BPP)
    {
        // More exotic pixel formats cause trouble for ms paint.
        aBitmap.Convert(BmpConversion::N24Bit);
    }
 
    if (GraphicConverter::Export(aNativeData, BitmapEx(aBitmap), ConvertDataFormat::BMP)
        != ERRCODE_NONE)
    {
        SAL_WARN("sw.html", "WrapGraphicInRtf: bmp conversion failed");
    }
    aOle1.WriteUInt32(aNativeData.TellEnd());
 
    // Write the actual native data.
    aNativeData.Seek(0);
    aOle1.WriteStream(aNativeData);
 
    // Prepare presentation data.
    const sal_uInt8* pPresentationData = nullptr;
    sal_uInt64 nPresentationData = 0;
    SvMemoryStream aGraphicStream;
    uno::Sequence<beans::PropertyValue> aFilterData
        = { comphelper::makePropertyValue(u"EmbedEMF"_ustr, false) };
    FilterConfigItem aConfigItem(&aFilterData);
    if (ConvertGraphicToWMF(rGraphic, aGraphicStream, &aConfigItem))
    {
        pPresentationData = static_cast<const sal_uInt8*>(aGraphicStream.GetData());
        nPresentationData = aGraphicStream.TellEnd();
        msfilter::rtfutil::StripMetafileHeader(pPresentationData, nPresentationData);
    }
 
    // Write Presentation.
    // OLEVersion.
    aOle1.WriteUInt32(0x00000501);
    // FormatID: constant means the ClassName field is present.
    aOle1.WriteUInt32(0x00000005);
    // ClassName: null terminated pascal string.
    OString aPresentationClassName("METAFILEPICT"_ostr);
    aOle1.WriteUInt32(aPresentationClassName.getLength() + 1);
    aOle1.WriteOString(aPresentationClassName);
    aOle1.WriteChar(0);
    const sal_uInt8* pBytes = nullptr;
    sal_uInt64 nBytes = 0;
    // Take presentation data for OLE1 from RTF.
    pBytes = pPresentationData;
    nBytes = nPresentationData;
    // Width.
    aOle1.WriteUInt32(nWidth);
    // Height.
    aOle1.WriteUInt32(nHeight * -1);
    // PresentationDataSize: size of (reserved fields + pBytes).
    aOle1.WriteUInt32(8 + nBytes);
    // Reserved1-4.
    aOle1.WriteUInt16(0x0008);
    aOle1.WriteUInt16(0x31b1);
    aOle1.WriteUInt16(0x1dd9);
    aOle1.WriteUInt16(0x0000);
    aOle1.WriteBytes(pBytes, nBytes);
 
    // End objdata.
    msfilter::rtfutil::WriteHex(static_cast<const sal_uInt8*>(aOle1.GetData()), aOle1.GetSize(),
                                &rRtf);
    rRtf.WriteOString("}");
    rRtf.WriteOString(SAL_NEWLINE_STRING);
 
    rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_RESULT);
    rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_PICT);
 
    Size aMapped(rGraphic.GetPrefSize());
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICW);
    rRtf.WriteOString(OString::number(aMapped.Width()));
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICH);
    rRtf.WriteOString(OString::number(aMapped.Height()));
 
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICWGOAL);
    rRtf.WriteOString(OString::number(nWidth));
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICHGOAL);
    rRtf.WriteOString(OString::number(nHeight));
    rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_WMETAFILE "8");
    rRtf.WriteOString(SAL_NEWLINE_STRING);
 
    if (pPresentationData)
    {
        msfilter::rtfutil::WriteHex(pPresentationData, nPresentationData, &rRtf);
        rRtf.WriteOString(SAL_NEWLINE_STRING);
    }
 
    // End pict.
    rRtf.WriteOString("}");
 
    // End result.
    rRtf.WriteOString("}");
 
    // End object.
    rRtf.WriteOString("}");
    return true;
}
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

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

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

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

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