Blob Blame History Raw
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */

/* libmwaw
* Version: MPL 2.0 / LGPLv2+
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License or as specified alternatively below. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Major Contributor(s):
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
* Copyright (C) 2006, 2007 Andrew Ziem
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
*
*
* All Rights Reserved.
*
* For minor contributions see the git repository.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
* in which case the provisions of the LGPLv2+ are applicable
* instead of those above.
*/


#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <set>
#include <sstream>

#include <librevenge/librevenge.h>

#include "MWAWCell.hxx"
#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWListener.hxx"
#include "MWAWParser.hxx"
#include "MWAWPosition.hxx"
#include "MWAWTable.hxx"

#include "ClarisWksDocument.hxx"
#include "ClarisWksStruct.hxx"
#include "ClarisWksStyleManager.hxx"

#include "ClarisWksTable.hxx"

/** Internal: the structures of a ClarisWksTable */
namespace ClarisWksTableInternal
{
/** Internal: the border of a ClarisWksTable */
struct Border {
  //! the constructor
  Border()
    : m_styleId(-1)
    , m_isSent(false) {}
  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, Border const &bord)
  {
    for (int i = 0; i < 2; i++) {
      MWAWVec2f pos(bord.m_position[i]);
      pos=(1.f/256.f)*pos;
      o << pos;
      if (i == 0) o << "<->";
      else o << ",";
    }
    if (bord.m_styleId>0)
      o << "m_styleId?=" << bord.m_styleId << ",";
    return o;
  }

  /** the origin and the end of edge position : unit librevenge::RVNG_POINT/256 */
  MWAWVec2i m_position[2];
  /// the style id
  int m_styleId;
  /// a flag to know if the border is already sent
  mutable bool m_isSent;
};

struct Table;
/** Internal: a cell inside a ClarisWksTable */
struct TableCell final : public MWAWCell {
  //! constructor
  TableCell()
    : MWAWCell()
    , m_zoneId(0)
    , m_styleId(-1)
  {
  }
  //! use table to finish updating cell
  void update(Table const &table);

  //! send the cell content to a listener
  bool sendContent(MWAWListenerPtr listener, MWAWTable &table) final;

  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, TableCell const &cell)
  {
    o << static_cast<MWAWCell const &>(cell);
    if (cell.m_zoneId) o << "zone=" << cell.m_zoneId << ",";
    if (cell.m_styleId >= 0)o << "style=" << cell.m_styleId << ",";
    return o;
  }

  /** the cell zone ( 0 is no content ) */
  int m_zoneId;
  /** the list of border id : Left, Top, Right, Bottom

     Normally, one id but merge cells can have mutiple border
  */
  std::vector<int> m_bordersId[4];
  /// the style id
  int m_styleId;

private:
  TableCell(TableCell const &orig) = delete;
  TableCell &operator=(TableCell const &orig) = delete;
};

////////////////////////////////////////
////////////////////////////////////////
/** the struct which stores the Table */
struct Table final : public ClarisWksStruct::DSET, public MWAWTable {
  friend struct TableCell;
  //! constructor
  Table(ClarisWksStruct::DSET const &dset, ClarisWksTable &parser,  ClarisWksStyleManager &styleManager)
    : ClarisWksStruct::DSET(dset)
    , MWAWTable()
    , m_parser(&parser)
    , m_styleManager(&styleManager)
    , m_bordersList()
    , m_mainPtr(-1)
    , m_auxiliaryDetached(false)
  {
  }
  //! destructor
  ~Table() final;
  //! return a cell corresponding to id
  TableCell *get(int id)
  {
    if (id < 0 || id >= numCells()) {
      MWAW_DEBUG_MSG(("ClarisWksTableInteral::Table::get: cell %d does not exists\n",id));
      return nullptr;
    }
    return static_cast<TableCell *>(MWAWTable::get(id).get());
  }
  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, Table const &doc)
  {
    o << static_cast<ClarisWksStruct::DSET const &>(doc);
    return o;
  }
  /** remove a child from a list.

      Normally, this function is not called, so optimizing it is not usefull
  */
  void removeChild(int cId, bool normalChild) final
  {
    DSET::removeChild(cId, normalChild);
    if (cId==m_id+1) {
      m_auxiliaryDetached=true;
      return;
    }
    for (auto &i : m_cellsList) {
      auto *cell = static_cast<TableCell *>(i.get());
      if (!cell || cell->m_zoneId!=cId) continue;
      cell->m_zoneId=0;
      return;
    }
    MWAW_DEBUG_MSG(("ClarisWksTableInternal::Zone can not detach %d\n", cId));
  }

  //! finish updating all cells
  void updateCells()
  {
    for (int c=0; c<numCells(); ++c) {
      if (!get(c)) continue;
      get(c)->update(*this);
    }
  }
  //! ask the main parser to send a zone
  bool askMainToSendZone(int number)
  {
    return m_parser->askMainToSendZone(number);
  }

  /** the main parser */
  ClarisWksTable *m_parser;
  /** the style manager */
  ClarisWksStyleManager *m_styleManager;
  /** the list of border */
  std::vector<Border> m_bordersList;
  /** the relative main pointer */
  long m_mainPtr;
  //! true if the auxiliary zone is detached
  bool m_auxiliaryDetached;

private:
  Table(Table const &orig) = delete;
  Table &operator=(Table const &orig) = delete;
};

Table::~Table()
{
}

void TableCell::update(Table const &table)
{
  auto *styleManager = table.m_styleManager;
  if (!styleManager) {
    MWAW_DEBUG_MSG(("ClarisWksTableInternal::TableCell::update: style manager is not defined\n"));
    return;
  }

  auto numTableBorders = static_cast<int>(table.m_bordersList.size());
  // now look for border id: L,T,R,B
  for (int w = 0; w < 4; w++) {
    size_t numBorders = m_bordersId[w].size();
    if (!numBorders) continue;

    int bId = m_bordersId[w][0];
    bool sameBorders = true;
    for (size_t b = 1; b < numBorders; b++) {
      if (m_bordersId[w][b]!=bId) {
        sameBorders = false;
        break;
      }
    }
    /** fixme: check that the opposite has a border, if not print the first border */
    if (!sameBorders) continue;
    if (bId < 0 || bId >= numTableBorders) {
      MWAW_DEBUG_MSG(("ClarisWksTableInternal::TableCell::get: can not find the border definition\n"));
      continue;
    }
    auto const &border = table.m_bordersList[size_t(bId)];
    ClarisWksStyleManager::Style bStyle;
    if (border.m_isSent || border.m_styleId < 0 || !styleManager->get(border.m_styleId, bStyle))
      continue;
    border.m_isSent = true;
    MWAWGraphicStyle graph;
    bool haveGraph = false;
    if (bStyle.m_graphicId >= 0)
      haveGraph = styleManager->get(bStyle.m_graphicId, graph);
    ClarisWksStyleManager::KSEN ksen;
    bool haveKSEN = false;
    if (bStyle.m_ksenId >= 0)
      haveKSEN = styleManager->get(bStyle.m_ksenId, ksen);

    MWAWBorder bord;
    if (haveGraph && !graph.hasLine())
      bord.m_style = MWAWBorder::None;
    else if (haveKSEN) {
      bord.m_style = ksen.m_lineType;
      bord.m_type = ksen.m_lineRepeat;
      if (bord.m_type == MWAWBorder::Double)
        bord.m_width = 0.5*double(graph.m_lineWidth);
      else
        bord.m_width = double(graph.m_lineWidth);
      bord.m_color = graph.m_lineColor;
    }
    static int const wh[] = { libmwaw::LeftBit, libmwaw::TopBit, libmwaw::RightBit, libmwaw::BottomBit};
    setBorders(wh[w], bord);
  }
}

bool TableCell::sendContent(MWAWListenerPtr listener, MWAWTable &table)
{
  if (!listener) return true;
  if (m_zoneId <= 0)
    listener->insertChar(' ');
  else
    static_cast<Table &>(table).askMainToSendZone(m_zoneId);
  return true;
}

////////////////////////////////////////
//! Internal: the state of a ClarisWksTable
struct State {
  //! constructor
  State()
    : m_tableMap()
  {
  }
  //! map id -> table
  std::map<int, std::shared_ptr<Table> > m_tableMap;
};

}

////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
ClarisWksTable::ClarisWksTable(ClarisWksDocument &document)
  : m_document(document)
  , m_parserState(document.m_parserState)
  , m_state(new ClarisWksTableInternal::State)
  , m_mainParser(&document.getMainParser())
{
}

ClarisWksTable::~ClarisWksTable()
{
}

int ClarisWksTable::version() const
{
  return m_parserState->m_version;
}

bool ClarisWksTable::askMainToSendZone(int number)
{
  return m_document.sendZone(number);
}

// fixme
int ClarisWksTable::numPages() const
{
  return 1;
}
////////////////////////////////////////////////////////////
// Intermediate level
////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////
// a document part
////////////////////////////////////////////////////////////
std::shared_ptr<ClarisWksStruct::DSET> ClarisWksTable::readTableZone
(ClarisWksStruct::DSET const &zone, MWAWEntry const &entry, bool &complete)
{
  complete = false;
  if (!entry.valid() || zone.m_fileType != 6 || entry.length() < 32)
    return std::shared_ptr<ClarisWksStruct::DSET>();
  long pos = entry.begin();
  MWAWInputStreamPtr &input= m_parserState->m_input;
  input->seek(pos+8+16, librevenge::RVNG_SEEK_SET); // avoid header+8 generic number
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  std::shared_ptr<ClarisWksTableInternal::Table> tableZone(new ClarisWksTableInternal::Table(zone, *this, *m_document.getStyleManager()));

  f << "Entries(TableDef):" << *tableZone << ",";
  float dim[2];
  for (auto &d : dim) d = float(input->readLong(4))/256.f;
  f << "dim=" << dim[0] << "x" << dim[1] << ",";
  long val;
  for (int i = 0; i < 3; i++) {
    // f1=parentZoneId ?
    val = input->readLong(2);
    if (val) f << "f" << i << "=" << val << ",";
  }
  tableZone->m_mainPtr = long(input->readULong(4));
  f << "PTR=X" << std::hex << tableZone->m_mainPtr << std::dec << ",";
  for (int i = 0; i < 2; i++) { // find 0,0 or -1,-1 here
    val = input->readLong(2);
    if (val) f << "f" << i+3 << "=" << val << ",";
  }
  f << "relPtr=PTR+[" << std::hex;
  for (int i = 0; i < 3; i++) {
    /** find 3 ptr here in general >= PTR, very often PTR+4,PTR+8,PTR+c,
     but can be more complex for instance PTR+354,PTR-6924,PTR+7fc, */
    val = long(input->readULong(4));
    if (val > tableZone->m_mainPtr)
      f << val-tableZone->m_mainPtr << ",";
    else
      f << "-" << tableZone->m_mainPtr-val << ",";
  }
  f << std::dec << "],";
  ascFile.addDelimiter(input->tell(), '|');
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  // read the last part
  long data0Length = zone.m_dataSz;
  long N = zone.m_numData;
  if (entry.length() -8-12 != data0Length*N + zone.m_headerSz) {
    if (data0Length == 0 && N) {
      MWAW_DEBUG_MSG(("ClarisWksTable::readTableZone: can not find definition size\n"));
      input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
      return std::shared_ptr<ClarisWksStruct::DSET>();
    }

    MWAW_DEBUG_MSG(("ClarisWksTable::readTableZone: unexpected size for zone definition, try to continue\n"));
  }

  if (long(input->tell())+N*data0Length > entry.end()) {
    MWAW_DEBUG_MSG(("ClarisWksTable::readTableZone: file is too short\n"));
    return std::shared_ptr<ClarisWksStruct::DSET>();
  }
  if (N) {
    MWAW_DEBUG_MSG(("ClarisWksTable::readTableZone: find some tabledef !!!\n"));
    input->seek(entry.end()-N*data0Length, librevenge::RVNG_SEEK_SET);

    for (int i = 0; i < N; i++) {
      pos = input->tell();

      f.str("");
      f << "TableDef#";
      ascFile.addPos(pos);
      ascFile.addNote(f.str().c_str());
      input->seek(pos+data0Length, librevenge::RVNG_SEEK_SET);
    }
  }

  input->seek(entry.end(), librevenge::RVNG_SEEK_SET);

  pos = input->tell();
  bool ok = readTableBorders(*tableZone);
  if (ok) {
    pos = input->tell();
    ok = readTableCells(*tableZone);
  }
  /** three fields which seems to follows the list of cells
      zone 0 : looks like a list of integer : related to last selected border ?
      zone 1 : looks like a list of integer : unknown meaning
   */
  for (int i = 0; ok && i < 2; i++) {
    std::stringstream s;
    s << "TableUnknown-" << i;
    std::vector<int> res;
    pos = input->tell();
    ok = ClarisWksStruct::readIntZone(*m_parserState, s.str().c_str(), false, 2, res);
  }
  if (ok) {
    pos = input->tell();
    ok = readTablePointers(*tableZone);
    if (!ok) {
      input->seek(pos, librevenge::RVNG_SEEK_SET);
      ok = ClarisWksStruct::readStructZone(*m_parserState, "TablePointers", false);
    }
  }
  if (ok) {
    pos = input->tell();
    ok = readTableBordersId(*tableZone);
  }

  if (!ok)
    input->seek(pos, librevenge::RVNG_SEEK_SET);

  tableZone->updateCells();
  if (m_state->m_tableMap.find(tableZone->m_id) != m_state->m_tableMap.end()) {
    MWAW_DEBUG_MSG(("ClarisWksTable::readTableZone: zone %d already exists!!!\n", tableZone->m_id));
  }
  else
    m_state->m_tableMap[tableZone->m_id] = tableZone;

  tableZone->m_otherChilds.push_back(tableZone->m_id+1);
  return tableZone;
}

bool ClarisWksTable::sendZone(int number)
{
  auto iter = m_state->m_tableMap.find(number);
  if (iter == m_state->m_tableMap.end())
    return false;
  auto table = iter->second;
  table->m_parsed = true;
  if (!table->m_auxiliaryDetached)
    m_document.forceParsed(number+1);

  MWAWListenerPtr listener=m_parserState->getMainListener();
  if (!listener)
    return true;

  if (table->sendTable(listener))
    return true;
  return table->sendAsText(listener);
}

void ClarisWksTable::flushExtra()
{
  for (auto iter : m_state->m_tableMap) {
    std::shared_ptr<ClarisWksTableInternal::Table> table = iter.second;
    if (table->m_parsed)
      continue;
    if (m_parserState->getMainListener()) m_parserState->getMainListener()->insertEOL();
    sendZone(iter.first);
  }
}

////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
bool ClarisWksTable::readTableBorders(ClarisWksTableInternal::Table &table)
{
  MWAWInputStreamPtr &input= m_parserState->m_input;
  long pos = input->tell();
  ClarisWksStruct::Struct header;
  if (!header.readHeader(input,true) || header.m_dataSize<18) {
    MWAW_DEBUG_MSG(("ClarisWksTable::readTableBorders: can not read the header\n"));
    return false;
  }
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  if (header.m_size==0) {
    ascFile.addPos(pos);
    ascFile.addNote("Nop");
    return true;
  }
  long endPos=pos+4+header.m_size;
  f << "Entries(TableBorders):" << header;
  if (header.m_headerSize) {
    ascFile.addDelimiter(input->tell(),'|');
    input->seek(header.m_headerSize, librevenge::RVNG_SEEK_CUR);
  }
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  for (int i = 0; i < header.m_numData; i++) {
    pos = input->tell();
    ClarisWksTableInternal::Border border;
    f.str("");
    f << "TableBorders-" << i << ":";
    int posi[4];
    for (auto &p : posi) p = static_cast<int>(input->readLong(4));
    border.m_position[0] = MWAWVec2i(posi[1], posi[0]);
    border.m_position[1] = MWAWVec2i(posi[3], posi[2]);
    border.m_styleId = static_cast<int>(input->readULong(2));
    table.m_bordersList.push_back(border);
    f << border;

    ClarisWksStyleManager::Style style;
    if (border.m_styleId < 0) ;
    else if (!m_document.getStyleManager()->get(border.m_styleId, style)) {
      MWAW_DEBUG_MSG(("ClarisWksTable::readTableBorders: can not find cell style\n"));
      f << "###style";
    }
    else {
      ClarisWksStyleManager::KSEN ksen;
      if (style.m_ksenId >= 0 && m_document.getStyleManager()->get(style.m_ksenId, ksen)) {
        f << "ksen=[" << ksen << "],";
      }
      MWAWGraphicStyle graph;
      if (style.m_graphicId >= 0 && m_document.getStyleManager()->get(style.m_graphicId, graph)) {
        f << "graph=[" << graph << "],";
      }
      //f << "[" << style << "],";
    }

    if (long(input->tell()) != pos+header.m_dataSize)
      ascFile.addDelimiter(input->tell(), '|');
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    input->seek(pos+header.m_dataSize, librevenge::RVNG_SEEK_SET);
  }

  input->seek(endPos, librevenge::RVNG_SEEK_SET);
  return true;
}

bool ClarisWksTable::readTableCells(ClarisWksTableInternal::Table &table)
{
  MWAWInputStreamPtr &input= m_parserState->m_input;
  long pos = input->tell();
  ClarisWksStruct::Struct header;
  if (!header.readHeader(input,true) || header.m_dataSize<32) {
    MWAW_DEBUG_MSG(("ClarisWksTable::readTableCells: can not read the header\n"));
    return false;
  }
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  if (header.m_size==0) {
    ascFile.addPos(pos);
    ascFile.addNote("Nop");
    return true;
  }
  long endPos = pos+4+header.m_size;
  f << "Entries(TableCell):" << header;
  if (header.m_headerSize) {
    ascFile.addDelimiter(input->tell(),'|');
    input->seek(header.m_headerSize, librevenge::RVNG_SEEK_CUR);
  }
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  for (int i = 0; i < header.m_numData; i++) {
    pos = input->tell();
    std::shared_ptr<ClarisWksTableInternal::TableCell> cell(new ClarisWksTableInternal::TableCell());
    float posi[6];
    for (auto &p : posi) p = float(input->readLong(4))/256.f;
    cell->setBdBox(MWAWBox2f(MWAWVec2f(posi[1], posi[0]), MWAWVec2f(posi[3], posi[2])));
    cell->setBdSize(MWAWVec2f(float(posi[5]), float(posi[4])));
    cell->m_zoneId = static_cast<int>(input->readULong(4));
    auto val = static_cast<int>(input->readLong(2));
    if (val) // find one time a number here, another id?...
      f << "#unkn=" << val << ",";
    cell->m_styleId = static_cast<int>(input->readULong(2));
    table.add(cell);
    if (cell->m_zoneId)
      table.m_otherChilds.push_back(cell->m_zoneId);
    f.str("");
    f << "TableCell-" << i << ":";
    ClarisWksStyleManager::Style style;
    if (cell->m_styleId < 0) ;
    else if (!m_document.getStyleManager()->get(cell->m_styleId, style)) {
      MWAW_DEBUG_MSG(("ClarisWksTable::readTableCells: can not find cell style\n"));
      f  << *cell << "###style";
    }
    else {
      ClarisWksStyleManager::KSEN ksen;
      bool hasExtraLines=false;
      if (style.m_ksenId >= 0 && m_document.getStyleManager()->get(style.m_ksenId, ksen)) {
        switch (ksen.m_valign) {
        case 1:
          cell->setVAlignment(MWAWCell::VALIGN_CENTER);
          break;
        case 2:
          cell->setVAlignment(MWAWCell::VALIGN_BOTTOM);
          break;
        default:
          break;
        }
        hasExtraLines=(ksen.m_lines & 3);
        switch (ksen.m_lines & 3) {
        case 1: // TL->BR
          cell->setExtraLine(MWAWCell::E_Line1);
          break;
        case 2: // BL->TR
          cell->setExtraLine(MWAWCell::E_Line2);
          break;
        case 3:
          cell->setExtraLine(MWAWCell::E_Cross);
          break;
        default:
        case 0: // None
          break;
        }
        f << "ksen=[" << ksen << "],";
      }
      MWAWGraphicStyle graph;
      if (style.m_graphicId >= 0 && m_document.getStyleManager()->get(style.m_graphicId, graph)) {
        // checkme: no always there
        if (graph.hasSurfaceColor())
          cell->setBackgroundColor(graph.m_surfaceColor);
        if (hasExtraLines) {
          MWAWBorder border;
          border.m_width=double(graph.m_lineWidth);
          border.m_color=graph.m_lineColor;
          cell->setExtraLine(cell->extraLine(), border);
        }
        f << "graph=[" << graph << "],";
      }
      f << *cell;
      //f << "[" << style << "],";
    }

    if (long(input->tell()) != pos+header.m_dataSize)
      ascFile.addDelimiter(input->tell(), '|');
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    input->seek(pos+header.m_dataSize, librevenge::RVNG_SEEK_SET);
  }

  input->seek(endPos, librevenge::RVNG_SEEK_SET);
  return true;
}

bool ClarisWksTable::readTableBordersId(ClarisWksTableInternal::Table &table)
{
  MWAWInputStreamPtr &input= m_parserState->m_input;
  int numCells = table.numCells();
  auto numBorders = int(table.m_bordersList.size());
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  for (int i = 0; i < 4*numCells; i++) {
    auto *cell = table.get(i/4);
    long pos = input->tell();
    ClarisWksStruct::Struct header;
    if (!header.readHeader(input,true) || header.m_dataSize != 2 || header.m_numData==0) {
      MWAW_DEBUG_MSG(("ClarisWksTable::readTableBordersId: can not read the header\n"));
      return false;
    }
    long endPos = pos+4+header.m_size;
    libmwaw::DebugStream f;
    f << "Entries(TableBordersId)[" << i/4 << "," << i%4 << "]," << header;
    if (header.m_headerSize) {
      ascFile.addDelimiter(input->tell(),'|');
      input->seek(header.m_headerSize, librevenge::RVNG_SEEK_CUR);
    }

    std::vector<int> idsList;
    for (int j = 0; j < header.m_numData; j++) {
      auto id = static_cast<int>(input->readLong(2));
      if (id < 0 || id >= numBorders) {
        input->seek(pos, librevenge::RVNG_SEEK_SET);
        MWAW_DEBUG_MSG(("ClarisWksTable::readTableBordersId: unexpected id\n"));
        return false;
      }
      idsList.push_back(id);
      if (j)
        f << "bordId" << j << "=" << id << ",";
      else
        f << "bordId=" << id << ",";
    }
    if (cell)
      cell->m_bordersId[i%4] = idsList;
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
  }
  return true;
}

bool ClarisWksTable::readTablePointers(ClarisWksTableInternal::Table &table)
{
  MWAWInputStreamPtr &input= m_parserState->m_input;
  long pos = input->tell();
  ClarisWksStruct::Struct header;
  if (!header.readHeader(input,true) || (header.m_size && header.m_dataSize<16)) {
    MWAW_DEBUG_MSG(("ClarisWksTable::readTablePointers: can not read the header\n"));
    return false;
  }
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  if (header.m_size==0) {
    // find this one time as the last entry which ends the table
    ascFile.addPos(pos);
    ascFile.addNote("Nop");
    return true;
  }
  long endPos=pos+4+header.m_size;
  f << "Entries(TablePointers):" << header;
  if (header.m_numData != table.numCells()) {
    MWAW_DEBUG_MSG(("ClarisWksTable::readTablePointers: the number of pointers seems odd\n"));
    f << "###N,";
  }
  if (header.m_headerSize) {
    ascFile.addDelimiter(input->tell(),'|');
    input->seek(header.m_headerSize, librevenge::RVNG_SEEK_CUR);
  }
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  for (int i = 0; i < header.m_numData; i++) {
    pos = input->tell();
    f.str("");
    f << "TablePointers-" << i << ":PTR+[" << std::hex;
    for (int j = 0; j < 4; j++) {
      auto val = long(input->readULong(4));
      if (val > table.m_mainPtr) f << val-table.m_mainPtr << ",";
      else f << "-" << table.m_mainPtr-val << ",";
    }
    f << "]" << std::dec;
    if (long(input->tell()) != pos+header.m_dataSize)
      ascFile.addDelimiter(input->tell(), '|');
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    input->seek(pos+header.m_dataSize, librevenge::RVNG_SEEK_SET);
  }

  input->seek(endPos, librevenge::RVNG_SEEK_SET);
  return true;
}


// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: