Commit 2ca535d9 authored by ElenaSubbotina's avatar ElenaSubbotina

DocFormat - fix ole object (old format)

parent 10aac610
......@@ -39,20 +39,40 @@ namespace DocFileFormat
AnnotationReferenceDescriptor *newObject = new AnnotationReferenceDescriptor();
//read the user initials (LPXCharBuffer9)
short cch = reader->ReadInt16();
unsigned char *chars = reader->ReadBytes(18, true);
if (reader->olderVersion)
{
short cch = reader->ReadByte();
unsigned char *chars = reader->ReadBytes(cch, true);
FormatUtils::GetSTLCollectionFromBytes<std::wstring>( &(newObject->m_UserInitials), chars, cch , ENCODING_WINDOWS_1250);
newObject->m_AuthorIndex = reader->ReadUInt16();
newObject->m_BookmarkId = reader->ReadInt16();
RELEASEARRAYOBJECTS(chars);
chars = reader->ReadBytes(length - cch - 1 - 4, true);
FormatUtils::GetSTLCollectionFromBytes<std::wstring>( &(newObject->m_UserInitials), chars, ( cch * 2 ), ENCODING_UTF16);
RELEASEARRAYOBJECTS(chars);
}
else
{
short cch = reader->ReadInt16();
newObject->m_AuthorIndex = reader->ReadUInt16();
unsigned char *chars = reader->ReadBytes(18, true);
//skip 4 bytes
reader->ReadBytes(4, false);
FormatUtils::GetSTLCollectionFromBytes<std::wstring>( &(newObject->m_UserInitials), chars, ( cch * 2 ), ENCODING_UTF16);
newObject->m_AuthorIndex = reader->ReadUInt16();
newObject->m_BookmarkId = reader->ReadInt32();
//skip 4 bytes
reader->ReadBytes(4, false);
newObject->m_BookmarkId = reader->ReadInt32();
RELEASEARRAYOBJECTS(chars);
}
RELEASEARRAYOBJECTS(chars);
return static_cast<ByteStructure*>(newObject);
}
......
......@@ -39,6 +39,12 @@ namespace DocFileFormat
{
public:
static const int STRUCTURE_SIZE = 30;
static const int STRUCTURE_SIZE_OLD = 20;
static const int GetSize(bool bOldVersion)
{
return bOldVersion ? STRUCTURE_SIZE_OLD : STRUCTURE_SIZE;
}
inline std::wstring GetUserInitials() const
{
......
......@@ -61,6 +61,8 @@ namespace DocFileFormat
m_context = context;
m_bInternalXmlWriter = false;
_writeWebHidden = false;
_writeInstrText = false;
_isSectionPageBreak = 0;
}
......@@ -642,52 +644,67 @@ namespace DocFileFormat
int fcPic = m_document->FindFileCharPos( cpPic );
std::list<CharacterPropertyExceptions*>* chpxs = m_document->GetCharacterPropertyExceptions(fcPic, fcPic + 1);
CharacterPropertyExceptions* chpxPic = chpxs->front();
CharacterPropertyExceptions* chpxObj = chpxs->front();
PictureDescriptor pic(chpxPic, m_document->DataStream, 0x7fffffff, m_document->bOlderVersion);
RevisionData oData = RevisionData(chpxPic);
RevisionData oData = RevisionData(chpxObj);
CharacterPropertiesMapping* rPr = new CharacterPropertiesMapping(m_pXmlWriter, m_document, &oData, _lastValidPapx, false);
if(rPr)
{
chpxPic->Convert(rPr);
chpxObj->Convert(rPr);
RELEASEOBJECT(rPr);
}
XmlUtils::CXmlWriter OleWriter;
OleWriter.WriteNodeBegin (_T( "w:object" ), TRUE);
}
XmlUtils::CXmlWriter OleWriter;
VMLPictureMapping oVmlMapper (m_context, &OleWriter, true, _caller);
//append the origin attributes
OleWriter.WriteAttribute( _T( "w:dxaOrig" ), FormatUtils::IntToWideString( ( pic.dxaGoal + pic.dxaOrigin ) ).c_str() );
OleWriter.WriteAttribute( _T( "w:dyaOrig" ), FormatUtils::IntToWideString( ( pic.dyaGoal + pic.dyaOrigin ) ).c_str() );
OleWriter.WriteNodeEnd( _T( "" ), TRUE, FALSE );
VMLPictureMapping oVmlMapper (m_context, &OleWriter, true, _caller);
pic.Convert(&oVmlMapper);
RELEASEOBJECT(chpxs);
if ( cpFieldSep < cpFieldEnd )
if (m_document->bOlderVersion)
{
int fcFieldSep = m_document->m_PieceTable->FileCharacterPositions->operator []( cpFieldSep );
int fcFieldSep1 = m_document->FindFileCharPos( cpFieldSep );
OleObject ole ( chpxObj, m_document->GetStorage(), m_document->bOlderVersion);
std::list<CharacterPropertyExceptions*>* chpxs = m_document->GetCharacterPropertyExceptions( fcFieldSep, ( fcFieldSep + 1 ) );
CharacterPropertyExceptions* chpxSep = chpxs->front();
OleWriter.WriteNodeBegin (_T( "w:object" ), TRUE);
OleWriter.WriteAttribute( _T( "w:dxaOrig" ), FormatUtils::IntToWideString( ( ole.pictureDesciptor.dxaGoal + ole.pictureDesciptor.dxaOrigin ) ).c_str() );
OleWriter.WriteAttribute( _T( "w:dyaOrig" ), FormatUtils::IntToWideString( ( ole.pictureDesciptor.dyaGoal + ole.pictureDesciptor.dyaOrigin ) ).c_str() );
OleWriter.WriteNodeEnd( _T( "" ), TRUE, FALSE );
ole.pictureDesciptor.Convert(&oVmlMapper);
OleObjectMapping oleObjectMapping( &OleWriter, m_context, &ole.pictureDesciptor, _caller, oVmlMapper.GetShapeId() );
OleObject ole ( chpxSep, m_document->GetStorage() );
OleObjectMapping oleObjectMapping( &OleWriter, m_context, &pic, _caller, oVmlMapper.GetShapeId() );
ole.Convert( &oleObjectMapping );
}
else
{
PictureDescriptor pic(chpxObj, m_document->DataStream, 0x7fffffff, m_document->bOlderVersion);
OleWriter.WriteNodeBegin (_T( "w:object" ), TRUE);
OleWriter.WriteAttribute( _T( "w:dxaOrig" ), FormatUtils::IntToWideString( ( pic.dxaGoal + pic.dxaOrigin ) ).c_str() );
OleWriter.WriteAttribute( _T( "w:dyaOrig" ), FormatUtils::IntToWideString( ( pic.dyaGoal + pic.dyaOrigin ) ).c_str() );
OleWriter.WriteNodeEnd( _T( "" ), TRUE, FALSE );
if (oVmlMapper.m_isEmbedded)
pic.Convert(&oVmlMapper);
RELEASEOBJECT(chpxs);
if ( cpFieldSep < cpFieldEnd && m_document->m_PieceTable)
{
ole.isEquation = oVmlMapper.m_isEquation;
ole.isEmbedded = oVmlMapper.m_isEmbedded;
ole.emeddedData = oVmlMapper.m_embeddedData;
int fcFieldSep = m_document->m_PieceTable->FileCharacterPositions->operator []( cpFieldSep );
int fcFieldSep1 = m_document->FindFileCharPos( cpFieldSep );
std::list<CharacterPropertyExceptions*>* chpxs = m_document->GetCharacterPropertyExceptions( fcFieldSep, ( fcFieldSep + 1 ) );
CharacterPropertyExceptions* chpxSep = chpxs->front();
OleObject ole ( chpxSep, m_document->GetStorage(), m_document->bOlderVersion);
OleObjectMapping oleObjectMapping( &OleWriter, m_context, &pic, _caller, oVmlMapper.GetShapeId() );
if (oVmlMapper.m_isEmbedded)
{
ole.isEquation = oVmlMapper.m_isEquation;
ole.isEmbedded = oVmlMapper.m_isEmbedded;
ole.emeddedData = oVmlMapper.m_embeddedData;
}
ole.Convert( &oleObjectMapping );
RELEASEOBJECT( chpxs );
}
ole.Convert( &oleObjectMapping );
RELEASEOBJECT( chpxs );
}
OleWriter.WriteNodeEnd( _T( "w:object" ) );
if (!oVmlMapper.m_isEmbedded && oVmlMapper.m_isEquation)
......@@ -699,7 +716,7 @@ namespace DocFileFormat
else
{
m_pXmlWriter->WriteString(OleWriter.GetXmlString());
}
}
}
if (bEMBED) _skipRuns = 3;
......@@ -758,15 +775,15 @@ namespace DocFileFormat
}
else if ((TextMark::DrawnObject == c) && fSpec)
{
Spa* pSpa = NULL;
Spa* pSpa = NULL;
if (typeid(*this) == typeid(MainDocumentMapping))
{
pSpa = static_cast<Spa*>(m_document->OfficeDrawingPlex->GetStruct(cp));
pSpa = static_cast<Spa*>(m_document->OfficeDrawingPlex->GetStruct(cp));
}
else if ((typeid(*this) == typeid(HeaderMapping) ) || ( typeid(*this) == typeid(FooterMapping)))
{
int headerCp = ( cp - m_document->FIB->m_RgLw97.ccpText - m_document->FIB->m_RgLw97.ccpFtn );
pSpa = static_cast<Spa*>(m_document->OfficeDrawingPlexHeader->GetStruct(headerCp));
pSpa = static_cast<Spa*>(m_document->OfficeDrawingPlexHeader->GetStruct(headerCp));
}
if (pSpa)
......@@ -815,7 +832,7 @@ namespace DocFileFormat
if (oVmlMapper.m_isEmbedded)
{
OleObject ole ( chpx, m_document->GetStorage() );
OleObject ole ( chpx, m_document->GetStorage(), m_document->bOlderVersion);
OleObjectMapping oleObjectMapping( m_pXmlWriter, m_context, &oPicture, _caller, oVmlMapper.GetShapeId() );
ole.isEquation = oVmlMapper.m_isEquation;
......@@ -1555,8 +1572,8 @@ namespace DocFileFormat
{
if (DocFileFormat::sprmCSymbol == iter->OpCode)
{
//special symbol
short fontIndex = FormatUtils::BytesToInt16( iter->Arguments, 0, iter->argumentsSize );
short code = FormatUtils::BytesToInt16( iter->Arguments, 2, iter->argumentsSize );
FontFamilyName* ffn = static_cast<FontFamilyName*>( m_document->FontTable->operator [] ( fontIndex ) );
......@@ -1566,6 +1583,19 @@ namespace DocFileFormat
break;
}
else if (DocFileFormat::sprmOldCSymbol == iter->OpCode)
{
short fontIndex = FormatUtils::BytesToInt16( iter->Arguments, 0, iter->argumentsSize ) ;
short code = FormatUtils::BytesToUChar( iter->Arguments, 2, iter->argumentsSize );
FontFamilyName* ffn = static_cast<FontFamilyName*>( m_document->FontTable->operator [] ( fontIndex ) );
ret.FontName = ffn->xszFtn;
ret.HexValue = L"f0" + FormatUtils::IntToFormattedWideString( code, _T( "%02x" ) );
break;
}
}
return ret;
......
......@@ -65,7 +65,7 @@ namespace DocFileFormat
//this additional paragraph mark shall not be converted.
cpMax--;
while ( cp < cpMax )
while ( cp < cpMax && cp < (int)m_document->Text->size())
{
int fc = m_document->FindFileCharPos(cp);
if (fc < 0) break;
......
......@@ -179,7 +179,7 @@ namespace DocFileFormat
//there are n offsets and n-1 fkp's in the bin table
if (fib->m_FibBase.fComplex == false)
if (fib->m_bOlderVersion && fib->m_FibBase.fComplex == false)
{
int n = ( ( (int)fib->m_FibWord97.lcbPlcfBtePapx - 8 ) / 6 ) + 1;
......
......@@ -42,7 +42,7 @@ namespace DocFileFormat
if (fib->m_FibWord97.fcPlcfHdd > tableReader.GetSize()) return;
unsigned int tableSize = fib->m_FibWord97.lcbPlcfHdd / (fib->m_bOlderVersion ? 1: 4);
unsigned int tableSize = fib->m_FibWord97.lcbPlcfHdd / 4;//in bytes
if ( ( tableSize > 0 ) && ( fib->m_RgLw97.ccpHdr > 0 ) )
{
......@@ -53,14 +53,15 @@ namespace DocFileFormat
table[i] = tableReader.ReadInt32();
}
int count = ( tableSize - 8 ) / 6;
int initialPos = fib->m_RgLw97.ccpText + fib->m_RgLw97.ccpFtn;
//the first 6 _entries are about footnote and endnote formatting
//so skip these _entries
int pos = 6;
int pos = (fib->m_FibBase.fComplex || !fib->m_bOlderVersion) ? 6 : 0;
int count = ( tableSize - pos - 2) / 6;
for (int i = 0; i < count; ++i)
{
//Even Header
......@@ -123,7 +124,7 @@ namespace DocFileFormat
pos++;
if (pos > tableSize)
if (pos >= tableSize)
break;
//First Page Footers
......
......@@ -65,7 +65,7 @@ namespace DocFileFormat
//this additional paragraph mark shall not be converted.
cpMax--;
while ( cp < cpMax )
while ( cp < cpMax && cp < (int)m_document->Text->size())
{
int fc = m_document->FindFileCharPos(cp);
if (fc < 0) break;
......
......@@ -300,11 +300,11 @@ typedef enum _BlipCompression
Record( _reader, size, typeCode, version, instance ), m_rgbUid(NULL), m_rgbUidPrimary(NULL), m_cb(0), m_cbSave(0),
m_fCompression(BlipCompressionNone), m_fFilter(false), m_pvBits(NULL)
{
this->m_rgbUid = this->Reader->ReadBytes( 16, true );
m_rgbUid = Reader->ReadBytes( 16, true );
if ( ( instance == 0x3D5 ) || ( instance == 0x217 ) || ( instance == 0x543 ) )
{
this->m_rgbUidPrimary = this->Reader->ReadBytes( 16, true );
m_rgbUidPrimary = Reader->ReadBytes( 16, true );
}
oMetaFile.m_bIsValid = TRUE;
......@@ -312,22 +312,22 @@ typedef enum _BlipCompression
CMetaHeader oMetaHeader;
this->m_cb = this->Reader->ReadInt32();
m_cb = Reader->ReadInt32();
this->m_rcBounds.left = this->Reader->ReadInt32();
this->m_rcBounds.top = this->Reader->ReadInt32();
this->m_rcBounds.right = this->Reader->ReadInt32() + this->m_rcBounds.left;
this->m_rcBounds.bottom = this->Reader->ReadInt32() + this->m_rcBounds.top;
m_rcBounds.left = Reader->ReadInt32();
m_rcBounds.top = Reader->ReadInt32();
m_rcBounds.right = Reader->ReadInt32() + m_rcBounds.left;
m_rcBounds.bottom = Reader->ReadInt32() + m_rcBounds.top;
this->m_ptSize.x = this->Reader->ReadInt32();
this->m_ptSize.y = this->Reader->ReadInt32();
m_ptSize.x = Reader->ReadInt32();
m_ptSize.y = Reader->ReadInt32();
this->m_cbSave = this->Reader->ReadInt32();
this->m_fCompression = (BlipCompression)this->Reader->ReadByte();
this->m_fFilter = ( this->Reader->ReadByte() == 1 ) ? (true) : (false);
m_cbSave = Reader->ReadInt32();
m_fCompression = (BlipCompression)Reader->ReadByte();
m_fFilter = ( Reader->ReadByte() == 1 ) ? (true) : (false);
int sz = Reader->GetSize() - Reader->GetPosition();
this->m_pvBits = this->Reader->ReadBytes( sz/*this->m_cbSave*/, true );
m_pvBits = Reader->ReadBytes( sz/*m_cbSave*/, true );
oMetaHeader.rcBounds = m_rcBounds;
oMetaHeader.cbSize = m_cb;
......@@ -369,9 +369,9 @@ typedef enum _BlipCompression
virtual ~MetafilePictBlip()
{
RELEASEARRAYOBJECTS( this->m_rgbUid );
RELEASEARRAYOBJECTS( this->m_rgbUidPrimary );
RELEASEARRAYOBJECTS( this->m_pvBits );
RELEASEARRAYOBJECTS( m_rgbUid );
RELEASEARRAYOBJECTS( m_rgbUidPrimary );
RELEASEARRAYOBJECTS( m_pvBits );
}
virtual Record* NewObject( IBinaryReader* _reader, unsigned int bodySize, unsigned int typeCode, unsigned int version, unsigned int instance )
......@@ -386,9 +386,9 @@ typedef enum _BlipCompression
unsigned long uncomprLen = 0;
if ( this->m_fCompression == BlipCompressionDeflate )
if ( m_fCompression == BlipCompressionDeflate )
{
uncomprLen = this->m_cb;
uncomprLen = m_cb;
*buffer = new unsigned char[uncomprLen];
HRESULT res = S_OK;
......@@ -396,19 +396,19 @@ typedef enum _BlipCompression
if (pOfficeUtils)
{
pOfficeUtils->Uncompress( *buffer, &uncomprLen, this->m_pvBits, this->m_cbSave );
pOfficeUtils->Uncompress( *buffer, &uncomprLen, m_pvBits, m_cbSave );
delete pOfficeUtils;
pOfficeUtils = NULL;
}
}
else if ( this->m_fCompression == BlipCompressionNone )
else if ( m_fCompression == BlipCompressionNone )
{
uncomprLen = this->m_cbSave;
uncomprLen = m_cbSave;
*buffer = new unsigned char[uncomprLen];
memcpy( *buffer, this->m_pvBits , this->m_cbSave );
memcpy( *buffer, m_pvBits , m_cbSave );
}
return uncomprLen;
......
......@@ -68,7 +68,7 @@ namespace DocFileFormat
m_pXmlWriter->WriteNodeBegin( _T( "o:OLEObject" ), TRUE );
//type
if ( ole->fLinked )
if ( ole->bLinked )
{
int relID = -1;
......
......@@ -31,6 +31,7 @@
*/
#include "PictureDescriptor.h"
#include "OfficeDrawing/MetafilePictBlip.h"
#ifndef MM_ISOTROPIC
#define MM_ISOTROPIC 7
......@@ -52,13 +53,18 @@ namespace DocFileFormat
//Get start and length of the PICT
int fc = GetFcPic( chpx );
if ( fc >= 0 )
{
parse( stream, fc, size, oldVersion);
}
}
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)
{
}
PictureDescriptor::~PictureDescriptor()
{
Clear();
......@@ -88,7 +94,7 @@ namespace DocFileFormat
if (lcb > 10000000)
return;
if (lcb > sz && sz != 2) //bullet picture с неверным размером
if (lcb > sz && sz != 1 && sz != 2) //bullet picture с неверным размером
{
unsigned char* bytes = reader.ReadBytes(sz - fc - 4, false);
if ( bytes )
......@@ -115,8 +121,14 @@ namespace DocFileFormat
dxaGoal = mfp.xExt;
dyaGoal = mfp.yExt;
embeddedDataSize = reader.GetSize() - reader.GetPosition(); //lcb ?
embeddedDataSize = lcb - 20;//reader.GetSize() - reader.GetPosition(); //lcb ?
embeddedData = reader.ReadBytes( embeddedDataSize, true );
WmfPlaceableFileHeader *header = (WmfPlaceableFileHeader *)embeddedData;
if (header)
{
}
}
else if (mfp.mm >= 98)
{
......@@ -199,7 +211,7 @@ namespace DocFileFormat
/// Returns -1 if the CHPX has no fcPic.
int PictureDescriptor::GetFcPic(const CharacterPropertyExceptions* chpx)
{
int ret = -1;
int ret = -1, ret1 = -1;
for ( std::list<SinglePropertyModifier>::const_iterator iter = chpx->grpprl->begin(); iter != chpx->grpprl->end(); iter++ )
{
......@@ -210,6 +222,11 @@ namespace DocFileFormat
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;
......
......@@ -78,14 +78,17 @@ namespace DocFileFormat
friend class VMLPictureMapping;
friend class VMLShapeMapping;
friend class NumberingMapping;
friend class OleObject;
public:
// Parses the CHPX for a fcPic an loads the PictureDescriptor at this offset
PictureDescriptor( CharacterPropertyExceptions* chpx, POLE::Stream* stream, int size, bool oldVersion);
PictureDescriptor ( );
PictureDescriptor ( CharacterPropertyExceptions* chpx, POLE::Stream* stream, int size, bool oldVersion);
virtual ~PictureDescriptor();
void parse( POLE::Stream* stream, int fc, int sz, bool oldVersion);
private:
void parse( POLE::Stream* stream, int fc, int sz, bool oldVersion);
// Returns the fcPic into the "data" stream, where the PIC begins.
// Returns -1 if the CHPX has no fcPic.
......@@ -127,6 +130,6 @@ namespace DocFileFormat
BlipStoreEntry * blipStoreEntry;
unsigned char *embeddedData;
short embeddedDataSize;
int embeddedDataSize;
};
}
......@@ -44,9 +44,9 @@ namespace DocFileFormat
protected:
static const int CP_LENGTH = 4;
std::vector<int> CharacterPositions;
std::vector<int> CharacterPositions;
std::vector<ByteStructure*> Elements;
bool m_bIsValid;
bool m_bIsValid;
public:
Plex(int structureLength, POLE::Stream* stream, unsigned int fc, unsigned int lcb, bool oldVersion)
......
......@@ -66,7 +66,6 @@ namespace DocFileFormat
while ( goOn )
{
//enough bytes to read?
if ( ( sprmStart + opCodeSize ) < size )
{
OperationCode opCode = oldVersion ? (OperationCode)FormatUtils::BytesToUChar ( bytes, sprmStart, size ) :
......
......@@ -105,17 +105,28 @@ namespace DocFileFormat
if (pTable)
{
unsigned char fHF = 255; //all headers & footers
for (std::list<SinglePropertyModifier>::iterator iter = sepx->grpprl->begin(); iter != sepx->grpprl->end(); ++iter)
{
switch (iter->OpCode)
{
case sprmOldSGprfIhdt:
case sprmSGprfIhdt:
fHF = FormatUtils::BytesToUChar( iter->Arguments, 0, iter->argumentsSize );
break;
}
}
// Header
WriteSectionStory (pTable->GetEvenHeaders (m_nSelectProperties), std::wstring(L"headerReference"), std::wstring(L"even"));
WriteSectionStory (pTable->GetOddHeaders (m_nSelectProperties), std::wstring(L"headerReference"), std::wstring(L"default"));
WriteSectionStory (pTable->GetFirstHeaders (m_nSelectProperties), std::wstring(L"headerReference"), std::wstring(L"first"));
if (FormatUtils::GetBitFromInt(fHF, 0)) WriteSectionStory (pTable->GetEvenHeaders (m_nSelectProperties), std::wstring(L"headerReference"), std::wstring(L"even"));
if (FormatUtils::GetBitFromInt(fHF, 1)) WriteSectionStory (pTable->GetOddHeaders (m_nSelectProperties), std::wstring(L"headerReference"), std::wstring(L"default"));
if (FormatUtils::GetBitFromInt(fHF, 4)) WriteSectionStory (pTable->GetFirstHeaders (m_nSelectProperties), std::wstring(L"headerReference"), std::wstring(L"first"));
// Footer
WriteSectionStory (pTable->GetEvenFooters (m_nSelectProperties), std::wstring(L"footerReference"), std::wstring(L"even"));
WriteSectionStory (pTable->GetOddFooters (m_nSelectProperties), std::wstring(L"footerReference"), std::wstring(L"default"));
WriteSectionStory (pTable->GetFirstFooters (m_nSelectProperties), std::wstring(L"footerReference"), std::wstring(L"first"));
if (FormatUtils::GetBitFromInt(fHF, 2)) WriteSectionStory (pTable->GetEvenFooters (m_nSelectProperties), std::wstring(L"footerReference"), std::wstring(L"even"));
if (FormatUtils::GetBitFromInt(fHF, 3)) WriteSectionStory (pTable->GetOddFooters (m_nSelectProperties), std::wstring(L"footerReference"), std::wstring(L"default"));
if (FormatUtils::GetBitFromInt(fHF, 5)) WriteSectionStory (pTable->GetFirstFooters (m_nSelectProperties), std::wstring(L"footerReference"), std::wstring(L"first"));
}
//MUST be ignored if the section does not have page number restart enabled.([MS-DOC] — v20101113. стр 152)
......
......@@ -47,7 +47,13 @@ namespace DocFileFormat
{
public:
friend class VMLShapeMapping;
static const int STRUCTURE_SIZE = 26;
static const int STRUCTURE_SIZE = 26;
static const int STRUCTURE_SIZE_OLD = 6;
static const int GetSize(bool bOldVersion)
{
return bOldVersion ? STRUCTURE_SIZE_OLD : STRUCTURE_SIZE;
}
Spa()
{
......
......@@ -31,6 +31,7 @@
*/
#include "VMLPictureMapping.h"
#include "OleObject.h"
#include "OfficeDrawing/GeometryBooleanProperties.h"
#include "OfficeDrawing/GeometryTextBooleanProperties.h"
......@@ -39,6 +40,28 @@
#include "../../Common/DocxFormat/Source/DocxFormat/Document.h"
#include "../../Common/DocxFormat/Source/DocxFormat/Document.h"
typedef struct
{
DWORD iType; // Record type EMR_HEADER
DWORD nSize; // Record size in bytes. This may be greater
// than the sizeof(ENHMETAHEADER).
RECT rclBounds; // Inclusive-inclusive bounds in device units
RECT rclFrame; // Inclusive-inclusive Picture Frame .01mm unit
DWORD dSignature; // Signature. Must be ENHMETA_SIGNATURE.
DWORD nVersion; // Version number
DWORD nBytes; // Size of the metafile in bytes
DWORD nRecords; // Number of records in the metafile
WORD nHandles; // Number of handles in the handle table
// Handle index zero is reserved.
WORD sReserved; // Reserved. Must be zero.
DWORD nDescription; // Number of chars in the unicode desc string
// This is 0 if there is no description string
DWORD offDescription; // Offset to the metafile description record.
// This is 0 if there is no description string
DWORD nPalEntries; // Number of entries in the metafile palette.
SIZE szlDevice; // Size of the reference device in pels
SIZE szlMillimeters; // Size of the reference device in millimeters
} ENHMETAHEADER3;
namespace DocFileFormat
{
......@@ -131,7 +154,41 @@ namespace DocFileFormat
void VMLPictureMapping::Apply( IVisitable* visited )
{
PictureDescriptor* pict = static_cast<PictureDescriptor*>(visited);
PictureDescriptor* pict = dynamic_cast<PictureDescriptor*>(visited);
if (pict) ApplyPict(pict);
OleObject* obj = dynamic_cast<OleObject*>(visited);
if (obj) ApplyObj(obj);
}
void VMLPictureMapping::ApplyObj( OleObject* obj )
{
if (!obj) return;
std::wstring widthString = FormatUtils::DoubleToWideString( 100 );
std::wstring heightString = FormatUtils::DoubleToWideString( 100 );
m_pXmlWriter->WriteNodeBegin( _T( "v:shape" ), true );
PictureFrameType type;
m_pXmlWriter->WriteAttribute( _T( "type" ), std::wstring( _T( "#" ) + VMLShapeTypeMapping::GenerateTypeId(&type)).c_str());
std::wstring style = std::wstring( _T( "width:" ) ) + widthString + std::wstring( _T( "pt;" ) ) + std::wstring( _T( "height:" ) ) + heightString + std::wstring( _T( "pt;" ) );
m_pXmlWriter->WriteAttribute( _T( "style" ), style.c_str() );
m_pXmlWriter->WriteAttribute( _T( "id" ), m_ShapeId.c_str() );
if (m_isOlePreview)
{
m_pXmlWriter->WriteAttribute( _T( "o:ole" ), _T( "" ) );
}
m_pXmlWriter->WriteNodeEnd( _T( "" ), TRUE, FALSE );
m_pXmlWriter->WriteNodeEnd( _T( "v:shape" ) );
}
void VMLPictureMapping::ApplyPict( PictureDescriptor* pict )
{
if (!pict) return;
double xScaling = pict->mx / 1000.0;
......@@ -277,7 +334,6 @@ namespace DocFileFormat
writePictureBorder( _T( "borderbottom" ), pict->brcBottom );
writePictureBorder( _T( "borderright" ), pict->brcRight );
//close v:shape
m_pXmlWriter->WriteNodeEnd( _T( "v:shape" ) );
}
......@@ -308,6 +364,45 @@ namespace DocFileFormat
if (pict->embeddedData && pict->embeddedDataSize > 0)
{
ENHMETAHEADER3 oHeader;
int w = 0, h = 0;
oHeader.iType = 0x00000001;
oHeader.nSize = sizeof(oHeader);
oHeader.rclBounds.left = 0;
oHeader.rclBounds.top = 0;
oHeader.rclBounds.right = w;
oHeader.rclBounds.bottom = h;
oHeader.rclFrame.left = 0;
oHeader.rclFrame.top = 0;
oHeader.rclFrame.right = w;
oHeader.rclFrame.bottom = h;
oHeader.dSignature = 0x464D4520;
oHeader.nVersion = 0x00010000;
oHeader.nBytes = pict->embeddedDataSize - 176;
oHeader.nRecords = 1;
oHeader.nHandles = 0;
oHeader.sReserved = 0;
oHeader.nDescription = 0;
oHeader.offDescription = 0;
oHeader.nPalEntries = 0;
oHeader.szlDevice.cx = 200;
oHeader.szlDevice.cy = 200;
oHeader.szlMillimeters.cx = 100;
oHeader.szlMillimeters.cy = 100;
memcpy(pict->embeddedData, &oHeader, sizeof(oHeader));
m_ctx->_docx->ImagesList.push_back(ImageFileStructure(GetTargetExt(Global::msoblipWMF), std::vector<unsigned char>(pict->embeddedData, pict->embeddedData + pict->embeddedDataSize)));
m_nImageId = m_ctx->_docx->RegisterImage(m_caller, Global::msoblipWMF);
result = true;
......
......@@ -44,6 +44,8 @@
namespace DocFileFormat
{
class OleObject;
bool ParseEmbeddedEquation( const std::string & xmlString, std::wstring & newXmlString );
class VMLPictureMapping: public PropertiesMapping, public IMapping
......@@ -58,6 +60,9 @@ namespace DocFileFormat
/// Writes a border element
void writePictureBorder (const std::wstring & name, const BorderCode* brc);
void appendStyleProperty( std::wstring* b, const std::wstring& propName, const std::wstring& propValue ) const;
void ApplyPict ( PictureDescriptor* p );
void ApplyObj ( OleObject* o );
protected:
/// Copies the picture from the binary stream to the zip archive
......
......@@ -288,7 +288,7 @@ namespace DocFileFormat
stroked = false;
}
if (!(booleans.fUsefFillOK && booleans.fFillOK))
if (booleans.fUsefFillOK && !booleans.fFillOK)
{
filled = false;
}
......
......@@ -187,28 +187,35 @@ public:
std::wstring ReadXst()
{
std::wstring wstrResult( _T( "" ) );
if (stream)
if (!stream) return L"";
std::wstring wstrResult;
unsigned char* xstz = NULL;
unsigned char* cch = NULL;
if (olderVersion)
{
int cchSize = 1;
cch = ReadBytes( cchSize, true );
int xstzSize = FormatUtils::BytesToUChar( cch, 0, cchSize ) * 1;
xstz = ReadBytes(xstzSize, true);
FormatUtils::GetSTLCollectionFromBytes<std::wstring>( &wstrResult, xstz, xstzSize, ENCODING_WINDOWS_1250 );
}
else
{
int cchSize = 2;
unsigned char* cch = this->ReadBytes( cchSize, true );
cch = ReadBytes( cchSize, true );
int xstzSize = FormatUtils::BytesToInt16( cch, 0, cchSize ) * 2;
unsigned char* xstz = ReadBytes(xstzSize, true);
xstz = ReadBytes(xstzSize, true);
if (this->olderVersion)
{
FormatUtils::GetSTLCollectionFromBytes<std::wstring>( &wstrResult, xstz, xstzSize, ENCODING_WINDOWS_1250 );
}
else
{
FormatUtils::GetSTLCollectionFromBytes<std::wstring>( &wstrResult, xstz, xstzSize, ENCODING_UTF16 );
}
RELEASEARRAYOBJECTS(xstz);
RELEASEARRAYOBJECTS(cch);
FormatUtils::GetSTLCollectionFromBytes<std::wstring>( &wstrResult, xstz, xstzSize, ENCODING_UTF16 );
}
RELEASEARRAYOBJECTS(xstz);
RELEASEARRAYOBJECTS(cch);
return wstrResult;
}
......@@ -239,21 +246,42 @@ public:
/// The string must have the following structure:
/// unsigned char 1-4: Character count (cch)
/// unsigned char 5-cch+4: ANSI characters terminated by \0
std::wstring ReadLengthPrefixedAnsiString()
std::wstring ReadLengthPrefixedAnsiString(int max_size)
{
std::wstring result;
int cch = this->ReadInt32();
unsigned int cch = ReadUInt32();
if ( cch > 0 )
unsigned char* stringBytes = NULL;
if (cch > max_size)
{
//dont read the terminating zero
unsigned char* stringBytes = ReadBytes( cch, true );
//error ... skip to 0
int pos_orinal = GetPosition();
int pos = 0;
stringBytes = ReadBytes( max_size, true );
FormatUtils::GetSTLCollectionFromBytes<std::wstring>( &result, stringBytes, ( cch - 1 ), ENCODING_WINDOWS_1250);
if (stringBytes)
{
while(pos < max_size)
{
if (stringBytes[pos] == 0)
break;
pos++;
}
}
Seek(pos_orinal + pos - 1, 0);
}else
if ( cch > 0 )
{
//dont read the terminating zero
stringBytes = ReadBytes( cch, true );
RELEASEARRAYOBJECTS( stringBytes );
}
FormatUtils::GetSTLCollectionFromBytes<std::wstring>( &result, stringBytes, ( cch - 1 ), ENCODING_WINDOWS_1250);
}
RELEASEARRAYOBJECTS( stringBytes );
return result;
}
......
......@@ -179,7 +179,7 @@ namespace DocFileFormat
if (document_code_page2 > 0)
document_code_page = document_code_page2;
}
if (!FIB->m_bOlderVersion)
if (!bOlderVersion)
document_code_page = ENCODING_UTF16;
FIB->m_CodePage = document_code_page;
......@@ -193,16 +193,16 @@ namespace DocFileFormat
DataStream = NULL;
}
if (TableStream->size() < 1 && FIB->m_bOlderVersion)
if (TableStream->size() < 1 && bOlderVersion)
{
RELEASEOBJECT(TableStream);
m_pStorage->GetStream ("WordDocument", &TableStream);
}
RevisionAuthorTable = new StringTable<WideString> (TableStream, FIB->m_FibWord97.fcSttbfRMark, FIB->m_FibWord97.lcbSttbfRMark, FIB->m_bOlderVersion);
FontTable = new StringTable<FontFamilyName> (TableStream, FIB->m_FibWord97.fcSttbfFfn, FIB->m_FibWord97.lcbSttbfFfn, FIB->m_bOlderVersion);
BookmarkNames = new StringTable<WideString> (TableStream, FIB->m_FibWord97.fcSttbfBkmk, FIB->m_FibWord97.lcbSttbfBkmk, FIB->m_bOlderVersion);
AutoTextNames = new StringTable<WideString> (TableStream, FIB->m_FibWord97.fcSttbfGlsy, FIB->m_FibWord97.lcbSttbfGlsy, FIB->m_bOlderVersion);
RevisionAuthorTable = new StringTable<WideString> (TableStream, FIB->m_FibWord97.fcSttbfRMark, FIB->m_FibWord97.lcbSttbfRMark, bOlderVersion);
FontTable = new StringTable<FontFamilyName> (TableStream, FIB->m_FibWord97.fcSttbfFfn, FIB->m_FibWord97.lcbSttbfFfn, bOlderVersion);
BookmarkNames = new StringTable<WideString> (TableStream, FIB->m_FibWord97.fcSttbfBkmk, FIB->m_FibWord97.lcbSttbfBkmk, bOlderVersion);
AutoTextNames = new StringTable<WideString> (TableStream, FIB->m_FibWord97.fcSttbfGlsy, FIB->m_FibWord97.lcbSttbfGlsy, bOlderVersion);
if (m_pCallFunc)
{
......@@ -221,34 +221,35 @@ namespace DocFileFormat
// Read all needed PLCFs
if (FIB->m_RgLw97.ccpFtn > 0)
{
IndividualFootnotesPlex = new Plex<EmptyStructure>(EmptyStructure::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcffndTxt, FIB->m_FibWord97.lcbPlcffndTxt, FIB->m_bOlderVersion);
FootnoteReferenceCharactersPlex = new Plex<FootnoteDescriptor>(FootnoteDescriptor::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcffndRef, FIB->m_FibWord97.lcbPlcffndRef, FIB->m_bOlderVersion);
IndividualFootnotesPlex = new Plex<EmptyStructure>(EmptyStructure::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcffndTxt, FIB->m_FibWord97.lcbPlcffndTxt, bOlderVersion);
FootnoteReferenceCharactersPlex = new Plex<FootnoteDescriptor>(FootnoteDescriptor::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcffndRef, FIB->m_FibWord97.lcbPlcffndRef, bOlderVersion);
}
if (FIB->m_RgLw97.ccpEdn > 0)
{
IndividualEndnotesPlex = new Plex<EmptyStructure>(EmptyStructure::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfendTxt, FIB->m_FibWord97.lcbPlcfendTxt, FIB->m_bOlderVersion);
EndnoteReferenceCharactersPlex = new Plex<EndnoteDescriptor>(EndnoteDescriptor::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfendRef, FIB->m_FibWord97.lcbPlcfendRef, FIB->m_bOlderVersion);
IndividualEndnotesPlex = new Plex<EmptyStructure>(EmptyStructure::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfendTxt, FIB->m_FibWord97.lcbPlcfendTxt, bOlderVersion);
EndnoteReferenceCharactersPlex = new Plex<EndnoteDescriptor>(EndnoteDescriptor::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfendRef, FIB->m_FibWord97.lcbPlcfendRef, bOlderVersion);
}
if (FIB->m_RgLw97.ccpHdr > 0)
{
HeaderStoriesPlex = new Plex<EmptyStructure>( EmptyStructure::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfHdd, FIB->m_FibWord97.lcbPlcfHdd, FIB->m_bOlderVersion);
HeaderStoriesPlex = new Plex<EmptyStructure>( EmptyStructure::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfHdd, FIB->m_FibWord97.lcbPlcfHdd, bOlderVersion);
}
if (FIB->m_RgLw97.ccpAtn > 0)
{
IndividualCommentsPlex = new Plex<EmptyStructure>(EmptyStructure::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfandTxt, FIB->m_FibWord97.lcbPlcfandTxt, FIB->m_bOlderVersion);
AnnotationsReferencePlex = new Plex<AnnotationReferenceDescriptor>(AnnotationReferenceDescriptor::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfandRef, FIB->m_FibWord97.lcbPlcfandRef, FIB->m_bOlderVersion);
IndividualCommentsPlex = new Plex<EmptyStructure>(EmptyStructure::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfandTxt, FIB->m_FibWord97.lcbPlcfandTxt, bOlderVersion);
AnnotationsReferencePlex = new Plex<AnnotationReferenceDescriptor>(AnnotationReferenceDescriptor::GetSize(bOlderVersion), TableStream, FIB->m_FibWord97.fcPlcfandRef, FIB->m_FibWord97.lcbPlcfandRef, bOlderVersion);
}
OfficeDrawingPlex = new Plex<Spa> (Spa::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcSpaMom, FIB->m_FibWord97.lcbPlcSpaMom, FIB->m_bOlderVersion);
OfficeDrawingPlexHeader = new Plex<Spa> (Spa::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcSpaHdr, FIB->m_FibWord97.lcbPlcSpaHdr, FIB->m_bOlderVersion);
SectionPlex = new Plex<SectionDescriptor> (SectionDescriptor::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfSed, FIB->m_FibWord97.lcbPlcfSed, FIB->m_bOlderVersion);
BookmarkStartPlex = new Plex<BookmarkFirst> (BookmarkFirst::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfBkf, FIB->m_FibWord97.lcbPlcfBkf, FIB->m_bOlderVersion);
BookmarkEndPlex = new Plex<EmptyStructure> (EmptyStructure::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfBkl, FIB->m_FibWord97.lcbPlcfBkl, FIB->m_bOlderVersion);
OfficeDrawingPlex = new Plex<Spa> (Spa::GetSize(bOlderVersion), TableStream, FIB->m_FibWord97.fcPlcSpaMom, FIB->m_FibWord97.lcbPlcSpaMom, bOlderVersion);
OfficeDrawingPlexHeader = new Plex<Spa> (Spa::GetSize(bOlderVersion), TableStream, FIB->m_FibWord97.fcPlcSpaHdr, FIB->m_FibWord97.lcbPlcSpaHdr, bOlderVersion);
SectionPlex = new Plex<SectionDescriptor> (SectionDescriptor::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfSed, FIB->m_FibWord97.lcbPlcfSed, bOlderVersion);
BookmarkStartPlex = new Plex<BookmarkFirst> (BookmarkFirst::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfBkf, FIB->m_FibWord97.lcbPlcfBkf, bOlderVersion);
BookmarkEndPlex = new Plex<EmptyStructure> (EmptyStructure::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfBkl, FIB->m_FibWord97.lcbPlcfBkl, bOlderVersion);
TextboxBreakPlex = new Plex<Tbkd> (Tbkd::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfTxbxBkd, FIB->m_FibWord97.lcbPlcfTxbxBkd, FIB->m_bOlderVersion);
TextboxBreakPlexHeader = new Plex<Tbkd> (Tbkd::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfTxbxHdrBkd, FIB->m_FibWord97.lcbPlcfTxbxHdrBkd, FIB->m_bOlderVersion);
TextboxBreakPlex = new Plex<Tbkd> (Tbkd::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfTxbxBkd, FIB->m_FibWord97.lcbPlcfTxbxBkd, bOlderVersion);
TextboxBreakPlexHeader = new Plex<Tbkd> (Tbkd::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfTxbxHdrBkd, FIB->m_FibWord97.lcbPlcfTxbxHdrBkd, bOlderVersion);
for (size_t i = 0; i < BookmarkStartPlex->Elements.size(); ++i)
{
......@@ -259,11 +260,11 @@ namespace DocFileFormat
}
}
AutoTextPlex = new Plex<EmptyStructure> (EmptyStructure::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfGlsy, FIB->m_FibWord97.lcbPlcfGlsy, FIB->m_bOlderVersion);
FieldsPlex = new Plex<FieldCharacter> (FieldCharacter::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfFldMom, FIB->m_FibWord97.lcbPlcfFldMom, FIB->m_bOlderVersion);
FootnoteDocumentFieldsPlex = new Plex<FieldCharacter> (FieldCharacter::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfFldFtn, FIB->m_FibWord97.lcbPlcfFldFtn, FIB->m_bOlderVersion);
EndnoteDocumentFieldsPlex = new Plex<FieldCharacter> (FieldCharacter::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfFldEdn, FIB->m_FibWord97.lcbPlcfFldEdn, FIB->m_bOlderVersion);
HeadersAndFootersDocumentFieldsPlex = new Plex<FieldCharacter> (FieldCharacter::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfFldHdr, FIB->m_FibWord97.lcbPlcfFldHdr, FIB->m_bOlderVersion);
AutoTextPlex = new Plex<EmptyStructure> (EmptyStructure::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfGlsy, FIB->m_FibWord97.lcbPlcfGlsy, bOlderVersion);
FieldsPlex = new Plex<FieldCharacter> (FieldCharacter::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfFldMom, FIB->m_FibWord97.lcbPlcfFldMom, bOlderVersion);
FootnoteDocumentFieldsPlex = new Plex<FieldCharacter> (FieldCharacter::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfFldFtn, FIB->m_FibWord97.lcbPlcfFldFtn, bOlderVersion);
EndnoteDocumentFieldsPlex = new Plex<FieldCharacter> (FieldCharacter::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfFldEdn, FIB->m_FibWord97.lcbPlcfFldEdn, bOlderVersion);
HeadersAndFootersDocumentFieldsPlex = new Plex<FieldCharacter> (FieldCharacter::STRUCTURE_SIZE, TableStream, FIB->m_FibWord97.fcPlcfFldHdr, FIB->m_FibWord97.lcbPlcfFldHdr, bOlderVersion);
if (m_pCallFunc)
{
......@@ -392,13 +393,13 @@ namespace DocFileFormat
int cp = SectionPlex->CharacterPositions[i + 1];
//Get the SEPX
VirtualStreamReader wordReader( this->WordDocumentStream, sed->fcSepx, FIB->m_bOlderVersion);
VirtualStreamReader wordReader( this->WordDocumentStream, sed->fcSepx, bOlderVersion);
//!!!TODO: cbSepx is the size in bytes of the rest properties part!!!
short cbSepx = wordReader.ReadInt16();
unsigned char* bytes = wordReader.ReadBytes( ( cbSepx /*- 2*/ ), true );
AllSepx->insert( std::pair<int, SectionPropertyExceptions*>( cp, new SectionPropertyExceptions( bytes, ( cbSepx /*- 2*/ ), FIB->m_bOlderVersion ) ) );
AllSepx->insert( std::pair<int, SectionPropertyExceptions*>( cp, new SectionPropertyExceptions( bytes, ( cbSepx /*- 2*/ ), bOlderVersion ) ) );
RELEASEARRAYOBJECTS( bytes );
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment