/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libcdr project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <locale.h>
#include <math.h>
#include <string.h>
#include <sstream>
#include <set>
#ifndef BOOST_ALL_NO_LIB
#define BOOST_ALL_NO_LIB 1
#endif
#include <boost/algorithm/string.hpp>
#include <boost/optional.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/spirit/include/qi.hpp>
#include "libcdr_utils.h"
#include "CDRDocumentStructure.h"
#include "CDRInternalStream.h"
#include "CDRParser.h"
#include "CDRCollector.h"
#include "CDRColorPalettes.h"

#ifndef DUMP_PREVIEW_IMAGE
#define DUMP_PREVIEW_IMAGE 0
#endif

namespace
{

unsigned getCDRVersion(char c)
{
  if (c == 0x20)
    return 300;
  else if (c < 0x31)
    return 0;
  else if (c < 0x3a)
    return 100 * ((unsigned char)c - 0x30);
  else if (c < 0x41)
    return 0;
  return 100 * ((unsigned char)c - 0x37);
}

struct CDRStltRecord
{
  CDRStltRecord()
    : parentId(0), fillId(0), outlId(0), fontRecId(0), alignId(0),
      intervalId(0), set5Id(0), set11Id(0), tabId(0),
      bulletId(0), indentId(0), hyphenId(0), dropCapId(0) {}
  unsigned parentId;
  unsigned fillId;
  unsigned outlId;
  unsigned fontRecId;
  unsigned alignId;
  unsigned intervalId;
  unsigned set5Id;
  unsigned set11Id;
  unsigned tabId;
  unsigned bulletId;
  unsigned indentId;
  unsigned hyphenId;
  unsigned dropCapId;
};

static void processNameForEncoding(librevenge::RVNGString &name, unsigned short &encoding)
{
  namespace qi = boost::spirit::qi;

  qi::symbols<char, unsigned short> codepage;
  codepage.add
  ("EC ", 0xee)
  ("cilliryC ", 0xcc)
  ("ryC ", 0xcc)
  ("RYC ", 0xcc)
  ("citlaB ", 0xba)
  ("keerG ", 0xa1)
  ("ruT ", 0xa2)
  ("RUT ", 0xa2)
  ("werbeH ", 0xb1)
  ("cibarA ", 0xb2)
  ("iahT ", 0xde)
  ;

  std::string fontName(name.cstr());
  unsigned short enc = encoding;
  std::string revName;
  if (qi::parse(fontName.rbegin(), fontName.rend(), codepage >> +qi::char_, enc, revName))
  {
    encoding = enc;
    name = std::string(revName.rbegin(), revName.rend()).c_str();
  }
  else if (boost::starts_with(fontName, "GOST"))
  {
    encoding = 0xcc;
  }
}

static int parseColourString(const char *colourString, libcdr::CDRColor &colour, double &opacity)
{
  using namespace boost::spirit::qi;
  bool bRes = false;

  boost::optional<unsigned> colourModel;
  unsigned val0, val1, val2, val3, val4;

  if (colourString)
  {
    symbols<char, unsigned> cmodel;
    cmodel.add
    ("CMYK", 2)
    ("CMYK255", 3)
    ;
    auto it = colourString;
    const auto end = it + std::strlen(it);
    bRes = phrase_parse(it, end,
                        //  Begin grammar
                        (
                          (cmodel | omit[+alnum]) >> -lit(',')
                          >> omit[+alnum] >> -lit(',')
                          >> uint_ >> -lit(',')
                          >> uint_ >> -lit(',')
                          >> uint_ >> -lit(',')
                          >> uint_ >> -lit(',')
                          >> uint_ >> -lit(',')
                          >> (repeat(8)[alnum] >> '-' >> repeat(3)[repeat(4)[alnum] >> '-'] >> repeat(12)[alnum])
                        ),
                        //  End grammar
                        space,
                        colourModel, val0, val1, val2, val3, val4)
           && it == end;
  }

  if (!bRes)
    return -1;

  if (colourModel)
    colour.m_colorModel = get(colourModel);
  colour.m_colorValue = val0 | (val1 << 8) | (val2 << 16) | (val3 << 24);
  opacity = (double)val4 / 100.0;

  return 1;
}

} // anonymous namespace

libcdr::CDRParser::CDRParser(const std::vector<librevenge::RVNGInputStream *> &externalStreams, libcdr::CDRCollector *collector)
  : CommonParser(collector), m_externalStreams(externalStreams),
    m_fonts(), m_fillStyles(), m_lineStyles(), m_arrows(), m_version(0), m_waldoOutlId(0), m_waldoFillId(0) {}

libcdr::CDRParser::~CDRParser()
{
  m_collector->collectLevel(0);
}

bool libcdr::CDRParser::parseWaldo(librevenge::RVNGInputStream *input)
{
  try
  {
    input->seek(0, librevenge::RVNG_SEEK_SET);
    unsigned short magic = readU16(input);
    if (magic != 0x4c57)
      return false;
    m_version = 200;
    m_precision = libcdr::PRECISION_16BIT;
    if ('e' >= readU8(input))
      m_version = 100;
    input->seek(1, librevenge::RVNG_SEEK_CUR);
    std::vector<unsigned> offsets;
    unsigned i = 0;
    for (i = 0; i < 8; ++i)
      offsets.push_back(readU32(input));
    input->seek(1, librevenge::RVNG_SEEK_CUR);
    for (i = 0; i < 10; i++)
      offsets.push_back(readU32(input));
    input->seek(offsets[0], librevenge::RVNG_SEEK_SET);
    CDR_DEBUG_MSG(("CDRParser::parseWaldo, Mcfg offset 0x%x\n", (unsigned)input->tell()));
    readMcfg(input, 275);
    std::vector<WaldoRecordInfo> records;
    std::map<unsigned, WaldoRecordInfo> records2;
    std::map<unsigned, WaldoRecordInfo> records3;
    std::map<unsigned, WaldoRecordInfo> records4;
    std::map<unsigned, WaldoRecordInfo> records6;
    std::map<unsigned, WaldoRecordInfo> records8;
    std::map<unsigned, WaldoRecordInfo> records7;
    std::map<unsigned, WaldoRecordInfo> recordsOther;
    if (offsets[3])
    {
      input->seek(offsets[3], librevenge::RVNG_SEEK_SET);
      if (!gatherWaldoInformation(input, records, records2, records3, records4, records6, records7, records8, recordsOther))
        return false;
    }
    if (offsets[5])
    {
      input->seek(offsets[5], librevenge::RVNG_SEEK_SET);
      gatherWaldoInformation(input, records, records2, records3, records4, records6, records7, records8, recordsOther);
    }
    if (offsets[11])
    {
      input->seek(offsets[11], librevenge::RVNG_SEEK_SET);
      gatherWaldoInformation(input, records, records2, records3, records4, records6, records7, records8, recordsOther);
    }
    std::map<unsigned, WaldoRecordType1> records1;
    for (auto &record : records)
    {
      input->seek(record.offset, librevenge::RVNG_SEEK_SET);
      unsigned length = readU32(input);
      if (length != 0x18)
      {
        CDR_DEBUG_MSG(("Throwing GenericException\n"));
        throw GenericException();
      }
      unsigned short next = readU16(input);
      unsigned short previous = readU16(input);
      unsigned short child = readU16(input);
      unsigned short parent = readU16(input);
      input->seek(4, librevenge::RVNG_SEEK_CUR);
      unsigned short moreDataID = readU16(input);
      double x0 = readCoordinate(input);
      double y0 = readCoordinate(input);
      double x1 = readCoordinate(input);
      double y1 = readCoordinate(input);
      unsigned short flags = readU16(input);
      CDRTransform trafo;
      if (moreDataID)
      {
        std::map<unsigned, WaldoRecordInfo>::const_iterator iter7 = records7.find(moreDataID);
        if (iter7 != records7.end())
          input->seek(iter7->second.offset, librevenge::RVNG_SEEK_SET);
        input->seek(0x26, librevenge::RVNG_SEEK_CUR);
        double v0 = readFixedPoint(input);
        double v1 = readFixedPoint(input);
        double v2 = readFixedPoint(input) / 1000.0;
        double v3 = readFixedPoint(input);
        double v4 = readFixedPoint(input);
        double v5 = readFixedPoint(input) / 1000.0;
        trafo = CDRTransform(v0, v1, v2, v3, v4, v5);
      }
      records1[record.id] = WaldoRecordType1(record.id, next, previous, child, parent, flags, x0, y0, x1, y1, trafo);
    }
    std::map<unsigned, WaldoRecordInfo>::const_iterator iter;
    for (iter = records3.begin(); iter != records3.end(); ++iter)
      readWaldoRecord(input, iter->second);
    for (iter = records6.begin(); iter != records6.end(); ++iter)
      readWaldoRecord(input, iter->second);
    for (iter = records8.begin(); iter != records8.end(); ++iter)
      readWaldoRecord(input, iter->second);
    for (iter = recordsOther.begin(); iter != recordsOther.end(); ++iter)
      readWaldoRecord(input, iter->second);
    if (!records1.empty() && !records2.empty())
    {

      std::map<unsigned, WaldoRecordType1>::iterator iter1 = records1.find(1);
      std::stack<WaldoRecordType1> waldoStack;
      if (iter1 != records1.end())
      {
        waldoStack.push(iter1->second);
        m_collector->collectVect(waldoStack.size());
        parseWaldoStructure(input, waldoStack, records1, records2);
      }
      iter1 = records1.find(0);
      if (iter1 == records1.end())
        return false;
      waldoStack = std::stack<WaldoRecordType1>();
      waldoStack.push(iter1->second);
      m_collector->collectPage(waldoStack.size());
      if (!parseWaldoStructure(input, waldoStack, records1, records2))
        return false;
    }
    return true;
  }
  catch (...)
  {
    return false;
  }
}

bool libcdr::CDRParser::gatherWaldoInformation(librevenge::RVNGInputStream *input, std::vector<WaldoRecordInfo> &records, std::map<unsigned, WaldoRecordInfo> &records2,
                                               std::map<unsigned, WaldoRecordInfo> &records3, std::map<unsigned, WaldoRecordInfo> &records4,
                                               std::map<unsigned, WaldoRecordInfo> &records6, std::map<unsigned, WaldoRecordInfo> &records7,
                                               std::map<unsigned, WaldoRecordInfo> &records8, std::map<unsigned, WaldoRecordInfo> recordsOther)
{
  try
  {
    unsigned short numRecords = readU16(input);
    for (; numRecords > 0 && !input->isEnd(); --numRecords)
    {
      unsigned char recordType = readU8(input);
      unsigned recordId = readU32(input);
      unsigned recordOffset = readU32(input);
      switch (recordType)
      {
      case 1:
        records.push_back(WaldoRecordInfo(recordType, recordId, recordOffset));
        break;
      case 2:
        records2[recordId]  = WaldoRecordInfo(recordType, recordId, recordOffset);
        break;
      case 3:
        records3[recordId]  = WaldoRecordInfo(recordType, recordId, recordOffset);
        break;
      case 4:
        records4[recordId]  = WaldoRecordInfo(recordType, recordId, recordOffset);
        break;
      case 6:
        records6[recordId]  = WaldoRecordInfo(recordType, recordId, recordOffset);
        break;
      case 7:
        records7[recordId]  = WaldoRecordInfo(recordType, recordId, recordOffset);
        break;
      case 8:
        records8[recordId]  = WaldoRecordInfo(recordType, recordId, recordOffset);
        break;
      default:
        recordsOther[recordId]  = WaldoRecordInfo(recordType, recordId, recordOffset);
        break;
      }
    }
    return true;
  }
  catch (...)
  {
    CDR_DEBUG_MSG(("CDRParser::gatherWaldoInformation: something went wrong during information gathering\n"));
    return false;
  }
}


bool libcdr::CDRParser::parseWaldoStructure(librevenge::RVNGInputStream *input, std::stack<WaldoRecordType1> &waldoStack,
                                            const std::map<unsigned, WaldoRecordType1> &records1, std::map<unsigned, WaldoRecordInfo> &records2)
{
  std::set<unsigned> visited;
  while (!waldoStack.empty() && visited.insert(waldoStack.top().m_id).second)
  {
    m_collector->collectBBox(waldoStack.top().m_x0, waldoStack.top().m_y0, waldoStack.top().m_x1, waldoStack.top().m_y1);
    std::map<unsigned, WaldoRecordType1>::const_iterator iter1;
    if (waldoStack.top().m_flags & 0x01)
    {
      if (waldoStack.size() > 1)
      {
        m_collector->collectGroup(waldoStack.size());
        m_collector->collectSpnd(waldoStack.top().m_id);
        CDRTransforms trafos;
        trafos.append(waldoStack.top().m_trafo);
        m_collector->collectTransform(trafos, true);
      }
      iter1 = records1.find(waldoStack.top().m_child);
      if (iter1 == records1.end())
        return false;
      waldoStack.push(iter1->second);
      m_collector->collectLevel(waldoStack.size());
    }
    else
    {
      if (waldoStack.size() > 1)
        m_collector->collectObject(waldoStack.size());
      std::map<unsigned, WaldoRecordInfo>::const_iterator iter2 = records2.find(waldoStack.top().m_child);
      if (iter2 == records2.end())
        return false;
      readWaldoRecord(input, iter2->second);
      while (!waldoStack.empty() && !waldoStack.top().m_next)
        waldoStack.pop();
      m_collector->collectLevel(waldoStack.size());
      if (waldoStack.empty())
        return true;
      iter1 = records1.find(waldoStack.top().m_next);
      if (iter1 == records1.end())
        return false;
      waldoStack.top() = iter1->second;
    }
  }
  return waldoStack.empty();
}

void libcdr::CDRParser::readWaldoRecord(librevenge::RVNGInputStream *input, const WaldoRecordInfo &info)
{
  CDR_DEBUG_MSG(("CDRParser::readWaldoRecord, type %i, id %x, offset %x\n", info.type, info.id, info.offset));
  input->seek(info.offset, librevenge::RVNG_SEEK_SET);
  switch (info.type)
  {
  case 2:
  {
    unsigned length = readU32(input);
    readWaldoLoda(input, length);
  }
  break;
  case 3:
  {
    unsigned length = readU32(input);
    readWaldoBmp(input, length, info.id);
  }
  break;
  case 6:
    readWaldoBmpf(input, info.id);
    break;
  default:
    break;
  }
}

void libcdr::CDRParser::readWaldoTrfd(librevenge::RVNGInputStream *input)
{
  if (m_version >= 400)
    return;
  double v0 = 0.0;
  double v1 = 0.0;
  double x0 = 0.0;
  double v3 = 0.0;
  double v4 = 0.0;
  double y0 = 0.0;
  if (m_version >= 300)
  {
    long startPosition = input->tell();
    input->seek(0x0a, librevenge::RVNG_SEEK_CUR);
    unsigned offset = readUnsigned(input);
    input->seek(startPosition+offset, librevenge::RVNG_SEEK_SET);
    v0 = readFixedPoint(input);
    v1 = readFixedPoint(input);
    x0 = (double)readS32(input) / 1000.0;
    v3 = readFixedPoint(input);
    v4 = readFixedPoint(input);
    y0 = (double)readS32(input) / 1000.0;
  }
  else
  {
    x0 = readCoordinate(input);
    y0 = readCoordinate(input);
    v0 = readFixedPoint(input);
    v1 = readFixedPoint(input);
    x0 += readFixedPoint(input) / 1000.0;
    v3 = readFixedPoint(input);
    v4 = readFixedPoint(input);
    y0 += readFixedPoint(input) / 1000.0;
  }
  CDR_DEBUG_MSG(("CDRParser::readWaldoTrfd %f %f %f %f %f %f %u\n", v0, v1, x0, v3, v4, y0, m_version));
  CDRTransforms trafos;
  trafos.append(v0, v1, x0, v3, v4, y0);
  m_collector->collectTransform(trafos, m_version < 400);
}

void libcdr::CDRParser::readWaldoLoda(librevenge::RVNGInputStream *input, unsigned length)
{
  if (m_version >= 300)
    return;
  long startPosition = input->tell();
  readWaldoTrfd(input);
  unsigned chunkType = readU8(input);
  unsigned shapeOffset = readU16(input);
  unsigned outlOffset = readU16(input);
  unsigned fillOffset = readU16(input);
  if (outlOffset)
  {
    input->seek(startPosition + outlOffset, librevenge::RVNG_SEEK_SET);
    readWaldoOutl(input);
  }
  if (fillOffset)
  {
    input->seek(startPosition + fillOffset, librevenge::RVNG_SEEK_SET);
    readWaldoFill(input);
  }
  if (shapeOffset)
  {
    input->seek(startPosition + shapeOffset, librevenge::RVNG_SEEK_SET);
    if (chunkType == 0x00) // Rectangle
      readRectangle(input);
    else if (chunkType == 0x01) // Ellipse
      readEllipse(input);
    else if (chunkType == 0x02) // Line and curve
      readLineAndCurve(input);
    /* else if (chunkType == 0x03) // Text
            readText(input); */
    else if (chunkType == 0x04) // Bitmap
      readBitmap(input);
  }
  input->seek(startPosition + length, librevenge::RVNG_SEEK_SET);
}

bool libcdr::CDRParser::parseRecords(librevenge::RVNGInputStream *input, const std::vector<unsigned> &blockLengths, unsigned level)
{
  if (!input)
  {
    return false;
  }
  m_collector->collectLevel(level);
  while (!input->isEnd())
  {
    if (!parseRecord(input, blockLengths, level))
      return false;
  }
  return true;
}

bool libcdr::CDRParser::parseRecord(librevenge::RVNGInputStream *input, const std::vector<unsigned> &blockLengths, unsigned level)
{
  if (!input)
  {
    return false;
  }
  try
  {
    m_collector->collectLevel(level);
    while (!input->isEnd() && readU8(input) == 0)
    {
    }
    if (!input->isEnd())
      input->seek(-1, librevenge::RVNG_SEEK_CUR);
    else
      return true;
    unsigned fourCC = readU32(input);
    unsigned length = readU32(input);
    if (blockLengths.size() > length)
      length=blockLengths[length];
    unsigned long position = input->tell();
    unsigned listType(0);
    if (fourCC == CDR_FOURCC_RIFF || fourCC == CDR_FOURCC_LIST)
    {
      listType = readU32(input);
      if (listType == CDR_FOURCC_stlt && m_version >= 700)
        fourCC = listType;
      else
        m_collector->collectOtherList();
    }
    CDR_DEBUG_MSG(("Record: level %u %s, length: 0x%.8x (%u)\n", level, toFourCC(fourCC), length, length));

    if (fourCC == CDR_FOURCC_RIFF || fourCC == CDR_FOURCC_LIST)
    {
      CDR_DEBUG_MSG(("CDR listType: %s\n", toFourCC(listType)));
      unsigned cmprsize = length-4;
      if (listType == CDR_FOURCC_cmpr)
      {
        cmprsize  = readU32(input);
        input->seek(12, librevenge::RVNG_SEEK_CUR);
        if (readU32(input) != CDR_FOURCC_CPng)
          return false;
        if (readU16(input) != 1)
          return false;
        if (readU16(input) != 4)
          return false;
      }
      else if (listType == CDR_FOURCC_page)
        m_collector->collectPage(level);
      else if (listType == CDR_FOURCC_obj)
        m_collector->collectObject(level);
      else if (listType == CDR_FOURCC_grp || listType == CDR_FOURCC_lnkg)
        m_collector->collectGroup(level);
      else if ((listType & 0xffffff) == CDR_FOURCC_CDR || (listType & 0xffffff) == CDR_FOURCC_cdr)
      {
        m_version = getCDRVersion((listType & 0xff000000) >> 24);
        if (m_version < 600)
          m_precision = libcdr::PRECISION_16BIT;
        else
          m_precision = libcdr::PRECISION_32BIT;
      }
      else if (listType == CDR_FOURCC_vect || listType == CDR_FOURCC_clpt)
        m_collector->collectVect(level);

      bool compressed = (listType == CDR_FOURCC_cmpr ? true : false);
      CDRInternalStream tmpStream(input, cmprsize, compressed);
      if (!compressed)
      {
        if (!parseRecords(&tmpStream, blockLengths, level+1))
          return false;
      }
      else
      {
        std::vector<unsigned> tmpBlockLengths;
        unsigned blocksLength = length + position - input->tell();
        CDRInternalStream tmpBlocksStream(input, blocksLength, compressed);
        while (!tmpBlocksStream.isEnd())
          tmpBlockLengths.push_back(readU32(&tmpBlocksStream));
        if (!parseRecords(&tmpStream, tmpBlockLengths, level+1))
          return false;
      }
    }
    else
      readRecord(fourCC, length, input);

    input->seek(position + length, librevenge::RVNG_SEEK_SET);
    return true;
  }
  catch (...)
  {
    return false;
  }
}

void libcdr::CDRParser::readRecord(unsigned fourCC, unsigned length, librevenge::RVNGInputStream *input)
{
  long recordStart = input->tell();
  switch (fourCC)
  {
  case CDR_FOURCC_DISP:
    readDisp(input, length);
    break;
  case CDR_FOURCC_loda:
  case CDR_FOURCC_lobj:
    readLoda(input, length);
    break;
  case CDR_FOURCC_vrsn:
    readVersion(input, length);
    break;
  case CDR_FOURCC_trfd:
    readTrfd(input, length);
    break;
  case CDR_FOURCC_outl:
    readOutl(input, length);
    break;
  case CDR_FOURCC_fild:
  case CDR_FOURCC_fill:
    readFild(input, length);
    break;
  case CDR_FOURCC_arrw:
    readArrw(input, length);
    break;
  case CDR_FOURCC_flgs:
    readFlags(input, length);
    break;
  case CDR_FOURCC_mcfg:
    readMcfg(input, length);
    break;
  case CDR_FOURCC_bmp:
    readBmp(input, length);
    break;
  case CDR_FOURCC_bmpf:
    readBmpf(input, length);
    break;
  case CDR_FOURCC_ppdt:
    readPpdt(input, length);
    break;
  case CDR_FOURCC_ftil:
    readFtil(input, length);
    break;
  case CDR_FOURCC_iccd:
    readIccd(input, length);
    break;
  case CDR_FOURCC_bbox:
    readBBox(input, length);
    break;
  case CDR_FOURCC_spnd:
    readSpnd(input, length);
    break;
  case CDR_FOURCC_uidr:
    readUidr(input, length);
    break;
  case CDR_FOURCC_vpat:
    readVpat(input, length);
    break;
  case CDR_FOURCC_font:
    readFont(input, length);
    break;
  case CDR_FOURCC_stlt:
    readStlt(input, length);
    break;
  case CDR_FOURCC_txsm:
    readTxsm(input, length);
    break;
  case CDR_FOURCC_udta:
    readUdta(input);
    break;
  case CDR_FOURCC_styd:
    readStyd(input);
    break;
  default:
    break;
  }
  input->seek(recordStart + length, librevenge::RVNG_SEEK_CUR);
}

double libcdr::CDRParser::readRectCoord(librevenge::RVNGInputStream *input)
{
  if (m_version < 1500)
    return readCoordinate(input);
  return readDouble(input) / 254000.0;
}

libcdr::CDRColor libcdr::CDRParser::readColor(librevenge::RVNGInputStream *input)
{
  unsigned short colorModel = 0;
  unsigned colorValue = 0;
  if (m_version >= 500)
  {
    colorModel = readU16(input);
    if (colorModel == 0x01 && m_version >= 1300)
      colorModel = 0x19;
    if (colorModel == 0x19 || colorModel == 0x1e)
    {
      unsigned char r = 0;
      unsigned char g = 0;
      unsigned char b = 0;
      unsigned char c = 0;
      unsigned char m = 0;
      unsigned char y = 0;
      unsigned char k = 100;
      unsigned short paletteID = 0;
      /* A bug in CorelDraw x7 creates a corrupted file. If we meet colorModel of 0x1e,
       * it is actually colorModel 0x19 and paletteId 0x1e */
      if (colorModel == 0x1e)
      {
        colorModel = 0x19;
        paletteID = 0x1e;
      }
      else
      {
        paletteID = readU16(input);
        input->seek(4, librevenge::RVNG_SEEK_CUR);
      }
      unsigned short ix = readU16(input);
      unsigned short tint = readU16(input);
      switch (paletteID)
      {
      case 0x03:
        if (ix > 0
            && ix <= sizeof(palette_19_03_C)/sizeof(palette_19_03_C[0])
            && ix <= sizeof(palette_19_03_M)/sizeof(palette_19_03_M[0])
            && ix <= sizeof(palette_19_03_Y)/sizeof(palette_19_03_Y[0])
            && ix <= sizeof(palette_19_03_K)/sizeof(palette_19_03_K[0]))
        {
          c = palette_19_03_C[ix-1];
          m = palette_19_03_M[ix-1];
          y = palette_19_03_Y[ix-1];
          k = palette_19_03_K[ix-1];
        }
        break;
      case 0x08:
        if (ix > 0
            && ix <= sizeof(palette_19_08_C)/sizeof(palette_19_08_C[0])
            && ix <= sizeof(palette_19_08_M)/sizeof(palette_19_08_M[0])
            && ix <= sizeof(palette_19_08_Y)/sizeof(palette_19_08_Y[0])
            && ix <= sizeof(palette_19_08_K)/sizeof(palette_19_08_K[0]))
        {
          c = palette_19_08_C[ix-1];
          m = palette_19_08_M[ix-1];
          y = palette_19_08_Y[ix-1];
          k = palette_19_08_K[ix-1];
        }
        break;
      case 0x09:
        if (ix > 0
            && ix <= sizeof(palette_19_09_L)/sizeof(palette_19_09_L[0])
            && ix <= sizeof(palette_19_09_A)/sizeof(palette_19_09_A[0])
            && ix <= sizeof(palette_19_09_B)/sizeof(palette_19_09_B[0]))
        {
          colorValue = palette_19_09_B[ix-1];
          colorValue <<= 8;
          colorValue |= palette_19_09_A[ix-1];
          colorValue <<= 8;
          colorValue |= palette_19_09_L[ix-1];
        }
        break;
      case 0x0a:
        if (ix > 0
            && ix <= sizeof(palette_19_0A_C)/sizeof(palette_19_0A_C[0])
            && ix <= sizeof(palette_19_0A_M)/sizeof(palette_19_0A_M[0])
            && ix <= sizeof(palette_19_0A_Y)/sizeof(palette_19_0A_Y[0])
            && ix <= sizeof(palette_19_0A_K)/sizeof(palette_19_0A_K[0]))
        {
          c = palette_19_0A_C[ix-1];
          m = palette_19_0A_M[ix-1];
          y = palette_19_0A_Y[ix-1];
          k = palette_19_0A_K[ix-1];
        }
        break;
      case 0x0b:
        if (ix > 0
            && ix <= sizeof(palette_19_0B_C)/sizeof(palette_19_0B_C[0])
            && ix <= sizeof(palette_19_0B_M)/sizeof(palette_19_0B_M[0])
            && ix <= sizeof(palette_19_0B_Y)/sizeof(palette_19_0B_Y[0])
            && ix <= sizeof(palette_19_0B_K)/sizeof(palette_19_0B_K[0]))
        {
          c = palette_19_0B_C[ix-1];
          m = palette_19_0B_M[ix-1];
          y = palette_19_0B_Y[ix-1];
          k = palette_19_0B_K[ix-1];
        }
        break;
      case 0x11:
        if (ix > 0
            && ix <= sizeof(palette_19_11_C)/sizeof(palette_19_11_C[0])
            && ix <= sizeof(palette_19_11_M)/sizeof(palette_19_11_M[0])
            && ix <= sizeof(palette_19_11_Y)/sizeof(palette_19_11_Y[0])
            && ix <= sizeof(palette_19_11_K)/sizeof(palette_19_11_K[0]))
        {
          c = palette_19_11_C[ix-1];
          m = palette_19_11_M[ix-1];
          y = palette_19_11_Y[ix-1];
          k = palette_19_11_K[ix-1];
        }
        break;
      case 0x12:
        if (ix > 0
            && ix <= sizeof(palette_19_12_C)/sizeof(palette_19_12_C[0])
            && ix <= sizeof(palette_19_12_M)/sizeof(palette_19_12_M[0])
            && ix <= sizeof(palette_19_12_Y)/sizeof(palette_19_12_Y[0])
            && ix <= sizeof(palette_19_12_K)/sizeof(palette_19_12_K[0]))
        {
          c = palette_19_12_C[ix-1];
          m = palette_19_12_M[ix-1];
          y = palette_19_12_Y[ix-1];
          k = palette_19_12_K[ix-1];
        }
        break;
      case 0x14:
        if (ix > 0
            && ix <= sizeof(palette_19_14_C)/sizeof(palette_19_14_C[0])
            && ix <= sizeof(palette_19_14_M)/sizeof(palette_19_14_M[0])
            && ix <= sizeof(palette_19_14_Y)/sizeof(palette_19_14_Y[0])
            && ix <= sizeof(palette_19_14_K)/sizeof(palette_19_14_K[0]))
        {
          c = palette_19_14_C[ix-1];
          m = palette_19_14_M[ix-1];
          y = palette_19_14_Y[ix-1];
          k = palette_19_14_K[ix-1];
        }
        break;
      case 0x15:
        if (ix > 0
            && ix <= sizeof(palette_19_15_C)/sizeof(palette_19_15_C[0])
            && ix <= sizeof(palette_19_15_M)/sizeof(palette_19_15_M[0])
            && ix <= sizeof(palette_19_15_Y)/sizeof(palette_19_15_Y[0])
            && ix <= sizeof(palette_19_15_K)/sizeof(palette_19_15_K[0]))
        {
          c = palette_19_15_C[ix-1];
          m = palette_19_15_M[ix-1];
          y = palette_19_15_Y[ix-1];
          k = palette_19_15_K[ix-1];
        }
        break;
      case 0x16:
        if (ix > 0
            && ix <= sizeof(palette_19_16_C)/sizeof(palette_19_16_C[0])
            && ix <= sizeof(palette_19_16_M)/sizeof(palette_19_16_M[0])
            && ix <= sizeof(palette_19_16_Y)/sizeof(palette_19_16_Y[0])
            && ix <= sizeof(palette_19_16_K)/sizeof(palette_19_16_K[0]))
        {
          c = palette_19_16_C[ix-1];
          m = palette_19_16_M[ix-1];
          y = palette_19_16_Y[ix-1];
          k = palette_19_16_K[ix-1];
        }
        break;
      case 0x17:
        if (ix > 0
            && ix <= sizeof(palette_19_17_C)/sizeof(palette_19_17_C[0])
            && ix <= sizeof(palette_19_17_M)/sizeof(palette_19_17_M[0])
            && ix <= sizeof(palette_19_17_Y)/sizeof(palette_19_17_Y[0])
            && ix <= sizeof(palette_19_17_K)/sizeof(palette_19_17_K[0]))
        {
          c = palette_19_17_C[ix-1];
          m = palette_19_17_M[ix-1];
          y = palette_19_17_Y[ix-1];
          k = palette_19_17_K[ix-1];
        }
        break;
      case 0x1a:
        if (ix < sizeof(palette_19_1A_C)/sizeof(palette_19_1A_C[0])
            && ix < sizeof(palette_19_1A_M)/sizeof(palette_19_1A_M[0])
            && ix < sizeof(palette_19_1A_Y)/sizeof(palette_19_1A_Y[0])
            && ix < sizeof(palette_19_1A_K)/sizeof(palette_19_1A_K[0]))
        {
          c = palette_19_1A_C[ix];
          m = palette_19_1A_M[ix];
          y = palette_19_1A_Y[ix];
          k = palette_19_1A_K[ix];
        }
        break;
      case 0x1b:
        if (ix < sizeof(palette_19_1B_C)/sizeof(palette_19_1B_C[0])
            && ix < sizeof(palette_19_1B_M)/sizeof(palette_19_1B_M[0])
            && ix < sizeof(palette_19_1B_Y)/sizeof(palette_19_1B_Y[0])
            && ix < sizeof(palette_19_1B_K)/sizeof(palette_19_1B_K[0]))
        {
          c = palette_19_1B_C[ix];
          m = palette_19_1B_M[ix];
          y = palette_19_1B_Y[ix];
          k = palette_19_1B_K[ix];
        }
        break;
      case 0x1c:
        if (ix < sizeof(palette_19_1C_C)/sizeof(palette_19_1C_C[0])
            && ix < sizeof(palette_19_1C_M)/sizeof(palette_19_1C_M[0])
            && ix < sizeof(palette_19_1C_Y)/sizeof(palette_19_1C_Y[0])
            && ix < sizeof(palette_19_1C_K)/sizeof(palette_19_1C_K[0]))
        {
          c = palette_19_1C_C[ix];
          m = palette_19_1C_M[ix];
          y = palette_19_1C_Y[ix];
          k = palette_19_1C_K[ix];
        }
        break;
      case 0x1d:
        if (ix < sizeof(palette_19_1D_C)/sizeof(palette_19_1D_C[0])
            && ix < sizeof(palette_19_1D_M)/sizeof(palette_19_1D_M[0])
            && ix < sizeof(palette_19_1D_Y)/sizeof(palette_19_1D_Y[0])
            && ix < sizeof(palette_19_1D_K)/sizeof(palette_19_1D_K[0]))
        {
          c = palette_19_1D_C[ix];
          m = palette_19_1D_M[ix];
          y = palette_19_1D_Y[ix];
          k = palette_19_1D_K[ix];
        }
        break;
      case 0x1e:
        if (ix > 0
            && ix <= sizeof(palette_19_1E_R)/sizeof(palette_19_1E_R[0])
            && ix <= sizeof(palette_19_1E_G)/sizeof(palette_19_1E_G[0])
            && ix <= sizeof(palette_19_1E_B)/sizeof(palette_19_1E_B[0]))
        {
          r = palette_19_1E_R[ix-1];
          g = palette_19_1E_G[ix-1];
          b = palette_19_1E_B[ix-1];
        }
        break;
      case 0x1f:
        if (ix > 0
            && ix <= sizeof(palette_19_1F_R)/sizeof(palette_19_1F_R[0])
            && ix <= sizeof(palette_19_1F_G)/sizeof(palette_19_1F_G[0])
            && ix <= sizeof(palette_19_1F_B)/sizeof(palette_19_1F_B[0]))
        {
          r = palette_19_1F_R[ix-1];
          g = palette_19_1F_G[ix-1];
          b = palette_19_1F_B[ix-1];
        }
        break;
      case 0x20:
        if (ix > 0
            && ix <= sizeof(palette_19_20_R)/sizeof(palette_19_20_R[0])
            && ix <= sizeof(palette_19_20_G)/sizeof(palette_19_20_G[0])
            && ix <= sizeof(palette_19_20_B)/sizeof(palette_19_20_B[0]))
        {
          r = palette_19_20_R[ix-1];
          g = palette_19_20_G[ix-1];
          b = palette_19_20_B[ix-1];
        }
        break;
      case 0x23:
        if (ix > 0
            && ix <= sizeof(palette_19_23_C)/sizeof(palette_19_23_C[0])
            && ix <= sizeof(palette_19_23_M)/sizeof(palette_19_23_M[0])
            && ix <= sizeof(palette_19_23_Y)/sizeof(palette_19_23_Y[0])
            && ix <= sizeof(palette_19_23_K)/sizeof(palette_19_23_K[0]))
        {
          c = palette_19_23_C[ix-1];
          m = palette_19_23_M[ix-1];
          y = palette_19_23_Y[ix-1];
          k = palette_19_23_K[ix-1];
        }
        break;
      case 0x24:
        if (ix > 0
            && ix <= sizeof(palette_19_24_C)/sizeof(palette_19_24_C[0])
            && ix <= sizeof(palette_19_24_M)/sizeof(palette_19_24_M[0])
            && ix <= sizeof(palette_19_24_Y)/sizeof(palette_19_24_Y[0])
            && ix <= sizeof(palette_19_24_K)/sizeof(palette_19_24_K[0]))
        {
          c = palette_19_24_C[ix-1];
          m = palette_19_24_M[ix-1];
          y = palette_19_24_Y[ix-1];
          k = palette_19_24_K[ix-1];
        }
        break;
      case 0x25:
        if (ix > 0
            && ix <= sizeof(palette_19_25_C)/sizeof(palette_19_25_C[0])
            && ix <= sizeof(palette_19_25_M)/sizeof(palette_19_25_M[0])
            && ix <= sizeof(palette_19_25_Y)/sizeof(palette_19_25_Y[0])
            && ix <= sizeof(palette_19_25_K)/sizeof(palette_19_25_K[0]))
        {
          c = palette_19_25_C[ix-1];
          m = palette_19_25_M[ix-1];
          y = palette_19_25_Y[ix-1];
          k = palette_19_25_K[ix-1];
        }
        break;
      default:
        colorValue = tint;
        colorValue <<= 16;
        colorValue |= ix;
        break;
      }

      switch (paletteID)
      {
      case 0x03:
      case 0x08:
      case 0x0a:
      case 0x0b:
      case 0x11:
      case 0x12:
      case 0x14:
      case 0x15:
      case 0x16:
      case 0x17:
      case 0x1a:
      case 0x1b:
      case 0x1c:
      case 0x1d:
      case 0x23:
      case 0x24:
      case 0x25:
      {
        colorModel = 0x02; // CMYK100
        unsigned cyan = (unsigned)tint * (unsigned)c / 100;
        unsigned magenta = (unsigned)tint * (unsigned)m / 100;
        unsigned yellow = (unsigned)tint * (unsigned)y / 100;
        unsigned black = (unsigned)tint * (unsigned)k / 100;
        colorValue = (black & 0xff);
        colorValue <<= 8;
        colorValue |= (yellow & 0xff);
        colorValue <<= 8;
        colorValue |= (magenta & 0xff);
        colorValue <<= 8;
        colorValue |= (cyan & 0xff);
        break;
      }
      case 0x1e:
      case 0x1f:
      case 0x20:
      {
        colorModel = 0x05; // RGB
        unsigned red = (unsigned)tint * (unsigned)r + 255 * (100 - tint);
        unsigned green = (unsigned)tint * (unsigned)g + 255 * (100 - tint);
        unsigned blue = (unsigned)tint * (unsigned)b + 255 * (100 - tint);
        red /= 100;
        green /= 100;
        blue /= 100;
        colorValue = (red & 0xff);
        colorValue <<= 8;
        colorValue |= (green & 0xff);
        colorValue <<= 8;
        colorValue |= (blue & 0xff);
        break;
      }
      case 0x09:
        colorModel = 0x12; // L*a*b
        break;
      default:
        break;
      }
    }
    else if (colorModel == 0x0e)
    {
      unsigned short paletteID = readU16(input);
      input->seek(4, librevenge::RVNG_SEEK_CUR);
      unsigned short ix = readU16(input);
      unsigned short tint = readU16(input);
      switch (paletteID)
      {
      case 0x0c:
        if (ix > 0
            && ix <= sizeof(palette_0E_0C_L)/sizeof(palette_0E_0C_L[0])
            && ix <= sizeof(palette_0E_0C_A)/sizeof(palette_0E_0C_A[0])
            && ix <= sizeof(palette_0E_0C_B)/sizeof(palette_0E_0C_B[0]))
        {
          colorValue = palette_0E_0C_B[ix-1];
          colorValue <<= 8;
          colorValue |= palette_0E_0C_A[ix-1];
          colorValue <<= 8;
          colorValue |= palette_0E_0C_L[ix-1];
        }
        break;
      case 0x18:
        if (ix > 0
            && ix <= sizeof(palette_0E_18_L)/sizeof(palette_0E_18_L[0])
            && ix <= sizeof(palette_0E_18_A)/sizeof(palette_0E_18_A[0])
            && ix <= sizeof(palette_0E_18_B)/sizeof(palette_0E_18_B[0]))
        {
          colorValue = palette_0E_18_B[ix-1];
          colorValue <<= 8;
          colorValue |= palette_0E_18_A[ix-1];
          colorValue <<= 8;
          colorValue |= palette_0E_18_L[ix-1];
        }
        break;
      case 0x21:
        if (ix > 0
            && ix <= sizeof(palette_0E_21_L)/sizeof(palette_0E_21_L[0])
            && ix <= sizeof(palette_0E_21_A)/sizeof(palette_0E_21_A[0])
            && ix <= sizeof(palette_0E_21_B)/sizeof(palette_0E_21_B[0]))
        {
          colorValue = palette_0E_21_B[ix-1];
          colorValue <<= 8;
          colorValue |= palette_0E_21_A[ix-1];
          colorValue <<= 8;
          colorValue |= palette_0E_21_L[ix-1];
        }
        break;
      case 0x22:
        if (ix > 0
            && ix <= sizeof(palette_0E_22_L)/sizeof(palette_0E_22_L[0])
            && ix <= sizeof(palette_0E_22_A)/sizeof(palette_0E_22_A[0])
            && ix <= sizeof(palette_0E_22_B)/sizeof(palette_0E_22_B[0]))
        {
          colorValue = palette_0E_22_B[ix-1];
          colorValue <<= 8;
          colorValue |= palette_0E_22_A[ix-1];
          colorValue <<= 8;
          colorValue |= palette_0E_22_L[ix-1];
        }
        break;
      default:
        colorValue = tint;
        colorValue <<= 16;
        colorValue |= ix;
        break;
      }

      switch (paletteID)
      {
      case 0x0c:
      case 0x18:
      case 0x21:
      case 0x22:
        colorModel = 0x12; // L*a*b
        break;
      default:
        break;
      }
    }
    else
    {
      input->seek(6, librevenge::RVNG_SEEK_CUR);
      colorValue = readU32(input);
    }
  }
  else if (m_version >= 400)
  {
    colorModel = readU16(input);
    unsigned short c = readU16(input);
    unsigned short m = readU16(input);
    unsigned short y = readU16(input);
    unsigned short k = readU16(input);
    colorValue = (k & 0xff);
    colorValue <<= 8;
    colorValue |= (y & 0xff);
    colorValue <<= 8;
    colorValue |= (m & 0xff);
    colorValue <<= 8;
    colorValue |= (c & 0xff);
    input->seek(2, librevenge::RVNG_SEEK_CUR);
  }
  else
  {
    colorModel = readU8(input);
    colorValue = readU32(input);
  }
  return libcdr::CDRColor(colorModel, colorValue);
}

void libcdr::CDRParser::readRectangle(librevenge::RVNGInputStream *input)
{
  double x0 = readRectCoord(input);
  double y0 = readRectCoord(input);
  double r3 = 0.0;
  double r2 = 0.0;
  double r1 = 0.0;
  double r0 = 0.0;
  unsigned int corner_type = 0;
  double scaleX = 1.0;
  double scaleY = 1.0;

  if (m_version < 1500)
  {
    r3 = readRectCoord(input);
    r2 = m_version < 900 ? r3 : readRectCoord(input);
    r1 = m_version < 900 ? r3 : readRectCoord(input);
    r0 = m_version < 900 ? r3 : readRectCoord(input);
  }
  else
  {
    double scale = readDouble(input);
    if (scale != 0)
      scaleX = scale;
    scale = readDouble(input);
    if (scale != 0)
      scaleY = scale;
    unsigned int scale_with = readU8(input);
    input->seek(7, librevenge::RVNG_SEEK_CUR);
    if (scale_with == 0)
    {
      r3 = readDouble(input);
      corner_type = readU8(input);
      input->seek(15, librevenge::RVNG_SEEK_CUR);
      r2 = readDouble(input);
      input->seek(16, librevenge::RVNG_SEEK_CUR);
      r1 = readDouble(input);
      input->seek(16, librevenge::RVNG_SEEK_CUR);
      r0 = readDouble(input);

      double width = fabs(x0*scaleX / 2.0);
      double height = fabs(y0*scaleY / 2.0);
      r3 *= width < height ? width : height;
      r2 *= width < height ? width : height;
      r1 *= width < height ? width : height;
      r0 *= width < height ? width : height;
    }
    else
    {
      r3 = readRectCoord(input);
      corner_type = readU8(input);
      input->seek(15, librevenge::RVNG_SEEK_CUR);
      r2 = readRectCoord(input);
      input->seek(16, librevenge::RVNG_SEEK_CUR);
      r1 = readRectCoord(input);
      input->seek(16, librevenge::RVNG_SEEK_CUR);
      r0 = readRectCoord(input);
    }
  }
  CDRPath path;
  if (r0 > 0.0)
    path.appendMoveTo(0.0, -r0/scaleY);
  else
    path.appendMoveTo(0.0, 0.0);
  if (r1 > 0.0)
  {
    path.appendLineTo(0.0, y0+r1/scaleY);
    if (corner_type == 0)
      path.appendQuadraticBezierTo(0.0, y0, r1/scaleX, y0);
    else if (corner_type == 1)
      path.appendQuadraticBezierTo(r1/scaleX, y0+r1/scaleY, r1/scaleX, y0);
    else if (corner_type == 2)
      path.appendLineTo(r1/scaleX, y0);
  }
  else
    path.appendLineTo(0.0, y0);
  if (r2 > 0.0)
  {
    path.appendLineTo(x0-r2/scaleX, y0);
    if (corner_type == 0)
      path.appendQuadraticBezierTo(x0, y0, x0, y0+r2/scaleY);
    else if (corner_type == 1)
      path.appendQuadraticBezierTo(x0-r2/scaleX, y0+r2/scaleY, x0, y0+r2/scaleY);
    else if (corner_type == 2)
      path.appendLineTo(x0, y0+r2/scaleY);
  }
  else
    path.appendLineTo(x0, y0);
  if (r3 > 0.0)
  {
    path.appendLineTo(x0, -r3/scaleY);
    if (corner_type == 0)
      path.appendQuadraticBezierTo(x0, 0.0, x0-r3/scaleX, 0.0);
    else if (corner_type == 1)
      path.appendQuadraticBezierTo(x0-r3/scaleX, -r3/scaleY, x0-r3/scaleX, 0.0);
    else if (corner_type == 2)
      path.appendLineTo(x0-r3/scaleX, 0.0);
  }
  else
    path.appendLineTo(x0, 0.0);
  if (r0 > 0.0)
  {
    path.appendLineTo(r0/scaleX, 0.0);
    if (corner_type == 0)
      path.appendQuadraticBezierTo(0.0, 0.0, 0.0, -r0/scaleY);
    else if (corner_type == 1)
      path.appendQuadraticBezierTo(r0/scaleX, -r0/scaleY, 0.0, -r0/scaleY);
    else if (corner_type == 2)
      path.appendLineTo(0.0, -r0/scaleY);
  }
  else
    path.appendLineTo(0.0, 0.0);
  path.appendClosePath();
  m_collector->collectPath(path);
}

void libcdr::CDRParser::readEllipse(librevenge::RVNGInputStream *input)
{
  CDR_DEBUG_MSG(("CDRParser::readEllipse\n"));

  double x = readCoordinate(input);
  double y = readCoordinate(input);
  double angle1 = readAngle(input);
  double angle2 = readAngle(input);
  bool pie(0 != readUnsigned(input));

  double cx = x/2.0;
  double cy = y/2.0;
  double rx = fabs(cx);
  double ry = fabs(cy);

  while (angle1 < 0.0)
    angle1 += 2*M_PI;
  while (angle1 > 2*M_PI)
    angle1 -= 2*M_PI;

  while (angle2 < 0.0)
    angle2 += 2*M_PI;
  while (angle2 > 2*M_PI)
    angle2 -= 2*M_PI;

  CDRPath path;
  if (angle1 != angle2)
  {
    if (angle2 < angle1)
      angle2 += 2*M_PI;
    double x0 = cx + rx*cos(angle1);
    double y0 = cy - ry*sin(angle1);

    double x1 = cx + rx*cos(angle2);
    double y1 = cy - ry*sin(angle2);

    bool largeArc = (angle2 - angle1 > M_PI);

    path.appendMoveTo(x0, y0);
    path.appendArcTo(rx, ry, 0.0, largeArc, false, x1, y1);
    if (pie)
    {
      path.appendLineTo(cx, cy);
      path.appendLineTo(x0, y0);
      path.appendClosePath();
    }
  }
  else
  {
    angle2 += M_PI/2.0;
    double x0 = cx + rx*cos(angle1);
    double y0 = cy - ry*sin(angle1);

    double x1 = cx + rx*cos(angle2);
    double y1 = cy - ry*sin(angle2);

    path.appendMoveTo(x0, y0);
    path.appendArcTo(rx, ry, 0.0, false, false, x1, y1);
    path.appendArcTo(rx, ry, 0.0, true, false, x0, y0);
    path.appendClosePath();
  }
  m_collector->collectPath(path);
}

void libcdr::CDRParser::readDisp(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
#if DUMP_PREVIEW_IMAGE
  librevenge::RVNGBinaryData previewImage;
  previewImage.append(0x42);
  previewImage.append(0x4d);

  previewImage.append((unsigned char)((length+8) & 0x000000ff));
  previewImage.append((unsigned char)(((length+8) & 0x0000ff00) >> 8));
  previewImage.append((unsigned char)(((length+8) & 0x00ff0000) >> 16));
  previewImage.append((unsigned char)(((length+8) & 0xff000000) >> 24));

  previewImage.append(0x00);
  previewImage.append(0x00);
  previewImage.append(0x00);
  previewImage.append(0x00);

  long startPosition = input->tell();
  input->seek(0x18, librevenge::RVNG_SEEK_CUR);
  int lengthX = length + 10 - readU32(input);
  input->seek(startPosition, librevenge::RVNG_SEEK_SET);

  previewImage.append((unsigned char)((lengthX) & 0x000000ff));
  previewImage.append((unsigned char)(((lengthX) & 0x0000ff00) >> 8));
  previewImage.append((unsigned char)(((lengthX) & 0x00ff0000) >> 16));
  previewImage.append((unsigned char)(((lengthX) & 0xff000000) >> 24));

  input->seek(4, librevenge::RVNG_SEEK_CUR);
  for (unsigned i = 4; i<length; i++)
    previewImage.append(readU8(input));
  FILE *f = fopen("previewImage.bmp", "wb");
  if (f)
  {
    const unsigned char *tmpBuffer = previewImage.getDataBuffer();
    for (unsigned long k = 0; k < previewImage.size(); k++)
      fprintf(f, "%c",tmpBuffer[k]);
    fclose(f);
  }
#endif
}

void libcdr::CDRParser::readLineAndCurve(librevenge::RVNGInputStream *input)
{
  CDR_DEBUG_MSG(("CDRParser::readLineAndCurve\n"));

  unsigned short pointNum = readU16(input);
  const unsigned short pointSize = 2 * (m_precision == PRECISION_16BIT ? 2 : 4) + 1;
  input->seek(2, librevenge::RVNG_SEEK_CUR);
  if (pointNum > getRemainingLength(input) / pointSize)
    pointNum = getRemainingLength(input) / pointSize;
  std::vector<std::pair<double, double> > points;
  std::vector<unsigned char> pointTypes;
  points.reserve(pointNum);
  pointTypes.reserve(pointNum);
  for (unsigned j=0; j<pointNum; j++)
  {
    std::pair<double, double> point;
    point.first = (double)readCoordinate(input);
    point.second = (double)readCoordinate(input);
    points.push_back(point);
  }
  for (unsigned k=0; k<pointNum; k++)
    pointTypes.push_back(readU8(input));
  outputPath(points, pointTypes);
}

void libcdr::CDRParser::readPath(librevenge::RVNGInputStream *input)
{
  CDR_DEBUG_MSG(("CDRParser::readPath\n"));

  input->seek(4, librevenge::RVNG_SEEK_CUR);
  unsigned short pointNum = readU16(input)+readU16(input);
  const unsigned short pointSize = 2 * (m_precision == PRECISION_16BIT ? 2 : 4) + 1;
  const unsigned long maxLength = getRemainingLength(input);
  if (maxLength < 16)
    pointNum = 0;
  else if (pointNum > (maxLength - 16) / pointSize)
    pointNum = (maxLength - 16) / pointSize;
  input->seek(16, librevenge::RVNG_SEEK_CUR);
  std::vector<std::pair<double, double> > points;
  std::vector<unsigned char> pointTypes;
  points.reserve(pointNum);
  pointTypes.reserve(pointNum);
  for (unsigned j=0; j<pointNum; j++)
  {
    std::pair<double, double> point;
    point.first = (double)readCoordinate(input);
    point.second = (double)readCoordinate(input);
    points.push_back(point);
  }
  for (unsigned k=0; k<pointNum; k++)
    pointTypes.push_back(readU8(input));
  outputPath(points, pointTypes);
}

void libcdr::CDRParser::readArrw(librevenge::RVNGInputStream *input, unsigned length)
{
  CDR_DEBUG_MSG(("CDRParser::readArrw\n"));
  if (m_version < 600)
    return;
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  unsigned arrowId = readU32(input);
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  unsigned short pointNum = readU16(input);
  const unsigned short pointSize = 2 * (m_precision == PRECISION_16BIT ? 2 : 4) + 1;
  const unsigned long maxLength = getRemainingLength(input);
  if (maxLength < 5)
    pointNum = 0;
  else if (pointNum > (maxLength - 5) / pointSize)
    pointNum = (maxLength - 5) / pointSize;
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  std::vector<unsigned char> pointTypes;
  pointTypes.reserve(pointSize);
  for (unsigned k=0; k<pointNum; k++)
    pointTypes.push_back(readU8(input));
  input->seek(1, librevenge::RVNG_SEEK_CUR);
  std::vector<std::pair<double, double> > points;
  points.reserve(pointSize);
  for (unsigned j=0; j<pointNum; j++)
  {
    std::pair<double, double> point;
    point.second = (double)readCoordinate(input);
    point.first = (double)readCoordinate(input);
    points.push_back(point);
  }
  CDRPath path;
  processPath(points, pointTypes, path);
  m_arrows[arrowId] = path;
}

void libcdr::CDRParser::readBitmap(librevenge::RVNGInputStream *input)
{
  CDR_DEBUG_MSG(("CDRParser::readBitmap\n"));

  double x1 = 0.0;
  double y1 = 0.0;
  double x2 = 0.0;
  double y2 = 0.0;
  unsigned imageId = 0;
  if (m_version < 600)
  {
    x1 = readCoordinate(input);
    y1 = readCoordinate(input);
    x2 = 0.0;
    y2 = 0.0;
    if (m_version < 400)
      input->seek(2, librevenge::RVNG_SEEK_CUR);
    input->seek(8, librevenge::RVNG_SEEK_CUR);
    imageId = readUnsigned(input);
    input->seek(20, librevenge::RVNG_SEEK_CUR);
    CDRPath path;
    path.appendMoveTo(x1, y1);
    path.appendLineTo(x1, y2);
    path.appendLineTo(x2, y2);
    path.appendLineTo(x2, y1);
    path.appendLineTo(x1, y1);
    m_collector->collectPath(path);
  }
  else
  {
    x1 = (double)readCoordinate(input);
    y1 = (double)readCoordinate(input);
    x2 = (double)readCoordinate(input);
    y2 = (double)readCoordinate(input);
    input->seek(16, librevenge::RVNG_SEEK_CUR);

    input->seek(16, librevenge::RVNG_SEEK_CUR);
    imageId = readUnsigned(input);
    if (m_version < 800)
      input->seek(8, librevenge::RVNG_SEEK_CUR);
    else if (m_version >= 800 && m_version < 900)
      input->seek(12, librevenge::RVNG_SEEK_CUR);
    else
      input->seek(20, librevenge::RVNG_SEEK_CUR);

    unsigned short pointNum = readU16(input);
    input->seek(2, librevenge::RVNG_SEEK_CUR);
    const unsigned short pointSize = 2 * (m_precision == PRECISION_16BIT ? 2 : 4) + 1;
    if (pointNum > getRemainingLength(input) / pointSize)
      pointNum = getRemainingLength(input) / pointSize;
    std::vector<std::pair<double, double> > points;
    std::vector<unsigned char> pointTypes;
    points.reserve(pointNum);
    pointTypes.reserve(pointNum);
    for (unsigned j=0; j<pointNum; j++)
    {
      std::pair<double, double> point;
      point.first = (double)readCoordinate(input);
      point.second = (double)readCoordinate(input);
      points.push_back(point);
    }
    for (unsigned k=0; k<pointNum; k++)
      pointTypes.push_back(readU8(input));
    outputPath(points, pointTypes);
  }
  m_collector->collectBitmap(imageId, x1, x2, y1, y2);
}

void libcdr::CDRParser::readWaldoOutl(librevenge::RVNGInputStream *input)
{
  if (m_version >= 400)
    return;
  unsigned short lineType = readU8(input);
  lineType <<= 1;
  double lineWidth = (double)readCoordinate(input);
  double stretch = (double)readU16(input) / 100.0;
  double angle = readAngle(input);
  libcdr::CDRColor color = readColor(input);
  input->seek(7, librevenge::RVNG_SEEK_CUR);
  unsigned short numDash = readU8(input);
  int fixPosition = input->tell();
  std::vector<unsigned> dashArray;
  for (unsigned short i = 0; i < numDash; ++i)
    dashArray.push_back(readU8(input));
  input->seek(fixPosition + 10, librevenge::RVNG_SEEK_SET);
  unsigned short joinType = readU16(input);
  unsigned short capsType = readU16(input);
  unsigned startMarkerId = readU32(input);
  std::map<unsigned, CDRPath>::const_iterator iter = m_arrows.find(startMarkerId);
  CDRPath startMarker;
  if (iter != m_arrows.end())
    startMarker = iter->second;
  unsigned endMarkerId = readU32(input);
  iter = m_arrows.find(endMarkerId);
  CDRPath endMarker;
  if (iter != m_arrows.end())
    endMarker = iter->second;
  m_collector->collectLineStyle(++m_waldoOutlId, CDRLineStyle(lineType, capsType, joinType, lineWidth, stretch, angle, color, dashArray, startMarker, endMarker));
  m_collector->collectLineStyleId(m_waldoOutlId);
}

void libcdr::CDRParser::readWaldoFill(librevenge::RVNGInputStream *input)
{
  if (m_version >= 400)
    return;
  unsigned short fillType = readU8(input);
  libcdr::CDRColor color1;
  libcdr::CDRColor color2;
  libcdr::CDRImageFill imageFill;
  libcdr::CDRGradient gradient;
  switch (fillType)
  {
  case 1: // Solid
  {
    color1 = readColor(input);
  }
  break;
  case 2: // Linear Gradient
  {
    gradient.m_type = 1;
    gradient.m_angle = readAngle(input);
    color1 = readColor(input);
    color2 = readColor(input);
    if (m_version >= 200)
    {
      input->seek(7, librevenge::RVNG_SEEK_CUR);
      gradient.m_edgeOffset = readS16(input);
      gradient.m_centerXOffset = readInteger(input);
      gradient.m_centerYOffset = readInteger(input);
    }
    libcdr::CDRGradientStop stop;
    stop.m_color = color1;
    stop.m_offset = 0.0;
    gradient.m_stops.push_back(stop);
    stop.m_color = color2;
    stop.m_offset = 1.0;
    gradient.m_stops.push_back(stop);
  }
  break;
  case 4: // Radial Gradient
  {
    gradient.m_type = 2;
    fillType = 2;
    gradient.m_angle = readAngle(input);
    color1 = readColor(input);
    color2 = readColor(input);
    if (m_version >= 200)
    {
      input->seek(7, librevenge::RVNG_SEEK_CUR);
      gradient.m_edgeOffset = readS16(input);
      gradient.m_centerXOffset = readInteger(input);
      gradient.m_centerYOffset = readInteger(input);
    }
    libcdr::CDRGradientStop stop;
    stop.m_color = color1;
    stop.m_offset = 0.0;
    gradient.m_stops.push_back(stop);
    stop.m_color = color2;
    stop.m_offset = 1.0;
    gradient.m_stops.push_back(stop);
  }
  break;
  case 7: // Pattern
  {
    unsigned patternId = m_version < 300 ? readU16(input) : readU32(input);
    double patternWidth = readCoordinate(input);
    double patternHeight = readCoordinate(input);
    double tileOffsetX = (double)readU16(input) / 100.0;
    double tileOffsetY = (double)readU16(input) / 100.0;
    double rcpOffset = (double)readU16(input) / 100.0;
    input->seek(1, librevenge::RVNG_SEEK_CUR);
    color1 = readColor(input);
    color2 = readColor(input);
    imageFill = libcdr::CDRImageFill(patternId, patternWidth, patternHeight, false, tileOffsetX, tileOffsetY, rcpOffset, 0);
  }
  break;
  case 10: // Full color
  {
    unsigned patternId = readU16(input);
    double patternWidth = readCoordinate(input);
    double patternHeight = readCoordinate(input);
    double tileOffsetX = (double)readU16(input) / 100.0;
    double tileOffsetY = (double)readU16(input) / 100.0;
    double rcpOffset = (double)readU16(input) / 100.0;
    input->seek(1, librevenge::RVNG_SEEK_CUR);
    imageFill = libcdr::CDRImageFill(patternId, patternWidth, patternHeight, false, tileOffsetX, tileOffsetY, rcpOffset, 0);
  }
  break;
  default:
    break;
  }
  m_collector->collectFillStyle(++m_waldoFillId, CDRFillStyle(fillType, color1, color2, gradient, imageFill));
  m_collector->collectFillStyleId(m_waldoFillId);
}

void libcdr::CDRParser::readTrfd(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  long startPosition = input->tell();
  const unsigned long maxLength = getLength(input);
  if (startPosition >= long(maxLength))
    return;
  if ((length > maxLength) || (long(maxLength - length) < startPosition))
    length = unsigned(maxLength - static_cast<unsigned long>(startPosition)); // sanitize length
  unsigned chunkLength = readUnsigned(input);
  unsigned numOfArgs = readUnsigned(input);
  unsigned startOfArgs = readUnsigned(input);
  if (startOfArgs >= length)
    return;
  if (numOfArgs > (length - startOfArgs) / 4) // avoid extra big allocation in case of a broken file
    numOfArgs = (length - startOfArgs) / 4;
  std::vector<unsigned> argOffsets(numOfArgs, 0);
  unsigned i = 0;
  input->seek(startPosition+startOfArgs, librevenge::RVNG_SEEK_SET);
  while (i<numOfArgs)
    argOffsets[i++] = readUnsigned(input);

  CDRTransforms trafos;
  for (i=0; i < argOffsets.size(); i++)
  {
    input->seek(startPosition+argOffsets[i], librevenge::RVNG_SEEK_SET);
    if (m_version >= 1300)
      input->seek(8, librevenge::RVNG_SEEK_CUR);
    unsigned short tmpType = readU16(input);
    if (tmpType == 0x08) // trafo
    {
      double v0 = 0.0;
      double v1 = 0.0;
      double x0 = 0.0;
      double v3 = 0.0;
      double v4 = 0.0;
      double y0 = 0.0;
      if (m_version >= 600)
        input->seek(6, librevenge::RVNG_SEEK_CUR);
      if (m_version >= 500)
      {
        v0 = readDouble(input);
        v1 = readDouble(input);
        x0 = readDouble(input) / (m_version < 600 ? 1000.0 : 254000.0);
        v3 = readDouble(input);
        v4 = readDouble(input);
        y0 = readDouble(input) / (m_version < 600 ? 1000.0 : 254000.0);
      }
      else
      {
        v0 = readFixedPoint(input);
        v1 = readFixedPoint(input);
        x0 = (double)readS32(input) / 1000.0;
        v3 = readFixedPoint(input);
        v4 = readFixedPoint(input);
        y0 = (double)readS32(input) / 1000.0;
      }
      trafos.append(v0, v1, x0, v3, v4, y0);
    }
    else if (tmpType == 0x10)
    {
#if 0
      input->seek(6, librevenge::RVNG_SEEK_CUR);
      unsigned short type = readU16(input);
      double x = readCoordinate(input);
      double y = readCoordinate(input);
      unsigned subType = readU32(input);
      if (!subType)
      {
      }
      else
      {
        if (subType&1) // Smooth
        {
        }
        if (subType&2) // Random
        {
        }
        if (subType&4) // Local
        {
        }
      }
      unsigned opt1 = readU32(input);
      unsigned opt2 = readU32(input);
#endif
    }
  }
  if (!trafos.empty())
    m_collector->collectTransform(trafos,m_version < 400);
  input->seek(startPosition+chunkLength, librevenge::RVNG_SEEK_SET);
}

void libcdr::CDRParser::readFild(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  unsigned fillId = readU32(input);
  unsigned short v13flag = 0;
  if (m_version >= 1300)
  {
    input->seek(4, librevenge::RVNG_SEEK_CUR);
    v13flag = readU16(input);
    input->seek(2, librevenge::RVNG_SEEK_CUR);
  }
  unsigned short fillType = readU16(input);
  libcdr::CDRColor color1;
  libcdr::CDRColor color2;
  libcdr::CDRImageFill imageFill;
  libcdr::CDRGradient gradient;
  switch (fillType)
  {
  case 1: // Solid
  {
    if (m_version >= 1300)
      input->seek(13, librevenge::RVNG_SEEK_CUR);
    else
      input->seek(2, librevenge::RVNG_SEEK_CUR);
    color1 = readColor(input);
  }
  break;
  case 2: // Gradient
  {
    if (m_version >= 1300)
      input->seek(8, librevenge::RVNG_SEEK_CUR);
    else
      input->seek(2, librevenge::RVNG_SEEK_CUR);
    gradient.m_type = readU8(input);
    if (m_version >= 1300)
    {
      input->seek(17, librevenge::RVNG_SEEK_CUR);
      gradient.m_edgeOffset = readS16(input);
    }
    else if (m_version >= 600)
    {
      input->seek(19, librevenge::RVNG_SEEK_CUR);
      gradient.m_edgeOffset = readS32(input);
    }
    else
    {
      input->seek(11, librevenge::RVNG_SEEK_CUR);
      gradient.m_edgeOffset = readS16(input);
    }
    gradient.m_angle = readAngle(input);
    gradient.m_centerXOffset = readInteger(input);
    gradient.m_centerYOffset = readInteger(input);
    if (m_version >= 600)
      input->seek(2, librevenge::RVNG_SEEK_CUR);
    gradient.m_mode = (unsigned char)(readUnsigned(input) & 0xff);
    gradient.m_midPoint = (double)readU8(input) / 100.0;
    input->seek(1, librevenge::RVNG_SEEK_CUR);
    unsigned short numStops = (unsigned short)(readUnsigned(input) & 0xffff);
    if (m_version >= 1300)
      input->seek(3, librevenge::RVNG_SEEK_CUR);
    for (unsigned short i = 0; i < numStops; ++i)
    {
      libcdr::CDRGradientStop stop;
      stop.m_color = readColor(input);
      if (m_version >= 1300)
      {
        if (v13flag == 0x9e || (m_version >= 1600 && v13flag == 0x96))
          input->seek(26, librevenge::RVNG_SEEK_CUR);
        else
          input->seek(5, librevenge::RVNG_SEEK_CUR);
      }
      stop.m_offset = (double)readUnsigned(input) / 100.0;
      if (m_version >= 1300)
        input->seek(3, librevenge::RVNG_SEEK_CUR);
      gradient.m_stops.push_back(stop);
    }
  }
  break;
  case 7: // Pattern
  {
    if (m_version >= 1300)
      input->seek(8, librevenge::RVNG_SEEK_CUR);
    else
      input->seek(2, librevenge::RVNG_SEEK_CUR);
    unsigned patternId = readU32(input);
    int tmpWidth = readInteger(input);
    int tmpHeight = readInteger(input);
    double tileOffsetX = 0.0;
    double tileOffsetY = 0.0;
    if (m_version < 900)
    {
      tileOffsetX = (double)readU16(input) / 100.0;
      tileOffsetY = (double)readU16(input) / 100.0;
    }
    else
      input->seek(4, librevenge::RVNG_SEEK_CUR);
    double rcpOffset = (double)readU16(input) / 100.0;
    unsigned char flags = readU8(input);
    double patternWidth = (double)tmpWidth / (m_version < 600 ? 1000.0 : 254000.0);
    double patternHeight = (double)tmpHeight / (m_version < 600 ? 1000.0 : 254000.0);
    bool isRelative = false;
    if ((flags & 0x04) && (m_version < 900))
    {
      isRelative = true;
      patternWidth = (double)tmpWidth / 100.0;
      patternHeight = (double)tmpHeight / 100.0;
    }
    if (m_version >= 1300)
      input->seek(6, librevenge::RVNG_SEEK_CUR);
    else
      input->seek(1, librevenge::RVNG_SEEK_CUR);
    color1 = readColor(input);
    if (m_version >= 1300)
    {
      if (v13flag == 0x94 || (m_version >= 1600 && v13flag == 0x8c))
        input->seek(31, librevenge::RVNG_SEEK_CUR);
      else
        input->seek(10, librevenge::RVNG_SEEK_CUR);
    }
    color2 = readColor(input);
    imageFill = libcdr::CDRImageFill(patternId, patternWidth, patternHeight, isRelative, tileOffsetX, tileOffsetY, rcpOffset, flags);
  }
  break;
  case 9: // bitmap
  {
    if (m_version < 600)
      fillType = 10;
    input->seek(2, librevenge::RVNG_SEEK_CUR);
    unsigned patternId = readUnsigned(input);
    if (m_version >= 1600)
      input->seek(26, librevenge::RVNG_SEEK_CUR);
    else if (m_version >= 1300)
      input->seek(2, librevenge::RVNG_SEEK_CUR);
    int tmpWidth = readUnsigned(input);
    int tmpHeight = readUnsigned(input);
    double tileOffsetX = 0.0;
    double tileOffsetY = 0.0;
    if (m_version < 900)
    {
      tileOffsetX = (double)readU16(input) / 100.0;
      tileOffsetY = (double)readU16(input) / 100.0;
    }
    else
      input->seek(4, librevenge::RVNG_SEEK_CUR);
    double rcpOffset = (double)readU16(input) / 100.0;
    unsigned char flags = readU8(input);
    double patternWidth = (double)tmpWidth / (m_version < 600 ? 1000.0 :254000.0);
    double patternHeight = (double)tmpHeight / (m_version < 600 ? 1000.0 :254000.0);
    bool isRelative = false;
    if ((flags & 0x04) && (m_version < 900))
    {
      isRelative = true;
      patternWidth = (double)tmpWidth / 100.0;
      patternHeight = (double)tmpHeight / 100.0;
    }
    if (m_version >= 1300)
      input->seek(17, librevenge::RVNG_SEEK_CUR);
    else
      input->seek(21, librevenge::RVNG_SEEK_CUR);
    if (m_version >= 600)
      patternId = readUnsigned(input);
    imageFill = libcdr::CDRImageFill(patternId, patternWidth, patternHeight, isRelative, tileOffsetX, tileOffsetY, rcpOffset, flags);
  }
  break;
  case 10: // Full color
  {
    if (m_version >= 1300)
    {
      if (v13flag == 0x4e)
        input->seek(28, librevenge::RVNG_SEEK_CUR);
      else
        input->seek(4, librevenge::RVNG_SEEK_CUR);
    }
    else
      input->seek(2, librevenge::RVNG_SEEK_CUR);
    unsigned patternId = readUnsigned(input);
    int tmpWidth = readUnsigned(input);
    int tmpHeight = readUnsigned(input);
    double tileOffsetX = 0.0;
    double tileOffsetY = 0.0;
    if (m_version < 900)
    {
      tileOffsetX = (double)readU16(input) / 100.0;
      tileOffsetY = (double)readU16(input) / 100.0;
    }
    else
      input->seek(4, librevenge::RVNG_SEEK_CUR);
    double rcpOffset = (double)readU16(input) / 100.0;
    unsigned char flags = readU8(input);
    double patternWidth = (double)tmpWidth / (m_version < 600 ? 1000.0 :254000.0);
    double patternHeight = (double)tmpHeight / (m_version < 600 ? 1000.0 :254000.0);
    bool isRelative = false;
    if ((flags & 0x04) && (m_version < 900))
    {
      isRelative = true;
      patternWidth = (double)tmpWidth / 100.0;
      patternHeight = (double)tmpHeight / 100.0;
    }
    if (m_version >= 1300)
      input->seek(17, librevenge::RVNG_SEEK_CUR);
    else
      input->seek(21, librevenge::RVNG_SEEK_CUR);
    if (m_version >= 600)
      patternId = readUnsigned(input);
    imageFill = libcdr::CDRImageFill(patternId, patternWidth, patternHeight, isRelative, tileOffsetX, tileOffsetY, rcpOffset, flags);
  }
  break;
  case 11: // Texture
  {
    if (m_version < 600)
      fillType = 10;
    if (m_version >= 1300)
    {
      if (v13flag == 0x18e)
        input->seek(36, librevenge::RVNG_SEEK_CUR);
      else
        input->seek(1, librevenge::RVNG_SEEK_CUR);
    }
    else
      input->seek(2, librevenge::RVNG_SEEK_CUR);
    unsigned patternId = readU32(input);
    double patternWidth = 1.0;
    double patternHeight = 1.0;
    bool isRelative = true;
    double tileOffsetX = 0.0;
    double tileOffsetY = 0.0;
    double rcpOffset = 0.0;
    unsigned char flags = 0;
    if (m_version >= 600)
    {
      int tmpWidth = readUnsigned(input);
      int tmpHeight = readUnsigned(input);
      if (m_version < 900)
      {
        tileOffsetX = (double)readU16(input) / 100.0;
        tileOffsetY = (double)readU16(input) / 100.0;
      }
      else
        input->seek(4, librevenge::RVNG_SEEK_CUR);
      rcpOffset = (double)readU16(input) / 100.0;
      flags = readU8(input);
      patternWidth = (double)tmpWidth / 254000.0;
      patternHeight = (double)tmpHeight / 254000.0;
      isRelative = false;
      if ((flags & 0x04) && (m_version < 900))
      {
        isRelative = true;
        patternWidth = (double)tmpWidth / 100.0;
        patternHeight = (double)tmpHeight / 100.0;
      }
      if (m_version >= 1300)
        input->seek(17, librevenge::RVNG_SEEK_CUR);
      else
        input->seek(21, librevenge::RVNG_SEEK_CUR);
      patternId = readUnsigned(input);
    }
    imageFill = libcdr::CDRImageFill(patternId, patternWidth, patternHeight, isRelative, tileOffsetX, tileOffsetY, rcpOffset, flags);
  }
  break;
  default:
    break;
  }
  m_fillStyles[fillId] = CDRFillStyle(fillType, color1, color2, gradient, imageFill);
  m_collector->collectFillStyle(fillId, CDRFillStyle(fillType, color1, color2, gradient, imageFill));
}

void libcdr::CDRParser::readOutl(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  unsigned lineId = readU32(input);
  if (m_version >= 1300)
  {
    unsigned id = 0;
    unsigned lngth = 0;
    while (id != 1)
    {
      input->seek(lngth, librevenge::RVNG_SEEK_CUR);
      id = readU32(input);
      lngth = readU32(input);
    }
  }
  unsigned short lineType = readU16(input);
  unsigned short capsType = readU16(input);
  unsigned short joinType = readU16(input);
  if (m_version < 1300 && m_version >= 600)
    input->seek(2, librevenge::RVNG_SEEK_CUR);
  double lineWidth = (double)readCoordinate(input);
  double stretch = (double)readU16(input) / 100.0;
  if (m_version >= 600)
    input->seek(2, librevenge::RVNG_SEEK_CUR);
  double angle = readAngle(input);
  if (m_version >= 1300)
    input->seek(46, librevenge::RVNG_SEEK_CUR);
  else if (m_version >= 600)
    input->seek(52, librevenge::RVNG_SEEK_CUR);
  libcdr::CDRColor color = readColor(input);
  if (m_version < 600)
    input->seek(10, librevenge::RVNG_SEEK_CUR);
  else
    input->seek(16, librevenge::RVNG_SEEK_CUR);
  unsigned short numDash = readU16(input);
  if (numDash > getRemainingLength(input) / 2)
    numDash = getRemainingLength(input) / 2;
  int fixPosition = input->tell();
  std::vector<unsigned> dashArray;
  dashArray.reserve(numDash);
  for (unsigned short i = 0; i < numDash; ++i)
    dashArray.push_back(readU16(input));
  if (m_version < 600)
    input->seek(fixPosition + 20, librevenge::RVNG_SEEK_SET);
  else
    input->seek(fixPosition + 22, librevenge::RVNG_SEEK_SET);
  unsigned startMarkerId = readU32(input);
  std::map<unsigned, CDRPath>::const_iterator iter = m_arrows.find(startMarkerId);
  CDRPath startMarker;
  if (iter != m_arrows.end())
    startMarker = iter->second;
  unsigned endMarkerId = readU32(input);
  iter = m_arrows.find(endMarkerId);
  CDRPath endMarker;
  if (iter != m_arrows.end())
    endMarker = iter->second;
  m_lineStyles[lineId] = CDRLineStyle(lineType, capsType, joinType, lineWidth, stretch, angle, color, dashArray, startMarker, endMarker);
  m_collector->collectLineStyle(lineId, CDRLineStyle(lineType, capsType, joinType, lineWidth, stretch, angle, color, dashArray, startMarker, endMarker));
}

void libcdr::CDRParser::readLoda(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  long startPosition = input->tell();
  const unsigned long maxLength = getLength(input);
  if (startPosition >= long(maxLength))
    return;
  if ((length > maxLength) || (long(maxLength - length) < startPosition))
    length = unsigned(maxLength - static_cast<unsigned long>(startPosition)); // sanitize length
  unsigned chunkLength = readUnsigned(input);
  unsigned numOfArgs = readUnsigned(input);
  unsigned startOfArgs = readUnsigned(input);
  if (startOfArgs >= length)
    return;
  unsigned startOfArgTypes = readUnsigned(input);
  if (startOfArgTypes >= length)
    return;
  if (numOfArgs > (length - startOfArgs) / 4) // avoid extra big allocation in case of a broken file
    numOfArgs = (length - startOfArgs) / 4;
  unsigned chunkType = readUnsigned(input);
  if (chunkType == 0x26)
    m_collector->collectSpline();
  std::vector<unsigned> argOffsets(numOfArgs, 0);
  std::vector<unsigned> argTypes(numOfArgs, 0);
  unsigned i = 0;
  input->seek(startPosition+startOfArgs, librevenge::RVNG_SEEK_SET);
  while (i<numOfArgs)
    argOffsets[i++] = readUnsigned(input);
  input->seek(startPosition+startOfArgTypes, librevenge::RVNG_SEEK_SET);
  while (i>0)
    argTypes[--i] = readUnsigned(input);

  for (i=0; i < argTypes.size(); i++)
  {
    input->seek(startPosition+argOffsets[i], librevenge::RVNG_SEEK_SET);
    if (argTypes[i] == 0x1e) // loda coords
    {
      if ((m_version >= 400 && chunkType == 0x01) || (m_version < 400 && chunkType == 0x00)) // Rectangle
        readRectangle(input);
      else if ((m_version >= 400 && chunkType == 0x02) || (m_version < 400 && chunkType == 0x01)) // Ellipse
        readEllipse(input);
      else if ((m_version >= 400 && chunkType == 0x03) || (m_version < 400 && chunkType == 0x02)) // Line and curve
        readLineAndCurve(input);
      else if (chunkType == 0x25) // Path
        readPath(input);
      else if ((m_version >= 400 && chunkType == 0x04) || (m_version < 400 && chunkType == 0x03)) // Artistic text
        readArtisticText(input);
      else if ((m_version >= 400 && chunkType == 0x05) || (m_version < 400 && chunkType == 0x04)) // Bitmap
        readBitmap(input);
      else if ((m_version >= 400 && chunkType == 0x06) || (m_version < 400 && chunkType == 0x05)) // Paragraph text
        readParagraphText(input);
      else if (chunkType == 0x14) // Polygon
        readPolygonCoords(input);
    }
    else if (argTypes[i] == 0x14)
    {
      if (m_version < 400)
        readWaldoFill(input);
      else
      {
        unsigned fillId = readU32(input);
        if (fillId)
          m_collector->collectFillStyleId(fillId);
      }
    }
    else if (argTypes[i] == 0x0a)
    {
      if (m_version < 400)
        readWaldoOutl(input);
      else
      {
        unsigned outlId = readU32(input);
        if (outlId)
          m_collector->collectLineStyleId(outlId);
      }
    }
    else if (argTypes[i] == 0xc8)
    {
      unsigned styleId = readUnsigned(input);
      if (styleId)
        m_collector->collectStyleId(styleId);
    }
    else if (argTypes[i] == 0x2af8)
      readPolygonTransform(input);
    else if (argTypes[i] == 0x1f40)
      readOpacity(input, length);
    else if (argTypes[i] == 0x64)
    {
      if (m_version < 400)
        readWaldoTrfd(input);
    }
    else if (argTypes[i] == 0x4aba)
      readPageSize(input);
  }
  input->seek(startPosition+chunkLength, librevenge::RVNG_SEEK_SET);
}

void libcdr::CDRParser::readFlags(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  unsigned flags = readU32(input);
  m_collector->collectFlags(flags, m_version >= 400);
}

void libcdr::CDRParser::readMcfg(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  if (m_version >= 1300)
    input->seek(12, librevenge::RVNG_SEEK_CUR);
  else if (m_version >= 900)
    input->seek(4, librevenge::RVNG_SEEK_CUR);
  else if (m_version < 700 && m_version >= 600)
    input->seek(0x1c, librevenge::RVNG_SEEK_CUR);
  double width = 0.0;
  double height = 0.0;
  if (m_version < 400)
  {
    input->seek(2, librevenge::RVNG_SEEK_CUR);
    double x0 = readCoordinate(input);
    double y0 = readCoordinate(input);
    double x1 = readCoordinate(input);
    double y1 = readCoordinate(input);
    width = fabs(x1-x0);
    height = fabs(y1-y0);
  }
  else
  {
    width = readCoordinate(input);
    height = readCoordinate(input);
  }
  m_collector->collectPageSize(width, height, -width/2.0, -height/2.0);
}

void libcdr::CDRParser::readPolygonCoords(librevenge::RVNGInputStream *input)
{
  CDR_DEBUG_MSG(("CDRParser::readPolygonCoords\n"));

  unsigned short pointNum = readU16(input);
  const unsigned short pointSize = 2 * (m_precision == PRECISION_16BIT ? 2 : 4) + 1;
  if (pointNum > getRemainingLength(input) / pointSize)
    pointNum = getRemainingLength(input) / pointSize;
  input->seek(2, librevenge::RVNG_SEEK_CUR);
  std::vector<std::pair<double, double> > points;
  std::vector<unsigned char> pointTypes;
  points.reserve(pointNum);
  pointTypes.reserve(pointNum);
  for (unsigned j=0; j<pointNum; j++)
  {
    std::pair<double, double> point;
    point.first = (double)readCoordinate(input);
    point.second = (double)readCoordinate(input);
    points.push_back(point);
  }
  for (unsigned k=0; k<pointNum; k++)
    pointTypes.push_back(readU8(input));
  outputPath(points, pointTypes);
  m_collector->collectPolygon();
}

void libcdr::CDRParser::readPolygonTransform(librevenge::RVNGInputStream *input)
{
  if (m_version < 1300)
    input->seek(4, librevenge::RVNG_SEEK_CUR);
  unsigned numAngles = readU32(input);
  unsigned nextPoint = readU32(input);
  if (nextPoint <= 1)
    nextPoint = readU32(input);
  else
    input->seek(4, librevenge::RVNG_SEEK_CUR);
  if (m_version >= 1300)
    input->seek(4, librevenge::RVNG_SEEK_CUR);
  double rx = readDouble(input);
  double ry = readDouble(input);
  double cx = readCoordinate(input);
  double cy = readCoordinate(input);
  m_collector->collectPolygonTransform(numAngles, nextPoint, rx, ry, cx, cy);
}

void libcdr::CDRParser::readPageSize(librevenge::RVNGInputStream *input)
{
  double width = readCoordinate(input);
  double height = readCoordinate(input);
  m_collector->collectPageSize(width, height, -width/2.0, -height/2.0);
}

void libcdr::CDRParser::readWaldoBmp(librevenge::RVNGInputStream *input, unsigned length, unsigned id)
{
  if (m_version >= 400)
    return;
  if (readU8(input) != 0x42)
    return;
  if (readU8(input) != 0x4d)
    return;
  input->seek(-2, librevenge::RVNG_SEEK_CUR);
  unsigned long tmpNumBytesRead = 0;
  const unsigned char *tmpBuffer = input->read(length, tmpNumBytesRead);
  if (!tmpNumBytesRead || length != tmpNumBytesRead)
    return;
  std::vector<unsigned char> bitmap(tmpNumBytesRead);
  memcpy(&bitmap[0], tmpBuffer, tmpNumBytesRead);
  m_collector->collectBmp(id, bitmap);
  return;
}

void libcdr::CDRParser::readBmp(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  unsigned imageId = readUnsigned(input);
  std::vector<unsigned char> bitmap;
  if (m_version < 500)
  {
    if (readU8(input) != 0x42)
      return;
    if (readU8(input) != 0x4d)
      return;
    unsigned lngth = readU32(input);
    input->seek(-6, librevenge::RVNG_SEEK_CUR);
    unsigned long tmpNumBytesRead = 0;
    const unsigned char *tmpBuffer = input->read(lngth, tmpNumBytesRead);
    if (!tmpNumBytesRead || lngth != tmpNumBytesRead)
      return;
    bitmap.resize(tmpNumBytesRead);
    memcpy(&bitmap[0], tmpBuffer, tmpNumBytesRead);
    m_collector->collectBmp(imageId, bitmap);
    return;
  }
  if (m_version < 600)
    input->seek(14, librevenge::RVNG_SEEK_CUR);
  else if (m_version < 700)
    input->seek(46, librevenge::RVNG_SEEK_CUR);
  else
    input->seek(50, librevenge::RVNG_SEEK_CUR);
  unsigned colorModel, width, height, bpp;
  std::vector<unsigned> palette;
  readRImage(colorModel, width, height, bpp, palette, bitmap, input);
  m_collector->collectBmp(imageId, colorModel, width, height, bpp, palette, bitmap);
}

void libcdr::CDRParser::readOpacity(librevenge::RVNGInputStream *input, unsigned /* length */)
{
  if (m_version < 1300)
    input->seek(10, librevenge::RVNG_SEEK_CUR);
  else
    input->seek(14, librevenge::RVNG_SEEK_CUR);
  double opacity = (double)readU16(input) / 1000.0;
  m_collector->collectFillOpacity(opacity);
}

void libcdr::CDRParser::readBmpf(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  unsigned patternId = readU32(input);
  unsigned width, height;
  std::vector<unsigned char> pattern;
  readBmpPattern(width, height, pattern, length-4, input);
  m_collector->collectBmpf(patternId, width, height, pattern);
}

void libcdr::CDRParser::readWaldoBmpf(librevenge::RVNGInputStream *input, unsigned id)
{
  unsigned headerLength = readU32(input);
  if (headerLength != 40)
    return;
  unsigned width = readU32(input);
  unsigned height = readU32(input);
  input->seek(2, librevenge::RVNG_SEEK_CUR);
  unsigned bpp = readU16(input);
  if (bpp != 1)
    return;
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  unsigned dataSize = readU32(input);
  unsigned long tmpNumBytesRead = 0;
  input->seek(24, librevenge::RVNG_SEEK_CUR); // TODO: is this empirical experience universal???
  const unsigned char *tmpBuffer = input->read(dataSize, tmpNumBytesRead);
  if (dataSize != tmpNumBytesRead)
    return;
  std::vector<unsigned char> pattern(dataSize);
  memcpy(&pattern[0], tmpBuffer, dataSize);
  m_collector->collectBmpf(id, width, height, pattern);
}

void libcdr::CDRParser::readPpdt(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  unsigned short pointNum = readU16(input);
  const unsigned short pointSize = 2 * (m_precision == PRECISION_16BIT ? 2 : 4) + 4;
  if (pointNum > getRemainingLength(input) / pointSize)
    pointNum = getRemainingLength(input) / pointSize;
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  std::vector<std::pair<double, double> > points;
  std::vector<unsigned> knotVector;
  points.reserve(pointNum);
  knotVector.reserve(pointNum);
  for (unsigned j=0; j<pointNum; j++)
  {
    std::pair<double, double> point;
    point.first = (double)readCoordinate(input);
    point.second = (double)readCoordinate(input);
    points.push_back(point);
  }
  for (unsigned k=0; k<pointNum; k++)
    knotVector.push_back(readU32(input));
  m_collector->collectPpdt(points, knotVector);
}

void libcdr::CDRParser::readFtil(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  double v0 = readDouble(input);
  double v1 = readDouble(input);
  double x0 = readDouble(input) / 254000.0;
  double v3 = readDouble(input);
  double v4 = readDouble(input);
  double y0 = readDouble(input) / 254000.0;
  CDRTransforms fillTrafos;
  fillTrafos.append(v0, v1, x0, v3, v4, y0);
  m_collector->collectFillTransform(fillTrafos);
}

void libcdr::CDRParser::readVersion(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  m_version = readU16(input);
  if (m_version < 600)
    m_precision = libcdr::PRECISION_16BIT;
  else
    m_precision = libcdr::PRECISION_32BIT;
}

bool libcdr::CDRParser::_redirectX6Chunk(librevenge::RVNGInputStream **input, unsigned &length)
{
  if (m_version >= 1600 && length == 0x10)
  {
    unsigned streamNumber = readU32(*input);
    length = readU32(*input);
    if (streamNumber < m_externalStreams.size())
    {
      unsigned streamOffset = readU32(*input);
      *input = m_externalStreams[streamNumber];
      if (*input)
      {
        (*input)->seek(streamOffset, librevenge::RVNG_SEEK_SET);
        return !(*input)->isEnd();
      }
      else
        return false;
    }
    else if (streamNumber == 0xffffffff)
      return true;
    return false;
  }
  return true;
}

void libcdr::CDRParser::readIccd(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  unsigned long numBytesRead = 0;
  const unsigned char *tmpProfile = input->read(length, numBytesRead);
  if (length != numBytesRead)
    throw EndOfStreamException();
  if (!numBytesRead)
    return;
  std::vector<unsigned char> profile(numBytesRead);
  memcpy(&profile[0], tmpProfile, numBytesRead);
  m_collector->collectColorProfile(profile);
}

void libcdr::CDRParser::readBBox(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  double x0 = readCoordinate(input);
  double y0 = readCoordinate(input);
  double x1 = readCoordinate(input);
  double y1 = readCoordinate(input);
  m_collector->collectBBox(x0, y0, x1, y1);
}

void libcdr::CDRParser::readSpnd(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  unsigned spnd = readUnsigned(input);
  m_collector->collectSpnd(spnd);
}

void libcdr::CDRParser::readVpat(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  unsigned fillId = readUnsigned(input);
  unsigned long numBytesRead = 0;
  const unsigned char *buffer = input->read(length-4, numBytesRead);
  if (numBytesRead)
  {
    librevenge::RVNGBinaryData data(buffer, numBytesRead);
    m_collector->collectVectorPattern(fillId, data);
  }
}

void libcdr::CDRParser::readUidr(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  unsigned colorId = readU32(input);
  unsigned userId = readU32(input);
  input->seek(36, librevenge::RVNG_SEEK_CUR);
  CDRColor color = readColor(input);
  m_collector->collectPaletteEntry(colorId, userId, color);
}

void libcdr::CDRParser::readFont(librevenge::RVNGInputStream *input, unsigned length)
{
  if (!_redirectX6Chunk(&input, length))
    throw GenericException();
  unsigned short fontId = readU16(input);
  unsigned short fontEncoding = readU16(input);
  input->seek(14, librevenge::RVNG_SEEK_CUR);
  std::vector<unsigned char> name;
  librevenge::RVNGString fontName;
  if (m_version >= 1200)
  {
    unsigned short character = 0;
    while (true)
    {
      character = readU16(input);
      if (character)
      {
        name.push_back((unsigned char)(character & 0xff));
        name.push_back((unsigned char)(character >> 8));
      }
      else
        break;
    }
    appendCharacters(fontName, name);
  }
  else
  {
    unsigned char character = 0;
    while (true)
    {
      character = readU8(input);
      if (character)
        name.push_back(character);
      else
        break;
    }
    appendCharacters(fontName, name, fontEncoding);
  }
  if (!fontEncoding)
    processNameForEncoding(fontName, fontEncoding);
  std::map<unsigned, CDRFont>::const_iterator iter = m_fonts.find(fontId);
  // Asume that the first font with the given ID is a font
  // that we want, the others are substitution fonts. We might
  // be utterly wrong in this one
  if (iter == m_fonts.end())
    m_fonts[fontId] = CDRFont(fontName, fontEncoding);
}

void libcdr::CDRParser::readStlt(librevenge::RVNGInputStream *input, unsigned length)
{
#ifndef DEBUG
  unsigned version = m_version;
  try
  {
#endif
    if (m_version < 700)
      return;
    if (!_redirectX6Chunk(&input, length))
      throw GenericException();
    long startPosition = input->tell();
    unsigned numRecords = readU32(input);
    CDR_DEBUG_MSG(("CDRParser::readStlt numRecords 0x%x\n", numRecords));
    if (!numRecords)
      return;
    unsigned numFills = readU32(input);
    const unsigned fillSize = 3 * 4 + (m_version >= 1300 ? 48 : 0);
    if (numFills > getRemainingLength(input) / fillSize)
      numFills = getRemainingLength(input) / fillSize;
    CDR_DEBUG_MSG(("CDRParser::readStlt numFills 0x%x\n", numFills));
    unsigned i = 0;
    std::map<unsigned, unsigned> fillIds;
    for (i=0; i<numFills; ++i)
    {
      unsigned fillId = readU32(input);
      input->seek(4, librevenge::RVNG_SEEK_CUR);
      fillIds[fillId] = readU32(input);
      if (m_version >= 1300)
        input->seek(48, librevenge::RVNG_SEEK_CUR);
    }
    unsigned numOutls = readU32(input);
    if (numOutls > getRemainingLength(input) / 12)
      numOutls = getRemainingLength(input) / 12;
    CDR_DEBUG_MSG(("CDRParser::readStlt numOutls 0x%x\n", numOutls));
    std::map<unsigned, unsigned> outlIds;
    for (i=0; i<numOutls; ++i)
    {
      unsigned outlId = readU32(input);
      input->seek(4, librevenge::RVNG_SEEK_CUR);
      outlIds[outlId] = readU32(input);
    }
    unsigned numFonts = readU32(input);
    const unsigned fontsSize = 4 + 2 * 2 + 8 + (m_precision == PRECISION_16BIT ? 2 : 4) + 2 * (m_version < 1000 ? 12 : 20);
    if (numFonts > getRemainingLength(input) / fontsSize)
      numFonts = getRemainingLength(input) / fontsSize;
    CDR_DEBUG_MSG(("CDRParser::readStlt numFonts 0x%x\n", numFonts));
    std::map<unsigned,unsigned short> fontIds, fontEncodings;
    std::map<unsigned,double> fontSizes;
    for (i=0; i<numFonts; ++i)
    {
      unsigned fontStyleId = readU32(input);
      if (m_version < 1000)
        input->seek(12, librevenge::RVNG_SEEK_CUR);
      else
        input->seek(20, librevenge::RVNG_SEEK_CUR);
      fontIds[fontStyleId] = readU16(input);
      fontEncodings[fontStyleId] = readU16(input);
      input->seek(8, librevenge::RVNG_SEEK_CUR);
      fontSizes[fontStyleId] = readCoordinate(input);
      if (m_version < 1000)
        input->seek(12, librevenge::RVNG_SEEK_CUR);
      else
        input->seek(20, librevenge::RVNG_SEEK_CUR);
    }
    unsigned numAligns = readU32(input);
    if (numAligns > getRemainingLength(input) / 12)
      numAligns = getRemainingLength(input) / 12;
    std::map<unsigned,unsigned> aligns;
    CDR_DEBUG_MSG(("CDRParser::readStlt numAligns 0x%x\n", numAligns));
    for (i=0; i<numAligns; ++i)
    {
      unsigned alignId = readU32(input);
      input->seek(4, librevenge::RVNG_SEEK_CUR);
      aligns[alignId] = readU32(input);
    }
    unsigned numIntervals = readU32(input);
    CDR_DEBUG_MSG(("CDRParser::readStlt numIntervals 0x%x\n", numIntervals));
    input->seek(52 * static_cast<long>(numIntervals), librevenge::RVNG_SEEK_CUR);
    unsigned numSet5s = readU32(input);
    CDR_DEBUG_MSG(("CDRParser::readStlt numSet5s 0x%x\n", numSet5s));
    input->seek(152 * static_cast<long>(numSet5s), librevenge::RVNG_SEEK_CUR);
    unsigned numTabs = readU32(input);
    CDR_DEBUG_MSG(("CDRParser::readStlt numTabs 0x%x\n", numTabs));
    input->seek(784 * static_cast<long>(numTabs), librevenge::RVNG_SEEK_CUR);
    unsigned numBullets = readU32(input);
    CDR_DEBUG_MSG(("CDRParser::readStlt numBullets 0x%x\n", numBullets));
    for (i=0; i<numBullets && getRemainingLength(input) >= 16; ++i)
    {
      input->seek(40, librevenge::RVNG_SEEK_CUR);
      if (m_version > 1300)
        input->seek(4, librevenge::RVNG_SEEK_CUR);
      if (m_version >= 1300)
      {
        if (readU32(input))
          input->seek(68, librevenge::RVNG_SEEK_CUR);
        else
          input->seek(12, librevenge::RVNG_SEEK_CUR);
      }
      else
      {
        input->seek(20, librevenge::RVNG_SEEK_CUR);
        if (m_version >= 1000)
          input->seek(8, librevenge::RVNG_SEEK_CUR);
        if (readU32(input))
          input->seek(8, librevenge::RVNG_SEEK_CUR);
        input->seek(8, librevenge::RVNG_SEEK_CUR);
      }
    }
    unsigned numIndents = readU32(input);
    const unsigned indentSize = 4 + 3 * (m_precision == PRECISION_16BIT ? 2 : 4);
    if (numIndents > getRemainingLength(input) / indentSize)
      numIndents = getRemainingLength(input) / indentSize;
    std::map<unsigned, double> rightIndents, firstIndents, leftIndents;
    CDR_DEBUG_MSG(("CDRParser::readStlt numIndents 0x%x\n", numIndents));
    for (i=0; i<numIndents; ++i)
    {
      unsigned indentId = readU32(input);
      input->seek(12, librevenge::RVNG_SEEK_CUR);
      rightIndents[indentId] = readCoordinate(input);
      firstIndents[indentId] = readCoordinate(input);
      leftIndents[indentId] = readCoordinate(input);
    }
    unsigned numHypens = readU32(input);
    CDR_DEBUG_MSG(("CDRParser::readStlt numHypens 0x%x\n", numHypens));
    long hypensLen = 32;
    if (m_version >= 1300)
      hypensLen += 4;
    input->seek(hypensLen * static_cast<long>(numHypens), librevenge::RVNG_SEEK_CUR);
    unsigned numDropcaps = readU32(input);
    CDR_DEBUG_MSG(("CDRParser::readStlt numDropcaps 0x%x\n", numDropcaps));
    input->seek(28 * static_cast<long>(numDropcaps), librevenge::RVNG_SEEK_CUR);
    try
    {
      bool set11Flag(false);
      if (m_version > 800)
      {
        set11Flag = true;
        unsigned numSet11s = readU32(input);
        CDR_DEBUG_MSG(("CDRParser::readStlt numSet11s 0x%x\n", numSet11s));
        input->seek(12 * static_cast<long>(numSet11s), librevenge::RVNG_SEEK_CUR);
      }
      std::map<unsigned, CDRStltRecord> styles;
      for (i=0; i<numRecords && getRemainingLength(input) >= 32; ++i)
      {
        CDR_DEBUG_MSG(("CDRParser::readStlt parsing styles\n"));
        unsigned num = readU32(input);
        CDR_DEBUG_MSG(("CDRParser::readStlt parsing styles num 0x%x\n", num));
        unsigned styleId = readU32(input);
        CDRStltRecord style;
        style.parentId = readU32(input);
        input->seek(8, librevenge::RVNG_SEEK_CUR);
        unsigned namelen = readU32(input);
        CDR_DEBUG_MSG(("CDRParser::readStlt parsing styles namelen 0x%x\n", namelen));
        if (m_version >= 1200)
          namelen *= 2;
        input->seek(namelen, librevenge::RVNG_SEEK_CUR);
        style.fillId = readU32(input);
        style.outlId = readU32(input);
        if (num > 1)
        {
          style.fontRecId = readU32(input);
          style.alignId = readU32(input);
          style.intervalId = readU32(input);
          style.set5Id = readU32(input);
          if (set11Flag)
            style.set11Id = readU32(input);
        }
        if (num > 2)
        {
          style.tabId = readU32(input);
          style.bulletId = readU32(input);
          style.indentId = readU32(input);
          style.hyphenId = readU32(input);
          style.dropCapId = readU32(input);
        }
        styles[styleId] = style;
      }
      for (std::map<unsigned, CDRStltRecord>::const_iterator iter = styles.begin();
           iter != styles.end(); ++iter)
      {
        CDRStyle tmpCharStyle;
        unsigned fontRecId =  iter->second.fontRecId;
        if (fontRecId)
        {
          std::map<unsigned, unsigned short>::const_iterator iterFontId = fontIds.find(fontRecId);
          if (iterFontId != fontIds.end())
          {
            std::map<unsigned, CDRFont>::const_iterator iterFonts = m_fonts.find(iterFontId->second);
            if (iterFonts != m_fonts.end())
            {
              tmpCharStyle.m_fontName = iterFonts->second.m_name;
              tmpCharStyle.m_charSet = iterFonts->second.m_encoding;
            }
          }
          std::map<unsigned, unsigned short>::const_iterator iterCharSet = fontEncodings.find(fontRecId);
          if (iterCharSet != fontEncodings.end())
            if (iterCharSet->second)
              tmpCharStyle.m_charSet = iterCharSet->second;
          std::map<unsigned, double>::const_iterator iterFontSize = fontSizes.find(fontRecId);
          if (iterFontSize != fontSizes.end())
            tmpCharStyle.m_fontSize = iterFontSize->second;
        }
        unsigned alignId = iter->second.alignId;
        if (alignId)
        {
          std::map<unsigned, unsigned>::const_iterator iterAlign = aligns.find(alignId);
          if (iterAlign != aligns.end())
            tmpCharStyle.m_align = iterAlign->second;
        }
        unsigned indentId = iter->second.indentId;
        if (indentId)
        {
          std::map<unsigned, double>::const_iterator iterRight = rightIndents.find(indentId);
          if (iterRight != rightIndents.end())
            tmpCharStyle.m_rightIndent = iterRight->second;
          std::map<unsigned, double>::const_iterator iterFirst = firstIndents.find(indentId);
          if (iterFirst != firstIndents.end())
            tmpCharStyle.m_firstIndent = iterFirst->second;
          std::map<unsigned, double>::const_iterator iterLeft = leftIndents.find(indentId);
          if (iterLeft != leftIndents.end())
            tmpCharStyle.m_leftIndent = iterLeft->second;
        }
        unsigned fillId = iter->second.fillId;
        if (fillId)
        {
          std::map<unsigned, unsigned>::const_iterator iterFillId = fillIds.find(fillId);
          if (iterFillId != fillIds.end())
          {
            std::map<unsigned, CDRFillStyle>::const_iterator iterFillStyle = m_fillStyles.find(iterFillId->second);
            if (iterFillStyle != m_fillStyles.end())
              tmpCharStyle.m_fillStyle = iterFillStyle->second;
          }
        }
        unsigned outlId = iter->second.outlId;
        if (outlId)
        {
          std::map<unsigned, unsigned>::const_iterator iterOutlId = outlIds.find(outlId);
          if (iterOutlId != outlIds.end())
          {
            std::map<unsigned, CDRLineStyle>::const_iterator iterLineStyle = m_lineStyles.find(iterOutlId->second);
            if (iterLineStyle != m_lineStyles.end())
              tmpCharStyle.m_lineStyle = iterLineStyle->second;
          }
        }
        unsigned parentId = iter->second.parentId;
        if (parentId)
          tmpCharStyle.m_parentId = parentId;
        m_collector->collectStld(iter->first, tmpCharStyle);
      }
    }
    catch (libcdr::EndOfStreamException &)
    {
      if (m_version == 800)
      {
        CDR_DEBUG_MSG(("Catching EndOfStreamException and trying to parse as version 801\n"));
        m_version = 801;
        input->seek(startPosition, librevenge::RVNG_SEEK_SET);
        readStlt(input, length);
        return;
      }
      else
      {
        CDR_DEBUG_MSG(("Rethrowing EndOfStreamException\n"));
        throw libcdr::EndOfStreamException();
      }
    }
#ifndef DEBUG
  }
  catch (...)
  {
    m_version = version;
  }
#endif
}

void libcdr::CDRParser::readTxsm(librevenge::RVNGInputStream *input, unsigned length)
{
#ifndef DEBUG
  try
  {
#endif
    if (m_version < 500)
      return;
    if (!_redirectX6Chunk(&input, length))
      throw GenericException();
    if (m_version < 600)
      return readTxsm5(input);
    if (m_version < 700)
      return readTxsm6(input);
    if (m_version >= 1600)
      return readTxsm16(input);
    if (m_version >= 1500)
      input->seek(0x25, librevenge::RVNG_SEEK_CUR);
    else
      input->seek(0x24, librevenge::RVNG_SEEK_CUR);
    if (readU32(input))
    {
      if (m_version < 800)
        input->seek(32, librevenge::RVNG_SEEK_CUR);
    }
    if (m_version < 800)
      input->seek(4, librevenge::RVNG_SEEK_CUR);
    unsigned textId = readU32(input);
    input->seek(48, librevenge::RVNG_SEEK_CUR);
    if (m_version >= 800)
    {
      if (readU32(input))
      {
        input->seek(32, librevenge::RVNG_SEEK_CUR);
        if (m_version >= 1300)
          input->seek(8, librevenge::RVNG_SEEK_CUR);
      }
    }
    if (m_version >= 1500)
      input->seek(12, librevenge::RVNG_SEEK_CUR);
    unsigned num = readU32(input);
    unsigned num4 = 1;
    if (!num)
    {
      if (m_version >= 800)
        input->seek(4, librevenge::RVNG_SEEK_CUR);
      if (m_version > 800)
        input->seek(2, librevenge::RVNG_SEEK_CUR);
      if (m_version >= 1400)
        input->seek(2, librevenge::RVNG_SEEK_CUR);
      input->seek(24, librevenge::RVNG_SEEK_CUR);
      if (m_version < 800)
        input->seek(8, librevenge::RVNG_SEEK_CUR);
      num4 = readU32(input);
    }

    for (unsigned j = 0; j < num4 && getRemainingLength(input) >= 14; ++j)
    {
      unsigned stlId = readU32(input);
      if (m_version >= 1300 && num)
        input->seek(1, librevenge::RVNG_SEEK_CUR);
      input->seek(1, librevenge::RVNG_SEEK_CUR);
      unsigned numRecords = readU32(input);
      std::map<unsigned, CDRStyle> styles;
      unsigned i = 0;
      for (i=0; i<numRecords && getRemainingLength(input) >= 3; ++i)
      {
        unsigned char fl0 = readU8(input);
        readU8(input);
        unsigned char fl2 = readU8(input);
        unsigned char fl3 = 0;
        if (m_version >= 800)
          fl3 = readU8(input);

        CDRStyle style;
        // Read more information depending on the flags
        if (fl2&1) // Font
        {
          unsigned short fontId = readU16(input);
          std::map<unsigned, CDRFont>::const_iterator iterFont = m_fonts.find(fontId);
          if (iterFont != m_fonts.end())
          {
            style.m_fontName = iterFont->second.m_name;
            style.m_charSet = iterFont->second.m_encoding;
          }
          unsigned short charSet = readU16(input);
          if (charSet)
            style.m_charSet = charSet;
        }
        if (fl2&2) // Bold/Italic, etc.
          input->seek(4, librevenge::RVNG_SEEK_CUR);
        if (fl2&4) // Font Size
          style.m_fontSize = readCoordinate(input);
        if (fl2&8) // assumption
          input->seek(4, librevenge::RVNG_SEEK_CUR);
        if (fl2&0x10) // Offset X
          input->seek(4, librevenge::RVNG_SEEK_CUR);
        if (fl2&0x20) // Offset Y
          input->seek(4, librevenge::RVNG_SEEK_CUR);
        if (fl2&0x40) // Font Colour
        {
          unsigned fillId = readU32(input);
          std::map<unsigned, CDRFillStyle>::const_iterator iter = m_fillStyles.find(fillId);
          if (iter != m_fillStyles.end())
            style.m_fillStyle = iter->second;
          if (m_version >= 1500)
            input->seek(48, librevenge::RVNG_SEEK_CUR);
        }
        if (fl2&0x80) // Font Outl Colour
        {
          unsigned outlId = readU32(input);
          std::map<unsigned, CDRLineStyle>::const_iterator iter = m_lineStyles.find(outlId);
          if (iter != m_lineStyles.end())
            style.m_lineStyle = iter->second;
        }
        if (fl3&8) // Encoding
        {
          if (m_version >= 1300)
          {
            unsigned tlen = readU32(input);
            input->seek(tlen*2, librevenge::RVNG_SEEK_CUR);
          }
          else
            input->seek(4, librevenge::RVNG_SEEK_CUR);
        }
        if (fl3&0x20) // Something
        {
          unsigned flag = readU8(input);
          if (flag)
            input->seek(52, librevenge::RVNG_SEEK_CUR);
        }
        if (fl0 == 0x02)
          if (m_version >= 1300)
            input->seek(48, librevenge::RVNG_SEEK_CUR);

        styles[2*i] = style;
      }
      unsigned numChars = readU32(input);
      const unsigned charSize = m_version >= 1200 ? 8 : 4;
      if (numChars > getRemainingLength(input) / charSize)
        numChars = getRemainingLength(input) / charSize;
      std::vector<unsigned char> charDescriptions(numChars);
      for (i=0; i<numChars; ++i)
      {
        unsigned tmpCharDescription = 0;
        if (m_version >= 1200)
          tmpCharDescription = readU64(input) & 0xffffffff;
        else
          tmpCharDescription = readU32(input);
        charDescriptions[i] = (tmpCharDescription >> 16) | (tmpCharDescription & 0x01);
      }
      unsigned numBytes = numChars;
      if (m_version >= 1200)
        numBytes = readU32(input);
      unsigned long numBytesRead = 0;
      const unsigned char *buffer = input->read(numBytes, numBytesRead);
      if (numBytesRead != numBytes)
        throw GenericException();
      std::vector<unsigned char> textData(numBytesRead);
      if (numBytesRead)
        memcpy(&textData[0], buffer, numBytesRead);
      input->seek(1, librevenge::RVNG_SEEK_CUR); //skip the 0 ending character

      if (!textData.empty() || !styles.empty())
        m_collector->collectText(textId, stlId, textData, charDescriptions, styles);
    }
#ifndef DEBUG
  }
  catch (...)
  {
  }
#endif
}

void libcdr::CDRParser::readTxsm16(librevenge::RVNGInputStream *input)
{
#ifndef DEBUG
  try
  {
#endif
    unsigned frameFlag = readU32(input);
    input->seek(41, librevenge::RVNG_SEEK_CUR);

    unsigned textId = readU32(input);

    input->seek(48, librevenge::RVNG_SEEK_CUR);
    if (!frameFlag)
    {
      input->seek(28, librevenge::RVNG_SEEK_CUR);
      unsigned tlen = readU32(input);
      if (m_version < 1700)
        tlen *= 2;
      input->seek(tlen + 4, librevenge::RVNG_SEEK_CUR);
    }
    else
    {
      unsigned textOnPath = readU32(input);
      if (textOnPath == 1)
      {
        input->seek(4, librevenge::RVNG_SEEK_CUR); // var1
        input->seek(4, librevenge::RVNG_SEEK_CUR); // Orientation
        input->seek(4, librevenge::RVNG_SEEK_CUR); // var2
        input->seek(4, librevenge::RVNG_SEEK_CUR); // var3
        input->seek(4, librevenge::RVNG_SEEK_CUR); // Offset
        input->seek(4, librevenge::RVNG_SEEK_CUR); // var4
        input->seek(4, librevenge::RVNG_SEEK_CUR); // Distance
        input->seek(4, librevenge::RVNG_SEEK_CUR); // var5
        input->seek(4, librevenge::RVNG_SEEK_CUR); // Mirror Vert
        input->seek(4, librevenge::RVNG_SEEK_CUR); // Mirror Hor
        input->seek(4, librevenge::RVNG_SEEK_CUR); // var6
        input->seek(4, librevenge::RVNG_SEEK_CUR); // var7
      }
      else
        input->seek(8, librevenge::RVNG_SEEK_CUR);
      input->seek(4, librevenge::RVNG_SEEK_CUR);
    }

    unsigned stlId = readU32(input);

    if (frameFlag)
      input->seek(1, librevenge::RVNG_SEEK_CUR);
    input->seek(1, librevenge::RVNG_SEEK_CUR);

    unsigned len2 = readU32(input);
    if (m_version < 1700)
      len2 *= 2;
    CDRStyle defaultStyle;
    _readX6StyleString(input, len2, defaultStyle);

    unsigned numRecords = readU32(input);

    unsigned i = 0;
    std::map<unsigned, CDRStyle> styles;
    for (i=0; i<numRecords && getRemainingLength(input) >= 17; ++i)
    {
      styles[i*2] = defaultStyle;
      input->seek(4, librevenge::RVNG_SEEK_CUR);
      unsigned flag = readU8(input);
      input->seek(1, librevenge::RVNG_SEEK_CUR);
      unsigned lenN = 0;
      if (flag & 0x04)
      {
        lenN = readU32(input);
        lenN *= 2;
        input->seek(lenN, librevenge::RVNG_SEEK_CUR);
      }
      lenN = readU32(input);
      if (m_version < 1700)
        lenN *= 2;
      _readX6StyleString(input, lenN, styles[i*2]);
    }

    unsigned numChars = readU32(input);
    if (numChars > getRemainingLength(input) / 8)
      numChars = getRemainingLength(input) / 8;
    std::vector<unsigned char> charDescriptions(numChars);
    for (i=0; i<numChars; ++i)
    {
      charDescriptions[i] = readU64(input);
    }
    unsigned numBytes = readU32(input);
    unsigned long numBytesRead = 0;
    const unsigned char *buffer = input->read(numBytes, numBytesRead);
    if (numBytesRead != numBytes)
      throw GenericException();
    std::vector<unsigned char> textData(numBytesRead);
    if (numBytesRead)
      memcpy(&textData[0], buffer, numBytesRead);

    if (!textData.empty())
      m_collector->collectText(textId, stlId, textData, charDescriptions, styles);
#ifndef DEBUG
  }
  catch (...)
  {
  }
#endif
}

void libcdr::CDRParser::readTxsm6(librevenge::RVNGInputStream *input)
{
  unsigned fflag1 = readU32(input);
  input->seek(0x20, librevenge::RVNG_SEEK_CUR);
  unsigned textId = readU32(input);
  input->seek(48, librevenge::RVNG_SEEK_CUR);
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  if (!fflag1)
    input->seek(8, librevenge::RVNG_SEEK_CUR);
  unsigned stlId = readU32(input);
  unsigned numSt = readU32(input);
  unsigned i = 0;
  std::map<unsigned, CDRStyle> styles;
  for (; i<numSt && getRemainingLength(input) >= 58; ++i)
  {
    CDRStyle style;
    unsigned char flag = readU8(input);
    input->seek(3, librevenge::RVNG_SEEK_CUR);
    if (flag&0x01)
    {
      unsigned short fontId = readU16(input);
      std::map<unsigned, CDRFont>::const_iterator iterFont = m_fonts.find(fontId);
      if (iterFont != m_fonts.end())
      {
        style.m_fontName = iterFont->second.m_name;
        style.m_charSet = iterFont->second.m_encoding;
      }
      unsigned short charSet = readU16(input);
      if (charSet)
        style.m_charSet = charSet;
    }
    else
      input->seek(4, librevenge::RVNG_SEEK_CUR);
    input->seek(4, librevenge::RVNG_SEEK_CUR);
    if (flag&0x04)
      style.m_fontSize = readCoordinate(input);
    else
      input->seek(4, librevenge::RVNG_SEEK_CUR);
    input->seek(44, librevenge::RVNG_SEEK_CUR);
    if (flag&0x10)
    {
      unsigned fillId = readU32(input);
      std::map<unsigned, CDRFillStyle>::const_iterator iter = m_fillStyles.find(fillId);
      if (iter != m_fillStyles.end())
        style.m_fillStyle = iter->second;
    }
    if (flag&0x20)
    {
      unsigned outlId = readU32(input);
      std::map<unsigned, CDRLineStyle>::const_iterator iter = m_lineStyles.find(outlId);
      if (iter != m_lineStyles.end())
        style.m_lineStyle = iter->second;
    }
    styles[2*i] = style;
  }
  unsigned numChars = readU32(input);
  if (numChars > getRemainingLength(input) / 12)
    numChars = getRemainingLength(input) / 12;
  std::vector<unsigned char> textData;
  std::vector<unsigned char> charDescriptions;
  textData.reserve(numChars);
  charDescriptions.reserve(numChars);
  for (i=0; i<numChars; ++i)
  {
    input->seek(4, librevenge::RVNG_SEEK_CUR);
    textData.push_back(readU8(input));
    input->seek(5, librevenge::RVNG_SEEK_CUR);
    charDescriptions.push_back(readU8(input) << 1);
    input->seek(1, librevenge::RVNG_SEEK_CUR);
  }
  if (!textData.empty())
    m_collector->collectText(textId, stlId, textData, charDescriptions, styles);
}

void libcdr::CDRParser::readTxsm5(librevenge::RVNGInputStream *input)
{
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  unsigned textId = readU16(input);
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  unsigned stlId = readU16(input);
  unsigned numSt = readU16(input);
  unsigned i = 0;
  std::map<unsigned, CDRStyle> styles;
  for (; i<numSt && getRemainingLength(input) >= 34; ++i)
  {
    CDRStyle style;
    unsigned char flag = readU8(input);
    input->seek(1, librevenge::RVNG_SEEK_CUR);
    if (flag&0x01)
    {
      unsigned short fontId = readU8(input);
      std::map<unsigned, CDRFont>::const_iterator iterFont = m_fonts.find(fontId);
      if (iterFont != m_fonts.end())
      {
        style.m_fontName = iterFont->second.m_name;
        style.m_charSet = iterFont->second.m_encoding;
      }
      unsigned short charSet = readU8(input);
      if (charSet)
        style.m_charSet = charSet;
    }
    else
      input->seek(2, librevenge::RVNG_SEEK_CUR);
    input->seek(6, librevenge::RVNG_SEEK_CUR);
    if (flag&0x04)
      style.m_fontSize = readCoordinate(input);
    else
      input->seek(2, librevenge::RVNG_SEEK_CUR);
    input->seek(2, librevenge::RVNG_SEEK_CUR);
    if (flag&0x10)
    {
      unsigned fillId = readU32(input);
      std::map<unsigned, CDRFillStyle>::const_iterator iter = m_fillStyles.find(fillId);
      if (iter != m_fillStyles.end())
        style.m_fillStyle = iter->second;
    }
    else
      input->seek(4, librevenge::RVNG_SEEK_CUR);
    if (flag&0x20)
    {
      unsigned outlId = readU32(input);
      std::map<unsigned, CDRLineStyle>::const_iterator iter = m_lineStyles.find(outlId);
      if (iter != m_lineStyles.end())
        style.m_lineStyle = iter->second;
    }
    else
      input->seek(4, librevenge::RVNG_SEEK_CUR);
    input->seek(14, librevenge::RVNG_SEEK_CUR);
    styles[2*i] = style;
  }
  unsigned numChars = readU16(input);
  if (numChars > getRemainingLength(input) / 8)
    numChars = getRemainingLength(input) / 8;
  std::vector<unsigned char> textData;
  std::vector<unsigned char> charDescriptions;
  textData.reserve(numChars);
  charDescriptions.reserve(numChars);
  for (i=0; i<numChars; ++i)
  {
    input->seek(4, librevenge::RVNG_SEEK_CUR);
    textData.push_back(readU8(input));
    input->seek(1, librevenge::RVNG_SEEK_CUR);
    charDescriptions.push_back((readU16(input) >> 3) & 0xff);
  }
  if (!textData.empty())
    m_collector->collectText(textId, stlId, textData, charDescriptions, styles);
}

void libcdr::CDRParser::readUdta(librevenge::RVNGInputStream *input)
{
  CDR_DEBUG_MSG(("libcdr::CDRParser::readUdta\n"));
  if (m_version >= 1300 && m_version < 1600)
  {
    input->seek(6, librevenge::RVNG_SEEK_CUR); // Not sure what these bytes are for.  Field id?
    std::vector<unsigned char> name;
    unsigned short c;
    for (;;)
    {
      if ((c = readU16(input)) == 0) break;
      name.push_back((unsigned char)(c & 0xff));
      name.push_back((unsigned char)(c >> 8));
    }
    librevenge::RVNGString fieldName;
    appendCharacters(fieldName, name);
  }
}

void libcdr::CDRParser::readStyd(librevenge::RVNGInputStream *input)
{
  CDR_DEBUG_MSG(("libcdr::CDRParser::readStyd\n"));
  if (m_version >= 700)
  {
    CDR_DEBUG_MSG(("Styd should not be present in this file version\n"));
    return;
  }
  unsigned styleId = readU16(input);
  long startPosition = input->tell();
  const unsigned long maxLength = getLength(input);
  if (startPosition >= long(maxLength))
    return;
  unsigned chunkLength = readUnsigned(input);
  if ((chunkLength > maxLength) || (long(maxLength - chunkLength) < startPosition))
    chunkLength = unsigned(maxLength - static_cast<unsigned long>(startPosition)); // sanitize length
  unsigned numOfArgs = readUnsigned(input);
  unsigned startOfArgs = readUnsigned(input);
  if (startOfArgs >= chunkLength)
    return;
  unsigned startOfArgTypes = readUnsigned(input);
  if (startOfArgTypes >= chunkLength)
    return;
  if (numOfArgs > (chunkLength - startOfArgs) / 4) // avoid extra big allocation in case of a broken file
    numOfArgs = (chunkLength - startOfArgs) / 4;
  CDRStyle style;
  style.m_parentId =  readUnsigned(input);
  std::vector<unsigned> argOffsets(numOfArgs, 0);
  std::vector<unsigned> argTypes(numOfArgs, 0);
  unsigned i = 0;
  input->seek(startPosition+startOfArgs, librevenge::RVNG_SEEK_SET);
  while (i<numOfArgs)
    argOffsets[i++] = readUnsigned(input);
  input->seek(startPosition+startOfArgTypes, librevenge::RVNG_SEEK_SET);
  while (i>0)
    argTypes[--i] = readUnsigned(input);

  for (i=0; i < argTypes.size(); i++)
  {
    input->seek(startPosition+argOffsets[i], librevenge::RVNG_SEEK_SET);
    CDR_DEBUG_MSG(("Styd: argument type: 0x%x\n", argTypes[i]));
    switch (argTypes[i])
    {
    case STYD_NAME:
      break;
    case STYD_FILL_ID:
    {
      unsigned fillId = readU32(input);
      std::map<unsigned, CDRFillStyle>::const_iterator iter = m_fillStyles.find(fillId);
      if (iter != m_fillStyles.end())
        style.m_fillStyle = iter->second;
      break;
    }
    case STYD_OUTL_ID:
    {
      unsigned outlId = readU32(input);
      std::map<unsigned, CDRLineStyle>::const_iterator iter = m_lineStyles.find(outlId);
      if (iter != m_lineStyles.end())
        style.m_lineStyle = iter->second;
      break;
    }
    case STYD_FONTS:
    {
      if (m_version >= 600)
        input->seek(4, librevenge::RVNG_SEEK_CUR);
      unsigned short fontId = readUnsignedShort(input);
      std::map<unsigned, CDRFont>::const_iterator iterFont = m_fonts.find(fontId);
      if (iterFont != m_fonts.end())
      {
        style.m_fontName = iterFont->second.m_name;
        style.m_charSet = iterFont->second.m_encoding;
      }
      unsigned short charSet = readUnsignedShort(input);
      if (charSet)
        style.m_charSet = charSet;
      if (m_version >= 600)
        input->seek(8, librevenge::RVNG_SEEK_CUR);
      style.m_fontSize = readCoordinate(input);
      break;
    }
    case STYD_ALIGN:
      style.m_align = readUnsigned(input);
      break;
    case STYD_BULLETS:
      break;
    case STYD_INTERVALS:
      break;
    case STYD_TABS:
      break;
    case STYD_IDENTS:
      break;
    case STYD_HYPHENS:
      break;
    case STYD_SET5S:
      break;
    case STYD_DROPCAPS:
      break;
    default:
      break;
    }
  }
  input->seek(startPosition+chunkLength, librevenge::RVNG_SEEK_SET);
  m_collector->collectStld(styleId, style);
}

void libcdr::CDRParser::readArtisticText(librevenge::RVNGInputStream *input)
{
  double x = readCoordinate(input);
  double y = readCoordinate(input);
  m_collector->collectArtisticText(x, y);
}

void libcdr::CDRParser::readParagraphText(librevenge::RVNGInputStream *input)
{
  input->seek(4, librevenge::RVNG_SEEK_CUR);
  double width = readCoordinate(input);
  double height = readCoordinate(input);
  m_collector->collectParagraphText(0.0, 0.0, width, height);
}

void libcdr::CDRParser::_readX6StyleString(librevenge::RVNGInputStream *input, unsigned length, libcdr::CDRStyle &style)
{
  if (length > getRemainingLength(input))
  {
    length = getRemainingLength(input);
    if ((m_version < 1700) && (length & 1))
      --length; // the length must be even
  }

  std::vector<unsigned char> styleBuffer(length);
  unsigned long numBytesRead = 0;
  const unsigned char *tmpBuffer = input->read(length, numBytesRead);
  if (numBytesRead)
    memcpy(&styleBuffer[0], tmpBuffer, numBytesRead);
  librevenge::RVNGString styleString;
  if (m_version >= 1700)
    libcdr::appendCharacters(styleString, styleBuffer, 0);
  else
    libcdr::appendCharacters(styleString, styleBuffer);
  CDR_DEBUG_MSG(("CDRParser::_readX6StyleString - styleString = \"%s\"\n", styleString.cstr()));

  boost::property_tree::ptree pt;
  try
  {
    std::stringstream ss;
    ss << styleString.cstr();
    boost::property_tree::read_json(ss, pt);
  }
  catch (...)
  {
    return;
  }

  if (pt.count("character"))
  {
    boost::optional<std::string> fontName = pt.get_optional<std::string>("character.latin.font");
    if (!!fontName)
      style.m_fontName = fontName.get().c_str();
    unsigned short encoding = pt.get("character.latin.charset", 0);
    if (encoding || style.m_charSet == (unsigned short)-1)
      style.m_charSet = encoding;
    processNameForEncoding(style.m_fontName, style.m_charSet);
    boost::optional<unsigned> fontSize = pt.get_optional<unsigned>("character.latin.size");
    if (!!fontSize)
      style.m_fontSize = (double)fontSize.get() / 254000.0;

    if (pt.count("character.outline"))
    {
      style.m_lineStyle.lineType = 0;
      boost::optional<unsigned> lineWidth = pt.get_optional<unsigned>("character.outline.width");
      if (!!lineWidth)
        style.m_lineStyle.lineWidth = (double)lineWidth.get() / 254000.0;
      boost::optional<std::string> color = pt.get_optional<std::string>("character.outline.color");
      if (!!color)
      {
        double opacity = 1.0;
        parseColourString(color.get().c_str(), style.m_lineStyle.color, opacity);
      }
    }

    if (pt.count("character.fill"))
    {
      boost::optional<unsigned short> type = pt.get_optional<unsigned short>("character.fill.type");
      if (!!type)
        style.m_fillStyle.fillType = type.get();
      boost::optional<std::string> color1 = pt.get_optional<std::string>("character.fill.primaryColor");
      if (!!color1)
      {
        double opacity = 1.0;
        parseColourString(color1.get().c_str(), style.m_fillStyle.color1, opacity);
      }
      boost::optional<std::string> color2 = pt.get_optional<std::string>("character.fill.primaryColor");
      if (!!color2)
      {
        double opacity = 1.0;
        parseColourString(color2.get().c_str(), style.m_fillStyle.color2, opacity);
      }
    }
  }

  if (pt.count("paragraph"))
  {
    boost::optional<unsigned> align = pt.get_optional<unsigned>("paragraph.justify");
    if (!!align)
      style.m_align = align.get();
  }
}


/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
