/* Swinder - Portable library for spreadsheet Copyright (C) 2003-2006 Ariya Hidayat Copyright (C) 2006 Marijn Kruisselbrink This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA */ #include "excel.h" #include #include #include #include #include #include // memcpy #include #include #include "pole.h" #include "swinder.h" // Use anonymous namespace to cover following functions namespace{ static inline unsigned long readU16( const void* p ) { const unsigned char* ptr = (const unsigned char*) p; return ptr[0]+(ptr[1]<<8); } static inline int readI16( const void* p ) { const unsigned char* ptr = (const unsigned char*) p; unsigned int v = ptr[0]+(ptr[1]<<8); if(v > 32768) v -= 65536; return v; } static inline unsigned long readU32( const void* p ) { const unsigned char* ptr = (const unsigned char*) p; return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24); } typedef double& data_64; inline void convert_64 (data_64 convert) { unsigned char temp; unsigned int u_int_temp; temp = ((unsigned char*)&convert)[0]; ((unsigned char*)&convert)[0] = ((unsigned char*)&convert)[3]; ((unsigned char*)&convert)[3] = temp; temp = ((unsigned char*)&convert)[1]; ((unsigned char*)&convert)[1] = ((unsigned char*)&convert)[2]; ((unsigned char*)&convert)[2] = temp; temp = ((unsigned char*)&convert)[4]; ((unsigned char*)&convert)[4] = ((unsigned char*)&convert)[7]; ((unsigned char*)&convert)[7] = temp; temp = ((unsigned char*)&convert)[5]; ((unsigned char*)&convert)[5] = ((unsigned char*)&convert)[6]; ((unsigned char*)&convert)[6] = temp; u_int_temp = ((unsigned int *)&convert)[0]; ((unsigned int *)&convert)[0] = ((unsigned int *)&convert)[1]; ((unsigned int *)&convert)[1] = u_int_temp; } inline bool isLittleEndian(void) { long i = 0x44332211; unsigned char* a = (unsigned char*) &i; return ( *a == 0x11 ); } // FIXME check that double is 64 bits static inline double readFloat64( const void*p ) { const double* ptr = (const double*) p; double num = 0.0; num = *ptr; if( !isLittleEndian() ) convert_64( num ); return num; } // RK value is special encoded integer or floating-point // see any documentation of Excel file format for detail description static inline void decodeRK( unsigned rkvalue, bool& isInteger, int& intResult, double& floatResult ) { bool div100 = rkvalue & 0x01; isInteger = rkvalue & 0x02; if( isInteger ) { // FIXME check that int is 32 bits ? intResult = *((int*) &rkvalue) >> 2; // divide by 100, fall to floating-point if(div100) { isInteger = false; floatResult = (double)intResult / 100.0; } } else { // TODO ensure double takes 8 bytes unsigned char* s = (unsigned char*) &rkvalue; unsigned char* r = (unsigned char*) &floatResult; if( isLittleEndian() ) { r[0] = r[1] = r[2] = r[3] = 0; r[4] = s[0] & 0xfc; r[5] = s[1]; r[6] = s[2]; r[7] = s[3]; } else { r[0] = r[1] = r[2] = r[3] = 0; r[4] = s[0] & 0xfc; r[5] = s[1]; r[6] = s[2]; r[7] = s[3]; } memcpy( &floatResult, r, 8 ); if( div100 ) floatResult *= 0.01; } } } namespace Swinder { std::ostream& operator<<( std::ostream& s, Swinder::UString ustring ) { char* str = ustring.ascii(); s << str; return s; } } using namespace Swinder; static Value errorAsValue( int errorCode ) { Value result( Value::Error ); switch( errorCode ) { case 0x00: result = Value::errorNULL(); break; case 0x07: result = Value::errorDIV0(); break; case 0x0f: result = Value::errorVALUE(); break; case 0x17: result = Value::errorREF(); break; case 0x1d: result = Value::errorNAME(); break; case 0x24: result = Value::errorNUM(); break; case 0x2A: result = Value::errorNA(); break; default: break; }; return result; } //============================================= // EString //============================================= class EString::Private { public: bool unicode; bool richText; UString str; unsigned size; }; EString::EString() { d = new EString::Private(); d->unicode = false; d->richText = false; d->str = UString::null; d->size = 0; } EString::EString( const EString& es ) { d = new EString::Private(); operator=( es ); } EString& EString::operator=( const EString& es ) { d->unicode = es.d->unicode; d->richText = es.d->richText; d->size = es.d->size; d->str = es.d->str; return *this; } EString::~EString() { delete d; } bool EString::unicode() const { return d->unicode; } void EString::setUnicode( bool u ) { d->unicode = u; } bool EString::richText() const { return d->richText; } void EString::setRichText( bool r ) { d->richText = r; } UString EString::str() const { return d->str; } void EString::setStr( const UString& str ) { d->str = str; } unsigned EString::size() const { return d->size; } void EString::setSize( unsigned s ) { d->size = s; } // FIXME use maxsize for sanity check EString EString::fromUnicodeString( const void* p, bool longString, unsigned /* maxsize */ ) { const unsigned char* data = (const unsigned char*) p; UString str = UString::null; unsigned offset = longString ? 2 : 1; unsigned len = longString ? readU16( data ): data[0]; unsigned char flag = data[ offset ]; offset++; // for flag (1 byte) bool unicode = flag & 0x01; bool richText = flag & 0x08; unsigned formatRuns = 0; if( richText ) { formatRuns = readU16( data + offset ); offset += 2; } // find out total bytes used in this string unsigned size = offset + len; // string data if( unicode ) size += len; // because unicode takes 2-bytes char if( richText ) size += (formatRuns*4); if( !unicode ) { char* buffer = new char[ len+1 ]; memcpy( buffer, data + offset, len ); buffer[ len ] = 0; str = UString( buffer ); delete[] buffer; } else { str = UString(); str.reserve(len); for( unsigned k=0; k datasize-2 ) len = datasize-2; if( len == 0 ) return EString(); unsigned offset = 2; if( !unicode ) { char* buffer = new char[ len+1 ]; memcpy( buffer, data + offset, len ); buffer[ len ] = 0; str = UString( buffer ); delete[] buffer; } else { for( unsigned k=0; k data; }; FormulaToken::FormulaToken() { d = new Private; d->ver = Excel97; d->id = Unused; } FormulaToken::FormulaToken( unsigned t ) { d = new Private; d->ver = Excel97; d->id = t; } FormulaToken::FormulaToken( const FormulaToken& token ) { d = new Private; d->ver = token.d->ver; d->id = token.id(); d->data.resize( token.d->data.size() ); for( unsigned i = 0; i < d->data.size(); i++ ) d->data[i] = token.d->data[i]; } FormulaToken::~FormulaToken() { delete d; } unsigned FormulaToken::version() const { return d->ver; } void FormulaToken::setVersion( unsigned v ) { d->ver = v; } unsigned FormulaToken::id() const { return d->id; } const char* FormulaToken::idAsString() const { const char* s = 0; switch( d->id ) { case Matrix: s = "Matrix"; break; case Table: s = "Table"; break; case Add: s = "Add"; break; case Sub: s = "Sub"; break; case Mul: s = "Mul"; break; case Div: s = "Div"; break; case Power: s = "Power"; break; case Concat: s = "Concat"; break; case LT: s = "LT"; break; case LE: s = "LE"; break; case EQ: s = "EQ"; break; case GE: s = "GE"; break; case GT: s = "GT"; break; case NE: s = "NE"; break; case Intersect: s = "Intersect"; break; case List: s = "List"; break; case Range: s = "Range"; break; case UPlus: s = "UPlus"; break; case UMinus: s = "UMinus"; break; case Percent: s = "Percent"; break; case Paren: s = "Paren"; break; case String: s = "String"; break; case MissArg: s = "MissArg"; break; case ErrorCode: s = "ErrorCode"; break; case Bool: s = "Bool"; break; case Integer: s = "Integer"; break; case Array: s = "Array"; break; case Function: s = "Function"; break; case FunctionVar: s = "FunctionVar"; break; case Name: s = "Name"; break; case Ref: s = "Ref"; break; case RefErr: s = "RefErr"; break; case RefN: s = "RefN"; break; case Area: s = "Area"; break; case AreaErr: s = "AreaErr"; break; case AreaN: s = "AreaN"; break; case NameX: s = "NameX"; break; case Ref3d: s = "Ref3d"; break; case RefErr3d: s = "RefErr3d"; break; case Float: s = "Float"; break; case Area3d: s = "Area3d"; break; case AreaErr3d: s = "AreaErr3d"; break; default: s = "Unknown"; break; }; return s; } unsigned FormulaToken::size() const { unsigned s = 0; // on most cases no data switch( d->id ) { case Add: case Sub: case Mul: case Div: case Power: case Concat: case LT: case LE: case EQ: case GE: case GT: case NE: case Intersect: case List: case Range: case UPlus: case UMinus: case Percent: case Paren: case MissArg: s = 0; break; case Attr: s = 3; break; case ErrorCode: case Bool: s = 1; break; case Integer: s = 2; break; case Array: s = 7; break; case Function: s = 2; break; case FunctionVar: s = 3; break; case Matrix: case Table: s = (d->ver == Excel97) ? 4 : 3; break; case Name: s = (d->ver == Excel97) ? 4 : 14; break; case Ref: case RefErr: case RefN: s = (d->ver == Excel97) ? 4 : 3; break; case Area: case AreaErr: case AreaN: s = (d->ver == Excel97) ? 8 : 6; break; case NameX: s = (d->ver == Excel97) ? 6 : 24; break; case Ref3d: case RefErr3d: s = (d->ver == Excel97) ? 6 : 17; break; case Float: s = 8; break; case Area3d: case AreaErr3d: s = (d->ver == Excel97) ? 10 : 20; break; default: // WARNING this is unhandled case break; }; return s; } void FormulaToken::setData( unsigned size, const unsigned char* data ) { d->data.resize( size ); for( unsigned i = 0; i < size; i++ ) d->data[i] = data[i]; } Value FormulaToken::value() const { // sentinel if(d->data.size() == 0) return Value::empty(); Value result; unsigned char* buf; buf = new unsigned char[d->data.size()]; for( unsigned k=0; kdata.size(); k++ ) buf[k] = d->data[k]; // FIXME sanity check: verify size of data switch( d->id ) { case ErrorCode: result = errorAsValue( buf[0] ); break; case Bool: result = Value( buf[0]!=0 ); break; case Integer: result = Value( (int)readU16( buf ) ); break; case Float: result = Value( readFloat64( buf ) ); break; case String: { EString estr = (version()==Excel97) ? EString::fromUnicodeString( buf, false, d->data.size() ) : EString::fromByteString( buf, false, d->data.size() ); result = Value( estr.str() ); } break; default: break; } delete [] buf; return result; } unsigned FormulaToken::functionIndex() const { // FIXME check data size unsigned index = 0; unsigned char buf[2]; if( d->id == Function ) { buf[0] = d->data[0]; buf[1] = d->data[1]; index = readU16( buf ); } if( d->id == FunctionVar ) { buf[0] = d->data[1]; buf[1] = d->data[2]; index = readU16( buf ); } return index; } struct FunctionEntry { const char *name; int params; }; static const FunctionEntry FunctionEntries[] = { { "COUNT", 1 }, // 0 { "IF", 0 }, // 1 { "ISNV", 1 }, // 2 { "ISERROR", 1 }, // 3 { "SUM", 0 }, // 4 { "AVERAGE", 0 }, // 5 { "MIN", 0 }, // 6 { "MAX", 0 }, // 7 { "ROW", 0 }, // 8 { "COLUMN", 0 }, // 9 { "NOVALUE", 0 }, // 10 { "NPV", 0 }, // 11 { "STDEV", 0 }, // 12 { "DOLLAR", 0 }, // 13 { "FIXED", 0 }, // 14 { "SIN", 1 }, // 15 { "COS", 1 }, // 16 { "TAN", 1 }, // 17 { "ATAN", 1 }, // 18 { "PI", 0 }, // 19 { "SQRT", 1 }, // 20 { "EXP", 1 }, // 21 { "LN", 1 }, // 22 { "LOG10", 1 }, // 23 { "ABS", 1 }, // 24 { "INT", 1 }, // 25 { "SIGN", 1 }, // 26 { "ROUND", 2 }, // 27 { "LOOKUP", 0 }, // 28 { "INDEX", 0 }, // 29 { "REPT", 2 }, // 30 { "MID", 3 }, // 31 { "LEN", 1 }, // 32 { "VALUE", 1 }, // 33 { "TRUE", 0 }, // 34 { "FALSE", 0 }, // 35 { "AND", 0 }, // 36 { "OR", 0 }, // 37 { "NOT", 1 }, // 38 { "MOD", 2 }, // 39 { "DCOUNT", 3 }, // 40 { "DSUM", 3 }, // 41 { "DAVERAGE", 3 }, // 42 { "DMIN", 3 }, // 43 { "DMAX", 3 }, // 44 { "DSTDEV", 3 }, // 45 { "VAR", 0 }, // 46 { "DVAR", 3 }, // 47 { "TEXT", 2 }, // 48 { "LINEST", 0 }, // 49 { "TREND", 0 }, // 50 { "LOGEST", 0 }, // 51 { "GROWTH", 0 }, // 52 { "GOTO", 0 }, // 53 { "HALT", 0 }, // 54 { "Unknown55", 0 }, // 55 { "PV", 0 }, // 56 { "FV", 0 }, // 57 { "NPER", 0 }, // 58 { "PMT", 0 }, // 59 { "RATE", 0 }, // 60 { "MIRR", 3 }, // 61 { "IRR", 0 }, // 62 { "RAND", 0 }, // 63 { "MATCH", 0 }, // 64 { "DATE", 3 }, // 65 { "TIME", 3 }, // 66 { "DAY", 1 }, // 67 { "MONTH", 1 }, // 68 { "YEAR", 1 }, // 69 { "DAYOFWEEK", 0 }, // 70 { "HOUR", 1 }, // 71 { "MIN", 1 }, // 72 { "SEC", 1 }, // 73 { "NOW", 0 }, // 74 { "AREAS", 1 }, // 75 { "ROWS", 1 }, // 76 { "COLUMNS", 1 }, // 77 { "OFFSET", 0 }, // 78 { "ABSREF", 2 }, // 79 { "RELREF", 0 }, // 80 { "ARGUMENT", 0 }, // 81 { "SEARCH", 0 }, // 82 { "TRANSPOSE", 1 }, // 83 { "ERROR", 0 }, // 84 { "STEP", 0 }, // 85 { "TYPE", 1 }, // 86 { "ECHO", 0 }, { "SETNAME", 0 }, { "CALLER", 0 }, { "DEREF", 0 }, { "WINDOWS", 0 }, { "SERIES", 4 }, { "DOCUMENTS", 0 }, { "ACTIVECELL", 0 }, { "SELECTION", 0 }, { "RESULT", 0 }, { "ATAN2", 2 }, // 97 { "ASIN", 1 }, // 98 { "ACOS", 1 }, // 99 { "CHOOSE", 0 }, // 100 { "HLOOKUP", 0 }, // 101 { "VLOOKUP", 0 }, // 102 { "LINKS", 0 }, { "INPUT", 0 }, { "ISREF", 1 }, // 105 { "GETFORMULA", 0 }, { "GETNAME", 0 }, { "SETVALUE", 0 }, { "LOG", 0 }, // 109 { "EXEC", 0 }, { "CHAR", 1 }, // 111 { "LOWER", 1 }, // 112 { "UPPER", 1 }, // 113 { "PROPER", 1 }, // 114 { "LEFT", 0 }, // 115 { "RIGHT", 0 }, // 116 { "EXACT", 2 }, // 117 { "TRIM", 1 }, // 118 { "REPLACE", 4 }, // 119 { "SUBSTITUTE", 0 }, // 120 { "CODE", 1 }, // 121 { "NAMES", 0 }, { "DIRECTORY", 0 }, { "FIND", 0 }, // 124 { "CELL", 0 }, // 125 { "ISERR", 1 }, // 126 { "ISTEXT", 1 }, // 127 { "ISNUMBER", 1 }, // 128 { "ISBLANK", 1 }, // 129 { "T", 1 }, // 130 { "N", 1 }, // 131 { "FOPEN", 0 }, { "FCLOSE", 0 }, { "FSIZE", 0 }, { "FREADLN", 0 }, { "FREAD", 0 }, { "FWRITELN", 0 }, { "FWRITE", 0 }, { "FPOS", 0 }, { "DATEVALUE", 1 }, // 140 { "TIMEVALUE", 1 }, // 141 { "SLN", 3 }, // 142 { "SYD", 4 }, // 143 { "DDB", 0 }, // 144 { "GETDEF", 0 }, { "REFTEXT", 0 }, { "TEXTREF", 0 }, { "INDIRECT", 0 }, // 148 { "REGISTER", 0 }, { "CALL", 0 }, { "ADDBAR", 0 }, { "ADDMENU", 0 }, { "ADDCOMMAND", 0 }, { "ENABLECOMMAND", 0 }, { "CHECKCOMMAND", 0 }, { "RENAMECOMMAND", 0 }, { "SHOWBAR", 0 }, { "DELETEMENU", 0 }, { "DELETECOMMAND", 0 }, { "GETCHARTITEM", 0 }, { "DIALOGBOX", 0 }, { "CLEAN", 1 }, // 162 { "MDETERM", 1 }, // 163 { "MINVERSE", 1 }, // 164 { "MMULT", 2 }, // 165 { "FILES", 0 }, { "IPMT", 0 }, // 167 { "PPMT", 0 }, // 168 { "COUNTA", 0 }, // 169 { "CANCELKEY", 1 }, { "Unknown171", 0 }, { "Unknown172", 0 }, { "Unknown173", 0 }, { "Unknown174", 0 }, { "INITIATE", 0 }, { "REQUEST", 0 }, { "POKE", 0 }, { "EXECUTE", 0 }, { "TERMINATE", 0 }, { "RESTART", 0 }, { "HELP", 0 }, { "GETBAR", 0 }, { "PRODUCT", 0 }, // 183 { "FACT", 1 }, // 184 { "GETCELL", 0 }, { "GETWORKSPACE", 0 }, { "GETWINDOW", 0 }, { "GETDOCUMENT", 0 }, { "DPRODUCT", 3 }, // 189 { "ISNONTEXT", 1 }, // 190 { "GETNOTE", 0 }, { "NOTE", 0 }, { "STDEVP", 0 }, // 193 { "VARP", 0 }, // 194 { "DSTDEVP", 3 }, // 195 { "DVARP", 3 }, // 196 { "TRUNC", 0 }, // 197 { "ISLOGICAL", 1 }, // 198 { "DCOUNTA", 3 }, // 199 { "DELETEBAR", 0 }, { "UNREGISTER", 0 }, { "Unknown202", 0 }, { "Unknown203", 0 }, { "USDOLLAR", 0 }, { "FINDB", 0 }, { "SEARCHB", 0 }, { "REPLACEB", 0 }, { "LEFTB", 0 }, { "RIGHTB", 0 }, { "MIDB", 0 }, { "LENB", 0 }, { "ROUNDUP", 2 }, // 212 { "ROUNDDOWN", 2 }, // 213 { "ASC", 0 }, { "DBCS", 0 }, { "RANK", 0 }, // 216 { "Unknown217", 0 }, { "Unknown218", 0 }, { "ADDRESS", 0 }, // 219 { "GETDIFFDATE360", 0 }, // 220 { "CURRENTDATE", 0 }, // 221 { "VBD", 0 }, // 222 { "Unknown223", 0 }, { "Unknown224", 0 }, { "Unknown225", 0 }, { "Unknown226", 0 }, { "MEDIAN", 0 }, // 227 { "SUMPRODUCT", 0 }, // 228 { "SINH", 1 }, // 229 { "COSH", 1 }, // 230 { "TANH", 1 }, // 231 { "ASINH", 1 }, // 232 { "ACOSH", 1 }, // 233 { "ATANH", 1 }, // 234 { "DGET", 3 }, // 235 { "CREATEOBJECT", 0 }, { "VOLATILE", 0 }, { "LASTERROR", 0 }, { "CUSTOMUNDO", 0 }, { "CUSTOMREPEAT", 0 }, { "FORMULACONVERT", 0 }, { "GETLINKINFO", 0 }, { "TEXTBOX", 0 }, { "INFO", 1 }, // 244 { "GROUP", 0 }, { "GETOBJECT", 0 }, { "DB", 0 }, // 247 { "PAUSE", 0 }, { "Unknown249", 0 }, { "Unknown250", 0 }, { "RESUME", 0 }, { "FREQUENCY", 2 }, // 252 { "ADDTOOLBAR", 0 }, { "DELETETOOLBAR", 0 }, { "Unknown255", 0 }, { "RESETTOOLBAR", 0 }, { "EVALUATE", 0 }, { "GETTOOLBAR", 0 }, { "GETTOOL", 0 }, { "SPELLINGCHECK", 0 }, { "ERRORTYPE", 1 }, // 261 { "APPTITLE", 0 }, { "WINDOWTITLE", 0 }, { "SAVETOOLBAR", 0 }, { "ENABLETOOL", 0 }, { "PRESSTOOL", 0 }, { "REGISTERID", 0 }, { "GETWORKBOOK", 0 }, { "AVEDEV", 0 }, // 269 { "BETADIST", 0 }, // 270 { "GAMMALN", 1 }, // 271 { "BETAINV", 0 }, // 272 { "BINOMDIST", 4 }, // 273 { "CHIDIST", 2 }, // 274 { "CHIINV", 2 }, // 275 { "COMBIN", 2 }, // 276 { "CONFIDENCE", 3 }, // 277 { "CRITBINOM", 3 }, // 278 { "EVEN", 1 }, // 279 { "EXPONDIST", 3 }, // 280 { "FDIST", 3 }, // 281 { "FINV", 3 }, // 282 { "FISHER", 1 }, // 283 { "FISHERINV", 1 }, // 284 { "FLOOR", 2 }, // 285 { "GAMMADIST", 4 }, // 286 { "GAMMAINV", 3 }, // 287 { "CEIL", 2 }, // 288 { "HYPGEOMDIST", 4 }, // 289 { "LOGNORMDIST", 3 }, // 290 { "LOGINV", 3 }, // 291 { "NEGBINOMDIST", 3 }, // 292 { "NORMDIST", 4 }, // 293 { "NORMSDIST", 1 }, // 294 { "NORMINV", 3 }, // 295 { "NORMSINV", 1 }, // 296 { "STANDARDIZE", 3 }, // 297 { "ODD", 1 }, // 298 { "PERMUT", 2 }, // 299 { "POISSON", 3 }, // 300 { "TDIST", 3 }, // 301 { "WEIBULL", 4 }, // 302 { "SUMXMY2", 2 }, // 303 { "SUMX2MY2", 2 }, // 304 { "SUMX2DY2", 2 }, // 305 { "CHITEST", 2 }, // 306 { "CORREL", 2 }, // 307 { "COVAR", 2 }, // 308 { "FORECAST", 3 }, // 309 { "FTEST", 2 }, // 310 { "INTERCEPT", 2 }, // 311 { "PEARSON", 2 }, // 312 { "RSQ", 2 }, // 313 { "STEYX", 2 }, // 314 { "SLOPE", 2 }, // 315 { "TTEST", 4 }, // 316 { "PROB", 0 }, // 317 { "DEVSQ", 0 }, // 318 { "GEOMEAN", 0 }, // 319 { "HARMEAN", 0 }, // 320 { "SUMSQ", 0 }, // 321 { "KURT", 0 }, // 322 { "SKEW", 0 }, // 323 { "ZTEST", 0 }, // 324 { "LARGE", 2 }, // 325 { "SMALL", 2 }, // 326 { "QUARTILE", 2 }, // 327 { "PERCENTILE", 2 }, // 328 { "PERCENTRANK", 0 }, // 329 { "MODALVALUE", 0 }, // 330 { "TRIMMEAN", 2 }, // 331 { "TINV", 2 }, // 332 { "Unknown333", 0 }, { "MOVIECOMMAND", 0 }, { "GETMOVIE", 0 }, { "CONCATENATE", 0 }, // 336 { "POWER", 2 }, // 337 { "PIVOTADDDATA", 0 }, { "GETPIVOTTABLE", 0 }, { "GETPIVOTFIELD", 0 }, { "GETPIVOTITEM", 0 }, { "RADIANS", 1 }, // 342 { "DEGREES", 1 }, // 343 { "SUBTOTAL", 0 }, // 344 { "SUMIF", 0 }, // 345 { "COUNTIF", 2 }, // 346 { "COUNTBLANK", 1 }, // 347 { "SCENARIOGET", 0 }, { "OPTIONSLISTSGET", 0 }, { "ISPMT", 4 }, { "DATEDIF", 3 }, { "DATESTRING", 0 }, { "NUMBERSTRING", 0 }, { "ROMAN", 0 }, // 354 { "OPENDIALOG", 0 }, { "SAVEDIALOG", 0 }, { "VIEWGET", 0 }, { "GETPIVOTDATA", 2 }, // 358 { "HYPERLINK", 1 }, { "PHONETIC", 0 }, { "AVERAGEA", 0 }, // 361 { "MAXA", 0 }, // 362 { "MINA", 0 }, // 363 { "STDEVPA", 0 }, // 364 { "VARPA", 0 }, // 365 { "STDEVA", 0 }, // 366 { "VARA", 0 }, // 367 }; const char* FormulaToken::functionName() const { if( functionIndex() > 367 ) return 0; return FunctionEntries[ functionIndex() ].name; } unsigned FormulaToken::functionParams() const { unsigned params = 0; if( d->id == Function ) { if( functionIndex() > 367 ) return 0; params = FunctionEntries[ functionIndex() ].params; } if( d->id == FunctionVar ) { params = (unsigned)d->data[0]; params &= 0x7f; } return params; } unsigned FormulaToken::attr() const { unsigned attr = 0; if( d->id == Attr ) { attr = (unsigned) d->data[0]; } return attr; } unsigned FormulaToken::nameIndex() const { // FIXME check data size ! unsigned ni = 0; unsigned char buf[2]; if( d->id == NameX ) if( d->ver == Excel97 ) { buf[0] = d->data[2]; buf[1] = d->data[3]; ni = readU16( buf ); } if( d->id == NameX ) if( d->ver == Excel95 ) { buf[0] = d->data[10]; buf[1] = d->data[11]; ni = readU16( buf ); } return ni; } UString FormulaToken::area( unsigned row, unsigned col ) const { // sanity check if(id() != Area) if(id() != Area3d) return UString::null; // check data size int minsize = 0; if((id() == Area3d)) minsize = (version() == Excel97) ? 10 : 20; else if(id() == Area) minsize = (version() == Excel97) ? 8 : 6; if(d->data.size() < minsize) return UString::null; unsigned char buf[2]; int row1Ref, row2Ref, col1Ref, col2Ref; bool row1Relative, col1Relative; bool row2Relative, col2Relative; if( version() == Excel97 ) { int ofs = (id() == Area) ? 0 : 2; buf[0] = d->data[ofs]; buf[1] = d->data[ofs+1]; row1Ref = readU16( buf ); buf[0] = d->data[ofs+2]; buf[1] = d->data[ofs+3]; row2Ref = readU16( buf ); buf[0] = d->data[ofs+4]; buf[1] = d->data[ofs+5]; col1Ref = readU16( buf ); buf[0] = d->data[ofs+6]; buf[1] = d->data[ofs+7]; col2Ref = readU16( buf ); row1Relative = col1Ref & 0x8000; col1Relative = col1Ref & 0x4000; col1Ref &= 0x3fff; row2Relative = col2Ref & 0x8000; col2Relative = col2Ref & 0x4000; col2Ref &= 0x3fff; } else { int ofs = (id() == Area) ? 0 : 14; buf[0] = d->data[ofs]; buf[1] = d->data[ofs+1]; row1Ref = readU16( buf ); buf[0] = d->data[ofs+2]; buf[1] = d->data[ofs+3]; row2Ref = readU16( buf ); buf[0] = d->data[ofs+4]; buf[1] = 0; col1Ref = readU16( buf ); buf[0] = d->data[ofs+5]; buf[1] = 0; col2Ref = readU16( buf ); row1Relative = row2Ref & 0x8000; col1Relative = row2Ref & 0x4000; row1Ref &= 0x3fff; row2Relative = row2Ref & 0x8000; col2Relative = row2Ref & 0x4000; row2Ref &= 0x3fff; } UString result; // not critical, just to improve performace // see also ref() function below result.reserve(40); // normal use if( !col1Relative ) result.append( '$' ); result.append( Cell::columnLabel( col1Ref ) ); if( !row1Relative ) result.append( '$' ); result.append( UString::number( row1Ref+1 ) ); result.append( ':' ); if( !col2Relative ) result.append( '$' ); result.append( Cell::columnLabel( col2Ref ) ); if( !row2Relative ) result.append( '$' ); result.append( UString::number( row2Ref+1 ) ); return result; } UString FormulaToken::ref( unsigned row, unsigned col ) const { // sanity check if(id() != Ref) if(id() != Ref3d) return UString::null; // FIXME check data size ! // FIXME handle shared formula unsigned char buf[2]; int rowRef, colRef; bool rowRelative, colRelative; if( version() == Excel97 ) { int ofs = (id() == Ref) ? 0 : 2; buf[0] = d->data[ofs]; buf[1] = d->data[ofs+1]; rowRef = readU16( buf ); buf[0] = d->data[ofs+2]; buf[1] = d->data[ofs+3]; colRef = readU16( buf ); rowRelative = colRef & 0x8000; colRelative = colRef & 0x4000; colRef &= 0x3fff; } else { int ofs = (id() == Ref) ? 0 : 14; buf[0] = d->data[ofs]; buf[1] = d->data[ofs+1]; rowRef = readU16( buf ); buf[0] = d->data[ofs+2]; buf[1] = 0; colRef = readU16( buf ); rowRelative = rowRef & 0x8000; colRelative = rowRef & 0x4000; rowRef &= 0x3fff; } UString result; // not critical, just to improve performace // absolute column 4294967295, row 4294967295 is "$AATYHWUR$4294967295" // (20 characters) result.reserve(20); if( !colRelative ) result.append('$'); result.append( Cell::columnLabel( colRef ) ); if( !rowRelative ) result.append('$'); result.append( UString::number( rowRef+1 ) ); return result; } // only when id is Ref3d or Area3d unsigned FormulaToken::externSheetRef() const { if(version() >= Excel97) { unsigned char buf[2]; buf[0] = d->data[0]; buf[1] = d->data[1]; return readU16(buf); } else { unsigned char buf[2]; buf[0] = d->data[0]; buf[1] = d->data[1]; int index = readI16(buf); // negative index means own workbook if(index < 0) { // the real index (absolute value) is one-based unsigned ref = -index - 1; return ref; } } return 0; // FIXME is this safe? } // only when id is Matrix unsigned FormulaToken::refRow() const { // FIXME check data size ! unsigned char buf[2]; buf[0] = d->data[0]; buf[1] = d->data[1]; unsigned ref = readU16(buf); return ref; } // only when id is Matrix unsigned FormulaToken::refColumn() const { // FIXME check data size ! unsigned char buf[2]; buf[0] = d->data[2]; buf[1] = d->data[3]; unsigned ref = readU16(buf); return ref; } std::ostream& Swinder::operator<<( std::ostream& s, Swinder::FormulaToken token ) { s << std::setw(2) << std::hex << token.id() << std::dec; // s << " Size: " << std::dec << token.size(); s << " "; switch( token.id() ) { case FormulaToken::ErrorCode: case FormulaToken::Bool: case FormulaToken::Integer: case FormulaToken::Float: case FormulaToken::String: { Value v = token.value(); s << v; } break; case FormulaToken::Function: s << "Function " << token.functionName(); break; default: s << token.idAsString(); break; } return s; } //============================================= // CellInfo //============================================= class CellInfo::Private { public: unsigned row; unsigned column; unsigned xfIndex; }; CellInfo::CellInfo() { info = new CellInfo::Private(); info->row = 0; info->column = 0; info->xfIndex = 0; } CellInfo::~CellInfo() { delete info; } unsigned CellInfo::row() const { return info->row; } void CellInfo::setRow( unsigned r ) { info->row = r; } unsigned CellInfo::column() const { return info->column; } void CellInfo::setColumn( unsigned c ) { info->column = c; } unsigned CellInfo::xfIndex() const { return info->xfIndex; } void CellInfo::setXfIndex( unsigned i ) { info->xfIndex = i; } //============================================= // ColumnSpanInfo //============================================= class ColumnSpanInfo::Private { public: unsigned firstColumn; unsigned lastColumn; }; ColumnSpanInfo::ColumnSpanInfo() { spaninfo = new ColumnSpanInfo::Private(); spaninfo->firstColumn = 0; spaninfo->lastColumn = 0; } ColumnSpanInfo::~ColumnSpanInfo() { delete spaninfo; } unsigned ColumnSpanInfo::firstColumn() const { return spaninfo->firstColumn; } void ColumnSpanInfo::setFirstColumn( unsigned c ) { spaninfo->firstColumn = c; } unsigned ColumnSpanInfo::lastColumn() const { return spaninfo->lastColumn; } void ColumnSpanInfo::setLastColumn( unsigned c ) { spaninfo->lastColumn = c; } // ========== base record ========== const unsigned int Record::id = 0; // invalid of-course Record::Record() { stream_position = 0; ver = Excel97; } Record::~Record() { } Record* Record::create( unsigned type ) { Record* record = 0; if( type == BOFRecord::id ) record = new BOFRecord(); else if( type == EOFRecord::id ) record = new EOFRecord(); if( type == BackupRecord::id ) record = new BackupRecord(); if( type == BlankRecord::id ) record = new BlankRecord(); if( type == BoolErrRecord::id ) record = new BoolErrRecord(); if( type == BottomMarginRecord::id ) record = new BottomMarginRecord(); if( type == BoundSheetRecord::id ) record = new BoundSheetRecord(); if( type == CalcModeRecord::id ) record = new CalcModeRecord(); if( type == ColInfoRecord::id ) record = new ColInfoRecord(); if( type == DateModeRecord::id ) record = new DateModeRecord(); if( type == DimensionRecord::id ) record = new DimensionRecord(); if( type == ExternNameRecord::id ) record = new ExternNameRecord(); if( type == ExternSheetRecord::id ) record = new ExternSheetRecord(); else if( type == FilepassRecord::id ) record = new FilepassRecord(); else if( type == FontRecord::id ) record = new FontRecord(); else if( type == FooterRecord::id ) record = new FooterRecord(); else if( type == FormatRecord::id ) record = new FormatRecord(); else if( type == FormulaRecord::id ) record = new FormulaRecord(); else if( type == FormulaRecord::idOld ) record = new FormulaRecord(); else if( type == HeaderRecord::id ) record = new HeaderRecord(); else if( type == LabelRecord::id ) record = new LabelRecord(); else if( type == LabelSSTRecord::id ) record = new LabelSSTRecord(); if( type == LeftMarginRecord::id ) record = new LeftMarginRecord(); else if( type == MergedCellsRecord::id ) record = new MergedCellsRecord(); else if( type == MulBlankRecord::id ) record = new MulBlankRecord(); else if( type == MulRKRecord::id ) record = new MulRKRecord(); if( type == NameRecord::id ) record = new NameRecord(); else if( type == NumberRecord::id ) record = new NumberRecord(); else if( type == PaletteRecord::id ) record = new PaletteRecord(); if( type == RightMarginRecord::id ) record = new RightMarginRecord(); else if( type == RKRecord::id ) record = new RKRecord(); else if( type == RowRecord::id ) record = new RowRecord(); else if( type == RStringRecord::id ) record = new RStringRecord(); else if( type == SSTRecord::id ) record = new SSTRecord(); else if( type == StringRecord::id ) record = new StringRecord(); else if( type == SupbookRecord::id ) record = new SupbookRecord(); else if( type == XFRecord::id ) record = new XFRecord(); else if( type == TopMarginRecord::id ) record = new TopMarginRecord(); return record; } void Record::setPosition( unsigned pos ) { stream_position = pos; } unsigned Record::position() const { return stream_position; } void Record::setData( unsigned, const unsigned char* ) { } void Record::dump( std::ostream& ) const { // nothing to dump } // ========== BACKUP ========== const unsigned int BackupRecord::id = 0x0040; class BackupRecord::Private { public: bool backup; }; BackupRecord::BackupRecord(): Record() { d = new BackupRecord::Private(); d->backup = false; } BackupRecord::~BackupRecord() { delete d; } bool BackupRecord::backup() const { return d->backup; } void BackupRecord::setBackup( bool b ) { d->backup = b; } void BackupRecord::setData( unsigned size, const unsigned char* data ) { if( size < 2 ) return; unsigned flag = readU16( data ); d->backup = flag != 0; } void BackupRecord::dump( std::ostream& out ) const { out << "BACKUP" << std::endl; out << " Backup on save : " << (backup() ? "Yes" : "No") << std::endl; } // ========== BLANK ========== const unsigned int BlankRecord::id = 0x0201; BlankRecord::BlankRecord(): Record(), CellInfo() { } void BlankRecord::setData( unsigned size, const unsigned char* data ) { if( size < 6 ) return; setRow( readU16( data ) ); setColumn( readU16( data+2 ) ); setXfIndex( readU16( data+4 ) ); } void BlankRecord::dump( std::ostream& out ) const { out << "BLANK" << std::endl; out << " Row : " << row() << std::endl; out << " Column : " << column() << std::endl; out << " XF Index : " << xfIndex() << std::endl; } // ========== BOF ========== const unsigned int BOFRecord::id = 0x0809; // helper class for BOFRecord class BOFRecord::Private { public: unsigned version; // 0x0500=Excel95, 0x0600=Excel97, and so on unsigned type; unsigned build; unsigned year; unsigned history; unsigned rversion; }; // constructor of BOFRecord BOFRecord::BOFRecord(): Record() { d = new BOFRecord::Private(); d->version = 0x600; // BIFF8; d->type = 0; d->build = 0; d->year = 0; d->history = 0; d->rversion = 0; } // destructor of BOFRecord BOFRecord::~BOFRecord() { delete d; } void BOFRecord::setData( unsigned size, const unsigned char* data ) { if( size < 4 ) return; d->version = readU16( data ); d->type = readU16( data+2 ); if( size > 6 ) { d->build = readU16( data+4 ); d->year = readU16( data+6); if( size > 12 ) { d->history = readU32( data+8 ); d->rversion = readU32( data+12 ); } } } unsigned BOFRecord::version() const { unsigned ver = UnknownExcel; switch( d->version ) { case 0x0500 : ver = Excel95; break; case 0x0600 : ver = Excel97; break; default: break; } return ver; } const char* BOFRecord::versionAsString() const { const char *result = "Unknown"; switch( version() ) { case Excel95 : result = "Excel95"; break; case Excel97 : result = "Excel97"; break; default: break; } return result; } unsigned BOFRecord::type() const { unsigned result = UnknownType; switch( d->type ) { case 0x005 : result = Workbook; break; case 0x006 : result = VBModule; break; case 0x010 : result = Worksheet; break; case 0x020 : result = Chart; break; case 0x040 : result = MacroSheet; break; case 0x100 : result = Workspace; break; default: break; } return result; } const char* BOFRecord::typeAsString() const { const char *result = "Unknown"; switch( type() ) { case Workbook : result = "Workbook"; break; case VBModule : result = "Visual Basic Module"; break; case Worksheet : result = "Worksheet"; break; case Chart : result = "Chart"; break; case MacroSheet : result = "Macro Sheet"; break; case Workspace : result = "Workspace File"; break; default: break; } return result; } void BOFRecord::dump( std::ostream& out ) const { out << "BOF" << std::endl; out << " Version : 0x" << std::hex << d->version << " (" << versionAsString() << ")" << std::endl; out << " Type : 0x" << d->type << " (" << typeAsString() << ")" << std::endl; out << " Build : 0x" << d->build << std::endl; out << " Year : " << std::dec << d->year << std::endl; out << " History : 0x" << std::hex << d->history << std::endl; out << " RVersion : 0x" << d->rversion << std::endl; out << std::dec; } // ========== BOOLERR ========== const unsigned int BoolErrRecord::id = 0x0205; class BoolErrRecord::Private { public: Value value; }; BoolErrRecord::BoolErrRecord(): Record(), CellInfo() { d = new BoolErrRecord::Private(); d->value = Value( false ); } BoolErrRecord::~BoolErrRecord() { delete d; } void BoolErrRecord::setData( unsigned size, const unsigned char* data ) { if( size != 8 ) return; setRow( readU16( data ) ); setColumn( readU16( data+2 ) ); setXfIndex( readU16( data+4 ) ); switch( data[7] ) { case 0 : d->value = Value( data[6] ? true : false ); break; case 1 : d->value = errorAsValue( data[6] ); break; default: // bad bad bad std::cerr << "Warning: bad BOOLERR record" << std::endl; break; } } Value BoolErrRecord::value() const { return d->value; } void BoolErrRecord::dump( std::ostream& out ) const { out << "BOOLERR" << std::endl; out << " Column : " << column() << std::endl; out << " Row : " << row() << std::endl; out << " XFIndex : " << xfIndex() << std::endl; out << " Value : " << value() << std::endl; } // ========== BOTTOMMARGIN ========== const unsigned int BottomMarginRecord::id = 0x0029; class BottomMarginRecord::Private { public: double bottomMargin; }; BottomMarginRecord::BottomMarginRecord(): Record() { d = new BottomMarginRecord::Private(); d->bottomMargin = 1.0; } BottomMarginRecord::~BottomMarginRecord() { delete d; } double BottomMarginRecord::bottomMargin() const { return d->bottomMargin; } void BottomMarginRecord::setBottomMargin( double m ) { d->bottomMargin = m; } void BottomMarginRecord::setData( unsigned size, const unsigned char* data ) { if( size < 8 ) return; setBottomMargin( readFloat64( data ) ); } void BottomMarginRecord::dump( std::ostream& out ) const { out << "BOTTOMMARGIN" << std::endl; out << " Bottom Margin : " << bottomMargin() << " inches" << std::endl; } // ========== BOUNDSHEET ========== const unsigned int BoundSheetRecord::id = 0x0085; // helper class for BoundSheetRecord class BoundSheetRecord::Private { public: unsigned type; // 0=Worksheet, 2=Chart, 6=VB Module unsigned visibility; // 0=visible, 1=hidden, 2=strong hidden UString name; unsigned bofPosition; }; BoundSheetRecord::BoundSheetRecord(): Record() { d = new BoundSheetRecord::Private(); d->type = 0; d->visibility = 0; d->name = "Sheet"; } void BoundSheetRecord::setType( unsigned t ) { switch( t ) { case Worksheet: d->type = 0; break; case Chart: d->type = 2; break; case VBModule: d->type = 6; break; default: d->type = 0; break; // fallback }; } unsigned BoundSheetRecord::type() const { unsigned t = Worksheet; switch( d->type ) { case 0: t = Worksheet; break; case 2: t = Chart; break; case 6: t = VBModule; break; default: break; }; return t; } const char* BoundSheetRecord::typeAsString() const { const char *result = "Unknown"; switch( type() ) { case Worksheet: result = "Worksheet"; break; case Chart: result = "Chart"; break; case VBModule: result = "Visual Basic Module"; break; default: break; } return result; } void BoundSheetRecord::setVisible( bool v ) { d->visibility = v ? 0 : 1; } bool BoundSheetRecord::visible() const { return d->visibility == 0; } void BoundSheetRecord::setSheetName( const UString& n ) { d->name = n; } UString BoundSheetRecord::sheetName() const { return d->name; } void BoundSheetRecord::setBofPosition( unsigned pos ) { d->bofPosition = pos; } unsigned BoundSheetRecord::bofPosition() const { return d->bofPosition; } BoundSheetRecord::~BoundSheetRecord() { delete d; } void BoundSheetRecord::setData( unsigned size, const unsigned char* data ) { if( size < 6 ) return; d->bofPosition = readU32( data ); d->visibility = data[4]; d->type = data[5]; /* FIXME: it turned out that sheet name is not normal unicode string where the first two bytes specifies string length, but instead only the first specifies it. the next byte could be correctly interpreted as flag. */ UString name = ( version() >= Excel97 ) ? EString::fromSheetName( data+6, size-6 ).str() : EString::fromByteString( data+6, false, size-6 ).str(); setSheetName( name ); } void BoundSheetRecord::dump( std::ostream& out ) const { out << "BOUNDSHEET" << std::endl; out << " Name : " << d->name << std::endl; out << " Type : " << d->type << " (" << typeAsString() << ")" << std::endl; out << " Visibility : " << d->visibility << " ("; if( visible() ) out << "Visible"; else out << "Hidden"; out << ")" << std::endl; out << " BOF pos : " << d->bofPosition << std::endl; } // ========== CALCMODE ========== const unsigned int CalcModeRecord::id = 0x000d; class CalcModeRecord::Private { public: bool autoCalc; }; CalcModeRecord::CalcModeRecord(): Record() { d = new CalcModeRecord::Private(); d->autoCalc = false; } CalcModeRecord::~CalcModeRecord() { delete d; } bool CalcModeRecord::autoCalc() const { return d->autoCalc; } void CalcModeRecord::setAutoCalc( bool b ) { d->autoCalc = b; } void CalcModeRecord::setData( unsigned size, const unsigned char* data ) { if( size < 2 ) return; unsigned flag = readU16( data ); d->autoCalc = flag != 0; } void CalcModeRecord::dump( std::ostream& out ) const { out << "CALCMODE" << std::endl; out << " Auto Calc : " << (autoCalc() ? "Yes" : "No") << std::endl; } // ========== COLINFO ========== const unsigned int ColInfoRecord::id = 0x007d; class ColInfoRecord::Private { public: unsigned width; unsigned xfIndex; bool hidden; bool collapsed; unsigned outlineLevel; }; ColInfoRecord::ColInfoRecord(): Record(), ColumnSpanInfo() { d = new ColInfoRecord::Private(); d->width = 2340; d->xfIndex = 0; d->hidden = false; d->collapsed = false; d->outlineLevel = 0; } ColInfoRecord::~ColInfoRecord() { delete d; } // FIXME how to find the real width (in pt/mm/inch) ? unsigned ColInfoRecord::width() const { return d->width; } void ColInfoRecord::setWidth( unsigned w ) { d->width = w; } unsigned ColInfoRecord::xfIndex() const { return d->xfIndex; } void ColInfoRecord::setXfIndex( unsigned i ) { d->xfIndex = i; } bool ColInfoRecord::hidden() const { return d->hidden; } void ColInfoRecord::setHidden( bool h ) { d->hidden = h; } bool ColInfoRecord::collapsed() const { return d->collapsed; } void ColInfoRecord::setCollapsed( bool c ) { d->collapsed = c; } unsigned ColInfoRecord::outlineLevel() const { return d->outlineLevel; } void ColInfoRecord::setOutlineLevel( unsigned l ) { d->outlineLevel = l; } void ColInfoRecord::setData( unsigned size, const unsigned char* data ) { if( size < 10 ) return; setFirstColumn( readU16( data ) ); setLastColumn( readU16( data+2 ) ); setWidth( readU16( data+4 ) ); setXfIndex( readU16( data+6 ) ); unsigned options = readU16( data+8 ); setHidden ( options & 1 ); setCollapsed ( options & 0x1000 ); setOutlineLevel( ( options >> 8 ) & 7 ); } void ColInfoRecord::dump( std::ostream& out ) const { out << "COLINFO" << std::endl; out << " First Column : " << firstColumn() << std::endl; out << " Last Column : " << lastColumn() << std::endl; out << " Width : " << width() << std::endl; out << " XF Index : " << xfIndex() << std::endl; out << " Hidden : " << ( hidden() ? "Yes" : "No" ) << std::endl; out << " Collapsed : " << ( collapsed() ? "Yes" : "No" ) << std::endl; out << " Outline Level : " << outlineLevel() << std::endl; } // ========== DATEMODE ========== const unsigned int DateModeRecord::id = 0x0022; class DateModeRecord::Private { public: bool base1904; }; DateModeRecord::DateModeRecord(): Record() { d = new DateModeRecord::Private(); d->base1904 = false; } DateModeRecord::~DateModeRecord() { delete d; } bool DateModeRecord::base1904() const { return d->base1904; } void DateModeRecord::setBase1904( bool r ) { d->base1904 = r; } void DateModeRecord::setData( unsigned size, const unsigned char* data ) { if( size < 2 ) return; unsigned flag = readU16( data ); d->base1904 = flag != 0; } void DateModeRecord::dump( std::ostream& out ) const { out << "DATEMODE" << std::endl; out << " 1904 base : " << (base1904() ? "Yes" : "No") << std::endl; } // ========== DIMENSION ========== const unsigned int DimensionRecord::id = 0x0200; class DimensionRecord::Private { public: unsigned firstRow; unsigned lastRow; unsigned firstColumn; unsigned lastColumn; }; DimensionRecord::DimensionRecord(): Record() { d = new DimensionRecord::Private; d->firstRow = 0; d->lastRow = 0; d->firstColumn = 0; d->lastColumn = 0; } DimensionRecord::~DimensionRecord() { delete d; } unsigned DimensionRecord::firstRow() const { return d->firstRow; } void DimensionRecord::setFirstRow( unsigned r ) { d->firstRow = r; } unsigned DimensionRecord::lastRow() const { return d->lastRow; } void DimensionRecord::setLastRow( unsigned r ) { d->lastRow = r; } unsigned DimensionRecord::firstColumn() const { return d->firstColumn; } void DimensionRecord::setFirstColumn( unsigned r ) { d->firstColumn = r; } unsigned DimensionRecord::lastColumn() const { return d->lastColumn; } void DimensionRecord::setLastColumn( unsigned r ) { d->lastColumn = r; } void DimensionRecord::setData( unsigned size, const unsigned char* data ) { if( size < 14 ) return; setFirstRow( readU32( data ) ); setLastRow( readU32( data+4 ) - 1 ); setFirstColumn( readU16( data + 8 ) ); setLastColumn( readU16( data + 10 ) - 1 ); } void DimensionRecord::dump( std::ostream& out ) const { out << "DIMENSION" << std::endl; out << " First Row : " << firstRow() << std::endl; out << " Last Row : " << lastRow() << std::endl; out << " First Column : " << firstColumn() << std::endl; out << " Last Column : " << lastColumn() << std::endl; } // ========== EOF ========== const unsigned int EOFRecord::id = 0x000a; EOFRecord::EOFRecord(): Record() { } EOFRecord::~EOFRecord() { } void EOFRecord::setData( unsigned, const unsigned char* ) { // no data associated with EOF record } void EOFRecord::dump( std::ostream& out ) const { out << "EOF" << std::endl; } // ========== EXTERNNAME ========== const unsigned int ExternNameRecord::id = 0x0023; class ExternNameRecord::Private { public: unsigned optionFlags; unsigned sheetIndex; // one-based, not zero-based UString externName; }; ExternNameRecord::ExternNameRecord() { d = new Private; d->optionFlags = 0; d->sheetIndex = 0; } ExternNameRecord::~ExternNameRecord() { delete d; } void ExternNameRecord::setSheetIndex( unsigned sheetIndex ) { d->sheetIndex = sheetIndex; } unsigned ExternNameRecord::sheetIndex() const { return d->sheetIndex; } void ExternNameRecord::setExternName( const UString& name ) { d->externName = name; } UString ExternNameRecord::externName() const { return d->externName; } void ExternNameRecord::setData( unsigned size, const unsigned char* data ) { if( size < 6 ) return; if ( version() == Excel97 ) { d->optionFlags = readU16( data ); d->sheetIndex = readU16( data+2 ); d->externName = EString::fromUnicodeString( data+6, false, size ).str(); } if ( version() == Excel95 ) { d->optionFlags = 0; d->sheetIndex = 0; d->externName = EString::fromByteString( data+6, false, size ).str(); } } void ExternNameRecord::dump( std::ostream& out ) const { } // ========== EXTERNSHEET ========== const unsigned int ExternSheetRecord::id = 0x0017; class ExternSheetRecord::Private { public: typedef struct { unsigned index; unsigned first; unsigned last ; } ExternSheetRef; std::vector refs; UString refName; }; ExternSheetRecord::ExternSheetRecord() { d = new Private; } ExternSheetRecord::~ExternSheetRecord() { delete d; } unsigned ExternSheetRecord::count() const { return d->refs.size(); } unsigned ExternSheetRecord::refIndex(unsigned i) const { if(i >= d->refs.size()) return 0; return d->refs[i].index; } unsigned ExternSheetRecord::firstSheet(unsigned i) const { if(i >= d->refs.size()) return 0; return d->refs[i].first; } unsigned ExternSheetRecord::lastSheet(unsigned i) const { if(i >= d->refs.size()) return 0; return d->refs[i].last; } UString ExternSheetRecord::refName() const { return d->refName; } void ExternSheetRecord::setData( unsigned size, const unsigned char* data ) { d->refs.clear(); d->refName = UString::null; // sanity if(size < 2) return; if(version() >= Excel97) { // don't really trust this unsigned c = readU16(data); unsigned ofs = 2; for(unsigned i = 0; i < c; i++, ofs+=6) { // sanity check if(ofs + 6 > size) break; ExternSheetRecord::Private::ExternSheetRef ref; ref.index = readU16(data+ofs); ref.first = readU16(data+ofs+2); ref.last = readU16(data+ofs+4); d->refs.push_back(ref); } } else { unsigned char dtype = data[1]; unsigned dlen = (unsigned) data[0]; if(dtype == 3) { UString url; url.reserve(dlen); for(int i = 0; i < dlen; i++) { if(i + 2 > size) break; char ch = data[i + 2]; if(ch >= 32) url.append(ch); } d->refName = url; } } } void ExternSheetRecord::dump( std::ostream& out ) const { out << "EXTERNSHEET" << std::endl; } // ========== FILEPASS ========== const unsigned int FilepassRecord::id = 0x002f; FilepassRecord::FilepassRecord(): Record() { } FilepassRecord::~FilepassRecord() { } void FilepassRecord::setData( unsigned, const unsigned char* ) { // TODO } void FilepassRecord::dump( std::ostream& out ) const { out << "FILEPASS" << std::endl; } // ========== FONT ========== const unsigned int FontRecord::id = 0x0031; class FontRecord::Private { public: unsigned height; UString fontName; unsigned fontFamily; unsigned characterSet; unsigned colorIndex; unsigned boldness; bool italic; bool strikeout; unsigned escapement; unsigned underline; }; FontRecord::FontRecord(): Record() { d = new FontRecord::Private; d->height = 11; d->fontName = "Arial"; d->fontFamily = 0; d->characterSet = 0; d->colorIndex = 0; d->boldness = 400; d->italic = false; d->strikeout = false; d->escapement = Normal; d->underline = None; } FontRecord::~FontRecord() { delete d; } FontRecord::FontRecord( const FontRecord& ef ): Record() { d = new FontRecord::Private; operator=( ef ); } FontRecord& FontRecord::operator=( const FontRecord& ef ) { d->height = ef.height(); d->fontName = ef.fontName(); d->fontFamily = ef.fontFamily(); d->characterSet = ef.characterSet(); d->boldness = ef.boldness(); d->italic = ef.italic(); d->strikeout = ef.strikeout(); d->escapement = ef.escapement(); d->underline = ef.underline(); d->colorIndex = ef.colorIndex(); return *this; } unsigned FontRecord::height() const { return d->height; } void FontRecord::setHeight( unsigned h ) { d->height = h; } UString FontRecord::fontName() const { return d->fontName; } void FontRecord::setFontName( const UString& fn ) { d->fontName = fn; } unsigned FontRecord::fontFamily() const { return d->fontFamily; } void FontRecord::setFontFamily( unsigned f ) { d->fontFamily = f; } unsigned FontRecord::characterSet() const { return d->characterSet; } void FontRecord::setCharacterSet( unsigned cs ) { d->characterSet = cs; } unsigned FontRecord::colorIndex() const { return d->colorIndex; } void FontRecord::setColorIndex( unsigned ci ) { d->colorIndex = ci; } unsigned FontRecord::boldness() const { return d->boldness; } void FontRecord::setBoldness( unsigned b ) { d->boldness = b; } bool FontRecord::italic() const { return d->italic; } void FontRecord::setItalic( bool i ) { d->italic = i; } bool FontRecord::strikeout() const { return d->strikeout; } void FontRecord::setStrikeout( bool s ) { d->strikeout = s; } unsigned FontRecord::escapement() const { return d->escapement; } void FontRecord::setEscapement( unsigned s ) { d->escapement = s; } unsigned FontRecord::underline() const { return d->underline; } void FontRecord::setUnderline( unsigned u ) { d->underline = u; } void FontRecord::setData( unsigned size, const unsigned char* data ) { if( size < 14 ) return; setHeight( readU16( data ) ); unsigned flag = readU16( data+2 ); setItalic( flag & 2 ); setStrikeout( flag & 8 ); setStrikeout( flag & 8 ); setColorIndex( readU16( data+4 ) ); setBoldness( readU16( data+6 ) ); setEscapement( readU16( data+8 ) ); setUnderline( data[10] ); setFontFamily( data[11] ); setCharacterSet( data[12] ); UString fn = ( version() >= Excel97 ) ? EString::fromSheetName( data+14, size-14 ).str() : EString::fromByteString( data+14, false, size-14 ).str(); setFontName( fn ); } void FontRecord::dump( std::ostream& out ) const { out << "FONT" << std::endl; out << " Height : " << height() << " twips" << std::endl; out << " Font Name : " << fontName() << std::endl; out << " Color Index : " << colorIndex() << std::endl; out << " Boldness : " << boldness() << std::endl; out << " Italic : " << (italic()?"Yes":"No") << std::endl; out << " Strikeout : " << (strikeout()?"Yes":"No") << std::endl; out << " Escapement : "; switch( escapement() ) { case Normal: out << "Normal" << std::endl; break; case Subscript: out << "Subscript" << std::endl; break; case Superscript: out << "Superscript" << std::endl; break; default: out << "Unkown " << escapement() << std::endl; break; }; } // ========== FOOTER ========== const unsigned int FooterRecord::id = 0x0015; class FooterRecord::Private { public: UString footer; }; FooterRecord::FooterRecord(): Record() { d = new FooterRecord::Private(); } FooterRecord::~FooterRecord() { delete d; } UString FooterRecord::footer() const { return d->footer; } void FooterRecord::setFooter( const UString& footer ) { d->footer = footer; } void FooterRecord::setData( unsigned size, const unsigned char* data ) { if( size < 2 ) return; UString footer = ( version() >= Excel97 ) ? EString::fromUnicodeString( data, true, size ).str() : EString::fromByteString( data, false, size ).str(); setFooter( footer ); } void FooterRecord::dump( std::ostream& out ) const { out << "FOOTER" << std::endl; out << " Footer : " << footer() << std::endl; } // ========== FORMAT ========== const unsigned int FormatRecord::id = 0x041e; class FormatRecord::Private { public: unsigned index; UString formatString; }; FormatRecord::FormatRecord(): Record() { d = new FormatRecord::Private; d->index = 0; } FormatRecord::~FormatRecord() { delete d; } FormatRecord::FormatRecord( const FormatRecord& fr ): Record() { d = new FormatRecord::Private; operator=( fr ); } FormatRecord& FormatRecord::operator=( const FormatRecord& fr ) { d->index = fr.index(); d->formatString = fr.formatString(); return *this; } unsigned FormatRecord::index() const { return d->index; } void FormatRecord::setIndex( unsigned i ) { d->index = i; } UString FormatRecord::formatString() const { return d->formatString; } void FormatRecord::setFormatString( const UString& fs ) { d->formatString = fs; } void FormatRecord::setData( unsigned size, const unsigned char* data ) { if( size < 3 ) return; setIndex( readU16( data ) ); UString fs = ( version() >= Excel97 ) ? EString::fromUnicodeString( data+2, true, size-2 ).str() : EString::fromByteString( data+2, false, size-2 ).str(); setFormatString( fs ); } void FormatRecord::dump( std::ostream& out ) const { out << "FORMAT" << std::endl; out << " Index : " << index() << std::endl; out << " Format String : " << formatString() << std::endl; } // ========== FORMULA ========== const unsigned int FormulaRecord::id = 0x0006; const unsigned int FormulaRecord::idOld = 0x0206; // BIFF3, BIFF4 class FormulaRecord::Private { public: Value result; FormulaTokens tokens; }; FormulaRecord::FormulaRecord(): Record() { d = new FormulaRecord::Private(); } FormulaRecord::~FormulaRecord() { delete d; } Value FormulaRecord::result() const { return d->result; } void FormulaRecord::setResult( const Value& r ) { d->result = r; } FormulaTokens FormulaRecord::tokens() const { return d->tokens; } void FormulaRecord::setData( unsigned size, const unsigned char* data ) { int formula_ofs = 20; // sanity check if( size < formula_ofs ) return; setRow( readU16( data ) ); setColumn( readU16( data+2 ) ); setXfIndex( readU16( data+4 ) ); if( readU16( data+12 ) != 0xffff ) { // Floating-point setResult( Value( readFloat64( data+6 ) ) ); } else { switch( data[6] ) { case 0: // string, real value in subsequent string record setResult( Value( Value::String ) ); break; case 1: // boolean setResult( Value( data[8] ? true : false ) ); break; case 2: // error code setResult( errorAsValue( data[8] ) ); break; case 3: // empty setResult( Value::empty() ); break; default: // fallback setResult( Value::empty() ); break; }; } unsigned formula_len = readU16( data+formula_ofs ); // reconstruct all tokens d->tokens.clear(); for( unsigned j = formula_ofs+2; j < size; ) { unsigned ptg = data[j++]; ptg = ((ptg & 0x40) ? (ptg | 0x20) : ptg) & 0x3F; FormulaToken token( ptg ); token.setVersion( version() ); if( token.id() == FormulaToken::String ) { // find bytes taken to represent the string EString estr = (version()==Excel97) ? EString::fromUnicodeString( data+j, false, formula_len ) : EString::fromByteString( data+j, false, formula_len ); token.setData( estr.size(), data+j ); j += estr.size(); } else { // normal, fixed-size token if( token.size() > 1 ) { token.setData( token.size(), data+j ); j += token.size(); } } d->tokens.push_back( token ); } } void FormulaRecord::dump( std::ostream& out ) const { out << "FORMULA" << std::endl; out << " Row : " << row() << std::endl; out << " Column : " << column() << std::endl; out << " XF Index : " << xfIndex() << std::endl; out << " Result : " << result() << std::endl; FormulaTokens ts = tokens(); out << " Tokens : " << ts.size() << std::endl; for( unsigned i = 0; i < ts.size(); i++ ) out << " " << ts[i] << std::endl; } // ========== LABEL ========== const unsigned int LabelRecord::id = 0x0204; class LabelRecord::Private { public: UString label; }; LabelRecord::LabelRecord(): Record(), CellInfo() { d = new LabelRecord::Private(); d->label = UString::null; } LabelRecord::~LabelRecord() { delete d; } UString LabelRecord::label() const { return d->label; } void LabelRecord::setLabel( const UString& l ) { d->label = l; } void LabelRecord::setData( unsigned size, const unsigned char* data ) { if( size < 6 ) return; setRow( readU16( data ) ); setColumn( readU16( data+2 ) ); setXfIndex( readU16( data+4 ) ); UString label = ( version() >= Excel97 ) ? EString::fromUnicodeString( data+6, true, size-6 ).str() : EString::fromByteString( data+6, true, size-6 ).str(); setLabel( label ); } void LabelRecord::dump( std::ostream& out ) const { out << "LABEL" << std::endl; out << " Row : " << row() << std::endl; out << " Column : " << column() << std::endl; out << " XF Index : " << xfIndex() << std::endl; out << " Label : " << label() << std::endl; } // ========== HEADER ========== const unsigned int HeaderRecord::id = 0x0014; class HeaderRecord::Private { public: UString header; }; HeaderRecord::HeaderRecord(): Record() { d = new HeaderRecord::Private(); } HeaderRecord::~HeaderRecord() { delete d; } UString HeaderRecord::header() const { return d->header; } void HeaderRecord::setHeader( const UString& header ) { d->header = header; } void HeaderRecord::setData( unsigned size, const unsigned char* data ) { if( size < 2 ) return; UString header = ( version() >= Excel97 ) ? EString::fromUnicodeString( data, true, size ).str() : EString::fromByteString( data, false, size ).str(); setHeader( header ); } void HeaderRecord::dump( std::ostream& out ) const { out << "HEADER" << std::endl; out << " Header: " << header() << std::endl; } // ========== LABELSST ========== const unsigned int LabelSSTRecord::id = 0x00fd; class LabelSSTRecord::Private { public: unsigned sstIndex; }; LabelSSTRecord::LabelSSTRecord(): Record(), CellInfo() { d = new LabelSSTRecord::Private(); d->sstIndex = 0; } LabelSSTRecord::~LabelSSTRecord() { delete d; } unsigned LabelSSTRecord::sstIndex() const { return d->sstIndex; } void LabelSSTRecord::setData( unsigned size, const unsigned char* data ) { if( size < 10 ) return; setRow( readU16( data ) ); setColumn( readU16( data+2 ) ); setXfIndex( readU16( data+4 ) ); d->sstIndex = readU32( data+6 ); } void LabelSSTRecord::dump( std::ostream& out ) const { out << "LABELSST" << std::endl; out << " Row : " << row() << std::endl; out << " Column : " << column() << std::endl; out << " XF Index : " << xfIndex() << std::endl; out << " SST Index : " << d->sstIndex << std::endl; } // ========== LEFTMARGIN ========== const unsigned int LeftMarginRecord::id = 0x0026; class LeftMarginRecord::Private { public: double leftMargin; }; LeftMarginRecord::LeftMarginRecord(): Record() { d = new LeftMarginRecord::Private(); d->leftMargin = 1.0; } LeftMarginRecord::~LeftMarginRecord() { delete d; } double LeftMarginRecord::leftMargin() const { return d->leftMargin; } void LeftMarginRecord::setLeftMargin( double m ) { d->leftMargin = m; } void LeftMarginRecord::setData( unsigned size, const unsigned char* data ) { if( size < 8 ) return; setLeftMargin( readFloat64( data ) ); } void LeftMarginRecord::dump( std::ostream& out ) const { out << "LEFTMARGIN" << std::endl; out << " Left Margin : " << leftMargin() << " inches" << std::endl; } // ========== MERGEDCELLS ========== const unsigned int MergedCellsRecord::id = 0x00e5; class MergedInfo { public: unsigned firstRow, lastRow, firstColumn, lastColumn; }; class MergedCellsRecord::Private { public: std::vector mergedCells; }; MergedCellsRecord::MergedCellsRecord(): Record() { d = new MergedCellsRecord::Private(); } MergedCellsRecord::~MergedCellsRecord() { delete d; } unsigned MergedCellsRecord::count() const { return d->mergedCells.size(); } unsigned MergedCellsRecord::firstRow( unsigned i ) const { if( i >= d->mergedCells.size() ) return 0; MergedInfo info = d->mergedCells[ i ]; return info.firstRow; } unsigned MergedCellsRecord::lastRow( unsigned i ) const { if( i >= d->mergedCells.size() ) return 0; MergedInfo info = d->mergedCells[ i ]; return info.lastRow; } unsigned MergedCellsRecord::firstColumn( unsigned i ) const { if( i >= d->mergedCells.size() ) return 0; MergedInfo info = d->mergedCells[ i ]; return info.firstColumn; } unsigned MergedCellsRecord::lastColumn( unsigned i ) const { if( i >= d->mergedCells.size() ) return 0; MergedInfo info = d->mergedCells[ i ]; return info.lastColumn; } void MergedCellsRecord::setData( unsigned size, const unsigned char* data ) { if( size < 2 ) return; unsigned num = readU16( data ); // sanity check if( size < 2 + num*4 ) return; unsigned p = 2; for( unsigned i = 0; i < num; i++ ) { MergedInfo info; info.firstRow = readU16( data + p ); info.lastRow = readU16( data + p + 2 ); info.firstColumn = readU16( data + p + 4 ); info.lastColumn = readU16( data + p + 6 ); p += 8; d->mergedCells.push_back( info ); } } void MergedCellsRecord::dump( std::ostream& out ) const { out << "MERGEDCELLS" << std::endl; out << " Count : " << count() << std::endl; for( unsigned c = 0; c < count(); c++ ) { out << " Merged Cell #" << c << " : "; out << "Column " << firstColumn(c) << "-" << lastColumn(c); out << " Row " << firstRow(c) << "-" << lastRow(c); out << std::endl; } } // ========== MULBLANK ========== const unsigned int MulBlankRecord::id = 0x00be; class MulBlankRecord::Private { public: std::vector xfIndexes; }; MulBlankRecord::MulBlankRecord(): Record(), CellInfo(), ColumnSpanInfo() { d = new MulBlankRecord::Private(); } MulBlankRecord::~MulBlankRecord() { delete d; } void MulBlankRecord::setData( unsigned size, const unsigned char* data ) { if( size < 6 ) return; setRow( readU16( data ) ); setFirstColumn( readU16( data+2 ) ); setLastColumn( readU16( data+size-2 ) ); d->xfIndexes.clear(); for( unsigned i = 4; i < size-2; i+= 2 ) d->xfIndexes.push_back( readU16( data+i ) ); // FIXME sentinel ! } unsigned MulBlankRecord::xfIndex( unsigned i ) const { if( i >= d->xfIndexes.size() ) return 0; return d->xfIndexes[ i ]; } void MulBlankRecord::dump( std::ostream& out ) const { out << "MULBLANK" << std::endl; out << " Row : " << row() << std::endl; out << " First Column : " << firstColumn() << std::endl; out << " Last Column : " << lastColumn() << std::endl; } // ========== MULRK ========== const unsigned int MulRKRecord::id = 0x00bd; class MulRKRecord::Private { public: std::vector xfIndexes; std::vector isIntegers; std::vector intValues; std::vector floatValues; std::vector rkValues; }; MulRKRecord::MulRKRecord(): Record(), CellInfo(), ColumnSpanInfo() { d = new MulRKRecord::Private(); } MulRKRecord::~MulRKRecord() { delete d; } unsigned MulRKRecord::xfIndex( unsigned i ) const { if( i >= d->xfIndexes.size() ) return 0; return d->xfIndexes[ i ]; } bool MulRKRecord::isInteger( unsigned i ) const { if( i >= d->isIntegers.size() ) return true; return d->isIntegers[ i ]; } int MulRKRecord::asInteger( unsigned i ) const { if( i >= d->intValues.size() ) return 0; return d->intValues[ i ]; } double MulRKRecord::asFloat( unsigned i ) const { if( i >= d->floatValues.size() ) return 0.0; return d->floatValues[ i ]; } unsigned MulRKRecord::encodedRK( unsigned i ) const { if( i >= d->rkValues.size() ) return 0; return d->rkValues[ i ]; } void MulRKRecord::setData( unsigned size, const unsigned char* data ) { if( size < 6 ) return; setRow( readU16( data ) ); setFirstColumn( readU16( data+2 ) ); setLastColumn( readU16( data+size-2 ) ); d->xfIndexes.clear(); d->isIntegers.clear(); d->intValues.clear(); d->floatValues.clear(); for( unsigned i = 4; i < size-2; i+= 6 ) { d->xfIndexes.push_back( readU16( data+i ) ); unsigned rk = readU32( data+i+2 ); d->rkValues.push_back( rk ); bool isInteger = true; int iv = 0; double fv = 0.0; decodeRK( rk, isInteger, iv, fv ); d->isIntegers.push_back( isInteger ); d->intValues.push_back( isInteger ? iv : (int)fv ); d->floatValues.push_back( !isInteger ? fv : (double)iv ); } // FIXME sentinel ! } void MulRKRecord::dump( std::ostream& out ) const { out << "MULRK" << std::endl; out << " Row : " << row() << std::endl; out << " First Column : " << firstColumn() << std::endl; out << " Last Column : " << lastColumn() << std::endl; for( unsigned c = firstColumn(); c <= lastColumn(); c++ ) { out << " Column " << c << " : " << asFloat( c-firstColumn() ); out << " Encoded: " << std::hex << encodedRK( c-firstColumn() ); out << std::endl; } } // ========== NAME ========== const unsigned int NameRecord::id = 0x0018; class NameRecord::Private { public: unsigned optionFlags; UString definedName; }; NameRecord::NameRecord() { d = new Private; d->optionFlags = 0; } NameRecord::~NameRecord() { delete d; } void NameRecord::setDefinedName( const UString& name ) { d->definedName = name; } UString NameRecord::definedName() const { return d->definedName; } void NameRecord::setData( unsigned size, const unsigned char* data ) { if( size < 14 ) return; d->optionFlags = readU16( data ); unsigned len = data[3]; if ( version() == Excel95 ) { char* buffer = new char[ len+1 ]; memcpy( buffer, data + 14, len ); buffer[ len ] = 0; d->definedName = UString( buffer ); delete[] buffer; } if ( version() == Excel97 ) { UString str = UString(); for( unsigned k=0; kdefinedName = str; } } void NameRecord::dump( std::ostream& out ) const { } // ========== Number ========== const unsigned int NumberRecord::id = 0x0203; class NumberRecord::Private { public: double number; }; NumberRecord::NumberRecord(): Record(), CellInfo() { d = new NumberRecord::Private(); d->number = 0.0; } NumberRecord::~NumberRecord() { delete d; } double NumberRecord::number() const { return d->number; } void NumberRecord::setNumber( double f ) { d->number = f; } // FIXME check that sizeof(double) is 64 void NumberRecord::setData( unsigned size, const unsigned char* data ) { if( size < 14 ) return; setRow( readU16( data ) ); setColumn( readU16( data+2 ) ); setXfIndex( readU16( data+4 ) ); setNumber( readFloat64( data+6 ) ); } void NumberRecord::dump( std::ostream& out ) const { out << "NUMBER" << std::endl; out << " Row : " << row() << std::endl; out << " Column : " << column() << std::endl; out << " XF Index : " << xfIndex() << std::endl; out << " Value : " << number() << std::endl; } // ========== PALETTE ========== const unsigned int PaletteRecord::id = 0x0092; class PaletteRecord::Private { public: std::vector colors; }; PaletteRecord::PaletteRecord(): Record() { d = new PaletteRecord::Private(); } PaletteRecord::~PaletteRecord() { delete d; } Color PaletteRecord::color( unsigned i ) const { return d->colors[ i ]; } unsigned PaletteRecord::count() const { return d->colors.size(); } void PaletteRecord::setData( unsigned size, const unsigned char* data ) { if( size < 14 ) return; unsigned num = readU16( data ); unsigned p = 2; for( unsigned i = 0; i < num; i++, p+=4 ) { unsigned red = data[ p ]; unsigned green = data[ p+1 ]; unsigned blue = data[ p+2 ]; d->colors.push_back( Color( red, green, blue ) ); } } void PaletteRecord::dump( std::ostream& out ) const { out << "PALETTE" << std::endl; out << " Count : " << count() << std::endl; for( unsigned i = 0; i < count(); i++ ) { out << " Color #" << std::setw(2) << i << " : "; Color c = color( i ); out << "R:" << std::setw(3) << c.red; out << " G:" << std::setw(3) << c.green; out << " B:" << std::setw(3) << c.blue << std::endl; } } // ========== RIGHTMARGIN ========== const unsigned int RightMarginRecord::id = 0x0027; class RightMarginRecord::Private { public: double rightMargin; }; RightMarginRecord::RightMarginRecord(): Record() { d = new RightMarginRecord::Private(); d->rightMargin = 1.0; } RightMarginRecord::~RightMarginRecord() { delete d; } double RightMarginRecord::rightMargin() const { return d->rightMargin; } void RightMarginRecord::setRightMargin( double m ) { d->rightMargin = m; } void RightMarginRecord::setData( unsigned size, const unsigned char* data ) { if( size < 8 ) return; setRightMargin( readFloat64( data ) ); } void RightMarginRecord::dump( std::ostream& out ) const { out << "RIGHTMARGIN" << std::endl; out << " Right Margin : " << rightMargin() << " inches " << std::endl; } // ========== RK ========== const unsigned int RKRecord::id = 0x027e; class RKRecord::Private { public: bool integer; unsigned rk; int i; double f; }; RKRecord::RKRecord(): Record(), CellInfo() { d = new RKRecord::Private(); d->integer = true; d->rk = 0; d->i = 0; d->f = 0.0; } RKRecord::~RKRecord() { delete d; } bool RKRecord::isInteger() const { return d->integer; } bool RKRecord::isFloat() const { return !d->integer; } int RKRecord::asInteger() const { if( d->integer ) return d->i; else return (int)d->f; } double RKRecord::asFloat() const { if( !d->integer ) return d->f; else return (double)d->i; } void RKRecord::setInteger( int i ) { d->integer = true; d->i = i; d->f = (double)i; } void RKRecord::setFloat( double f ) { d->integer = false; d->i = (int)f; d->f = f; } unsigned RKRecord::encodedRK() const { return d->rk; } // FIXME check sizeof(int) is 32 // big vs little endian problem void RKRecord::setData( unsigned size, const unsigned char* data ) { if( size < 10 ) return; setRow( readU16( data ) ); setColumn( readU16( data+2 ) ); setXfIndex( readU16( data+4 ) ); int i = 0; double f = 0.0; d->rk = readU32( data+6 ); decodeRK( d->rk, d->integer, i, f ); if( d->integer ) setInteger( i ); else setFloat( f ); } void RKRecord::dump( std::ostream& out ) const { out << "RK" << std::endl; out << " Row : " << row() << std::endl; out << " Column : " << column() << std::endl; out << " XF Index : " << xfIndex() << std::endl; out << " Value : " << asFloat() << std::endl; out << " Encoded RK : 0x" << std::hex << encodedRK() << std::endl; out << std::dec; } // ========== Row ========== const unsigned int RowRecord::id = 0x0208; class RowRecord::Private { public: unsigned row; unsigned height; unsigned xfIndex; bool hidden; }; RowRecord::RowRecord(): Record(), ColumnSpanInfo() { d = new RowRecord::Private(); d->row = 0; d->height = 50; d->xfIndex = 0; d->hidden = false; } RowRecord::~RowRecord() { delete d; } unsigned RowRecord::row() const { return d->row; } void RowRecord::setRow( unsigned r ) { d->row = r; } unsigned RowRecord::height() const { return d->height; } void RowRecord::setHeight( unsigned h ) { d->height = h; } unsigned RowRecord::xfIndex() const { return d->xfIndex; } void RowRecord::setXfIndex( unsigned i ) { d->xfIndex = i; } bool RowRecord::hidden() const { return d->hidden; } void RowRecord::setHidden( bool h ) { d->hidden = h; } void RowRecord::setData( unsigned size, const unsigned char* data ) { if( size < 16 ) return; setRow( readU16( data ) ); setFirstColumn( readU16( data+2 ) ); setLastColumn( readU16( data+4 ) ); setHeight( readU16( data+6 ) & 0x7fff ); setXfIndex( readU16( data+14 ) & 0xfff ); unsigned options = readU16( data+12 ); setHidden ( options & 0x20 ); } void RowRecord::dump( std::ostream& out ) const { out << "ROW" << std::endl; out << " Row : " << row() << std::endl; out << " First Column : " << firstColumn() << std::endl; out << " Last Column : " << lastColumn() << std::endl; out << " Height : " << height() << std::endl; out << " XF Index : " << xfIndex() << std::endl; out << " Hidden : " << ( hidden() ? "Yes" : "No" ) << std::endl; } // ========== RSTRING ========== const unsigned int RStringRecord::id = 0x00d6; class RStringRecord::Private { public: UString label; }; RStringRecord::RStringRecord(): Record(), CellInfo() { d = new RStringRecord::Private(); d->label = UString::null; } RStringRecord::~RStringRecord() { delete d; } UString RStringRecord::label() const { return d->label; } void RStringRecord::setLabel( const UString& l ) { d->label = l; } // FIXME formatting runs ? in EString perhaps ? void RStringRecord::setData( unsigned size, const unsigned char* data ) { if( size < 6 ) return; setRow( readU16( data ) ); setColumn( readU16( data+2 ) ); setXfIndex( readU16( data+4 ) ); // FIXME check Excel97 UString label = ( version() >= Excel97 ) ? EString::fromUnicodeString( data+6, true, size-6 ).str() : EString::fromByteString( data+6, true, size-6 ).str(); setLabel( label ); } void RStringRecord::dump( std::ostream& out ) const { out << "RSTRING" << std::endl; out << " Row : " << row() << std::endl; out << " Column : " << column() << std::endl; out << " XF Index : " << xfIndex() << std::endl; out << " Label : " << label() << std::endl; } // ========== SST ========== const unsigned int SSTRecord::id = 0x00fc; class SSTRecord::Private { public: unsigned total; unsigned count; std::vector strings; }; SSTRecord::SSTRecord(): Record() { d = new SSTRecord::Private(); d->total = 0; d->count = 0; } SSTRecord::~SSTRecord() { delete d; } UString sstrecord_get_plain_string( const unsigned char* data, unsigned length ) { char* buffer = new char[ length+1 ]; memcpy( buffer, data, length ); buffer[ length ] = 0; UString str = UString( buffer ); delete[] buffer; return str; } void SSTRecord::setData( unsigned size, const unsigned char* data ) { if( size < 8 ) return; d->total = readU32( data ); d->count = readU32( data+4 ); unsigned offset = 8; d->strings.clear(); for( unsigned i = 0; i < d->count; i++ ) { // check against size if (offset >= size) { std::cerr << "Warning: reached end of SST record, but not all strings have been read!" << std::endl; break; } EString es = EString::fromUnicodeString( data+offset, true, size - offset ); d->strings.push_back( es.str() ); offset += es.size(); } // safety, add null string if we can't read all of the rest while( d->strings.size() < d->count ) d->strings.push_back( UString() ); // sanity check, adjust to safer condition if( d->count < d->strings.size() ) { std::cerr << "Warning: mismatch number of string in SST record!" << std::endl; d->count = d->strings.size(); } } unsigned SSTRecord::count() const { return d->count; } // why not just string() ? to avoid easy confusion with std::string UString SSTRecord::stringAt( unsigned index ) const { if( index >= count()) return UString::null; return d->strings[ index ]; } void SSTRecord::dump( std::ostream& out ) const { out << "SST" << std::endl; out << " Occurences : " << d->total << std::endl; out << " Count : " << count() << std::endl; for( unsigned i = 0; i < count(); i++ ) out << " String #" << std::setw(2) << i << " : " << stringAt( i ) << std::endl; } // ========== STRING ========== const unsigned int StringRecord::id = 0x0207; class StringRecord::Private { public: UString string; }; StringRecord::StringRecord(): Record() { d = new StringRecord::Private(); } StringRecord::~StringRecord() { delete d; } void StringRecord::setData( unsigned size, const unsigned char* data ) { if( size < 3 ) return; // TODO simple string for BIFF7 EString es = EString::fromUnicodeString( data, true, size ); d->string = es.str(); } UString StringRecord::ustring() const { return d->string; } Value StringRecord::value() const { return Value( d->string ); } void StringRecord::dump( std::ostream& out ) const { out << "STRING" << std::endl; out << " String : " << ustring() << std::endl; } // ========== SUPBOOK ========== const unsigned int SupbookRecord::id = 0x01ae; class SupbookRecord::Private { public: SupbookRecord::ReferenceType type; }; SupbookRecord::SupbookRecord() { d = new Private; d->type = UnknownRef; } SupbookRecord::~SupbookRecord() { delete d; } SupbookRecord::ReferenceType SupbookRecord::referenceType() const { return d->type; } void SupbookRecord::setReferenceType(SupbookRecord::ReferenceType type) { d->type = type; } void SupbookRecord::setData( unsigned size, const unsigned char* data ) { setReferenceType(UnknownRef); if( version() >= Excel97 ) { // check for add-in function or internal ref first if(size == 4) { unsigned id1 = readU16(data); unsigned id2 = readU16(data+2); if((id1 == 1) && (id2 == 0x3a01)) setReferenceType(AddInRef); // check for internal reference if((id1 > 0) && (id2 == 0x0401)) setReferenceType(InternalRef); } // check for object (DDE/OLE) link if(referenceType() == UnknownRef) if(size > 2) { unsigned id1 = readU16(data); if(id1 == 0) setReferenceType(ObjectLink); } // no match, must be external ref if(referenceType() == UnknownRef) setReferenceType(ExternalRef); } } void SupbookRecord::dump( std::ostream& out ) const { out << "SUPBOOK" << std::endl; } // ========== TOPMARGIN ========== const unsigned int TopMarginRecord::id = 0x0028; class TopMarginRecord::Private { public: double topMargin; }; TopMarginRecord::TopMarginRecord(): Record() { d = new TopMarginRecord::Private(); d->topMargin = 1.0; } TopMarginRecord::~TopMarginRecord() { delete d; } double TopMarginRecord::topMargin() const { return d->topMargin; } void TopMarginRecord::setTopMargin( double m ) { d->topMargin = m; } void TopMarginRecord::setData( unsigned size, const unsigned char* data ) { if( size < 8 ) return; setTopMargin( readFloat64( data ) ); } void TopMarginRecord::dump( std::ostream& out ) const { out << "TOPMARGIN" << std::endl; out << " Top Margin : " << topMargin() << " inches " << std::endl; } // ========== XF ========== const unsigned int XFRecord::id = 0x00e0; class XFRecord::Private { public: unsigned fontIndex; unsigned formatIndex; bool locked; bool formulaHidden; unsigned parentStyle; unsigned horizontalAlignment; unsigned verticalAlignment; bool textWrap; unsigned rotationAngle; bool stackedLetters; unsigned indentLevel; bool shrinkContent; unsigned leftBorderStyle; unsigned leftBorderColor; unsigned rightBorderStyle; unsigned rightBorderColor; unsigned topBorderStyle; unsigned topBorderColor; unsigned bottomBorderStyle; unsigned bottomBorderColor; bool diagonalTopLeft; bool diagonalBottomLeft; unsigned diagonalStyle; unsigned diagonalColor; unsigned fillPattern; unsigned patternForeColor; unsigned patternBackColor; }; XFRecord::XFRecord(): Record() { d = new XFRecord::Private(); d->fontIndex = 0; d->formatIndex = 0; d->locked = false; d->formulaHidden = false; d->parentStyle = 0; d->horizontalAlignment = Left; d->verticalAlignment = VCentered; d->textWrap = false; d->rotationAngle = 0; d->stackedLetters = 0; d->indentLevel = 0; d->shrinkContent = 0; d->leftBorderStyle = 0; d->leftBorderColor = 0; d->rightBorderStyle = 0; d->rightBorderColor = 0; d->topBorderStyle = 0; d->topBorderColor = 0; d->bottomBorderStyle = 0; d->bottomBorderColor = 0; d->diagonalTopLeft = false; d->diagonalBottomLeft = false; d->diagonalStyle = 0; d->diagonalColor = 0; d->fillPattern = 0; d->patternForeColor = 0; d->patternBackColor = 0; } XFRecord::~XFRecord() { delete d; } XFRecord::XFRecord( const XFRecord& xf ): Record() { d = new XFRecord::Private(); operator=( xf ); } XFRecord& XFRecord::operator=( const XFRecord& xf ) { d->fontIndex = xf.fontIndex(); d->formatIndex = xf.formatIndex(); d->locked = xf.locked(); d->formulaHidden = xf.formulaHidden(); d->parentStyle = xf.parentStyle(); d->horizontalAlignment = xf.horizontalAlignment(); d->verticalAlignment = xf.verticalAlignment(); d->textWrap = xf.textWrap(); d->rotationAngle = xf.rotationAngle(); d->stackedLetters = xf.stackedLetters(); d->indentLevel = xf.indentLevel(); d->shrinkContent = xf.shrinkContent(); d->leftBorderStyle = xf.leftBorderStyle(); d->leftBorderColor = xf.leftBorderColor(); d->rightBorderStyle = xf.rightBorderStyle(); d->rightBorderColor = xf.rightBorderColor(); d->topBorderStyle = xf.topBorderStyle(); d->topBorderColor = xf.topBorderColor(); d->bottomBorderStyle = xf.bottomBorderStyle(); d->bottomBorderColor = xf.bottomBorderColor(); d->diagonalTopLeft = xf.diagonalTopLeft(); d->diagonalBottomLeft = xf.diagonalBottomLeft(); d->diagonalStyle = xf.diagonalStyle(); d->diagonalColor = xf.diagonalColor(); d->fillPattern = xf.fillPattern(); d->patternForeColor = xf.patternForeColor(); d->patternBackColor = xf.patternBackColor(); return *this; } unsigned XFRecord::fontIndex() const { return d->fontIndex; } void XFRecord::setFontIndex( unsigned fi ) { d->fontIndex = fi; } unsigned XFRecord::formatIndex() const { return d->formatIndex; } void XFRecord::setFormatIndex( unsigned fi ) { d->formatIndex = fi; } bool XFRecord::locked() const { return d->locked; } void XFRecord::setLocked( bool l ) { d->locked = l; } bool XFRecord::formulaHidden() const { return d->formulaHidden; } void XFRecord::setFormulaHidden( bool f ) { d->formulaHidden = f; } unsigned XFRecord::parentStyle() const { return d->parentStyle; } void XFRecord::setParentStyle( unsigned p ) { d->parentStyle = p; } unsigned XFRecord::horizontalAlignment() const { return d->horizontalAlignment; } void XFRecord::setHorizontalAlignment( unsigned ha ) { d->horizontalAlignment = ha; } const char* XFRecord::horizontalAlignmentAsString() const { const char *result = "Unknown"; switch( horizontalAlignment() ) { case General: result = "General"; break; case Left: result = "Left"; break; case Centered: result = "Centered"; break; case Right: result = "Right"; break; case Justified: result = "Justified"; break; case Filled: result = "Filled"; break; default: break; } return result; } unsigned XFRecord::verticalAlignment() const { return d->verticalAlignment; } void XFRecord::setVerticalAlignment( unsigned va ) { d->verticalAlignment = va; } const char* XFRecord::verticalAlignmentAsString() const { const char *result = "Unknown"; switch( verticalAlignment() ) { case Top: result = "Top"; break; case VCentered: result = "Centered"; break; case Bottom: result = "Bottom"; break; case VJustified: result = "Justified"; break; case VDistributed: result = "Distributed"; break; default: break; } return result; } bool XFRecord::textWrap() const { return d->textWrap; } void XFRecord::setTextWrap( bool wrap ) { d->textWrap = wrap; } unsigned XFRecord::rotationAngle() const { return d->rotationAngle; } void XFRecord::setRotationAngle( unsigned angle ) { d->rotationAngle = angle; } bool XFRecord::stackedLetters() const { return d->stackedLetters; } void XFRecord::setStackedLetters( bool stacked ) { d->stackedLetters = stacked; } unsigned XFRecord::indentLevel() const { return d->indentLevel; } void XFRecord::setIndentLevel( unsigned i ) { d->indentLevel = i; } bool XFRecord::shrinkContent() const { return d->shrinkContent; } void XFRecord::setShrinkContent( bool s ) { d->shrinkContent = s; } unsigned XFRecord::leftBorderStyle() const { return d->leftBorderStyle; } void XFRecord::setLeftBorderStyle( unsigned style ) { d->leftBorderStyle = style; } unsigned XFRecord::leftBorderColor() const { return d->leftBorderColor; } void XFRecord::setLeftBorderColor( unsigned color ) { d->leftBorderColor = color; } unsigned XFRecord::rightBorderStyle() const { return d->rightBorderStyle; } void XFRecord::setRightBorderStyle( unsigned style ) { d->rightBorderStyle = style; } unsigned XFRecord::rightBorderColor() const { return d->rightBorderColor; } void XFRecord::setRightBorderColor( unsigned color ) { d->rightBorderColor = color; } unsigned XFRecord::topBorderStyle() const { return d->topBorderStyle; } void XFRecord::setTopBorderStyle( unsigned style ) { d->topBorderStyle = style; } unsigned XFRecord::topBorderColor() const { return d->topBorderColor; } void XFRecord::setTopBorderColor( unsigned color ) { d->topBorderColor = color; } unsigned XFRecord::bottomBorderStyle() const { return d->bottomBorderStyle; } void XFRecord::setBottomBorderStyle( unsigned style ) { d->bottomBorderStyle = style; } unsigned XFRecord::bottomBorderColor() const { return d->bottomBorderColor; } void XFRecord::setBottomBorderColor( unsigned color ) { d->bottomBorderColor = color; } bool XFRecord::diagonalTopLeft() const { return d->diagonalTopLeft; } void XFRecord::setDiagonalTopLeft( bool dd ) { d->diagonalTopLeft = dd; } bool XFRecord::diagonalBottomLeft() const { return d->diagonalBottomLeft; } void XFRecord::setDiagonalBottomLeft( bool dd ) { d->diagonalBottomLeft = dd; } unsigned XFRecord::diagonalStyle() const { return d->diagonalStyle; } void XFRecord::setDiagonalStyle( unsigned style ) { d->diagonalStyle = style; } unsigned XFRecord::diagonalColor() const { return d->diagonalColor; } void XFRecord::setDiagonalColor( unsigned color ) { d->diagonalColor = color; } unsigned XFRecord::fillPattern() const { return d->fillPattern; } void XFRecord::setFillPattern( unsigned pattern ) { d->fillPattern = pattern; } unsigned XFRecord::patternForeColor() const { return d->patternForeColor; } void XFRecord::setPatternForeColor( unsigned color ) { d->patternForeColor = color; } unsigned XFRecord::patternBackColor() const { return d->patternBackColor; } void XFRecord::setPatternBackColor( unsigned color ) { d->patternBackColor = color; } void XFRecord::setData( unsigned size, const unsigned char* data ) { unsigned recordSize = ( version() == Excel97 ) ? 20: 16; if( size < recordSize ) return; setFontIndex( readU16( data ) ); setFormatIndex( readU16( data+2 ) ); unsigned protection = readU16( data+4 ) & 7; setLocked( protection & 1 ); setFormulaHidden( protection & 2 ); setParentStyle( readU16( data+4 ) >> 4 ); unsigned align = data[6]; setHorizontalAlignment( align & 0x07 ); setVerticalAlignment( align >> 4 ); setTextWrap( align & 0x08 ); unsigned angle = data[7]; setRotationAngle( ( angle != 255 ) ? ( angle & 0x7f ) : 0 ); setStackedLetters( angle == 255 ); if( version() == Excel97 ) { unsigned options = data[8]; unsigned attributes = data[9]; setIndentLevel( options & 0x0f ); setShrinkContent( options & 0x10 ); unsigned linestyle = readU16( data + 10 ); unsigned color1 = readU16( data + 12 ); // unsigned color2 = readU16( data + 14 ); unsigned flag = readU16( data + 16 ); unsigned fill = readU16( data + 18 ); setLeftBorderStyle( linestyle & 0xf ); setRightBorderStyle( ( linestyle >> 4 ) & 0xf ); setTopBorderStyle( ( linestyle >> 8 ) & 0xf ); setBottomBorderStyle( ( linestyle >> 12 ) & 0xf ); setLeftBorderColor( color1 & 0x7f ); setRightBorderColor( ( color1 >> 7 ) & 0x7f ); setTopBorderColor( color1 & 0x7f ); setBottomBorderColor( ( color1 >> 7 ) & 0x7f ); setDiagonalTopLeft( color1 & 0x40 ); setDiagonalBottomLeft( color1 & 0x40 ); setDiagonalStyle( ( flag >> 4 ) & 0x1e ); setDiagonalColor( ( ( flag & 0x1f ) << 2 ) + ( ( color1 >> 14 ) & 3 )); setFillPattern( ( flag >> 10 ) & 0x3f ); setPatternForeColor( fill & 0x7f ); setPatternBackColor( ( fill >> 7 ) & 0x7f ); } else { unsigned data1 = readU32( data + 8 ); unsigned data2 = readU32( data + 12 ); setPatternForeColor( data1 & 0x7f ); setPatternBackColor( ( data1 >> 7 ) & 0x7f ); setFillPattern( ( data1 >> 16 ) & 0x3f ); setBottomBorderStyle( ( data1 >> 22 ) & 0x07 ); setBottomBorderColor( ( data1 >> 25 ) & 0x7f ); setTopBorderStyle( data2 & 0x07 ); setLeftBorderStyle( ( data2 >> 3 ) & 0x07 ); setRightBorderStyle( ( data2 >> 6 ) & 0x07 ); setTopBorderColor( ( data2 >> 9 ) & 0x7f ); setLeftBorderColor( ( data2 >> 16 ) & 0x7f ); setRightBorderColor( ( data2 >> 23 ) & 0x7f ); } } void XFRecord::dump( std::ostream& out ) const { out << "XF" << std::endl; out << " Parent Style : " << parentStyle() << std::endl; out << " Font Index : " << fontIndex() << std::endl; out << " Format Index : " << formatIndex() << std::endl; out << " Locked : " << (locked()?"Yes":"No") << std::endl; out << " Formula Visibility : " << (formulaHidden()?"Hidden":"Visible") << std::endl; out << " Horizontal Align : " << horizontalAlignmentAsString() << std::endl; out << " Vertical Align : " << verticalAlignmentAsString() << std::endl; out << " Text Wrap : " << ( textWrap() ? "yes" : "no" ) << std::endl; out << " Rotation : " << rotationAngle() << std::endl; out << " Stacked Letters : " << ( stackedLetters() ? "yes" : "no" ) << std::endl; out << " Indent Level : " << indentLevel() << std::endl; out << " Shrink To Fit : " << ( shrinkContent() ? "yes" : "no" ) << std::endl; out << " Left Border : Style " << leftBorderStyle(); out << " Color: " << leftBorderColor() << std::endl; out << " Right Border : Style " << rightBorderStyle(); out << " Color: " << rightBorderColor() << std::endl; out << " Top Border : Style " << topBorderStyle(); out << " Color: " << topBorderColor() << std::endl; out << " Bottom Border : Style " << bottomBorderStyle(); out << " Color: " << bottomBorderColor() << std::endl; out << " Diagonal Lines : " ; if ( diagonalTopLeft() ) out << "TopLeft "; if ( diagonalBottomLeft() ) out << "BottomLeft "; out << "Style " << diagonalStyle() << " Color: " << diagonalColor() << std::endl; out << " Fill Pattern : " << fillPattern() << std::endl; out << " Fill Color : Fore " << patternForeColor() << " Back: " << patternBackColor() << std::endl; } //============================================= // ExcelReader //============================================= struct ExcelReaderExternalWorkbook { bool addin; bool external; bool internal; bool objectLink; }; class ExcelReader::Private { public: // the workbook Workbook* workbook; // password protection flag // TODO: password hash for record decryption bool passwordProtected; // active sheet, all cell records will be stored here Sheet* activeSheet; // for FORMULA+STRING record pair Cell* formulaCell; // mapping from BOF pos to actual Sheet std::map bofMap; // shared-string table std::vector stringTable; // table of format std::map formatTable; std::map formatsTable; // table of font std::vector fontTable; // table of Xformat std::vector xfTable; // color table (from Palette record) std::vector colorTable; // mapping from font index to Swinder::FormatFont std::map fontCache; // for NAME and EXTERNNAME std::vector nameTable; // for SUPBOOK std::vector externalWorkbooks; // for EXTERNSHEET std::vector sheetRefs; // for mergeToken functions UString mergedTokens; }; ExcelReader::ExcelReader() { d = new ExcelReader::Private(); d->workbook = 0; d->activeSheet = 0; d->formulaCell = 0; d->passwordProtected = false; d->mergedTokens.reserve(1024); // initialize palette // default palette for all but the first 8 colors static const char *const default_palette[64-8] = { "#000000", "#ffffff", "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", "#00ffff", "#800000", "#008000", "#000080", "#808000", "#800080", "#008080", "#c0c0c0", "#808080", "#9999ff", "#993366", "#ffffcc", "#ccffff", "#660066", "#ff8080", "#0066cc", "#ccccff", "#000080", "#ff00ff", "#ffff00", "#00ffff", "#800080", "#800000", "#008080", "#0000ff", "#00ccff", "#ccffff", "#ccffcc", "#ffff99", "#99ccff", "#ff99cc", "#cc99ff", "#ffcc99", "#3366ff", "#33cccc", "#99cc00", "#ffcc00", "#ff9900", "#ff6600", "#666699", "#969696", "#003366", "#339966", "#003300", "#333300", "#993300", "#993366", "#333399", "#333333", }; for( int i = 0; i < 64-8; i++ ) { d->colorTable.push_back(Color(default_palette[i])); } // default number formats for( int format = 0; format < 50; format++) { UString valueFormat; switch(format) { case 0: break; case 1: valueFormat = "0"; break; case 2: valueFormat = "0.00"; break; case 3: valueFormat = "#,##0"; break; case 4: valueFormat = "#,##0.00"; break; case 5: valueFormat = "\"$\"#,##0_);(\"S\"#,##0)"; break; case 6: valueFormat = "\"$\"#,##0_);[Red](\"S\"#,##0)"; break; case 7: valueFormat = "\"$\"#,##0.00_);(\"S\"#,##0.00)"; break; case 8: valueFormat = "\"$\"#,##0.00_);[Red](\"S\"#,##0.00)"; break; case 9: valueFormat = "0%"; break; case 10: valueFormat = "0.00%"; break; case 11: valueFormat = "0.00E+00"; break; case 12: valueFormat = "#?/?"; break; case 13: valueFormat = "#\?\?/\?\?"; break; case 14: valueFormat = "M/D/YY"; break; case 15: valueFormat = "D-MMM-YY"; break; case 16: valueFormat = "D-MMM"; break; case 17: valueFormat = "MMM-YY"; break; case 18: valueFormat = "h:mm AM/PM"; break; case 19: valueFormat = "h:mm:ss AM/PM"; break; case 20: valueFormat = "h:mm"; break; case 21: valueFormat = "h:mm:ss"; break; case 22: valueFormat = "M/D/YY h:mm"; break; case 37: valueFormat = "_(#,##0_);(#,##0)"; break; case 38: valueFormat = "_(#,##0_);[Red](#,##0)"; break; case 39: valueFormat = "_(#,##0.00_);(#,##0)"; break; case 40: valueFormat = "_(#,##0.00_);[Red](#,##0)"; break; case 41: valueFormat = "_(\"$\"*#,##0_);_(\"$\"*#,##0_);_(\"$\"*\"-\");(@_)"; break; case 42: valueFormat = "_(*#,##0_);(*(#,##0);_(*\"-\");_(@_)"; break; case 43: valueFormat = "_(\"$\"*#,##0.00_);_(\"$\"*#,##0.00_);_(\"$\"*\"-\");(@_)"; break; case 44: valueFormat = "_(\"$\"*#,##0.00_);_(\"$\"*#,##0.00_);_(\"$\"*\"-\");(@_)"; break; case 45: valueFormat = "mm:ss"; break; case 46: valueFormat = "[h]:mm:ss"; break; case 47: valueFormat = "mm:ss.0"; break; case 48: valueFormat = "##0.0E+0"; break; case 49: valueFormat = "@"; break; } d->formatsTable[format] = valueFormat; } } ExcelReader::~ExcelReader() { delete d; } // convert border style, e.g MediumDashed to a Pen static Pen convertBorderStyle( unsigned style ) { Pen pen; switch( style ) { case XFRecord::NoLine: pen.width = 0; pen.style = Pen::NoLine; break; case XFRecord::Thin: pen.width = 1; pen.style = Pen::SolidLine; break; case XFRecord::Medium: pen.width = 3; pen.style = Pen::SolidLine; break; case XFRecord::Dashed: pen.width = 1; pen.style = Pen::DashLine; break; case XFRecord::Dotted: pen.width = 1; pen.style = Pen::DotLine; break; case XFRecord::Thick: pen.width = 4; pen.style = Pen::SolidLine; break; case XFRecord::Double: // FIXME no equivalent ? pen.width = 4; pen.style = Pen::SolidLine; break; case XFRecord::Hair: // FIXME no equivalent ? pen.width = 1; pen.style = Pen::DotLine; break; case XFRecord::MediumDashed: pen.width = 3; pen.style = Pen::DashLine; break; case XFRecord::ThinDashDotted: pen.width = 1; pen.style = Pen::DashDotLine; break; case XFRecord::MediumDashDotted: pen.width = 3; pen.style = Pen::DashDotLine; break; case XFRecord::ThinDashDotDotted: pen.width = 1; pen.style = Pen::DashDotDotLine; break; case XFRecord::MediumDashDotDotted: pen.width = 3; pen.style = Pen::DashDotDotLine; break; case XFRecord::SlantedMediumDashDotted: // FIXME no equivalent ? pen.width = 3; pen.style = Pen::DashDotLine; break; default: // fallback, simple solid line pen.width = 1; pen.style = Pen::SolidLine; break; }; return pen; } unsigned convertPatternStyle( unsigned pattern ) { switch ( pattern ) { case 0x00: return FormatBackground::EmptyPattern; case 0x01: return FormatBackground::SolidPattern; case 0x02: return FormatBackground::Dense4Pattern; case 0x03: return FormatBackground::Dense3Pattern; case 0x04: return FormatBackground::Dense5Pattern; case 0x05: return FormatBackground::HorPattern; case 0x06: return FormatBackground::VerPattern; case 0x07: return FormatBackground::FDiagPattern; case 0x08: return FormatBackground::BDiagPattern; case 0x09: return FormatBackground::Dense1Pattern; case 0x0A: return FormatBackground::Dense2Pattern; case 0x0B: return FormatBackground::HorPattern; case 0x0C: return FormatBackground::VerPattern; case 0x0D: return FormatBackground::FDiagPattern; case 0x0E: return FormatBackground::BDiagPattern; case 0x0F: return FormatBackground::CrossPattern; case 0x10: return FormatBackground::DiagCrossPattern; case 0x11: return FormatBackground::Dense6Pattern; case 0x12: return FormatBackground::Dense7Pattern; default: return FormatBackground::SolidPattern; // fallback } } bool ExcelReader::load( Workbook* workbook, const char* filename ) { POLE::Storage storage( filename ); if( !storage.open() ) { //std::cerr << "Cannot open " << filename << std::endl; return false; } unsigned version = Swinder::Excel97; POLE::Stream* stream; stream = new POLE::Stream( &storage, "/Workbook" ); if( stream->fail() ) { delete stream; stream = new POLE::Stream( &storage, "/Book" ); version = Swinder::Excel95; } if( stream->fail() ) { //std::cerr << filename << " is not Excel workbook" << std::endl; delete stream; return false; } unsigned long stream_size = stream->size(); unsigned int buffer_size = 65536; // current size of the buffer unsigned char *buffer = (unsigned char *) malloc(buffer_size); unsigned char small_buffer[128]; // small, fixed size buffer workbook->clear(); d->workbook = workbook; d->passwordProtected = false; // assume while( stream->tell() < stream_size ) { // this is set by FILEPASS record // subsequent records will need to be decrypted // since we do not support it yet, we have to bail out if(d->passwordProtected) { d->workbook->setPasswordProtected( true ); break; } // get record type and data size unsigned long pos = stream->tell(); unsigned bytes_read = stream->read( buffer, 4 ); if( bytes_read != 4 ) break; unsigned long type = readU16( buffer ); unsigned long size = readU16( buffer + 2 ); // verify buffer is large enough to hold the record data if (size > buffer_size) { buffer = (unsigned char *) realloc(buffer, size); buffer_size = size; } // load actual record data bytes_read = stream->read( buffer, size ); if( bytes_read != size ) break; // save current position in stream, to be able to restore the position later on unsigned long saved_pos; // repeatedly check if the next record is type 0x3C, a continuation record unsigned long next_type; // the type of the next record do { saved_pos = stream->tell(); bytes_read = stream->read( small_buffer, 4 ); if (bytes_read != 4) break; next_type = readU16( small_buffer ); unsigned long next_size = readU16( small_buffer + 2 ); if (next_type == 0x3C) { // type of next record is 0x3C, so go ahead and append the contents of the next record to the buffer // first verify the buffer is large enough to hold all the data if ( (size + next_size) > buffer_size) { buffer = (unsigned char *) realloc(buffer, size + next_size); buffer_size = size + next_size; } // next read the data of the record bytes_read = stream->read( buffer + size, next_size ); if (bytes_read != next_size) { std::cout << "ERROR!" << std::endl; break; } // if the first read byte is a zero, remove it (at least that is what the old excel97 filter did...) if (buffer[size] == 0) { memmove( buffer + size, buffer + size + 1, --next_size ); } // and finally update size size += next_size; } } while (next_type == 0x3C); // restore position in stream to the beginning of the next record stream->seek( saved_pos ); // skip record type 0, this is just for filler if( type == 0 ) continue; // create the record using the factory Record* record = Record::create( type ); if( record ) { // setup the record and invoke handler record->setVersion( version ); record->setData( size, buffer ); record->setPosition( pos ); handleRecord( record ); // special handling to find Excel version if ( record->rtti() == BOFRecord::id ) { BOFRecord* bof = static_cast(record); if( bof ) if( bof->type() == BOFRecord::Workbook ) version = bof->version(); } #ifdef SWINDER_XLS2RAW std::cout << "Record 0x"; std::cout << std::setfill('0') << std::setw(4) << std::hex << record->rtti(); std::cout << " "; std::cout << std::dec; std::cout << "(Pos: " << record->position() << ") "; record->dump( std::cout ); std::cout << std::endl; #endif delete record; } #ifdef SWINDER_XLS2RAW if( !record ) { std::cout << "Record 0x"; std::cout << std::setfill('0') << std::setw(4) << std::hex << type; std::cout << std::dec; std::cout << "(Pos: " << pos << ") "; std::cout << std::endl; std::cout << std::endl; } #endif } free(buffer); delete stream; storage.close(); // for each XF, create the corresponding format for(int i = 0; i < d->xfTable.size(); i++ ) { Format format; const XFRecord& xf = d->xfTable[i]; UString valueFormat = d->formatsTable[xf.formatIndex()]; format.setValueFormat( valueFormat ); format.setFont( convertFont( xf.fontIndex() ) ); FormatAlignment alignment; switch( xf.horizontalAlignment() ) { case XFRecord::Left: alignment.setAlignX( Format::Left ); break; case XFRecord::Right: alignment.setAlignX( Format::Right ); break; case XFRecord::Centered: alignment.setAlignX( Format::Center ); break; default: break; // FIXME still unsupported: Repeat, Justified, Filled, Distributed }; switch( xf.verticalAlignment() ) { case XFRecord::Top: alignment.setAlignY( Format::Top ); break; case XFRecord::VCentered: alignment.setAlignY( Format::Middle ); break; case XFRecord::Bottom: alignment.setAlignY( Format::Bottom ); break; default: break; // FIXME still unsupported: Justified, Distributed } alignment.setWrap( xf.textWrap() ); format.setAlignment( alignment ); FormatBorders borders; Pen pen; pen = convertBorderStyle( xf.leftBorderStyle() ); pen.color = convertColor( xf.leftBorderColor() ); borders.setLeftBorder( pen ); pen = convertBorderStyle( xf.rightBorderStyle() ); pen.color = convertColor( xf.rightBorderColor() ); borders.setRightBorder( pen ); pen = convertBorderStyle( xf.topBorderStyle() ); pen.color = convertColor( xf.topBorderColor() ); borders.setTopBorder( pen ); pen = convertBorderStyle( xf.bottomBorderStyle() ); pen.color = convertColor( xf.bottomBorderColor() ); borders.setBottomBorder( pen ); format.setBorders( borders ); FormatBackground background; background.setForegroundColor( convertColor( xf.patternForeColor() ) ); background.setBackgroundColor( convertColor( xf.patternBackColor() ) ); background.setPattern( convertPatternStyle( xf.fillPattern() ) ); format.setBackground( background ); d->workbook->setFormat( i, format ); } return true; } void ExcelReader::handleRecord( Record* record ) { if( !record ) return; unsigned type = record->rtti(); switch( type ) { case BottomMarginRecord::id: handleBottomMargin( static_cast( record ) ); break; case BoundSheetRecord::id: handleBoundSheet( static_cast( record ) ); break; case BOFRecord::id: handleBOF( static_cast( record ) ); break; case BoolErrRecord::id: handleBoolErr( static_cast( record ) ); break; case BlankRecord::id: handleBlank( static_cast( record ) ); break; case CalcModeRecord::id: handleCalcMode( static_cast( record ) ); break; case ColInfoRecord::id: handleColInfo( static_cast( record ) ); break; case ExternNameRecord::id: handleExternName( static_cast( record ) ); break; case ExternSheetRecord::id: handleExternSheet( static_cast( record ) ); break; case FilepassRecord::id: handleFilepass( static_cast( record ) ); break; case FormatRecord::id: handleFormat( static_cast( record ) ); break; case FormulaRecord::id: handleFormula( static_cast( record ) ); break; case FontRecord::id: handleFont( static_cast( record ) ); break; case FooterRecord::id: handleFooter( static_cast( record ) ); break; case HeaderRecord::id: handleHeader( static_cast( record ) ); break; case LabelRecord::id: handleLabel( static_cast( record ) ); break; case LabelSSTRecord::id: handleLabelSST( static_cast( record ) ); break; case LeftMarginRecord::id: handleLeftMargin( static_cast( record ) ); break; case MergedCellsRecord::id: handleMergedCells( static_cast( record ) ); break; case MulBlankRecord::id: handleMulBlank( static_cast( record ) ); break; case MulRKRecord::id: handleMulRK( static_cast( record ) ); break; case NameRecord::id: handleName( static_cast( record ) ); break; case NumberRecord::id: handleNumber( static_cast( record ) ); break; case PaletteRecord::id: handlePalette( static_cast( record ) ); break; case RightMarginRecord::id: handleRightMargin( static_cast( record ) ); break; case RKRecord::id: handleRK( static_cast( record ) ); break; case RowRecord::id: handleRow( static_cast( record ) ); break; case RStringRecord::id: handleRString( static_cast( record ) ); break; case SSTRecord::id: handleSST( static_cast( record ) ); break; case StringRecord::id: handleString( static_cast( record ) ); break; case SupbookRecord::id: handleSupbook( static_cast( record ) ); break; case TopMarginRecord::id: handleTopMargin( static_cast( record ) ); break; case XFRecord::id: handleXF( static_cast( record ) ); break; default: break; } } void ExcelReader::handleBottomMargin( BottomMarginRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; // convert from inches to points double margin = record->bottomMargin() * 72.0; d->activeSheet->setBottomMargin( margin ); } // FIXME does the order of sheet follow BOUNDSHEET of BOF(Worksheet) ? // for now, assume BOUNDSHEET, hence we should create the sheet here void ExcelReader::handleBoundSheet( BoundSheetRecord* record ) { if( !record ) return; // only care for Worksheet, forget everything else if( record->type() == BoundSheetRecord::Worksheet ) { // create a new sheet Sheet* sheet = new Sheet( d->workbook ); sheet->setName( record->sheetName() ); sheet->setVisible( record->visible() ); d->workbook->appendSheet( sheet ); // update bof position map unsigned bofPos = record->bofPosition(); d->bofMap[ bofPos ] = sheet; } } void ExcelReader::handleBOF( BOFRecord* record ) { if( !record ) return; if( record->type() == BOFRecord::Worksheet ) { // find the sheet and make it active // which sheet ? look from from previous BoundSheet Sheet* sheet = d->bofMap[ record->position() ]; if( sheet ) d->activeSheet = sheet; } } void ExcelReader::handleBoolErr( BoolErrRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; unsigned column = record->column(); unsigned row = record->row(); unsigned xfIndex = record->xfIndex(); Cell* cell = d->activeSheet->cell( column, row, true ); if( cell ) { cell->setValue( record->value() ); cell->setFormatIndex( xfIndex ); } } void ExcelReader::handleBlank( BlankRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; unsigned column = record->column(); unsigned row = record->row(); unsigned xfIndex = record->xfIndex(); Cell* cell = d->activeSheet->cell( column, row, true ); if( cell ) { cell->setFormatIndex( xfIndex ); } } void ExcelReader::handleCalcMode( CalcModeRecord* record ) { if( !record ) return; d->workbook->setAutoCalc( record->autoCalc() ); } void ExcelReader::handleColInfo( ColInfoRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; unsigned firstColumn = record->firstColumn(); unsigned lastColumn = record->lastColumn(); unsigned xfIndex = record->xfIndex(); unsigned width = record->width(); bool hidden = record->hidden(); for( unsigned i = firstColumn; i <= lastColumn; i++ ) { Column* column = d->activeSheet->column( i, true ); if( column ) { column->setWidth( width / 120 ); column->setFormatIndex( xfIndex ); column->setVisible( !hidden ); } } } void ExcelReader::handleDateMode( DateModeRecord* record ) { if( !record ) return; // FIXME FIXME what to do ?? std::cerr << "WARNING: Workbook uses unsupported 1904 Date System " << std::endl; } void ExcelReader::handleDimension( DimensionRecord* record ) { if( !record ) return; // in the mean time we don't need to handle this because we don't care // about the used range of the sheet } void ExcelReader::handleLabel( LabelRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; unsigned column = record->column(); unsigned row = record->row(); unsigned xfIndex = record->xfIndex(); UString label = record->label(); Cell* cell = d->activeSheet->cell( column, row, true ); if( cell ) { cell->setValue( Value( label ) ); cell->setFormatIndex( xfIndex ); } } void ExcelReader::handleLeftMargin( LeftMarginRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; // convert from inches to points double margin = record->leftMargin() * 72.0; d->activeSheet->setLeftMargin( margin ); } void ExcelReader::handleFormat( FormatRecord* record ) { if( !record ) return; d->formatTable[ record->index() ] = *record; d->formatsTable[ record->index() ] = record->formatString(); } void ExcelReader::handleFormula( FormulaRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; unsigned column = record->column(); unsigned row = record->row(); unsigned xfIndex = record->xfIndex(); Value value = record->result(); #if 1 // this gives the formula in OpenDocument format, e.g. "=SUM([A1]; [A2]) UString formula = decodeFormula( row, column, record->tokens(), true ); #else // this gives the formula in standard Excel format, e.g. "=SUM(A1, A2) UString formula = decodeFormula( row, column, record->tokens(), true ); #endif Cell* cell = d->activeSheet->cell( column, row, true ); if( cell ) { cell->setValue( value ); if( !formula.isEmpty() ) cell->setFormula( formula ); cell->setFormatIndex( xfIndex ); // if value is string, real value is in subsequent String record if( value.isString() ) d->formulaCell = cell; } } void ExcelReader::handleExternName( ExternNameRecord* record ) { if( !record ) return; d->nameTable.push_back( record->externName() ); } void ExcelReader::handleExternSheet( ExternSheetRecord* record ) { if( !record ) return; if(record->version() >= Excel97) for(unsigned i = 0; i < record->count(); i++) { UString decodedRef("#REF"); unsigned index = record->refIndex(i); unsigned first = record->firstSheet(i); unsigned last = record->lastSheet(i); if(index < d->externalWorkbooks.size()) { // handle reference to own workbook if(d->externalWorkbooks[index].internal) if(first < d->workbook->sheetCount()) decodedRef = d->workbook->sheet(first)->name(); // add-in? let's (re)use # marker as in Excel 95 if(d->externalWorkbooks[index].addin) decodedRef = UString("#"); } d->sheetRefs.push_back(decodedRef); } else { UString ref = record->refName(); d->sheetRefs.push_back(ref); } } void ExcelReader::handleFilepass( FilepassRecord* record ) { if( !record ) return; d->passwordProtected = true; } void ExcelReader::handleFont( FontRecord* record ) { if( !record ) return; d->fontTable.push_back( *record ); // font #4 is never used, so add a dummy one if( d->fontTable.size() == 4 ) d->fontTable.push_back( FontRecord() ); } void ExcelReader::handleFooter( FooterRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; UString footer = record->footer(); UString left, center, right; int pos = -1, len = 0; // left part pos = footer.find( UString("&L") ); if( pos >= 0 ) { pos += 2; len = footer.find( UString("&C") ) - pos; if( len > 0 ) { left = footer.substr( pos, len ); footer = footer.substr( pos+len, footer.length() ); } } // center part pos = footer.find( UString("&C") ); if( pos >= 0 ) { pos += 2; len = footer.find( UString("&R") ) - pos; if( len > 0 ) { center = footer.substr( pos, len ); footer = footer.substr( pos+len, footer.length() ); } } // right part pos = footer.find( UString("&R") ); if( pos >= 0 ) { pos += 2; right = footer.substr( pos, footer.length() - pos ); } d->activeSheet->setLeftFooter( left ); d->activeSheet->setCenterFooter( center ); d->activeSheet->setRightFooter( right ); } void ExcelReader::handleHeader( HeaderRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; UString header = record->header(); UString left, center, right; int pos = -1, len = 0; // left part of the header pos = header.find( UString("&L") ); if( pos >= 0 ) { pos += 2; len = header.find( UString("&C") ) - pos; if( len > 0 ) { left = header.substr( pos, len ); header = header.substr( pos+len, header.length() ); } } // center part of the header pos = header.find( UString("&C") ); if( pos >= 0 ) { pos += 2; len = header.find( UString("&R") ) - pos; if( len > 0 ) { center = header.substr( pos, len ); header = header.substr( pos+len, header.length() ); } } // right part of the header pos = header.find( UString("&R") ); if( pos >= 0 ) { pos += 2; right = header.substr( pos, header.length() - pos ); } d->activeSheet->setLeftHeader( left ); d->activeSheet->setCenterHeader( center ); d->activeSheet->setRightHeader( right ); } void ExcelReader::handleLabelSST( LabelSSTRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; unsigned column = record->column(); unsigned row = record->row(); unsigned index = record->sstIndex(); unsigned xfIndex = record->xfIndex(); UString str; if( index < d->stringTable.size() ) str = d->stringTable[ index ]; Cell* cell = d->activeSheet->cell( column, row, true ); if( cell ) { cell->setValue( Value( str ) ); cell->setFormatIndex( xfIndex ); } } void ExcelReader::handleMergedCells( MergedCellsRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; for( unsigned i = 0; i < record->count(); i++ ) { unsigned firstRow = record->firstRow( i ); unsigned lastRow = record->lastRow( i ); unsigned firstColumn = record->firstColumn( i ); unsigned lastColumn = record->lastColumn( i ); Cell* cell = d->activeSheet->cell( firstColumn, firstRow, true ); if( cell ) { cell->setColumnSpan( lastColumn - firstColumn + 1 ); cell->setRowSpan( lastRow - firstRow + 1 ); } } } void ExcelReader::handleMulBlank( MulBlankRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; unsigned firstColumn = record->firstColumn(); unsigned lastColumn = record->lastColumn(); unsigned row = record->row(); for( unsigned column = firstColumn; column <= lastColumn; column++ ) { Cell* cell = d->activeSheet->cell( column, row, true ); if( cell ) { cell->setFormatIndex( record->xfIndex( column - firstColumn ) ); } } } void ExcelReader::handleMulRK( MulRKRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; unsigned firstColumn = record->firstColumn(); unsigned lastColumn = record->lastColumn(); unsigned row = record->row(); for( unsigned column = firstColumn; column <= lastColumn; column++ ) { Cell* cell = d->activeSheet->cell( column, row, true ); if( cell ) { unsigned i = column - firstColumn; Value value; if( record->isInteger( i ) ) value.setValue( record->asInteger( i ) ); else value.setValue( record->asFloat( i ) ); cell->setValue( value ); cell->setFormatIndex( record->xfIndex( column-firstColumn ) ); } } } void ExcelReader::handleName( NameRecord* record ) { if( !record ) return; d->nameTable.push_back( record->definedName() ); } void ExcelReader::handleNumber( NumberRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; unsigned column = record->column(); unsigned row = record->row(); unsigned xfIndex = record->xfIndex(); double number = record->number(); Cell* cell = d->activeSheet->cell( column, row, true ); if( cell ) { cell->setValue( Value( number ) ); cell->setFormatIndex( xfIndex ); } } void ExcelReader::handlePalette( PaletteRecord* record ) { if( !record ) return; d->colorTable.clear(); for( unsigned i = 0; i < record->count(); i++ ) d->colorTable.push_back( record->color( i ) ); } void ExcelReader::handleRightMargin( RightMarginRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; // convert from inches to points double margin = record->rightMargin() * 72.0; d->activeSheet->setRightMargin( margin ); } void ExcelReader::handleRK( RKRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; unsigned column = record->column(); unsigned row = record->row(); unsigned xfIndex = record->xfIndex(); Value value; if( record->isInteger() ) value.setValue( record->asInteger() ); else value.setValue( record->asFloat() ); Cell* cell = d->activeSheet->cell( column, row, true ); if( cell ) { cell->setValue( value ); cell->setFormatIndex( xfIndex ); } } void ExcelReader::handleRow( RowRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; unsigned index = record->row(); unsigned xfIndex = record->xfIndex(); unsigned height = record->height(); bool hidden = record->hidden(); Row* row = d->activeSheet->row( index, true ); if( row ) { row->setHeight( height / 20.0 ); row->setFormatIndex( xfIndex ); row->setVisible( !hidden ); } } void ExcelReader::handleRString( RStringRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; unsigned column = record->column(); unsigned row = record->row(); unsigned xfIndex = record->xfIndex(); UString label = record->label(); Cell* cell = d->activeSheet->cell( column, row, true ); if( cell ) { cell->setValue( Value( label ) ); cell->setFormatIndex( xfIndex ); } } void ExcelReader::handleSST( SSTRecord* record ) { if( !record ) return; d->stringTable.clear(); for( unsigned i = 0; i < record->count();i++ ) { UString str = record->stringAt( i ); d->stringTable.push_back( str ); } } void ExcelReader::handleString( StringRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; if( !d->formulaCell ) return; d->formulaCell->setValue( record->value() ); d->formulaCell = 0; } void ExcelReader::handleSupbook( SupbookRecord* record ) { if( !record ) return; ExcelReaderExternalWorkbook ext; ext.addin = record->referenceType() == SupbookRecord::AddInRef; ext.internal = record->referenceType() == SupbookRecord::InternalRef; ext.external = record->referenceType() == SupbookRecord::ExternalRef; ext.objectLink = record->referenceType() == SupbookRecord::ObjectLink; d->externalWorkbooks.push_back(ext); } void ExcelReader::handleTopMargin( TopMarginRecord* record ) { if( !record ) return; if( !d->activeSheet ) return; // convert from inches to points double margin = record->topMargin() * 72.0; d->activeSheet->setTopMargin( margin ); } FormatFont ExcelReader::convertFont( unsigned fontIndex ) { // speed-up trick: check in the cache first FormatFont font = d->fontCache[ fontIndex ]; if( font.isNull() && ( fontIndex < d->fontTable.size() )) { FontRecord fr = d->fontTable[ fontIndex ]; font.setFontSize( fr.height() / 20.0 ); font.setFontFamily( fr.fontName() ); font.setColor( convertColor( fr.colorIndex() ) ); font.setBold( fr.boldness() > 500 ); font.setItalic( fr.italic() ); font.setStrikeout( fr.strikeout() ); font.setSubscript( fr.escapement() == FontRecord::Subscript ); font.setSuperscript( fr.escapement() == FontRecord::Superscript ); font.setUnderline( fr.underline() != FontRecord::None ); // put in the cache for further use d->fontCache[ fontIndex ] = font; } return font; } Color ExcelReader::convertColor( unsigned colorIndex ) { if( ( colorIndex >= 8 ) && ( colorIndex < 0x40 ) ) if( colorIndex-8 < d->colorTable.size() ) return d->colorTable[ colorIndex-8 ]; // FIXME the following colors depend on system color settings // 0x0040 system window text colour for border lines // 0x0041 system window background colour for pattern background // 0x7fff system window text colour for fonts if( colorIndex == 0x40 ) return Color( 0, 0, 0 ); if( colorIndex == 0x41 ) return Color( 255, 255, 255 ); if( colorIndex == 0x7fff ) return Color( 0, 0, 0 ); // fallback: just "black" Color color; // standard colors: black, white, red, green, blue, // yellow, magenta, cyan switch( colorIndex ) { case 0: color = Color( 0, 0, 0 ); break; case 1: color = Color( 255, 255, 255 ); break; case 2: color = Color( 255, 0, 0 ); break; case 3: color = Color( 0, 255, 0 ); break; case 4: color = Color( 0, 0, 255 ); break; case 5: color = Color( 255, 255, 0 ); break; case 6: color = Color( 255, 0, 255 ); break; case 7: color = Color( 0, 255, 255 ); break; default: break; } return color; } // big task: convert Excel XFormat into Swinder::Format Format ExcelReader::convertFormat( unsigned xfIndex ) { Format format; if( xfIndex >= d->xfTable.size() ) return format; XFRecord xf = d->xfTable[ xfIndex ]; UString valueFormat = d->formatsTable[xf.formatIndex()]; format.setValueFormat( valueFormat ); format.setFont( convertFont( xf.fontIndex() ) ); FormatAlignment alignment; switch( xf.horizontalAlignment() ) { case XFRecord::Left: alignment.setAlignX( Format::Left ); break; case XFRecord::Right: alignment.setAlignX( Format::Right ); break; case XFRecord::Centered: alignment.setAlignX( Format::Center ); break; default: break; // FIXME still unsupported: Repeat, Justified, Filled, Distributed }; switch( xf.verticalAlignment() ) { case XFRecord::Top: alignment.setAlignY( Format::Top ); break; case XFRecord::VCentered: alignment.setAlignY( Format::Middle ); break; case XFRecord::Bottom: alignment.setAlignY( Format::Bottom ); break; default: break; // FIXME still unsupported: Justified, Distributed } alignment.setWrap( xf.textWrap() ); format.setAlignment( alignment ); FormatBorders borders; Pen pen; pen = convertBorderStyle( xf.leftBorderStyle() ); pen.color = convertColor( xf.leftBorderColor() ); borders.setLeftBorder( pen ); pen = convertBorderStyle( xf.rightBorderStyle() ); pen.color = convertColor( xf.rightBorderColor() ); borders.setRightBorder( pen ); pen = convertBorderStyle( xf.topBorderStyle() ); pen.color = convertColor( xf.topBorderColor() ); borders.setTopBorder( pen ); pen = convertBorderStyle( xf.bottomBorderStyle() ); pen.color = convertColor( xf.bottomBorderColor() ); borders.setBottomBorder( pen ); format.setBorders( borders ); FormatBackground background; background.setForegroundColor( convertColor( xf.patternForeColor() ) ); background.setBackgroundColor( convertColor( xf.patternBackColor() ) ); background.setPattern( convertPatternStyle( xf.fillPattern() ) ); format.setBackground( background ); return format; } void ExcelReader::handleXF( XFRecord* record ) { if( !record ) return; d->xfTable.push_back( *record ); } void ExcelReader::mergeTokens( UStringStack* stack, int count, const char* mergeString ) { if( !stack ) return; if( !stack->size() ) return; if( count < 1 ) return; d->mergedTokens.truncate(0); while(count) { count--; // sanity check if(stack->size() == 0) break; d->mergedTokens.prepend((*stack)[ stack->size()-1 ]); if( count ) d->mergedTokens.prepend(mergeString); stack->resize( stack->size()-1 ); } stack->push_back( d->mergedTokens ); } void ExcelReader::mergeTokens( UStringStack* stack, int count, const char mergeChar ) { if( !stack ) return; if( !stack->size() ) return; if( count < 1 ) return; d->mergedTokens.truncate(0); while(count) { count--; // sanity check if(stack->size() == 0) break; d->mergedTokens.prepend((*stack)[ stack->size()-1 ]); if( count ) d->mergedTokens.prepend(mergeChar); stack->resize( stack->size()-1 ); } stack->push_back( d->mergedTokens ); } #ifdef SWINDER_XLS2RAW void dumpStack( std::vector stack ) { std::cout << std::endl; std::cout << "Stack now is: " ; if( !stack.size() ) std::cout << "(empty)" ; for( unsigned i = 0; i < stack.size(); i++ ) std::cout << " " << i << ": " << stack[i].ascii() << std::endl; std::cout << std::endl; } #endif UString ExcelReader::decodeFormula( unsigned row, unsigned col, const FormulaTokens& tokens, bool openDocumentFormat ) { UStringStack stack; char argumentSeparator = ','; if( openDocumentFormat ) argumentSeparator = ';'; for( unsigned c=0; c < tokens.size(); c++ ) { FormulaToken token = tokens[c]; #ifdef SWINDER_XLS2RAW std::cout << "Token " << c << ": "; std::cout << token.id() << " "; std::cout << token.idAsString() << std::endl; #endif switch( token.id() ) { case FormulaToken::Add: mergeTokens( &stack, 2, '+' ); break; case FormulaToken::Sub: mergeTokens( &stack, 2, '-' ); break; case FormulaToken::Mul: mergeTokens( &stack, 2, '*' ); break; case FormulaToken::Div: mergeTokens( &stack, 2, '/' ); break; case FormulaToken::Power: mergeTokens( &stack, 2, '^' ); break; case FormulaToken::Concat: mergeTokens( &stack, 2, '&' ); break; case FormulaToken::LT: mergeTokens( &stack, 2, '<' ); break; case FormulaToken::LE: mergeTokens( &stack, 2, "<=" ); break; case FormulaToken::EQ: mergeTokens( &stack, 2, '=' ); break; case FormulaToken::GE: mergeTokens( &stack, 2, ">=" ); break; case FormulaToken::GT: mergeTokens( &stack, 2, '>' ); break; case FormulaToken::NE: mergeTokens( &stack, 2, "<>" ); break; case FormulaToken::Intersect: mergeTokens( &stack, 2, ' ' ); break; case FormulaToken::List: mergeTokens( &stack, 2, ';' ); break; case FormulaToken::Range: mergeTokens( &stack, 2, ';' ); break; case FormulaToken::UPlus: if(stack.size() > 1) stack[ stack.size()-1 ].prepend('+'); break; case FormulaToken::UMinus: if(stack.size() > 1) stack[ stack.size()-1 ].prepend('-'); break; case FormulaToken::Percent: stack[ stack.size()-1 ].append('%'); break; case FormulaToken::Paren: { UString str(stack[ stack.size()-1 ]); str.prepend('('); str.append(')'); stack[ stack.size()-1 ] = str; } break; case FormulaToken::MissArg: // just ignore stack.push_back( UString(" ") ); break; case FormulaToken::String: { UString str(token.value().asString()); str.prepend('\"'); str.append('\"'); stack.push_back( str ); } break; case FormulaToken::Bool: if( token.value().asBoolean() ) stack.push_back( UString( "TRUE" ) ); else stack.push_back( UString( "FALSE" ) ); break; case FormulaToken::Integer: stack.push_back( UString::number( token.value().asInteger() ) ); break; case FormulaToken::Float: stack.push_back( UString::number( token.value().asFloat() ) ); break; case FormulaToken::Array: // FIXME handle this ! break; case FormulaToken::Ref: { UString refName(token.ref( row, col )); if( openDocumentFormat ) { refName.prepend('['); refName.append(']'); } stack.push_back( refName ); } break; case FormulaToken::Ref3d: { UString refName("#REF"); refName.reserve(32); unsigned sheetIndex = token.externSheetRef(); if(sheetIndex < d->sheetRefs.size()) { UString cellName = token.ref(row, col); UString sheetName = d->sheetRefs[sheetIndex]; // OpenDocument example: [Sheet1.B1] if( openDocumentFormat ) { refName = UString("["); refName.append( sheetName ); if(!sheetName.isEmpty()) refName.append(UString(".")); refName.append( cellName ); refName.append(UString("]")); } else { refName = sheetName; if(!sheetName.isEmpty()) refName.append(UString(".")); refName.append(UString("!")); refName.append(cellName); } } stack.push_back( refName ); } break; case FormulaToken::Area: { UString areaName( token.area( row, col ) ); if( openDocumentFormat ) { areaName.prepend('['); areaName.append(']'); } stack.push_back( areaName); } break; case FormulaToken::Area3d: { UString areaName("#REF"); areaName.reserve(32); unsigned sheetIndex = token.externSheetRef(); if(sheetIndex < d->sheetRefs.size()) { UString rangeName = token.area(row, col); UString sheetName = d->sheetRefs[sheetIndex]; // OpenDocument example: [Sheet1.B1:B3] if( openDocumentFormat ) { areaName = UString("["); areaName.append( sheetName ); if(!sheetName.isEmpty()) areaName.append('.'); areaName.append( rangeName ); areaName.append(']'); } else { areaName = sheetName; if(!sheetName.isEmpty()) { areaName.append('.'); areaName.append('!'); } areaName.append(rangeName); } } stack.push_back( areaName); } break; case FormulaToken::Function: { mergeTokens( &stack, token.functionParams(), argumentSeparator ); if( stack.size() ) { UString str( token.functionName() ? token.functionName() : "??" ); str.reserve(256); str.append( '(' ); str.append( stack[ stack.size()-1 ] ); str.append( ')' ); stack[ stack.size()-1 ] = str; } } break; case FormulaToken::FunctionVar: if( token.functionIndex() != 255 ) { mergeTokens( &stack, token.functionParams(), argumentSeparator ); if( stack.size() ) { UString str; if( token.functionIndex() != 255 ) str = token.functionName() ? token.functionName() : "??"; str.reserve(256); str.append( '(' ); str.append( stack[ stack.size()-1 ] ); str.append( ')' ); stack[ stack.size()-1 ] = str; } } else { unsigned count = token.functionParams()-1; mergeTokens( &stack, count, argumentSeparator ); if( stack.size() ) { UString str; str.append( '(' ); str.append( stack[ stack.size()-1 ] ); str.append( ')' ); stack[ stack.size()-1 ] = str; } } break; case FormulaToken::Attr: if( token.attr() & 0x10 ) // SUM { mergeTokens( &stack, 1, argumentSeparator ); if( stack.size() ) { UString str( "SUM" ); str.append( '(' ); str.append( stack[ stack.size()-1 ] ); str.append( ')' ); stack[ stack.size()-1 ] = str; } } break; case FormulaToken::NameX: if( token.nameIndex() > 0 ) if( token.nameIndex() <= d->nameTable.size() ) stack.push_back( d->nameTable[ token.nameIndex()-1 ] ); break; case FormulaToken::Matrix: { int row = token.refRow(); int col = token.refColumn(); //std::cout << "row is " << row << " col is " << col << std::endl; } break; case FormulaToken::NatFormula: case FormulaToken::Sheet: case FormulaToken::EndSheet: case FormulaToken::ErrorCode: case FormulaToken::Name: case FormulaToken::MemArea: case FormulaToken::MemErr: case FormulaToken::MemNoMem: case FormulaToken::MemFunc: case FormulaToken::RefErr: case FormulaToken::AreaErr: case FormulaToken::RefN: case FormulaToken::AreaN: case FormulaToken::MemAreaN: case FormulaToken::MemNoMemN: case FormulaToken::RefErr3d: case FormulaToken::AreaErr3d: default: // FIXME handle this ! stack.push_back( UString("UnknownToken") ); //std::cout << "UnknownToken ID=" << token.id() << std::endl; break; }; #ifdef SWINDER_XLS2RAW dumpStack( stack ); #endif } UString result; for( unsigned i = 0; i < stack.size(); i++ ) result.append( stack[i] ); #ifdef SWINDER_XLS2RAW std::cout << "FORMULA Result: " << result << std::endl; #endif return result; } #ifdef SWINDER_XLS2RAW #include int main( int argc, char ** argv ) { if( argc < 2 ) { std::cout << "Usage: xls2raw filename" << std::endl; return 0; } char* filename = argv[1]; std::cout << "Checking " << filename << std::endl; Workbook* workbook = new Workbook(); ExcelReader* reader = new ExcelReader(); reader->load( workbook, filename ); delete reader; delete workbook; return 0; } #endif // XLS2RAW