Files
core/MsBinaryFile/DocFile/PictureDescriptor.cpp
Alexander Trofimov 76ee07f61c [copyright] Update copyright header
Co-authored-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
Co-committed-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
2026-05-14 08:23:56 +00:00

375 lines
11 KiB
C++

/*
* Copyright (C) Ascensio System SIA, 2009-2026
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation, together with the
* additional terms provided in the LICENSE file.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: https://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA by email at info@onlyoffice.com
* or by postal mail at 20A-6 Ernesta Birznieka-Upisha Street, Riga,
* LV-1050, Latvia, European Union.
*
* The interactive user interfaces in modified versions of the Program
* are required to display Appropriate Legal Notices in accordance with
* Section 5 of the GNU AGPL version 3.
*
* No trademark rights are granted under this License.
*
* All non-code elements of the Product, including illustrations,
* icon sets, and technical writing content, are licensed under the
* Creative Commons Attribution-ShareAlike 4.0 International License:
* https://creativecommons.org/licenses/by-sa/4.0/legalcode
*
* This license applies only to such non-code elements and does not
* modify or replace the licensing terms applicable to the Program's
* source code, which remains licensed under the GNU Affero General
* Public License v3.
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
#include "PictureDescriptor.h"
#include "OfficeDrawing/MetafilePictBlip.h"
#include "../../DesktopEditor/raster/BgraFrame.h"
#ifndef MM_ISOTROPIC
#define MM_ISOTROPIC 7
#endif
#ifndef MM_ANISOTROPIC
#define MM_ANISOTROPIC 8
#endif
namespace ImageHelper
{
Global::_BlipType SaveImageToFileFromDIB(unsigned char* data, int size, std::wstring& file_name)//without ext
{
Global::_BlipType result = Global::msoblipERROR;
CBgraFrame oFrame;
int offset = 0, biSizeImage = 0;
__BITMAPINFOHEADER * header = (__BITMAPINFOHEADER*)data;
if (!header) return result;
result = Global::msoblipDIB;
if (header->biWidth > 100000 || header->biHeight > 100000 || header->biSize != 40)
{
__BITMAPCOREHEADER * header_core = (__BITMAPCOREHEADER *)data;
if (header_core->bcSize != 12)
{
result = Global::msoblipWMF;
}
else
{
offset = 12; //sizeof(BITMAPCOREHEADER)
oFrame.put_Height (header_core->bcHeight );
oFrame.put_Width (header_core->bcWidth );
int sz_bitmap = header_core->bcHeight * header_core->bcWidth * header_core->bcBitCount/ 8;
//if (header_core->bcWidth % 2 != 0 && sz_bitmap < size - offset)
// header_core->bcWidth++;
///???? todooo unclear .. in biff5 need to flip image, in biff8 unclear ( -
int stride = -(size - offset) / header_core->bcHeight;
oFrame.put_Stride (stride/*header_core->bcBitCount * header_core->bcWidth /8 */);
biSizeImage = size - offset;
if (-stride >= header_core->bcWidth && header_core->bcBitCount >=24 )
{
result = Global::msoblipPNG;
}
}
}
else
{
offset = 40; //sizeof(BITMAPINFOHEADER)
oFrame.put_Height (header->biHeight );
oFrame.put_Width (header->biWidth );
int sz_bitmap = header->biHeight * header->biWidth * header->biBitCount/ 8;
//if (header->biWidth % 2 != 0 && sz_bitmap < size -offset)
// header->biWidth++;
if (header->biClrUsed > 0)
{
oFrame.put_Palette((unsigned char*)data + offset, header->biClrUsed);
offset += header->biClrUsed * 4;
}
int stride = -(size - offset) / header->biHeight;
if (-stride >= header->biWidth/* && header->biBitCount >= 24*/)
{
result = Global::msoblipPNG;
}
oFrame.put_Stride(stride);
biSizeImage = header->biSizeImage > 0 ? header->biSizeImage : (size - offset);
}
//------------------------------------------------------------------------------------------
if (result == Global::msoblipPNG)
{
oFrame.put_Data((unsigned char*)data + offset);
file_name += L".png";
if (!oFrame.SaveFile(file_name, 4/*CXIMAGE_FORMAT_PNG*/))
{
result = Global::msoblipDIB;
}
oFrame.put_Data(NULL);
}
else if (result == Global::msoblipWMF)
{
file_name += L".wmf";
NSFile::CFileBinary file;
if (file.CreateFileW(file_name))
{
file.WriteFile((BYTE*)data, size);
file.CloseFile();
}
}
if (biSizeImage > 0 && result == Global::msoblipDIB)
{
file_name += L".bmp";
NSFile::CFileBinary file;
if (file.CreateFileW(file_name))
{
_UINT16 vtType = 0x4D42; file.WriteFile((BYTE*)&vtType, 2);
_UINT32 dwLen = biSizeImage; file.WriteFile((BYTE*)&dwLen, 4);
_UINT32 dwRes = 0; file.WriteFile((BYTE*)&dwRes, 4);
_UINT32 dwOffset = 2; file.WriteFile((BYTE*)&dwOffset, 4);
file.WriteFile((BYTE*)data, size);
file.CloseFile();
}
}
return result;
}
}
namespace DocFileFormat
{
/// Parses the CHPX for a fcPic an loads the PictureDescriptor at this offset
PictureDescriptor::PictureDescriptor(CharacterPropertyExceptions* chpx, POLE::Stream* stream, int size, int nWordVersion)
:
dxaGoal(0), dyaGoal(0), mx(0), my(0), Type(jpg), mfp(), dxaCropLeft(0), dyaCropTop(0),
dxaCropRight(0), dyaCropBottom(0), brcTop(NULL), brcLeft(NULL), brcBottom(NULL), brcRight(NULL), dxaOrigin(0), dyaOrigin(0),
cProps(0), shapeContainer(NULL), blipStoreEntry(NULL), embeddedData(NULL), embeddedDataSize(0), embeddedDataHeader(NULL)
{
//Get start and length of the PICT
int fc = GetFcPic( chpx );
if ( fc >= 0 )
{
parse( stream, fc, size, nWordVersion);
}
}
PictureDescriptor::PictureDescriptor()
:
dxaGoal(0), dyaGoal(0), mx(0), my(0), Type(jpg), mfp(), dxaCropLeft(0), dyaCropTop(0),
dxaCropRight(0), dyaCropBottom(0), brcTop(NULL), brcLeft(NULL), brcBottom(NULL), brcRight(NULL), dxaOrigin(0), dyaOrigin(0),
cProps(0), shapeContainer(NULL), blipStoreEntry(NULL), embeddedData(NULL), embeddedDataSize(0), embeddedDataHeader(NULL)
{
}
PictureDescriptor::~PictureDescriptor()
{
Clear();
}
void PictureDescriptor::Clear()
{
RELEASEOBJECT(brcTop);
RELEASEOBJECT(brcLeft);
RELEASEOBJECT(brcBottom);
RELEASEOBJECT(brcRight);
RELEASEOBJECT(shapeContainer);
RELEASEOBJECT(blipStoreEntry);
RELEASEARRAYOBJECTS(embeddedData);
RELEASEARRAYOBJECTS(embeddedDataHeader);
}
void PictureDescriptor::parse(POLE::Stream* stream, int fc, int sz, int nWordVersion)
{
Clear();
VirtualStreamReader reader(stream, fc, nWordVersion);
int sz_stream = reader.GetSize();
int lcb = reader.ReadInt32();
int pos_start = reader.GetPosition();
if (lcb > 10000000)
return;
if (lcb > sz && sz != 1 && sz != 2) //bullet picture with incorrect size
{
unsigned char* bytes = reader.ReadBytes(sz - fc - 4, false);
if ( bytes )
{
RELEASEARRAYOBJECTS( bytes );
}
return;
}
if (lcb < 10)
return;
int cbHeader = reader.ReadUInt16();
mfp.mm = reader.ReadInt16();
mfp.xExt = reader.ReadInt16();
mfp.yExt = reader.ReadInt16();
mfp.hMf = reader.ReadInt16();
unsigned char* bytes = reader.ReadBytes(14, true);
rcWinMf = std::vector<unsigned char>(bytes, (bytes + 14));
RELEASEARRAYOBJECTS(bytes);
//dimensions
dxaGoal = reader.ReadInt16();
dyaGoal = reader.ReadInt16();
mx = reader.ReadUInt16();
my = reader.ReadUInt16();
//cropping
dxaCropLeft = reader.ReadInt16();
dyaCropTop = reader.ReadInt16();
dxaCropRight = reader.ReadInt16();
dyaCropBottom = reader.ReadInt16();
int brcl = reader.ReadInt16();
// borders
int bytesCount = (nWordVersion > 0) ? 2 : 4;
bytes = reader.ReadBytes( bytesCount, true );
brcTop = new BorderCode( bytes, bytesCount );
RELEASEARRAYOBJECTS( bytes );
bytes = reader.ReadBytes( bytesCount, true );
brcLeft = new BorderCode( bytes, bytesCount );
RELEASEARRAYOBJECTS( bytes );
bytes = reader.ReadBytes( bytesCount, true );
brcBottom = new BorderCode( bytes, bytesCount );
RELEASEARRAYOBJECTS( bytes );
bytes = reader.ReadBytes( bytesCount, true );
brcRight = new BorderCode( bytes, bytesCount );
RELEASEARRAYOBJECTS( bytes );
dxaOrigin = reader.ReadInt16();
dyaOrigin = reader.ReadInt16();
int pos_end = reader.GetPosition();
if (nWordVersion > 0)
{
int flag = brcl;
brcl = FormatUtils::BitmaskToBool(flag, 0x000F);
//( 0 single 1 thick 2 double 3 shadow )
bool fFrameEmpty = FormatUtils::BitmaskToBool(flag, 0x0010);// picture consists of a single frame
bool fBitmap = FormatUtils::BitmaskToBool(flag, 0x0020);// ==1, when picture is just a bitmap
bool fDrawHatch = FormatUtils::BitmaskToBool(flag, 0x0040);// ==1, when picture is an active OLE object
bool fError = FormatUtils::BitmaskToBool(flag, 0x0080);// ==1, when picture is just an error message
short bpp = FormatUtils::BitmaskToBool(flag, 0x8000);// bits per pixel
//(0 unknown 1 monochrome 4 VGA)
int sz_hdr = pos_end - pos_start;
int header_size = 114;
embeddedDataSize = lcb - sz_hdr - header_size;
embeddedDataHeader = reader.ReadBytes( header_size, true);
embeddedData = reader.ReadBytes( embeddedDataSize, true );
}
else
{
cProps = reader.ReadInt16();
if (mfp.mm == MM_SHAPEFILE)
{
unsigned char cchPicName = reader.ReadByte();
unsigned char* stPicName = reader.ReadBytes(cchPicName, true);
if ( stPicName != NULL )
{
std::wstring picName;
FormatUtils::GetWStringFromBytes( picName, stPicName, cchPicName, ENCODING_WINDOWS_1250 );
RELEASEARRAYOBJECTS(stPicName);
}
}
shapeContainer = dynamic_cast<ShapeContainer*>(RecordFactory::ReadRecord(&reader, 0));
long pos = reader.GetPosition();
if( pos < ( fc + lcb ))
{
Record* rec = RecordFactory::ReadRecord( &reader, 0 );
if ((rec) && ( typeid(*rec) == typeid(BlipStoreEntry) ))
{
blipStoreEntry = dynamic_cast<BlipStoreEntry*>( rec );
}
else
{
RELEASEOBJECT(rec);
}
}
}
}
/// Returns the fcPic into the "data" stream, where the PIC begins.
/// Returns -1 if the CHPX has no fcPic.
int PictureDescriptor::GetFcPic(const CharacterPropertyExceptions* chpx)
{
int ret = -1, ret1 = -1;
for ( std::vector<SinglePropertyModifier>::const_iterator iter = chpx->grpprl->begin(); iter != chpx->grpprl->end(); iter++ )
{
switch ( iter->OpCode )
{
case sprmOldCPicLocation:
case sprmCPicLocation:
ret = FormatUtils::BytesToInt32( iter->Arguments, 0, iter->argumentsSize );
break;
case sprmOldCHps:
case sprmCHps:
ret1 = FormatUtils::BytesToInt32( iter->Arguments, 0, iter->argumentsSize );
break;
case sprmCHsp:
ret = FormatUtils::BytesToInt32( iter->Arguments, 0, iter->argumentsSize );
break;
case sprmOldCFData:
case sprmCFData:
break;
default:
break;
}
}
return ret;
}
}