/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This file is part of xlslib -- A multiplatform, C/C++ library * for dynamic generation of Excel(TM) files. * * Copyright 2004 Yeico S. A. de C. V. All Rights Reserved. * Copyright 2008-2014 David Hoerl All Rights Reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY David Hoerl ''AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David Hoerl OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "../xlslib/record.h" #include "../xlslib/note.h" #include "../xlslib/globalrec.h" #include "../xlslib/datast.h" #include "../xlslib/rectypes.h" using namespace xlslib_core; using namespace xlslib_strings; static unsigned char draw001[18] = { 0xbf, 0x00, 0x08, 0x00, 0x08, 0x00, 0x81, 0x01, 0x09, 0x00, 0x00, 0x08, 0xc0, 0x01, 0x40, 0x00, 0x00, 0x08, }; static unsigned char draw002[16] = { 0x0d, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x00, 0x08, 0x17, 0x00, 0x00, 0x08, 0xf7, 0x00, 0x00, 0x10, }; static unsigned char draw101[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static unsigned char draw102[6] = { 0xBF, 0x00, 0x20, 0x00, 0x20, 0x00, }; /* ********************************* * note_t class implementation ********************************* */ note_t::note_t(CGlobalRecords& gRecords, unsigned32_t rowval, unsigned32_t colval, const std::string& auth, const std::string& msg, xf_t* pxfval) : cell_t(gRecords, rowval, colval, pxfval), author(), text(), noteRec(), fillColor(0x08000050), idx(0) { gRecords.char2str16(auth, this->author); gRecords.char2str16(msg, this->text); Finalize((unsigned16_t)rowval, (unsigned16_t)colval); } note_t::note_t(CGlobalRecords& gRecords, unsigned32_t rowval, unsigned32_t colval, const ustring& auth, const ustring& msg, xf_t* pxfval) : cell_t(gRecords, rowval, colval, pxfval), author(), text(), noteRec(), fillColor(0x08000050), idx(0) { gRecords.wide2str16(auth, this->author); gRecords.wide2str16(msg, this->text); Finalize((unsigned16_t)rowval, (unsigned16_t)colval); } #ifndef __FRAMEWORK__ note_t::note_t(CGlobalRecords& gRecords, unsigned32_t rowval, unsigned32_t colval, const u16string& auth, const u16string& msg, xf_t* pxfval) : cell_t(gRecords, rowval, colval, pxfval), author(auth), text(msg), noteRec(), fillColor(0x08000050), idx(0) { Finalize((unsigned16_t)rowval, (unsigned16_t)colval); } #endif void note_t::Finalize(unsigned16_t rowval, unsigned16_t colval) { size_t alen = author.length(); if(alen == 0 || alen > 54) { xchar16_t warning[] = { 'A', 'u', 't', 'h', 'o', 'r', ' ', 'M', 'u', 's', 't', ' ', 'B', 'e', ' ', '1', ' ', 't', 'o', ' ', '5', '4', 'c', 'h', 'a', 'r', 's', ' ', 'l', 'o', 'n', 'g', 0}; author.assign(warning); alen = author.length(); } size_t tlen = text.length(); if(tlen == 0 || tlen > 2048) { xchar16_t warning[] = { 'T', 'e', 'x', 't', ' ', 'M', 'u', 's', 't', ' ', 'B', 'e', ' ', '1', ' ', 't', 'o', ' ', '2', '0', '4', '8', 'c', 'h', 'a', 'r', 's', ' ', 'l', 'o', 'n', 'g', 0}; text.assign(warning); tlen = text.length(); } size_t totalLen = alen + tlen + 2; size_t count = totalLen/11; bool lastIsNL = false; for(size_t i=0; i 20) { cols *= 2; lines /= 2; } noteRec.Flag = (unsigned16_t)0x3; noteRec.Col1 = colval+(unsigned16_t)1; noteRec.DX1 = (unsigned16_t)0xCD; noteRec.Row1 = rowval; noteRec.DY1 = (unsigned16_t)0x27; noteRec.Col2 = colval+(unsigned16_t)1 + cols; noteRec.DX2 = (unsigned16_t)0x7B; noteRec.Row2 = rowval+lines; noteRec.DY2 = (unsigned16_t)0x3B; } static size_t dumpDrawData(CRecord *data, unsigned16_t rec, unsigned16_t instance, unsigned16_t type, unsigned32_t len, size_t arr_len, const unsigned8_t *arr) { unsigned16_t val = rec & 0x000F; val |= instance << 4; data->AddValue16(val); data->AddValue16(type); size_t len_position = data->GetDataSize(); data->AddValue32(len); if(arr_len) { data->AddDataArray(arr, arr_len); } return len_position; } static void drawDataOPID(CRecord *data, unsigned16_t opid, unsigned32_t val) { data->AddValue16(opid); data->AddValue32(val); } static const uint32_t drawingSize = 8 + 128; // header, 120 is the main blob, 8 is the trailing blob void note_t::dumpDrawingContainer(CRecord *data, unsigned16_t sheetIndex, unsigned32_t& currentSPIDidx, uint32_t noteCount) const { uint32_t thisDrawingExtra = 40 + 8; // uint32_t completeDrawingSize = 8/*f002*/ + 16/*f008*/ + thisDrawingExtra/*first one*/ + drawingSize*noteCount/*every note*/; dumpDrawData(data, 0xf, 0x0, 0xf002, completeDrawingSize, 0, NULL /* len=0 */ ) ; // OfficeArtDgContainer dumpDrawData(data, 0x0, 0x1, 0xf008, 8, 0, NULL /* len=0 */ ) ; // OfficeArtFDG - spidCur needs to be set data->AddValue32(noteCount+1); // csp (4 bytes): An unsigned integer that specifies the number of shapes in this drawing data->AddValue32(CGlobalRecords::MakeSPID(sheetIndex, currentSPIDidx)+noteCount); // spidCur (4 bytes): An MSOSPID structure, as defined in section 2.1.2, that specifies the shape identifier of the last shape in this drawing dumpDrawData(data, 0xF, 0x0, 0xF003, thisDrawingExtra+drawingSize*noteCount, 0, NULL /* len=0 */ ) ; // OfficeArtSpgrContainer dumpDrawData(data, 0xF, 0x0, 0xF004, 40, 0, NULL /* len=0 */ ) ; // OfficeArtSpContainer dumpDrawData(data, 0x1, 0x0, 0xF009, 16, 16, draw101 /* len=16 */ ) ; // OfficeArtFSPGR dumpDrawData(data, 0x2, 0x0, 0xF00A, 8, 0, NULL /* len=0 */ ) ; // OfficeArtFSP data->AddValue32(CGlobalRecords::MakeSPID(sheetIndex, currentSPIDidx++)); data->AddValue32(0x05); } void note_t::dumpDrawingText(CRecord *data, unsigned16_t sheetIndex, unsigned32_t& currentSPIDidx) const { dumpDrawData(data, 0xF, 0x0, 0xF004, drawingSize-8, 0, NULL /* len=0 */ ) ; // OfficeArtSpContainer dumpDrawData(data, 0x2, 0xCA, 0xF00A, 8, 0, NULL /* len=0 */ ) ; // OfficeArtFSP data->AddValue32(CGlobalRecords::MakeSPID(sheetIndex, currentSPIDidx++)); data->AddValue32(0x0A00); dumpDrawData(data, 0x3, 0x8, 0xF00B, 48, 0, NULL /* len=48 */ ) ; // OfficeArtFOPT drawDataOPID(data, 0x0085, 0x00000001); drawDataOPID(data, 0x00BF, 0x000A0008); drawDataOPID(data, 0x0158, 0x00000000); drawDataOPID(data, 0x0181, fillColor); drawDataOPID(data, 0x0183, 0x08000050); drawDataOPID(data, 0x01BF, 0x00100010); drawDataOPID(data, 0x023F, 0x00030003); drawDataOPID(data, 0x03BF, 0x00020002); dumpDrawData(data, 0x3, 0x1, 0xF122, 6, 6, draw102 /* len=6 */ ) ; // OfficeArtTertiaryFOPT dumpDrawData(data, 0x0, 0x0, 0xF010, 18, 0, NULL /* len=18 */ ) ; // msofbtClientAnchor (was draw104) data->AddValue16(noteRec.Flag); data->AddValue16(noteRec.Col1); data->AddValue16(noteRec.DX1); data->AddValue16(noteRec.Row1); data->AddValue16(noteRec.DY1); data->AddValue16(noteRec.Col2); data->AddValue16(noteRec.DX2); data->AddValue16(noteRec.Row2); data->AddValue16(noteRec.DY2); dumpDrawData(data, 0x0, 0x0, 0xF011, 0, 0, NULL /* len=0 */ ) ; // msofbtClientData } void note_t::MakeDrawing(CRecord *data, unsigned32_t& currentSPID, unsigned16_t sheetIndex, unsigned16_t notesInThisSheet) const { xlslib_strings::u16string fullStr = author; fullStr.append(1, (unsigned16_t)':'); fullStr.append(1, (unsigned16_t)'\n'); unsigned16_t authorLen = (unsigned16_t)fullStr.length(); fullStr.append(text); unsigned16_t totalLen = (unsigned16_t)fullStr.length(); // All the records below data->Inflate(4 + (idx == 0 ? 90 : 0) + drawingSize + 30 + 16 + 22 + 4+2+totalLen*(CGlobalRecords::IsASCII(fullStr)?1:2) + 28); data->SetRecordType(RECTYPE_MSODRAWING); if(idx == 0) { dumpDrawingContainer(data, sheetIndex, currentSPID, notesInThisSheet); } dumpDrawingText(data, sheetIndex, currentSPID); size_t offset = data->GetDataSize(); data->SetRecordLength(offset - RECORD_HEADER_SIZE); #if 0 OBJ from Apple 'Numbers', so we dont need to create a GUID for the item ID: 005Dh OBJ (Describes a Graphic Object) Size: 26 OBJ ft=0x15 cb=0x12 ot=0x19 idx=0x3 flags= len=4 0x00 0x00 #endif data->AddValue16(RECTYPE_OBJ); data->AddValue16(26); data->AddValue16(0x15); // ft data->AddValue16(0x12); // cb data->AddValue16(0x19); // ot data->AddValue16(idx+1); // id data->AddValue16(0x6011); // flag bitfield data->AddValue32(0); // must be 0 data->AddValue32(0); // must be 0 data->AddValue32(0); // must be 0 data->AddValue32(0); // must be 0 data->AddValue16(RECTYPE_MSODRAWING); data->AddValue16(8); dumpDrawData(data, 0x0, 0x0, 0xF00D, 0, 0, NULL /* len=0 */ ) ; // msofbtClientTextbox #if 0 ID: 01B6h TXO (Text Object) Size: 18 TXO: grbit=0x0212 rot=0x0000 chText=0x0013 cbRuns=0x0018 #endif data->AddValue16(RECTYPE_TXO); data->AddValue16(18); data->AddValue16(0x0212); // grbit data->AddValue16(0); // rot data->AddFixedDataArray(0, 6); // reserved data->AddValue16(totalLen); // chText - len of text, first CONTINUE data->AddValue16(8*3); // cbRuns - len or runs, second CONTINUE. Always 3 data->AddFixedDataArray(0, 4); // reserved data->AddValue16(RECTYPE_CONTINUE); offset = data->GetDataSize(); data->AddValue16(0); size_t stringStart = data->GetDataSize(); data->AddUnicodeString(fullStr, CRecord::NOLEN_FLAGS_UNICODE); data->SetValueAt16((unsigned16_t)(data->GetDataSize() - stringStart), (unsigned)offset); data->AddValue16(RECTYPE_CONTINUE); data->AddValue16(3*8); data->AddValue16(0); // text index data->AddValue16(0x1); // text font data->AddValue32(0); // unknown data->AddValue16(authorLen); // text index data->AddValue16(0x0); // text font data->AddValue32(0); // unknown data->AddValue16(totalLen); // text index data->AddValue16(0); // text font data->AddValue32(0); // unknown } note_t::~note_t() { } size_t note_t::GetSize(void) const { return 12; } CUnit* note_t::GetData(CDataStorage &datastore) const { return datastore.MakeCNote(*this); // NOTE: this pointer HAS to be deleted elsewhere. } /* ********************************* * CNote class implementation ********************************* * */ void CNote::MakeDrawingGroup(CRecord *data, unsigned32_t count, const sheet_notes *notes) { size_t curr_offset; data->Inflate(82 + count*8); data->SetRecordType(RECTYPE_MSODRAWINGGROUP); size_t f000_len = dumpDrawData(data, 0xf, 0x0, 0xf000, 0, 0, NULL /* len=0 */ ); // OfficeArtDggContainer // Need to get he max spid, which is the last sheet combined with the number of notes #if 0 spidMax (4 bytes): An MSOSPID structure, as defined in section 2.1.2, specifying the current maximum shape identifier that is used in any drawing. This value MUST be less than 0x03FFD7FF. cidcl (4 bytes): An unsigned integer that specifies the number of OfficeArtIDCL records, as defined in section 2.2.46, + 1. This value MUST be less than 0x0FFFFFFF. cspSaved (4 bytes): An unsigned integer specifying the total number of shapes that have been saved in all of the drawings. cdgSaved (4 bytes): An unsigned integer specifying the total number of drawings that have been saved in the file. #endif size_t f006_offset = data->GetDataSize(); size_t f006_len = dumpDrawData(data, 0x0, 0x0, 0xf006, 0, 0, NULL /* len=0 */ ); struct sheet_notes last_sheet = notes[count-1]; // last one unsigned32_t spid = CGlobalRecords::MakeSPID(last_sheet.sheet_idx, last_sheet.sheet_notes); data->AddValue32(spid); // spidMax data->AddValue32(count+1); // cidcl size_t cspSaved_offset = data->GetDataSize(); data->AddValue32(0); // cspSaved data->AddValue32(count); // cdgSaved unsigned32_t total = 0; for(unsigned32_t i=0; iAddValue32(i+1); // dgid data->AddValue32(1 + sn.sheet_notes); // cspidCur } data->SetValueAt32(count + total, (unsigned)cspSaved_offset); curr_offset = data->GetDataSize(); data->SetValueAt32((unsigned)(curr_offset - f006_offset - 8), (unsigned)f006_len); dumpDrawData(data, 0x3, 0x3, 0xf00b, 18, 18, draw001 /* len=18 */ ) ; // OfficeArtFOPT dumpDrawData(data, 0x0, 0x4, 0xf11e, 16, 16, draw002 /* len=16 */ ) ; // OfficeArtSplitMenuColorContainer curr_offset = data->GetDataSize(); data->SetValueAt32((unsigned)(curr_offset - 8 - RECORD_HEADER_SIZE), (unsigned)f000_len); data->SetRecordLength(curr_offset-RECORD_HEADER_SIZE); } CNote::CNote(CDataStorage &datastore, const note_t& notedef) : CRecord(datastore) { bool isAscii = CGlobalRecords::IsASCII(notedef.GetAuthor()); Inflate(4 + 2 + 2 + 2 + 2 + 2 + 1 + notedef.GetAuthor().size() * (isAscii ? 1 : 2) ); SetRecordType(RECTYPE_NOTE); AddValue16((unsigned16_t)notedef.GetRow()); AddValue16((unsigned16_t)notedef.GetCol()); AddValue16(0); // grBit AddValue16(notedef.GetIndex()+1); AddUnicodeString(notedef.GetAuthor(), LEN2_FLAGS_UNICODE); AddValue8(0); SetRecordLength(GetDataSize()-RECORD_HEADER_SIZE); } CNote::~CNote() { }