Logo Search packages:      
Sourcecode: koffice version File versions

excel.cpp

/* Sidewinder - Portable library for spreadsheet
   Copyright (C) 2003 Ariya Hidayat <ariya@kde.org>

   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., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, US
*/

#include "excel.h"

#include <iostream>
#include <vector>
#include <sstream>
#include <string>
#include <map>
#include <stdio.h> // memcpy

#include "pole.h"
#include "sidewinder.h"

using namespace Sidewinder;

static inline unsigned long readU16( const void* p )
{
  const unsigned char* ptr = (const unsigned char*) p;
  return ptr[0]+(ptr[1]<<8);
}

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);
}

// FIXME check that double is 64 bits
static inline double readFloat64( const void*p )
{
  const unsigned char* ptr = (const unsigned char*) p;
  double num = 0.0;
  memcpy( (char*)&num, ptr, 8 );
//  *((unsigned*) &num) = readU32( ptr );
//  *((unsigned*) &num + 1) = readU32( ptr+4 );
  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& i, double& f )
{
  double factor = (rkvalue & 0x01) ? 0.01 : 1;
  if( rkvalue & 0x02 )
  {
    // FIXME check that int is 32 bits ?
    isInteger = true;
    i = (int)(factor * (*((int*) &rkvalue) >> 2) );
  }
  else
  {
    // FIXME litte vs big endian ?
    isInteger = false;
    *((unsigned*) &f) = 0;  // lower 32 bits = 0
    *((unsigned*) &f + 1) = rkvalue & 0xFFFFFFFC; // bit 0, 1 = 0
    f *= factor;
  }
}

//=============================================
//          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, unsigned maxsize )
{
  const unsigned char* data = (const unsigned char*) p;
  UString str = UString::null;
  
  unsigned len = readU16( data  );
  unsigned char flag = data[ 2 ];
  
  bool unicode = flag & 0x01;
  bool richText = flag & 0x08;

  unsigned formatRuns = 0;
  
  unsigned offset = 3; // taken for len and flag
  
  unsigned size = unicode ? len*2 : len;
  
  if( richText )
  {
    formatRuns = readU16( data + offset );
    offset += 2;
  }
  
  // find out total bytes used in this string
  size = 3; // length(2) + flag(1)
  size += len; // string data
  if( unicode ) size += len; // because unicode takes 2-bytes char
  if( richText ) size += (2+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();
    for( unsigned k=0; k<len; k++ )
    {
      unsigned uchar = readU16( data + offset + k*2 );
      str.append( UString(uchar) );
    }
  }
  
  EString result;
  result.setUnicode( unicode );
  result.setRichText( richText );
  result.setSize( size );
  result.setStr( str );
  
  return result;
}

// FIXME use maxsize for sanity check
EString EString::fromByteString( const void* p, unsigned maxsize )
{
  const unsigned char* data = (const unsigned char*) p;
  UString str = UString::null;
  
  unsigned len = readU16( data  );
  char* buffer = new char[ len+1 ];
  memcpy( buffer, data + 2, len );
  buffer[ len ] = 0;
  str = UString( buffer );
  
  unsigned size = 2 + len;
  
  EString result;
  result.setUnicode( false );
  result.setRichText( false );
  result.setSize( size );
  result.setStr( str );
  
  return result;
}

// FIXME use maxsize for sanity check
EString EString::fromByteString( 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];
  
  char* buffer = new char[ len+1 ];
  memcpy( buffer, data + offset, len );
  buffer[ len ] = 0;
  str = UString( buffer );
  delete[] buffer;
  
  unsigned size = offset + len;
  
  EString result;
  result.setUnicode( false );
  result.setRichText( false );
  result.setSize( size );
  result.setStr( str );
  
  return result;
}



// why different ? see BoundSheetRecord
EString EString::fromSheetName( const void* p, unsigned datasize )
{
  const unsigned char* data = (const unsigned char*) p;
  UString str = UString::null;
  
  bool richText = false;
  unsigned formatRuns = 0;
  
  unsigned len = data[0];
  unsigned flag = data[1];
  bool unicode = flag & 1;
  
  if( len > 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<len; k++ )
    {
      unsigned uchar = readU16( data + offset + k*2 );
      str.append( UString(uchar) );
    }
  }
  
  EString result;
  result.setUnicode( unicode );
  result.setRichText( richText );
  result.setSize( datasize );
  result.setStr( str );
  
  return result;
}

//=============================================
//          CellInfo
//=============================================

class CellInfo::Private
{
public:
  unsigned row;
  unsigned column;
  unsigned xfIndex;
};

00327 CellInfo::CellInfo()
{
  info = new CellInfo::Private();
  info->row = 0;
  info->column = 0;
  info->xfIndex = 0;
}

00335 CellInfo::~CellInfo()
{
  delete info;
}

00340 unsigned CellInfo::row() const
{
  return info->row;
}

00345 void CellInfo::setRow( unsigned r )
{
  info->row = r;
}

00350 unsigned CellInfo::column() const
{
  return info->column;
}

00355 void CellInfo::setColumn( unsigned c )
{
  info->column = c;
}

00360 unsigned CellInfo::xfIndex() const
{
  return info->xfIndex;
}

00365 void CellInfo::setXfIndex( unsigned i )
{
  info->xfIndex = i;
}

//=============================================
//          ColumnSpanInfo
//=============================================

class ColumnSpanInfo::Private
{
public:
  unsigned firstColumn;
  unsigned lastColumn;
};

00381 ColumnSpanInfo::ColumnSpanInfo()
{
  spaninfo = new ColumnSpanInfo::Private();
  spaninfo->firstColumn = 0;
  spaninfo->lastColumn = 0;
}

00388 ColumnSpanInfo::~ColumnSpanInfo()
{
  delete spaninfo;
}

00393 unsigned ColumnSpanInfo::firstColumn() const
{
  return spaninfo->firstColumn;
}

00398 void ColumnSpanInfo::setFirstColumn( unsigned c )
{
  spaninfo->firstColumn = c;
}

00403 unsigned ColumnSpanInfo::lastColumn() const
{
  return spaninfo->lastColumn;
}

00408 void ColumnSpanInfo::setLastColumn( unsigned c )
{
  spaninfo->lastColumn = c;
}

// ========== base record ==========

const unsigned int Record::id = 0; // invalid of-course

00417 Record::Record()
{
  stream_position = 0;
  ver = Excel97;
}

00423 Record::~Record()
{
}

00427 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();
    
  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 == 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();
    
  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 == XFRecord::id )
    record = new XFRecord();
  
  else if( type == TopMarginRecord::id )
    record = new TopMarginRecord();
    
  return record;
};

00527 void Record::setPosition( unsigned pos )
{
  stream_position = pos;
}
  
00532 unsigned Record::position() const
{
  return stream_position;
}

00537 void Record::setData( unsigned size, const unsigned char* data )
{
}

00541 void Record::dump( std::ostream& out ) const
{
  // nothing to dump
}

// ========== BACKUP ========== 

const unsigned int BackupRecord::id = 0x0040;

class BackupRecord::Private
{
public:
  bool backup;
};

00556 BackupRecord::BackupRecord():
  Record()
{
  d = new BackupRecord::Private();
  d->backup = false;
}

00563 BackupRecord::~BackupRecord()
{
  delete d;
}

00568 bool BackupRecord::backup() const
{
  return d->backup;
}

00573 void BackupRecord::setBackup( bool b )
{
  d->backup = b;
}

00578 void BackupRecord::setData( unsigned size, const unsigned char* data )
{
  if( size < 2 ) return;
  
  unsigned flag = readU16( data );
  d->backup = flag != 0;
}

00586 void BackupRecord::dump( std::ostream& out ) const
{
  out << "BACKUP" << std::endl;
  out << " Backup on save : " << (backup() ? "Yes" : "No") << 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
00609 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
00622 BOFRecord::~BOFRecord()
{
  delete d;
}

00627 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 );
    }
  }
}

00645 unsigned BOFRecord::version() const
{
  unsigned ver = UnknownExcel;
  switch( d->version )
  {
    case 0x0500 : ver = Excel95; break;
    case 0x0600 : ver = Excel97; break;
    default: break;
  }
  return ver;
}

00657 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;
}

00669 unsigned BOFRecord::type() const
{
  unsigned result = UnknownType;
  switch( d->type )
  {
    case 0x05  : result = Workbook; break;
    case 0x06  : result = VBModule; break;
    case 0x10  : result = Worksheet; break;
    case 0x20  : result = Chart; break;
    default: break;
  }
  return result;
}

00683 const char* BOFRecord::typeAsString() const
{
  const char *result = "Unknown";
  switch( type() )
  {
    case Workbook  : result = "Workbook"; break;
    case Worksheet : result = "Worksheet"; break;
    case Chart     : result = "Chart"; break;
    case VBModule  : result = "Visual Basic Module"; break;
    default: break;
  }
  return result;
}

00697 void BOFRecord::dump( std::ostream& out ) const
{
  out << "BOF" << std::endl;
  out << "  Version : " << std::hex << d->version << " (" << versionAsString() << ")" << std::endl;
  out << "     Type : " << d->type << " (" << typeAsString() << ")" << std::endl;
  out << "    Build : " << d->build << std::endl;
  out << "     Year : " << d->year << std::endl;
  out << "  History : " << d->history << std::endl;
  out << " RVersion : " << d->rversion << std::endl;
}

// ========== BLANK ========== 

const unsigned int BlankRecord::id = 0x0201;

00712 BlankRecord::BlankRecord():
  Record(), CellInfo()
{
}

00717 void BlankRecord::setData( unsigned size, const unsigned char* data )
{
  if( size < 6 ) return;

  setRow( readU16( data ) );
  setColumn( readU16( data+2 ) );
  setXfIndex( readU16( data+4 ) );
}

00726 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;
}

// ========== BOOLERR ==========

const unsigned int BoolErrRecord::id = 0x0205;

class BoolErrRecord::Private
{
public:
  enum { Boolean, Error } type;
  bool value;
  int errorCode;  // raw code: 00=Null, 07=Div0, 0F=Value, ...
};

00746 BoolErrRecord::BoolErrRecord():
  Record(), CellInfo()
{
  d = new BoolErrRecord::Private();
  d->type = Private::Boolean;
  d->value = false;
  d->errorCode = 0;
}

00755 BoolErrRecord::~BoolErrRecord()
{
}

00759 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->type = Private::Boolean;
    d->value = (data[6] != 0);
    break;
  case 1 :
    d->type = Private::Error;
    d->errorCode = data[6];
    break;
  default:
    // bad bad bad
    std::cerr << "Warning: bad BOOLERR record" << std::endl;
    break;
  }
}

00784 bool BoolErrRecord::isBool() const
{
  return d->type == Private::Boolean;
}

00789 bool BoolErrRecord::isError() const
{
  return d->type == Private::Error;
}

00794 bool BoolErrRecord::value() const
{
  return d->value;
}

00799 unsigned BoolErrRecord::errorCode() const
{
  unsigned result = ErrorUnknown;
  switch( d->errorCode )
  {
    case 0x00:  result = ErrorNull; break;
    case 0x07:  result = ErrorDivZero; break;
    case 0x0f:  result = ErrorValue; break;
    case 0x17:  result = ErrorRef; break;
    case 0x1D:  result = ErrorName; break;
    case 0x24:  result = ErrorNum; break;
    case 0x2A:  result = ErrorNA; break;
    default  :  result = ErrorUnknown; break;
  };
  
  return result;
}

00817 const char* BoolErrRecord::errorCodeAsString() const
{
  const char* result = "Unknown";
  switch( errorCode() )
  {
    case ErrorNull    : result = "Intersection of two cell ranges is empty"; break;
    case ErrorDivZero : result = "Division by zero"; break;
    case ErrorValue   : result = "Wrong type of operand"; break;
    case ErrorRef     : result = "Illegal or deleted cell reference"; break;
    case ErrorName    : result = "Wrong function or range name"; break;
    case ErrorNum     : result = "Value range overflow"; break;
    case ErrorNA      : result = "Argument or function not available"; break;
    default: result = "Unknown" ; break;
  }
  return result;
}


00835 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;
  if( isBool() )
  {
    out << "     Type : Bool" << std::endl;
    std::string val = value() ? "True" : "False";
    out << "    Value : " << val << std::endl;
  }
  if( isError() )
  {
    out << "     Type : Error" << std::endl;
    out << "ErrorCode : " << std::hex << d->errorCode << " ";
    out << errorCodeAsString() << std::endl;
  }
}

// ========== BOTTOMMARGIN ==========

const unsigned int BottomMarginRecord::id = 0x0029;

class BottomMarginRecord::Private
{
public:
  double bottomMargin;
};

00865 BottomMarginRecord::BottomMarginRecord():
  Record()
{
  d = new BottomMarginRecord::Private();
  d->bottomMargin = 1.0;
}

00872 BottomMarginRecord::~BottomMarginRecord()
{
  delete d;
}

00877 double BottomMarginRecord::bottomMargin() const
{
  return d->bottomMargin;
}

00882 void BottomMarginRecord::setBottomMargin( double m )
{
  d->bottomMargin = m;
}

00887 void BottomMarginRecord::setData( unsigned size, const unsigned char* data )
{
  if( size < 8 ) return;
  setBottomMargin( readFloat64( data ) );
}

00893 void BottomMarginRecord::dump( std::ostream& out ) const
{
  out << "BOTTOMMARGIN" << std::endl;
  out << "   Margin : " << bottomMargin() << 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;
};

00914 BoundSheetRecord::BoundSheetRecord():
  Record()
{
  d = new BoundSheetRecord::Private();
  d->type = 0;
  d->visibility = 0;
  d->name = "Sheet";
}

00923 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
  };
}

00934 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;
}

00947 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;
}

00960 void BoundSheetRecord::setVisible( bool v )
{
  d->visibility = v ? 0 : 1;
}

00965 bool BoundSheetRecord::visible() const
{
  return d->visibility == 0;
}

00970 void BoundSheetRecord::setSheetName( const UString& n )
{
  d->name = n;
}

00975 UString BoundSheetRecord::sheetName() const
{
  return d->name;
}

00980 void BoundSheetRecord::setBofPosition( unsigned pos )
{
  d->bofPosition = pos;
}

00985 unsigned BoundSheetRecord::bofPosition() const
{
  return d->bofPosition;
}

00990 BoundSheetRecord::~BoundSheetRecord()
{
  delete d;
}

00995 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 );
}

01015 void BoundSheetRecord::dump( std::ostream& out ) const
{
  out << "BOUNDSHEET" << std::endl;
  out << "       Name : " << d->name.ascii() << 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;
}

// ========== BACKUP ========== 

const unsigned int CalcModeRecord::id = 0x000d;

class CalcModeRecord::Private
{
public:
  bool autoCalc;
};

01035 CalcModeRecord::CalcModeRecord():
  Record()
{
  d = new CalcModeRecord::Private();
  d->autoCalc = false;
}

01042 CalcModeRecord::~CalcModeRecord()
{
  delete d;
}

01047 bool CalcModeRecord::autoCalc() const
{
  return d->autoCalc;
}

01052 void CalcModeRecord::setAutoCalc( bool b )
{
  d->autoCalc = b;
}

01057 void CalcModeRecord::setData( unsigned size, const unsigned char* data )
{
  if( size < 2 ) return;
  
  unsigned flag = readU16( data );
  d->autoCalc = flag != 0;
}

01065 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;
};

01085 ColInfoRecord::ColInfoRecord():
  Record(), ColumnSpanInfo()
{
  d = new ColInfoRecord::Private();
  d->width        = 2340;
  d->xfIndex      = 0;
  d->hidden       = false;
  d->collapsed    = false;
  d->outlineLevel = 0;
}

01096 ColInfoRecord::~ColInfoRecord()
{
  delete d;
}

// FIXME how to find the real width (in pt/mm/inch) ?
01102 unsigned ColInfoRecord::width() const
{
  return d->width;
}

01107 void ColInfoRecord::setWidth( unsigned w )
{
  d->width = w;
}

01112 unsigned ColInfoRecord::xfIndex() const
{
  return d->xfIndex;
}

01117 void ColInfoRecord::setXfIndex( unsigned i )
{
  d->xfIndex = i;
}

01122 bool ColInfoRecord::hidden() const
{
  return d->hidden;
}

01127 void ColInfoRecord::setHidden( bool h )
{
  d->hidden = h;
}

01132 bool ColInfoRecord::collapsed() const
{
  return d->collapsed;
}

01137 void ColInfoRecord::setCollapsed( bool c )
{
  d->collapsed = c;
}

01142 unsigned ColInfoRecord::outlineLevel() const
{
  return d->outlineLevel;
}

01147 void ColInfoRecord::setOutlineLevel( unsigned l )
{
  d->outlineLevel = l;
}

01152 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 );
}

01167 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;
};

01189 DateModeRecord::DateModeRecord():
  Record()
{
  d = new DateModeRecord::Private();
  d->base1904 = false;
}

01196 DateModeRecord::~DateModeRecord()
{
  delete d;
}

01201 bool DateModeRecord::base1904() const
{
  return d->base1904;
}

01206 void DateModeRecord::setBase1904( bool r )
{
  d->base1904 = r;
}

01211 void DateModeRecord::setData( unsigned size, const unsigned char* data )
{
  if( size < 2 ) return;
  
  unsigned flag = readU16( data );
  d->base1904 = flag != 0;
}

01219 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;
};

01239 DimensionRecord::DimensionRecord():
  Record()
{
  d = new DimensionRecord::Private;
  d->firstRow    = 0;
  d->lastRow     = 0;
  d->firstColumn = 0;
  d->lastColumn  = 0;
}

01249 DimensionRecord::~DimensionRecord()
{
  delete d;
}

01254 unsigned DimensionRecord::firstRow() const
{
  return d->firstRow;
}

01259 void DimensionRecord::setFirstRow( unsigned r )
{
  d->firstRow = r;
}

01264 unsigned DimensionRecord::lastRow() const
{
  return d->lastRow;
}

01269 void DimensionRecord::setLastRow( unsigned r )
{
  d->lastRow = r;
}

01274 unsigned DimensionRecord::firstColumn() const
{
  return d->firstColumn;
}

01279 void DimensionRecord::setFirstColumn( unsigned r )
{
  d->firstColumn = r;
}

01284 unsigned DimensionRecord::lastColumn() const
{
  return d->lastColumn;
}

01289 void DimensionRecord::setLastColumn( unsigned r )
{
  d->lastColumn = r;
}

01294 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 );
}

01304 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;

01317 EOFRecord::EOFRecord():
  Record()
{
}

01322 EOFRecord::~EOFRecord()
{
}

01326 void EOFRecord::setData( unsigned size, const unsigned char* data )
{
  // no data associated with EOF record
}

01331 void EOFRecord::dump( std::ostream& out ) const
{
  out << "EOF" << 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 script;
  unsigned underline;
};

01355 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->script       = Normal;
  d->underline    = None;
}

01370 FontRecord::~FontRecord()
{
  delete d;
}

01375 FontRecord::FontRecord( const FontRecord& ef ):  Record()
{
  d = new FontRecord::Private;
  operator=( ef );
}

01381 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->script       = ef.script();
  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;
}

01406 UString FontRecord::fontName() const
{
  return d->fontName;
}

01411 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;
}

01436 unsigned FontRecord::colorIndex() const
{
  return d->colorIndex;
}

01441 void FontRecord::setColorIndex( unsigned ci )
{
  d->colorIndex = ci;
}

01446 unsigned FontRecord::boldness() const
{
  return d->boldness;
}

01451 void FontRecord::setBoldness( unsigned b )
{
  d->boldness = b;
}

01456 bool FontRecord::italic() const
{
  return d->italic;
}

01461 void FontRecord::setItalic( bool i )
{
  d->italic = i;
}

01466 bool FontRecord::strikeout() const
{
  return d->strikeout;
}

01471 void FontRecord::setStrikeout( bool s )
{
  d->strikeout = s;
}

01476 unsigned FontRecord::script() const
{
  return d->script;
}

01481 void FontRecord::setScript( unsigned s )
{
  d->script = s;
}

01486 unsigned FontRecord::underline() const
{
  return d->underline;
}

01491 void FontRecord::setUnderline( unsigned u )
{
  d->underline = u;
}


01497 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 ) );
  setScript( 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 );
}


01523 void FontRecord::dump( std::ostream& out ) const
{
  out << "FONT" << std::endl;
  out << "  Height : " << height() << std::endl;
  out << " Font Name: " << fontName().ascii() << std::endl;
}

// ========== FOOTER ==========

const unsigned int FooterRecord::id = 0x0015;

class FooterRecord::Private
{
public:
  UString footer;
};

01540 FooterRecord::FooterRecord():
  Record()
{
  d = new FooterRecord::Private();
}

01546 FooterRecord::~FooterRecord()
{
  delete d;
}

01551 UString FooterRecord::footer() const
{
  return d->footer;
}

01556 void FooterRecord::setFooter( const UString& footer )
{
  d->footer = footer;
}

01561 void FooterRecord::setData( unsigned size, const unsigned char* data )
{
  if( size < 2 ) return;

  UString footer = ( version() >= Excel97 ) ?
    EString::fromUnicodeString( data, size ).str() :
    EString::fromByteString( data, false, size ).str();
  setFooter( footer );
}

01571 void FooterRecord::dump( std::ostream& out ) const
{
  out << "FOOTER" << std::endl;
  out << " footer: " << footer().ascii() << std::endl;
}

// ========== FORMAT ==========

const unsigned int FormatRecord::id = 0x041e;

class FormatRecord::Private
{
public:
  unsigned index;
  UString formatString;
};

01588 FormatRecord::FormatRecord():
  Record()
{
  d = new FormatRecord::Private;
  d->index = 0;
  d->formatString = "General";
}

01596 FormatRecord::~FormatRecord()
{
  delete d;
}

01601 FormatRecord::FormatRecord( const FormatRecord& fr ):
  Record()
{
  d = new FormatRecord::Private;
  operator=( fr );
}

01608 FormatRecord& FormatRecord::operator=( const FormatRecord& fr )
{
  d->index = fr.index();
  d->formatString = fr.formatString();
  return *this;
}

01615 unsigned FormatRecord::index() const
{
  return d->index;
}

01620 void FormatRecord::setIndex( unsigned i )
{
  d->index = i;
}

01625 UString FormatRecord::formatString() const
{
  return d->formatString;
}

01630 void FormatRecord::setFormatString( const UString& fs )
{
  d->formatString = fs;
}

01635 void FormatRecord::setData( unsigned size, const unsigned char* data )
{
  if( size < 3 ) return;
  
  setIndex( readU16( data ) );

  UString fs = ( version() >= Excel97 ) ? 
    EString::fromUnicodeString( data+2, size-2 ).str() :
    EString::fromByteString( data+2, false, size-2 ).str();
  setFormatString( fs );
}

// ========== FORMULA ========== 

const unsigned int FormulaRecord::id = 0x0006;

class FormulaRecord::Private
{
public:
  Value result;
};

01657 FormulaRecord::FormulaRecord():
  Record()
{
  d = new FormulaRecord::Private();
}

01663 FormulaRecord::~FormulaRecord()
{
  delete d;
}

01668 Value FormulaRecord::result() const
{
  return d->result;
}

01673 void FormulaRecord::setResult( const Value& r )
{
  d->result = r;
}

01678 void FormulaRecord::setData( unsigned size, const unsigned char* data )
{
  if( size < 20 ) return;
  
  setRow( readU16( data ) );
  setColumn( readU16( data+2 ) );
  setXfIndex( readU16( data+4 ) );
  
}

01688 void FormulaRecord::dump( std::ostream& out ) const
{
  out << "FORMULA" << std::endl;
}

// ========== LABEL ========== 

const unsigned int LabelRecord::id = 0x0204;

class LabelRecord::Private
{
public:
  UString label;
};

01703 LabelRecord::LabelRecord():
  Record(), CellInfo()
{
  d = new LabelRecord::Private();
  d->label = UString::null;
}

01710 LabelRecord::~LabelRecord()
{
  delete d;
}

01715 UString LabelRecord::label() const
{
  return d->label;
}

01720 void LabelRecord::setLabel( const UString& l )
{
  d->label = l;
}

01725 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, size-6 ).str() :
    EString::fromByteString( data+6, size-6 ).str();
  setLabel( label );
}

01739 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().ascii() << std::endl;
}

// ========== HEADER ==========

const unsigned int HeaderRecord::id = 0x0014;

class HeaderRecord::Private
{
public:
  UString header;
};

01758 HeaderRecord::HeaderRecord():
  Record()
{
  d = new HeaderRecord::Private();
}

01764 HeaderRecord::~HeaderRecord()
{
  delete d;
}

01769 UString HeaderRecord::header() const
{
  return d->header;
}

01774 void HeaderRecord::setHeader( const UString& header )
{
  d->header = header;
}

01779 void HeaderRecord::setData( unsigned size, const unsigned char* data )
{
  if( size < 2 ) return;
  
  UString header = ( version() >= Excel97 ) ?
    EString::fromUnicodeString( data, size ).str() :
    EString::fromByteString( data, false, size ).str();
  setHeader( header );
}

01789 void HeaderRecord::dump( std::ostream& out ) const
{
  out << "HEADER" << std::endl;
  out << " header: " << header().ascii() << std::endl;
}

// ========== LABELSST ========== 

const unsigned int LabelSSTRecord::id = 0x00fd;

class LabelSSTRecord::Private
{
public:
  unsigned sstIndex;
};

01805 LabelSSTRecord::LabelSSTRecord():
  Record(), CellInfo()
{
  d = new LabelSSTRecord::Private();
  d->sstIndex = 0;
}

01812 LabelSSTRecord::~LabelSSTRecord()
{
  delete d;
}

01817 unsigned LabelSSTRecord::sstIndex() const
{
  return d->sstIndex;
}

01822 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 );
}

01833 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;
};

01852 LeftMarginRecord::LeftMarginRecord():
  Record()
{
  d = new LeftMarginRecord::Private();
  d->leftMargin = 1.0;
}

01859 LeftMarginRecord::~LeftMarginRecord()
{
  delete d;
}

01864 double LeftMarginRecord::leftMargin() const
{
  return d->leftMargin;
}

01869 void LeftMarginRecord::setLeftMargin( double m )
{
  d->leftMargin = m;
}

01874 void LeftMarginRecord::setData( unsigned size, const unsigned char* data )
{
  if( size < 8 ) return;
  setLeftMargin( readFloat64( data ) );
}

01880 void LeftMarginRecord::dump( std::ostream& out ) const
{
  out << "LEFTMARGIN" << std::endl;
  out << "   Margin : " << leftMargin() << std::endl;
}

// ========== MERGEDCELLS ==========

const unsigned int MergedCellsRecord::id = 0x00e5;

class MergedInfo
{
public:
  unsigned firstRow, lastRow, firstColumn, lastColumn;
};

class MergedCellsRecord::Private
{
public:
  std::vector<MergedInfo> mergedCells;
};

01902 MergedCellsRecord::MergedCellsRecord():
  Record()
{
  d = new MergedCellsRecord::Private();
}

01908 MergedCellsRecord::~MergedCellsRecord()
{
  delete d;
}

01913 unsigned MergedCellsRecord::count() const
{
  return d->mergedCells.size();
}

01918 unsigned MergedCellsRecord::firstRow( unsigned i ) const
{
  if( i >= d->mergedCells.size() ) return 0;
  MergedInfo info = d->mergedCells[ i ];
  return info.firstRow;
}

01925 unsigned MergedCellsRecord::lastRow( unsigned i ) const
{
  if( i >= d->mergedCells.size() ) return 0;
  MergedInfo info = d->mergedCells[ i ];
  return info.lastRow;
}

01932 unsigned MergedCellsRecord::firstColumn( unsigned i ) const
{
  if( i >= d->mergedCells.size() ) return 0;
  MergedInfo info = d->mergedCells[ i ];
  return info.firstColumn;
}

01939 unsigned MergedCellsRecord::lastColumn( unsigned i ) const
{
  if( i >= d->mergedCells.size() ) return 0;
  MergedInfo info = d->mergedCells[ i ];
  return info.lastColumn;
}

01946 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 );
  }
}

01968 void MergedCellsRecord::dump( std::ostream& out ) const
{
  out << "MERGEDCELLS" << std::endl;
}

// ========== MULBLANK ==========

const unsigned int MulBlankRecord::id = 0x00be;

class MulBlankRecord::Private
{
public:
  std::vector<unsigned> xfIndexes;
};

01983 MulBlankRecord::MulBlankRecord():
  Record(), CellInfo(), ColumnSpanInfo()
{
  d = new MulBlankRecord::Private();
}

01989 MulBlankRecord::~MulBlankRecord()
{
  delete d;
}

01994 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 !
}

02010 unsigned MulBlankRecord::xfIndex( unsigned i ) const
{
  if( i >= d->xfIndexes.size() ) return 0;
  return d->xfIndexes[ i ];
}

02016 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<unsigned> xfIndexes;
  std::vector<bool> isIntegers;
  std::vector<int> intValues;
  std::vector<double> floatValues;
};

02037 MulRKRecord::MulRKRecord():
  Record(), CellInfo(), ColumnSpanInfo()
{
  d = new MulRKRecord::Private();
}

02043 MulRKRecord::~MulRKRecord()
{
  delete d;
}

02048 unsigned MulRKRecord::xfIndex( unsigned i ) const
{
  if( i >= d->xfIndexes.size() ) return 0;
  return d->xfIndexes[ i ];
}

02054 bool MulRKRecord::isInteger( unsigned i ) const
{
  if( i >= d->isIntegers.size() ) return true;
  return d->isIntegers[ i ];
}

02060 int MulRKRecord::asInteger( unsigned i ) const
{
  if( i >= d->intValues.size() ) return 0;
  return d->intValues[ i ];
}

02066 double MulRKRecord::asFloat( unsigned i ) const
{
  if( i >= d->floatValues.size() ) return 0.0;
  return d->floatValues[ i ];
}

02072 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 );
    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 !
}

02100 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;
}

// ========== Number ========== 

const unsigned int NumberRecord::id = 0x0203;

class NumberRecord::Private
{
public:
  double number;
};

02118 NumberRecord::NumberRecord():
  Record(), CellInfo()
{
  d = new NumberRecord::Private();
  d->number = 0.0;
}

02125 NumberRecord::~NumberRecord()
{
  delete d;
}

02130 double NumberRecord::number() const
{
  return d->number;
}

02135 void NumberRecord::setNumber( double f )
{
  d->number = f;
}

// FIXME check that sizeof(double) is 64
02141 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 ) );
}

02151 void NumberRecord::dump( std::ostream& out ) const
{
  out << "NUMBER" << std::endl;
  out << "      Row : " << row() << 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<Color> colors;
};

02169 PaletteRecord::PaletteRecord():
  Record()
{
  d = new PaletteRecord::Private();
}

02175 PaletteRecord::~PaletteRecord()
{
  delete d;
}

02180 Color PaletteRecord::color( unsigned i ) const
{
  return d->colors[ i ];
}

02185 unsigned PaletteRecord::count() const
{
  return d->colors.size();
}

02190 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++ )
  {
    unsigned red = data[ p ];
    unsigned green = data[ p+1 ];
    unsigned blue = data[ p+2 ];
    d->colors.push_back( Color( red, green, blue ) );
  }
}

02206 void PaletteRecord::dump( std::ostream& out ) const
{
  out << "PALETTE" << std::endl;
  for( unsigned i = 0; i < count(); i++ )
  {
    Color c = color( i );
    out << "RGB: " << c.red << " " << c.green << " " << c.blue << std::endl;
  }
}

// ========== RIGHTMARGIN ========== 

const unsigned int RightMarginRecord::id = 0x0027;

class RightMarginRecord::Private
{
public:
  double rightMargin;
};

02226 RightMarginRecord::RightMarginRecord():
  Record()
{
  d = new RightMarginRecord::Private();
  d->rightMargin = 1.0;
}

02233 RightMarginRecord::~RightMarginRecord()
{
  delete d;
}

02238 double RightMarginRecord::rightMargin() const
{
  return d->rightMargin;
}

02243 void RightMarginRecord::setRightMargin( double m )
{
  d->rightMargin = m;
}

02248 void RightMarginRecord::setData( unsigned size, const unsigned char* data )
{
  if( size < 8 ) return;
  setRightMargin( readFloat64( data ) );
}

02254 void RightMarginRecord::dump( std::ostream& out ) const
{
  out << "RIGHTMARGIN" << std::endl;
  out << "   Margin : " << rightMargin() << std::endl;
}

// ========== RK ==========

const unsigned int RKRecord::id = 0x027e;

class RKRecord::Private
{
public:
  bool integer;
  int i;
  double f;
};

02272 RKRecord::RKRecord():
  Record(), CellInfo()
{
  d = new RKRecord::Private();
  d->integer = true;
  d->i = 0;
  d->f = 0.0;
}

02281 RKRecord::~RKRecord()
{
  delete d;
}

02286 bool RKRecord::isInteger() const
{
  return d->integer;
}

02291 bool RKRecord::isFloat() const
{
  return !d->integer;
}

02296 int RKRecord::asInteger() const
{
  if( d->integer )
    return d->i;
  else
    return (int)d->f;
}

02304 double RKRecord::asFloat() const
{
  if( !d->integer )
    return d->f;
  else
    return (double)d->i;
}

02312 void RKRecord::setInteger( int i )
{
  d->integer = true;
  d->i = i;
  d->f = (double)i;
}

02319 void RKRecord::setFloat( double f )
{
  d->integer = false;
  d->i = (int)f;
  d->f = f;
}

// FIXME check sizeof(int) is 32
// big vs little endian problem
02328 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;
  decodeRK( readU32( data+6 ), d->integer, i, f );
  if( d->integer ) setInteger( i );
  else setFloat( f );
}

02342 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;
}

// ========== Row ==========

const unsigned int RowRecord::id = 0x0208;

class RowRecord::Private
{
public:
  unsigned row;
  unsigned height;
  unsigned xfIndex;
  bool hidden;
};

02364 RowRecord::RowRecord():
  Record(), ColumnSpanInfo()
{
  d = new RowRecord::Private();
  d->row     = 0;
  d->height  = 50;
  d->xfIndex = 0;
  d->hidden  = false;
}

02374 RowRecord::~RowRecord()
{
  delete d;
}

02379 unsigned RowRecord::row() const
{
  return d->row;
}

02384 void RowRecord::setRow( unsigned r )
{
  d->row = r;
}

02389 unsigned RowRecord::height() const
{
  return d->height;
}

02394 void RowRecord::setHeight( unsigned h )
{
  d->height = h;
}

02399 unsigned RowRecord::xfIndex() const
{
  return d->xfIndex;
}

02404 void RowRecord::setXfIndex( unsigned i )
{
  d->xfIndex = i;
}

02409 bool RowRecord::hidden() const
{
  return d->hidden;
}

02414 void RowRecord::setHidden( bool h )
{
  d->hidden = h;
}

02419 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 );
}

02433 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;
};

02454 RStringRecord::RStringRecord():
  Record(), CellInfo()
{
  d = new RStringRecord::Private();
  d->label = UString::null;
}

02461 RStringRecord::~RStringRecord()
{
  delete d;
}

02466 UString RStringRecord::label() const
{
  return d->label;
}

02471 void RStringRecord::setLabel( const UString& l )
{
  d->label = l;
}

// FIXME formatting runs ? in EString perhaps ?
02477 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, size-6 ).str() :
    EString::fromByteString( data+6, true, size-6 ).str();
  setLabel( label );
}

02492 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().ascii() << std::endl;
}

// ========== SST ==========

const unsigned int SSTRecord::id = 0x00fc;

class SSTRecord::Private
{
public:
  unsigned total;
  unsigned count;  
  std::vector<UString> strings;
};

02513 SSTRecord::SSTRecord():
  Record()
{
  d = new SSTRecord::Private();
  d->total = 0;
  d->count = 0;
}

02521 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;
}

02536 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();
  
  //TODO check against size !!
  for( unsigned i = 0; i < d->count; i++ )
  {
    EString es = EString::fromUnicodeString( data+offset );
    d->strings.push_back( es.str() );
    offset += es.size();
  }
  
  // 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();
  }
}

02562 unsigned SSTRecord::count() const
{
  return d->count;
}

// why not just string() ? to avoid easy confusion with std::string
02568 UString SSTRecord::stringAt( unsigned index ) const
{
  if( index >= count()) return UString::null;
  return d->strings[ index ];
}

02574 void SSTRecord::dump( std::ostream& out ) const
{
  out << "SST" << std::endl;
  out << " Occurences : " << d->total << std::endl;
  out << "      Count : " << count() << std::endl;
  if( count() )
  {
    out << "    Strings : " << std::endl;
    for( unsigned i = 0; i < count(); i++ )
    {
      out << "          " << i+1;
      out << " : " << stringAt( i ).ascii() << std::endl;
    }  
  }
}

// ========== TOPMARGIN ==========

const unsigned int TopMarginRecord::id = 0x0028;

class TopMarginRecord::Private
{
public:
  double topMargin;
};

02600 TopMarginRecord::TopMarginRecord():
  Record()
{
  d = new TopMarginRecord::Private();
  d->topMargin = 1.0;
}

02607 TopMarginRecord::~TopMarginRecord()
{
  delete d;
}

02612 double TopMarginRecord::topMargin() const
{
  return d->topMargin;
}

02617 void TopMarginRecord::setTopMargin( double m )
{
  d->topMargin = m;
}

02622 void TopMarginRecord::setData( unsigned size, const unsigned char* data )
{
  if( size < 8 ) return;
  setTopMargin( readFloat64( data ) );
}

02628 void TopMarginRecord::dump( std::ostream& out ) const
{
  out << "TOPMARGIN" << std::endl;
  out << "   Margin : " << topMargin() << 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;
};

02670 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;
}

02702 XFRecord::~XFRecord()
{
  delete d;
}

02707 XFRecord::XFRecord( const XFRecord& xf ):  Record()
{
  d = new XFRecord::Private();
  operator=( xf );
}

02713 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;
}

02745 unsigned XFRecord::fontIndex() const
{
  return d->fontIndex;
}

02750 void XFRecord::setFontIndex( unsigned fi )
{
  d->fontIndex = fi;
}

02755 unsigned XFRecord::formatIndex() const
{
  return d->formatIndex;
}

02760 void XFRecord::setFormatIndex( unsigned fi )
{
  d->formatIndex = fi;
}

02765 bool XFRecord::locked() const
{
  return d->locked;
}

02770 void XFRecord::setLocked( bool l )
{
  d->locked = l;
}

02775 bool XFRecord::formulaHidden() const
{
  return d->formulaHidden;
}

02780 void XFRecord::setFormulaHidden( bool f )
{
  d->formulaHidden = f;
}

02785 unsigned XFRecord::parentStyle() const
{
  return d->parentStyle;
}

02790 void XFRecord::setParentStyle( unsigned p )
{
  d->parentStyle = p;
}

02795 unsigned XFRecord::horizontalAlignment() const
{
  return d->horizontalAlignment;
}

02800 void XFRecord::setHorizontalAlignment( unsigned ha )
{
  d->horizontalAlignment = ha;
}

02805 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;
}

02821 unsigned XFRecord::verticalAlignment() const
{
  return d->verticalAlignment;
}

02826 void XFRecord::setVerticalAlignment( unsigned va )
{
  d->verticalAlignment = va;
}

02831 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;
}

02846 bool XFRecord::textWrap() const
{
  return d->textWrap;
}

02851 void XFRecord::setTextWrap( bool wrap )
{
  d->textWrap = wrap;
}

02856 unsigned XFRecord::rotationAngle() const
{
  return d->rotationAngle;
}

02861 void XFRecord::setRotationAngle( unsigned angle )
{
  d->rotationAngle = angle;
}

02866 bool XFRecord::stackedLetters() const
{
  return d->stackedLetters;
}

02871 void XFRecord::setStackedLetters( bool stacked )
{
  d->stackedLetters = stacked;
}

02876 unsigned XFRecord::indentLevel() const
{
  return d->indentLevel;
}

02881 void XFRecord::setIndentLevel( unsigned i )
{
  d->indentLevel = i;
}

02886 bool XFRecord::shrinkContent() const
{
  return d->shrinkContent;
}

02891 void XFRecord::setShrinkContent( bool s )
{
  d->shrinkContent = s;
}

02896 unsigned XFRecord::leftBorderStyle() const
{
  return d->leftBorderStyle;
}

02901 void XFRecord::setLeftBorderStyle( unsigned style )
{
  d->leftBorderStyle = style;
}

02906 unsigned XFRecord::leftBorderColor() const
{
  return d->leftBorderColor;
}

02911 void XFRecord::setLeftBorderColor( unsigned color )
{
  d->leftBorderColor = color;
}

02916 unsigned XFRecord::rightBorderStyle() const
{
  return d->rightBorderStyle;
}

02921 void XFRecord::setRightBorderStyle( unsigned style )
{
  d->rightBorderStyle = style;
}

02926 unsigned XFRecord::rightBorderColor() const
{
  return d->rightBorderColor;
}

02931 void XFRecord::setRightBorderColor( unsigned color )
{
  d->rightBorderColor = color;
}

02936 unsigned XFRecord::topBorderStyle() const
{
  return d->topBorderStyle;
}

02941 void XFRecord::setTopBorderStyle( unsigned style )
{
  d->topBorderStyle = style;
}

02946 unsigned XFRecord::topBorderColor() const
{
  return d->topBorderColor;
}

02951 void XFRecord::setTopBorderColor( unsigned color )
{
  d->topBorderColor = color;
}

02956 unsigned XFRecord::bottomBorderStyle() const
{
  return d->bottomBorderStyle;
}

02961 void XFRecord::setBottomBorderStyle( unsigned style )
{
  d->bottomBorderStyle = style;
}

02966 unsigned XFRecord::bottomBorderColor() const
{
  return d->bottomBorderColor;
}

02971 void XFRecord::setBottomBorderColor( unsigned color )
{
  d->bottomBorderColor = color;
}

02976 bool XFRecord::diagonalTopLeft() const
{
  return d->diagonalTopLeft;
}

02981 void XFRecord::setDiagonalTopLeft( bool dd )
{
  d->diagonalTopLeft = dd;
}

02986 bool XFRecord::diagonalBottomLeft() const
{
  return d->diagonalBottomLeft;
}

02991 void XFRecord::setDiagonalBottomLeft( bool dd )
{
  d->diagonalBottomLeft = d;
}

02996 unsigned XFRecord::diagonalStyle() const
{
  return d->diagonalStyle;
}

03001 void XFRecord::setDiagonalStyle( unsigned style )
{
  d->diagonalStyle = style;
}

03006 unsigned XFRecord::diagonalColor() const
{
  return d->diagonalColor;
}

03011 void XFRecord::setDiagonalColor( unsigned color )
{
  d->diagonalColor = color;
}

03016 unsigned XFRecord::fillPattern() const
{
  return d->fillPattern;
}

03021 void XFRecord::setFillPattern( unsigned pattern ) 
{
  d->fillPattern = pattern;
}

03026 unsigned XFRecord::patternForeColor() const
{
  return d->patternForeColor;
}

03031 void XFRecord::setPatternForeColor( unsigned color )
{
  d->patternForeColor = color;
}

03036 unsigned XFRecord::patternBackColor() const
{
  return d->patternBackColor;
}

03041 void XFRecord::setPatternBackColor( unsigned color )
{
  d->patternBackColor = color;
}

03046 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 & 0x80 );
  
  unsigned angle = data[7];
  setRotationAngle( ( angle != 255 ) ? ( angle & 0x7f ) : 0 );
  setStackedLetters( angle == 255 );
  
  if( version() == Excel97 )
  { 
    unsigned options = data[8];
    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 );
  }
}

03122 void XFRecord::dump( std::ostream& out ) const
{
  out << "XF" << std::endl;
  out << "       Hor-Align : " << horizontalAlignmentAsString() << std::endl;
  out << "       Ver-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 Content : " << ( shrinkContent() ? "yes" : "no" ) << std::endl;
}

//=============================================
//          ExcelReader
//=============================================

class ExcelReader::Private
{
public:

  // the workbook
  Workbook* workbook;

  // active sheet, all cell records will be stored here
  Sheet* activeSheet;
  
  // mapping from BOF pos to actual Sheet
  std::map<unsigned,Sheet*> bofMap;
  
  // shared-string table
  std::vector<UString> stringTable;
  
  // table of format
  std::map<unsigned,FormatRecord> formatTable;
  
  // table of font
  std::vector<FontRecord> fontTable;
  
  // table of Xformat
  std::vector<XFRecord> xfTable;
  
  // color table (from Palette record)
  std::vector<Color> colorTable;
  
  // mapping from font index to Sidewinder::FormatFont
  std::map<unsigned,FormatFont> fontCache;
};

ExcelReader::ExcelReader(): Reader()
{
  d = new ExcelReader::Private();
  d->workbook = 0;
}

ExcelReader::~ExcelReader()
{
  delete d;
}

Workbook* 
ExcelReader::load(
const char* filename )
{
  POLE::Storage storage;
  if( !storage.open( filename ) )
  {
    std::cerr << "Cannot open " << filename << std::endl;
    //return Error::CannotOpen;    
    setResult( CannotOpen );
    return (Workbook*)0;
  }
  
  // TODO check ole correctness

  POLE::Stream* stream;
  stream = storage.stream( "Workbook" ); // Excel 97/2000/XP/2003
  if( !stream ) stream = storage.stream( "Book" ); // Excel 5.0/7.0
  
  if( !stream )
  {
    std::cerr << filename << " is not Excel doc" << std::endl;
    setResult( CannotOpen );
    return (Workbook*)0;
  }

  unsigned long stream_size = stream->size();
  
  // FIXME
  unsigned char buffer[65536];
  
  d->workbook = new Workbook();
  
  // assume
  unsigned version = Sidewinder::Excel97;

  while( stream->tell() < stream_size )
  {
    // 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 );
    
    // load actual record data
    bytes_read = stream->read( buffer, size );
    if( bytes_read != size ) break;
    
    // 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
      BOFRecord* bof = dynamic_cast<BOFRecord*>(record);
      if( bof ) if( bof->type() == BOFRecord::Workbook )
        version = bof->version();

      //record->dump( std::cout );

      delete record;
    }

  }

  delete stream;
  
  storage.close();
  
  setResult( Ok );
  return d->workbook;
}

void ExcelReader::handleRecord( Record* record )
{
  if( !record ) return;

  handleBottomMargin( dynamic_cast<BottomMarginRecord*>( record ) );
  handleBoundSheet( dynamic_cast<BoundSheetRecord*>( record ) );
  handleBOF( dynamic_cast<BOFRecord*>( record ) );
  handleBoolErr( dynamic_cast<BoolErrRecord*>( record ) );
  handleBlank( dynamic_cast<BlankRecord*>( record ) );
  handleColInfo( dynamic_cast<ColInfoRecord*>( record ) );
  handleFormat( dynamic_cast<FormatRecord*>( record ) );
  handleFont( dynamic_cast<FontRecord*>( record ) );
  handleFooter( dynamic_cast<FooterRecord*>( record ) );
  handleHeader( dynamic_cast<HeaderRecord*>( record ) );
  handleLabel( dynamic_cast<LabelRecord*>( record ) );
  handleLabelSST( dynamic_cast<LabelSSTRecord*>( record ) );
  handleLeftMargin( dynamic_cast<LeftMarginRecord*>( record ) );
  handleMergedCells( dynamic_cast<MergedCellsRecord*>( record ) );
  handleMulBlank( dynamic_cast<MulBlankRecord*>( record ) );
  handleMulRK( dynamic_cast<MulRKRecord*>( record ) );
  handleNumber( dynamic_cast<NumberRecord*>( record ) );
  handlePalette( dynamic_cast<PaletteRecord*>( record ) );
  handleRightMargin( dynamic_cast<RightMarginRecord*>( record ) );
  handleRK( dynamic_cast<RKRecord*>( record ) );
  handleRow( dynamic_cast<RowRecord*>( record ) );
  handleRString( dynamic_cast<RStringRecord*>( record ) );
  handleSST( dynamic_cast<SSTRecord*>( record ) );
  handleTopMargin( dynamic_cast<TopMarginRecord*>( record ) );
  handleXF( dynamic_cast<XFRecord*>( record ) );
}

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();
  
  Value value;
  if( record->isBool() )
    value.setValue( record->value() );
  else
  {
    switch( record->errorCode() )
    {
      case BoolErrRecord::ErrorNull:    value = Value::errorNULL(); break;
      case BoolErrRecord::ErrorDivZero: value = Value::errorDIV0(); break;
      case BoolErrRecord::ErrorValue:   value = Value::errorVALUE(); break;
      case BoolErrRecord::ErrorRef:     value = Value::errorREF(); break;
      case BoolErrRecord::ErrorName:    value = Value::errorNAME(); break;
      case BoolErrRecord::ErrorNum:     value = Value::errorNUM(); break;
      case BoolErrRecord::ErrorNA:      value = Value::errorNA(); break;
      default: break;
    }
  }
  
  Cell* cell = d->activeSheet->cell( column, row, true );
  if( cell )
  {
    cell->setValue( value );
    cell->setFormat( convertFormat( 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->setFormat( convertFormat( 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->setFormat( convertFormat( 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->setFormat( convertFormat( 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;
}

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->setFormat( convertFormat( 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->setFormat( convertFormat( 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->setFormat( convertFormat( record->xfIndex( column-firstColumn ) ) );
    }
  }
}

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->setFormat( convertFormat( 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->setFormat( convertFormat( 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->setFormat( convertFormat( 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->setFormat( convertFormat( 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::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.script() == FontRecord::Subscript );
    font.setSuperscript( fr.script() == 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;
}

// 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 = 2;
    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;
}

// big task: convert Excel XFormat into Sidewinder::Format
Format ExcelReader::convertFormat( unsigned xfIndex )
{
  Format format;

  if( xfIndex >= d->xfTable.size() ) return format;

  XFRecord xf = d->xfTable[ xfIndex ];
    
  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
  };
  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 );

  return format;
}

void ExcelReader::handleXF( XFRecord* record )
{
  if( !record ) return;
  
  d->xfTable.push_back( *record );  
}



Generated by  Doxygen 1.6.0   Back to index