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 <sstream>

#include <librevenge/librevenge.h>

#include "MWAWTextListener.hxx"
#include "MWAWDebug.hxx"
#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWGraphicListener.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWPosition.hxx"
#include "MWAWRSRCParser.hxx"
#include "MWAWSection.hxx"

#include "HanMacWrdKParser.hxx"

#include "HanMacWrdKText.hxx"

/** Internal: the structures of a HanMacWrdKText */
namespace HanMacWrdKTextInternal
{
/** Internal: class to store the paragraph properties of a HanMacWrdKText */
struct Paragraph final : public MWAWParagraph {
  //! Constructor
  Paragraph()
    : MWAWParagraph()
    , m_type(0)
    , m_addPageBreak(false)
  {
  }
  //! destructor
  ~Paragraph() final;
  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, Paragraph const &ind)
  {
    switch (ind.m_type) {
    case 0:
      break;
    case 1:
      o << "header,";
      break;
    case 2:
      o << "footer,";
      break;
    case 5:
      o << "footnote,";
      break;
    default:
      o << "#type=" << ind.m_type << ",";
      break;
    }
    o << static_cast<MWAWParagraph const &>(ind) << ",";
    if (ind.m_addPageBreak) o << "pageBreakBef,";
    return o;
  }
  //! the type
  int m_type;
  //! flag to store a force page break
  bool m_addPageBreak;
};

Paragraph::~Paragraph()
{
}

/** Internal: class to store a section of a HanMacWrdKText */
struct Section {
  //! constructor
  Section()
    : m_numCols(1)
    , m_colWidth()
    , m_colSep()
    , m_id(0)
    , m_extra("")
  {
  }
  //! returns a MWAWSection
  MWAWSection getSection() const
  {
    MWAWSection sec;
    if (m_colWidth.size()==0) {
      MWAW_DEBUG_MSG(("HanMacWrdKTextInternal::Section:getSection can not find any width\n"));
      return sec;
    }
    if (m_numCols <= 1)
      return sec;
    bool hasSep = m_colWidth.size()==m_colSep.size();
    sec.m_columns.resize(size_t(m_numCols));
    if (m_colWidth.size()==size_t(m_numCols)) {
      for (size_t c=0; c < size_t(m_numCols); c++) {
        sec.m_columns[c].m_width = double(m_colWidth[c]);
        sec.m_columns[c].m_widthUnit = librevenge::RVNG_POINT;
        if (!hasSep) continue;
        sec.m_columns[c].m_margins[libmwaw::Left]=
          sec.m_columns[c].m_margins[libmwaw::Right]=double(m_colSep[c])/2.0/72.;
      }
    }
    else {
      if (m_colWidth.size()>1) {
        MWAW_DEBUG_MSG(("HanMacWrdKTextInternal::Section:getSection colWidth is not coherent with numCols\n"));
      }
      sec.setColumns(m_numCols, double(m_colWidth[0]), librevenge::RVNG_POINT,
                     hasSep ? double(m_colSep[0])/72.0 : 0);
    }
    return sec;
  }
  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, Section const &sec)
  {
    if (sec.m_numCols!=1)
      o << "numCols=" << sec.m_numCols << ",";
    if (!sec.m_colWidth.empty()) {
      o << "colWidth=[";
      for (size_t i = 0; i < sec.m_colWidth.size(); i++)
        o << sec.m_colWidth[i] << ":" << sec.m_colSep[i] << ",";
      o << "],";
    }
    if (sec.m_id)
      o << "id=" << std::hex << sec.m_id << std::dec << ",";
    o << sec.m_extra;
    return o;
  }
  //! the number of column
  int m_numCols;
  //! the columns width
  std::vector<double> m_colWidth;
  //! the columns separator width
  std::vector<double> m_colSep;
  //! the id
  long m_id;
  //! extra string string
  std::string m_extra;
};

/** Internal: class to store the token properties of a HanMacWrdKText */
struct Token {
  //! Constructor
  Token()
    : m_type(0)
    , m_id(-1)
    , m_extra("")
  {
  }
  //! destructor
  ~Token()
  {
  }
  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, Token const &tkn)
  {
    switch (tkn.m_type) {
    case 0: // main text
      break;
    case 1:
      o << "header,";
      break;
    case 2:
      o << "footer,";
      break;
    case 3:
      o << "footnote[frame],";
      break;
    case 4:
      o << "textbox,";
      break;
    case 6:
      o << "picture,";
      break;
    case 8:
      o << "basicGraphic,";
      break;
    case 9:
      o << "table,";
      break;
    case 10:
      o << "comments,"; // memo
      break;
    case 11:
      o << "group";
      break;
    default:
      o << "#type=" << tkn.m_type << ",";
      break;
    }
    o << "id=" << std::hex << tkn.m_id << std::dec << ",";
    o << tkn.m_extra;
    return o;
  }
  //! the type
  int m_type;
  //! the identificator
  long m_id;
  //! extra data, mainly for debugging
  std::string m_extra;
};

////////////////////////////////////////
//! Internal: the state of a HanMacWrdKText
struct State {
  //! constructor
  State()
    : m_version(-1)
    , m_IdTextMaps()
    , m_IdTypeMaps()
    , m_tokenIdList()
    , m_sectionList()
    , m_numPages(-1)
    , m_actualPage(0)
    , m_headerId(0)
    , m_footerId(0)
  {
  }

  //! the file version
  mutable int m_version;
  //! the map of id -> text zone
  std::multimap<long, std::shared_ptr<HanMacWrdKZone> > m_IdTextMaps;
  //! the zone frame type if known
  std::map<long,int> m_IdTypeMaps;
  //! the token id list
  std::vector<long> m_tokenIdList;
  //! the list of section
  std::map<long, Section> m_sectionList;
  int m_numPages /* the number of pages */, m_actualPage /* the actual page */;
  /** the header text zone id or 0*/
  long m_headerId;
  /** the footer text zone id or 0*/
  long m_footerId;
};

Section getSection(const State &state, const long id)
{
  auto it = state.m_sectionList.find(id);
  if (it != state.m_sectionList.end())
    return it->second;
  MWAW_DEBUG_MSG(("HanMacWrdKTextInternal::getSection: can not find section %ld\n", id));
  return Section();
}

}

////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
HanMacWrdKText::HanMacWrdKText(HanMacWrdKParser &parser)
  : m_parserState(parser.getParserState())
  , m_state(new HanMacWrdKTextInternal::State)
  , m_mainParser(&parser)
{
}

HanMacWrdKText::~HanMacWrdKText()
{
}

int HanMacWrdKText::version() const
{
  if (m_state->m_version < 0)
    m_state->m_version = m_parserState->m_version;
  return m_state->m_version;
}

int HanMacWrdKText::numPages() const
{
  int nPages = 1;
  for (auto tIt : m_state->m_IdTextMaps) {
    if (!tIt.second) continue;
    int pages = const_cast<HanMacWrdKText *>(this)->computeNumPages(*tIt.second);
    if (pages > nPages) nPages=pages;
  }
  m_state->m_numPages = nPages;
  return nPages;
}

void HanMacWrdKText::getHeaderFooterId(long &headerId, long &footerId) const
{
  headerId=m_state->m_headerId;
  footerId=m_state->m_footerId;
}

std::vector<long> const &HanMacWrdKText::getTokenIdList() const
{
  return m_state->m_tokenIdList;
}

void HanMacWrdKText::updateTextZoneTypes(std::map<long,int> const &idTypeMap)
{
  m_state->m_IdTypeMaps=idTypeMap;
  if (m_state->m_headerId)
    m_state->m_IdTypeMaps[m_state->m_headerId]=1;
  if (m_state->m_footerId)
    m_state->m_IdTypeMaps[m_state->m_footerId]=1;
  int numUnkns=0;
  for (auto tIt : m_state->m_IdTextMaps) {
    if (m_state->m_IdTypeMaps.find(tIt.first)!=m_state->m_IdTypeMaps.end())
      continue;
    m_state->m_IdTypeMaps[tIt.first]=0;
    numUnkns++;
  }
  if (numUnkns!=1) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::updateTextZoneTypes: find unexpected number of unknown zone %d\n", numUnkns));
  }
}

////////////////////////////////////////////////////////////
// Intermediate level
////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////
//     Text
////////////////////////////////////////////////////////////
int HanMacWrdKText::computeNumPages(HanMacWrdKZone const &zone) const
{
  if (!zone.valid())
    return 0;
  // first check if this is the main zone
  if (m_state->m_IdTypeMaps.find(zone.m_id)== m_state->m_IdTypeMaps.end() ||
      m_state->m_IdTypeMaps.find(zone.m_id)->second != 0)
    return 1;

  long endPos = zone.end();
  MWAWInputStreamPtr input = zone.m_input;
  long pos = zone.begin();
  input->seek(pos, librevenge::RVNG_SEEK_SET);

  int actPage = 1, actCol = 0, numCol=1, actSection = 1;
  if (!m_state->m_sectionList.empty()) {
    HanMacWrdKTextInternal::Section sec = getSection(*m_state, 0);
    if (sec.m_numCols >= 1 && sec.m_colWidth.size() > 0)
      numCol = sec.m_numCols;
  }

  input->seek(0, librevenge::RVNG_SEEK_SET);
  while (!input->isEnd()) {
    pos = input->tell();
    auto val = long(input->readULong(1));
    if (val != 1 || input->readLong(1) != 0) break;

    auto type = static_cast<int>(input->readLong(2));
    bool done=false;
    switch (type) {
    case 1: {
      MWAWFont font(-1,-1);
      done=readFont(zone,font);
      break;
    }
    case 2: { // ruler
      HanMacWrdKTextInternal::Paragraph para;
      if (!readParagraph(zone,para))
        break;
      if (para.m_addPageBreak) {
        ++actPage;
        actCol = 0;
      }
      done=true;
      break;
    }
    case 3: { // footnote, attachment
      HanMacWrdKTextInternal::Token token;
      done=readToken(zone,token);
      break;
    }
    case 4: {
      actCol = 0;
      actPage++;
      auto const &sec = getSection(*m_state, actSection++);
      numCol = sec.m_numCols >= 1 ? sec.m_numCols : 1;
      break;
    }
    default:
      break;
    }

    if (!done) {
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
      auto sz = long(input->readULong(2));
      if (pos+6+sz>endPos)
        break;
      input->seek(pos+6+sz, librevenge::RVNG_SEEK_SET);
    }
    bool ok=true;
    while (!input->isEnd()) {
      auto c=static_cast<int>(input->readLong(2));
      if (c==0x100) {
        input->seek(-2, librevenge::RVNG_SEEK_CUR);
        break;
      }
      if (c==0 && input->isEnd())
        break;
      if (c==0) {
        ok = false;
        break;
      }
      if (c==2) {
        if (actCol < numCol-1 && numCol > 1)
          actCol++;
        else {
          actCol = 0;
          actPage++;
        }
      }
      else if (c==3)
        actPage++;
    }
    if (!ok)
      break;
  }
  return actPage;
}

bool HanMacWrdKText::readTextZone(std::shared_ptr<HanMacWrdKZone> zone)
{
  if (!zone || !zone->valid()) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readTextZone: called without any zone\n"));
    return false;
  }
  m_state->m_IdTextMaps.insert
  (std::multimap<long, std::shared_ptr<HanMacWrdKZone> >::value_type(zone->m_id, zone));
  long endPos = zone->end();
  MWAWInputStreamPtr input = zone->m_input;
  input->seek(zone->begin(), librevenge::RVNG_SEEK_SET);

  while (!input->isEnd()) {
    long pos = input->tell();
    if (pos >= endPos)
      break;
    auto val = long(input->readULong(1));
    if (val == 0 && input->isEnd()) break;
    if (val != 1 || input->readLong(1) != 0)
      break;
    auto type = static_cast<int>(input->readLong(2));
    bool done=false;
    switch (type) {
    case 2: { // ruler
      HanMacWrdKTextInternal::Paragraph para;
      done=readParagraph(*zone,para);
      break;
    }
    case 3: { // token
      HanMacWrdKTextInternal::Token token;
      done=readToken(*zone,token);
      if (done)
        m_state->m_tokenIdList.push_back(token.m_id);
      break;
    }
    default:
      break;
    }

    if (!done) {
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
      auto sz = long(input->readULong(2));
      if (pos+6+sz > endPos)
        break;
      input->seek(pos+6+sz, librevenge::RVNG_SEEK_SET);
    }

    bool ok=true;
    while (!input->isEnd()) {
      auto c=static_cast<int>(input->readLong(2));
      if (c==0x100) {
        input->seek(-2, librevenge::RVNG_SEEK_CUR);
        break;
      }
      if (c==0 && input->isEnd())
        break;
      if (c==0) {
        ok = false;
        break;
      }
    }
    if (!ok)
      break;
  }
  return true;
}

bool HanMacWrdKText::canSendTextAsGraphic(long id, long subId)
{
  auto tIt=m_state->m_IdTextMaps.lower_bound(id);
  if (tIt == m_state->m_IdTextMaps.end() || tIt->first != id)
    return false;
  while (tIt != m_state->m_IdTextMaps.end() && tIt->first == id) {
    auto zone = (tIt++)->second;
    if (!zone || zone->m_subId != subId) continue;
    return canSendTextAsGraphic(*zone);
  }
  return false;
}

bool HanMacWrdKText::sendText(long id, long subId, MWAWListenerPtr listener)
{
  auto tIt=m_state->m_IdTextMaps.lower_bound(id);
  if (tIt == m_state->m_IdTextMaps.end() || tIt->first != id) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::sendText: can not find the text zone\n"));
    return false;
  }
  while (tIt != m_state->m_IdTextMaps.end() && tIt->first == id) {
    auto zone = (tIt++)->second;
    if (!zone || zone->m_subId != subId) continue;
    sendText(*zone, listener);
    return true;
  }
  MWAW_DEBUG_MSG(("HanMacWrdKText::sendText: can not find the text zone\n"));
  return false;
}

bool HanMacWrdKText::sendMainText()
{
  for (auto tIt : m_state->m_IdTypeMaps) {
    if (tIt.second != 0)
      continue;
    sendText(tIt.first, 0);
    return true;
  }
  MWAW_DEBUG_MSG(("HanMacWrdKText::sendMainText: can not find the main zone\n"));
  return false;
}

bool HanMacWrdKText::canSendTextAsGraphic(HanMacWrdKZone &zone)
{
  if (!zone.valid()) return false;

  long endPos = zone.end();
  MWAWInputStreamPtr input = zone.m_input;
  long pos = zone.begin();
  input->seek(pos, librevenge::RVNG_SEEK_SET);
  if (m_state->m_IdTypeMaps.find(zone.m_id)!= m_state->m_IdTypeMaps.end()
      && m_state->m_IdTypeMaps.find(zone.m_id)->second == 0) // isMain
    return false;
  MWAWFont font;
  while (!input->isEnd()) {
    pos = input->tell();
    if (pos>=endPos)
      break;
    auto val = long(input->readULong(1));
    if (val == 0 && input->isEnd()) break;
    if (val != 1 || input->readLong(1) != 0)
      return false;
    auto type = static_cast<int>(input->readLong(2));
    bool done=false;
    switch (type) {
    case 1:
      if (!readFont(zone,font))
        return false;
      done = true;
      break;
    case 2: { // ruler
      HanMacWrdKTextInternal::Paragraph para;
      if (!readParagraph(zone,para))
        return false;
      done = true;
      break;
    }
    case 3: // footnote, attachment
    case 4: // section
      return false;
    default:
      break;
    }

    if (!done) {
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
      auto sz = long(input->readULong(2));
      if (pos+6+sz > endPos) return false;
    }

    while (!input->isEnd()) {
      auto c=static_cast<int>(input->readULong(2));
      if (c==0x100) {
        input->seek(-2, librevenge::RVNG_SEEK_CUR);
        break;
      }
      if (c==0 && input->isEnd())
        break;
      if (c==0) return false;
    }
  }
  return true;
}

bool HanMacWrdKText::sendText(HanMacWrdKZone &zone, MWAWListenerPtr listener)
{
  if (!zone.valid()) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::sendText: called without any zone\n"));
    return false;
  }
  if (!listener)
    listener=m_parserState->m_textListener;
  if (!listener) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::sendText: can not find a listener\n"));
    return false;
  }

  long endPos = zone.end();
  MWAWInputStreamPtr input = zone.m_input;
  libmwaw::DebugFile &asciiFile = zone.ascii();
  libmwaw::DebugStream f;
  zone.m_parsed = true;

  long pos = zone.begin();
  input->seek(pos, librevenge::RVNG_SEEK_SET);
  f << "PTR=" << std::hex << zone.fileBeginPos() << std::dec << ",";
  asciiFile.addPos(pos);
  asciiFile.addNote(f.str().c_str());

  bool isMain = false;
  if (m_state->m_IdTypeMaps.find(zone.m_id)!= m_state->m_IdTypeMaps.end())
    isMain = m_state->m_IdTypeMaps.find(zone.m_id)->second == 0;
  int actPage = 1, actCol = 0, numCol=1, actSection = 1;
  auto width = float(72.0*m_mainParser->getPageWidth());

  if (isMain)
    m_mainParser->newPage(1);
  if (isMain && !m_state->m_sectionList.size()) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::sendText: can not find section 0\n"));
  }
  else if (isMain) {
    auto sec = getSection(*m_state, 0);
    if (sec.m_numCols >= 1 && sec.m_colWidth.size() > 0) {
      if (listener->isSectionOpened())
        listener->closeSection();
      listener->openSection(sec.getSection());
      numCol = listener->getSection().numColumns();
    }
  }

  while (!input->isEnd()) {
    pos = input->tell();
    if (pos>=endPos)
      break;
    f.str("");
    f << zone.name()<< ":";
    auto val = long(input->readULong(1));
    if (val == 0 && input->isEnd()) break;
    if (val != 1 || input->readLong(1) != 0) {
      f << "###";
      asciiFile.addPos(pos);
      asciiFile.addNote(f.str().c_str());
      break;
    }
    auto type = static_cast<int>(input->readLong(2));
    bool done=false;
    switch (type) {
    case 1: {
      f << "font,";
      MWAWFont font(-1,-1);
      if (!readFont(zone,font))
        break;
      done = true;
      listener->setFont(font);
      break;
    }
    case 2: { // ruler
      f << "ruler,";
      HanMacWrdKTextInternal::Paragraph para;
      if (!readParagraph(zone,para))
        break;
      if (para.m_addPageBreak) {
        m_mainParser->newPage(++actPage);
        actCol = 0;
      }
      setProperty(para, width);
      done=true;
      break;
    }
    case 3: { // footnote, attachment
      f << "token,";
      HanMacWrdKTextInternal::Token token;
      done=readToken(zone,token);
      if (!done) break;
      switch (token.m_type) {
      case 3:
      case 4:
      case 6:
      case 8:
      case 9:
      case 10:
      case 11:
        m_mainParser->sendZone(token.m_id);
        break;
      default:
        MWAW_DEBUG_MSG(("HanMacWrdKText::sendText: do not send how to send token with type: %d\n", token.m_type));
        break;
      }
      break;
    }
    case 4:
      f << "section[new],";
      if (!isMain) {
        MWAW_DEBUG_MSG(("HanMacWrdKText::sendText: find section in auxilliary block\n"));
        break;
      }
      {
        auto sec = getSection(*m_state, actSection++);
        actCol = 0;
        if (listener->isSectionOpened())
          listener->closeSection();
        m_mainParser->newPage(++actPage);
        listener->openSection(sec.getSection());
        numCol = listener->getSection().numColumns();
      }
      break;
    case 6: // follow by id?
      f << "toc,";
      break;
    case 7:
      f << "bookmark,";
      break;
    case 9: // follow by id?
      f << "rubi,";
      break;
    case 11: // follow by id?
      f << "endToc,";
      break;
    case 12: // follow by id?
      f << "endBookmark,";
      break;
    case 14:
      f << "endRubi,";
      break;
    default:
      break;
    }

    bool ok=true;
    if (!done) {
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
      auto sz = long(input->readULong(2));
      ok = pos+6+sz <= endPos;
      if (!ok)
        f << "###";
      else
        input->seek(pos+6+sz, librevenge::RVNG_SEEK_SET);

      asciiFile.addPos(pos);
      asciiFile.addNote(f.str().c_str());
    }
    if (!ok) {
      MWAW_DEBUG_MSG(("HanMacWrdKText::sendText: can not read a zone\n"));
      break;
    }

    pos = input->tell();
    f.str("");
    f << zone.name()<< ":text,";
    while (!input->isEnd()) {
      auto c=static_cast<int>(input->readULong(2));
      if (c==0x100) {
        input->seek(-2, librevenge::RVNG_SEEK_CUR);
        break;
      }
      if (c==0 && input->isEnd())
        break;
      if (c==0) {
        ok = false;
        f << "###";
        break;
      }
      switch (c) {
      case 0x1000:
        f << "[pgNum]";
        listener->insertField(MWAWField(MWAWField::PageNumber));
        break;
      case 0x1001:
        f << "[pgCount]";
        listener->insertField(MWAWField(MWAWField::PageCount));
        break;
      case 0x1002: {
        f << "[date]";
        MWAWField field(MWAWField::Date);
        field.m_DTFormat="%A, %b %d, %Y";
        listener->insertField(field);
        break;
      }
      case 0x1003: {
        f << "[time]";
        MWAWField field(MWAWField::Time);
        field.m_DTFormat="%I:%M %p";
        listener->insertField(field);
        break;
      }
      case 0x1004:
        f << "[title]";
        listener->insertField(MWAWField(MWAWField::Title));
        break;
      case 0x1005: {
        std::stringstream s;
        f << "[section]";
        s << actSection;
        listener->insertUnicodeString(librevenge::RVNGString(s.str().c_str()));
        break;
      }
      case 2:
        f << "[colBreak]";
        if (!isMain) {
          MWAW_DEBUG_MSG(("HanMacWrdKText::sendText: find column break in auxilliary block\n"));
          break;
        }
        if (actCol < numCol-1 && numCol > 1) {
          listener->insertBreak(MWAWTextListener::ColumnBreak);
          actCol++;
        }
        else {
          actCol = 0;
          m_mainParser->newPage(++actPage);
        }
        break;
      case 3:
        f << "[pageBreak]";
        if (isMain) {
          m_mainParser->newPage(++actPage);
          actCol = 0;
        }
        break;
      case 9:
        f << char(c);
        listener->insertTab();
        break;
      case 0xd:
        f << char(c);
        listener->insertEOL();
        break;
      default: {
        if (c <= 0x1f || c >= 0x100) {
          f << "###" << std::hex << c << std::dec;
          MWAW_DEBUG_MSG(("HanMacWrdKText::sendText: find a odd char %x\n", static_cast<unsigned int>(c)));
          break;
        }
        f << char(c);
        listener->insertCharacter(static_cast<unsigned char>(c), input);
        break;
      }
      }
    }
    if (input->tell() != pos) {
      asciiFile.addPos(pos);
      asciiFile.addNote(f.str().c_str());
    }
    if (!ok) {
      MWAW_DEBUG_MSG(("HanMacWrdKText::sendText: can not read a text zone\n"));
      break;
    }
  }
  // FIXME: remove this when the frame/textbox are sent normally
  listener->insertEOL();
  return true;
}

////////////////////////////////////////////////////////////
//     Fonts
////////////////////////////////////////////////////////////

// a font in the text zone
bool HanMacWrdKText::readFont(HanMacWrdKZone const &zone, MWAWFont &font) const
{
  font = MWAWFont(-1,-1);

  MWAWInputStreamPtr input = zone.m_input;
  long pos = input->tell();
  if (pos+30 > zone.length()) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readFont: the zone is too short\n"));
    return false;
  }

  libmwaw::DebugStream f;
  auto val = static_cast<int>(input->readLong(2));
  if (val!=28) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readFont: the data size seems bad\n"));
    f << "##dataSz=" << val << ",";
  }
  font.setId(static_cast<int>(input->readLong(2)));
  val = static_cast<int>(input->readLong(2));
  if (val) f << "#f1=" << val << ",";
  font.setSize(float(input->readLong(4))/65536.f);
  float expand = float(input->readLong(4))/65536.f;
  if (expand < 0 || expand > 0)
    font.setDeltaLetterSpacing(expand*font.size());
  float xScale = float(input->readLong(4))/65536.f;
  if (xScale < 1 || xScale > 1)
    font.setWidthStreching(xScale);

  auto flag =static_cast<int>(input->readULong(2));
  uint32_t flags=0;
  if (flag&1) {
    font.setUnderlineStyle(MWAWFont::Line::Simple);
    font.setUnderlineType(MWAWFont::Line::Double);
  }
  if (flag&2)
    font.setUnderlineStyle(MWAWFont::Line::Dot);
  if (flag&4) {
    font.setUnderlineStyle(MWAWFont::Line::Dot);
    font.setUnderlineWidth(2.0);
  }
  if (flag&8)
    font.setUnderlineStyle(MWAWFont::Line::Dash);
  if (flag&0x10)
    font.setStrikeOutStyle(MWAWFont::Line::Simple);
  if (flag&0x20) {
    font.setStrikeOutStyle(MWAWFont::Line::Simple);
    font.setStrikeOutType(MWAWFont::Line::Double);
  }
  if (flag&0xFFC0)
    f << "#flag0=" << std::hex << (flag&0xFFF2) << std::dec << ",";
  flag =static_cast<int>(input->readULong(2));
  if (flag&1) flags |= MWAWFont::boldBit;
  if (flag&0x2) flags |= MWAWFont::italicBit;
  if (flag&0x4) flags |= MWAWFont::outlineBit;
  if (flag&0x8) flags |= MWAWFont::shadowBit;
  if (flag&0x10) flags |= MWAWFont::reverseVideoBit;
  if (flag&0x20) font.set(MWAWFont::Script::super100());
  if (flag&0x40) font.set(MWAWFont::Script::sub100());
  if (flag&0x80) {
    if (flag&0x20)
      font.set(MWAWFont::Script(48,librevenge::RVNG_PERCENT,58));
    else if (flag&0x40)
      font.set(MWAWFont::Script(16,librevenge::RVNG_PERCENT,58));
    else
      font.set(MWAWFont::Script::super());
  }
  if (flag&0x100) {
    font.setOverlineStyle(MWAWFont::Line::Dot);
    font.setOverlineWidth(2.0);
  }
  if (flag&0x200) flags |= MWAWFont::boxedBit;
  if (flag&0x400) flags |= MWAWFont::boxedRoundedBit;
  if (flag&0x800) {
    font.setUnderlineStyle(MWAWFont::Line::Simple);
    font.setUnderlineWidth(0.5);
  }
  if (flag&0x1000) font.setUnderlineStyle(MWAWFont::Line::Simple);
  if (flag&0x2000) {
    font.setUnderlineStyle(MWAWFont::Line::Simple);
    font.setUnderlineWidth(2.0);
  }
  if (flag&0x4000) {
    font.setUnderlineStyle(MWAWFont::Line::Simple);
    font.setUnderlineWidth(3.0);
  }
  if (flag&0x8000) {
    font.setUnderlineStyle(MWAWFont::Line::Simple);
    font.setUnderlineType(MWAWFont::Line::Double);
    font.setUnderlineWidth(0.5);
  }
  auto color = static_cast<int>(input->readLong(2));
  MWAWColor col;
  if (color && m_mainParser->getColor(color, 1, col))
    font.setColor(col);
  else if (color)
    f << "##fColor=" << color << ",";
  val = static_cast<int>(input->readLong(2));
  if (val) f << "#unk=" << val << ",";
  color = static_cast<int>(input->readLong(2));
  auto pattern = static_cast<int>(input->readLong(2));
  if ((color || pattern) && m_mainParser->getColor(color, pattern, col))
    font.setBackgroundColor(col);
  else if (color || pattern)
    f << "#backColor=" << color << ", #pattern=" << pattern << ",";
  font.setFlags(flags);
  font.m_extra = f.str();

  static bool first=true;
  f.str("");
  if (first) {
    f << "Entries(FontDef):";
    first = false;
  }
  else
    f << "FontDef:";
  f << font.getDebugString(m_parserState->m_fontConverter) << ",";

  zone.ascii().addPos(pos-4);
  zone.ascii().addNote(f.str().c_str());

  input->seek(pos+30, librevenge::RVNG_SEEK_SET);
  return true;
}

// the list of fonts
bool HanMacWrdKText::readFontNames(std::shared_ptr<HanMacWrdKZone> zone)
{
  if (!zone) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readFontNames: called without any zone\n"));
    return false;
  }
  long dataSz = zone->length();
  if (dataSz < 2) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readFontNames: the zone seems too short\n"));
    return false;
  }
  MWAWInputStreamPtr input = zone->m_input;
  libmwaw::DebugFile &asciiFile = zone->ascii();
  libmwaw::DebugStream f;
  zone->m_parsed = true;

  f << zone->name() << ":PTR=" << std::hex << zone->fileBeginPos() << std::dec << ",";

  long pos = zone->begin();
  input->seek(pos, librevenge::RVNG_SEEK_SET);
  auto N = static_cast<int>(input->readLong(2));
  f << "N=" << N << ",";
  long expectedSz = N*68+2;
  if (expectedSz != dataSz && expectedSz+1 != dataSz) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readFontNames: the zone size seems odd\n"));
    return false;
  }
  asciiFile.addPos(pos);
  asciiFile.addNote(f.str().c_str());

  for (int i = 0; i < N; i++) {
    pos = input->tell();
    f.str("");
    f << zone->name() << "-" << i << ":";
    auto fId = static_cast<int>(input->readLong(2));
    f << "fId=" << fId << ",";
    auto val = static_cast<int>(input->readLong(2));
    if (val != fId)
      f << "#fId2=" << val << ",";
    auto fSz = static_cast<int>(input->readULong(1));
    if (fSz+5 > 68) {
      f << "###fSz";
      MWAW_DEBUG_MSG(("HanMacWrdKText::readFontNames: can not read a font\n"));
    }
    else {
      std::string name("");
      for (int c = 0; c < fSz; c++)
        name += char(input->readULong(1));
      f << name;
      m_parserState->m_fontConverter->setCorrespondance(fId, name);
    }
    asciiFile.addPos(pos);
    asciiFile.addNote(f.str().c_str());
    input->seek(pos+68, librevenge::RVNG_SEEK_SET);
  }
  return true;
}

////////////////////////////////////////////////////////////
//     Style
////////////////////////////////////////////////////////////
bool HanMacWrdKText::readStyles(std::shared_ptr<HanMacWrdKZone> zone)
{
  if (!zone) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readStyles: called without any zone\n"));
    return false;
  }

  long dataSz = zone->length();
  if (dataSz < 24) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readStyles: the zone seems too short\n"));
    return false;
  }
  MWAWInputStreamPtr input = zone->m_input;
  libmwaw::DebugFile &asciiFile = zone->ascii();
  libmwaw::DebugStream f;
  zone->m_parsed = true;

  f << zone->name() << ":PTR=" << std::hex << zone->fileBeginPos() << std::dec << ",";

  long pos = zone->begin();
  input->seek(pos, librevenge::RVNG_SEEK_SET);
  long fieldSz = 636;
  auto N = static_cast<int>(input->readULong(2));
  f << "N=" << N << ",";
  long expectedSz = N*fieldSz+2;
  if (expectedSz != dataSz && expectedSz+1 != dataSz) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readStyles: the zone size seems odd\n"));
    return false;
  }
  asciiFile.addPos(pos);
  asciiFile.addNote(f.str().c_str());

  for (int i = 0; i < N; i++) {
    f.str("");
    f << zone->name() << "-" << i << ":";
    pos = input->tell();
    auto val = static_cast<int>(input->readULong(2));
    if (val != i) f << "#id=" << val << ",";

    // f0=c2|c6, f2=0|44, f3=1|14|15|16: fontId?
    for (int j=0; j < 4; j++) {
      val = static_cast<int>(input->readULong(1));
      if (val)
        f << "f" << j << "=" << std::hex << val << std::dec << ",";
    }
    /* g1=9|a|c|e|12|18: size ?, g5=1, g8=0|1, g25=0|18|30|48, g31=1, g35=0|1 */
    for (int j=0; j < 37; j++) {
      val = static_cast<int>(input->readULong(2));
      if (val)
        f << "g" << j << "=" << val  << ",";
    }
    for (int j=0; j < 4; j++) { // b,b,b,0
      val = static_cast<int>(input->readULong(1));
      if ((j < 3 && val != 0xb) || (j==3 && val))
        f << "h" << j << "=" << val  << ",";
    }

    for (int j=0; j < 17; j++) { // always 0
      val = static_cast<int>(input->readULong(2));
      if (val)
        f << "l" << j << "=" << val  << ",";
    }
    asciiFile.addPos(pos);
    asciiFile.addNote(f.str().c_str());
    long pos2 = input->tell();
    f.str("");
    f << "Style-" << i << "[B]:";
    // checkme probably f15=numTabs  ..
    for (int j = 0; j < 50; j++) {
      val = static_cast<int>(input->readULong(2));
      if ((j < 5 && val != 1) || (j >= 5 && val))
        f << "f" << j << "=" << val  << ",";
    }
    for (int j = 0; j < 50; j++) {
      val = static_cast<int>(input->readULong(2));
      if (val)
        f << "g" << j << "=" << val  << ",";
    }
    for (int j = 0; j < 100; j++) {
      val = static_cast<int>(input->readULong(2));
      if (val)
        f << "h" << j << "=" << val  << ",";
    }
    for (int j = 0; j < 41; j++) {
      val = static_cast<int>(input->readULong(2));
      if (val)
        f << "l" << j << "=" << val  << ",";
    }
    asciiFile.addPos(pos2);
    asciiFile.addNote(f.str().c_str());

    pos2 = input->tell();
    f.str("");
    f << "Style-" << i << "[C]:";
    val = static_cast<int>(input->readLong(2));
    if (val != -1) f << "unkn=" << val << ",";
    val  = static_cast<int>(input->readLong(2));
    if (val != i) f << "#id" << val << ",";
    auto fSz = static_cast<int>(input->readULong(1));
    if (input->tell()+fSz > pos+fieldSz) {
      MWAW_DEBUG_MSG(("HanMacWrdKText::readStyles: can not read styleName\n"));
      f << "###";
    }
    else {
      std::string name("");
      for (int j = 0; j < fSz; j++)
        name +=char(input->readULong(1));
      f << name;
    }
    asciiFile.addPos(pos2);
    asciiFile.addNote(f.str().c_str());
    if (input->tell() != pos+fieldSz)
      asciiFile.addDelimiter(input->tell(),'|');

    input->seek(pos+fieldSz, librevenge::RVNG_SEEK_SET);
  }

  if (!input->isEnd()) {
    asciiFile.addPos(input->tell());
    asciiFile.addNote("_");
  }
  return true;
}

////////////////////////////////////////////////////////////
//     Paragraph
////////////////////////////////////////////////////////////
void HanMacWrdKText::setProperty(HanMacWrdKTextInternal::Paragraph const &para, float)
{
  if (!m_parserState->m_textListener) return;
  m_parserState->m_textListener->setParagraph(para);
}

bool HanMacWrdKText::readParagraph(HanMacWrdKZone const &zone, HanMacWrdKTextInternal::Paragraph &para) const
{
  para = HanMacWrdKTextInternal::Paragraph();

  MWAWInputStreamPtr input = zone.m_input;
  long pos = input->tell();
  long dataSz = zone.length();
  if (pos+102 > dataSz) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readParagraph: the zone is too short\n"));
    return false;
  }

  libmwaw::DebugStream f;
  libmwaw::DebugFile &asciiFile = zone.ascii();
  auto val = static_cast<int>(input->readLong(2));
  if (val!=100) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readParagraph: the data size seems bad\n"));
    f << "##dataSz=" << val << ",";
  }
  auto flags = static_cast<int>(input->readULong(1));
  if (flags&0x80)
    para.m_breakStatus = para.m_breakStatus.get()|MWAWParagraph::NoBreakWithNextBit;
  if (flags&0x40)
    para.m_breakStatus = para.m_breakStatus.get()|MWAWParagraph::NoBreakBit;
  if (flags&0x2)
    para.m_addPageBreak = true;
  if (flags&0x4)
    f << "linebreakByWord,";

  if (flags & 0x39) f << "#fl=" << std::hex << (flags&0x39) << std::dec << ",";

  val = static_cast<int>(input->readLong(2));
  if (val) f << "#f0=" << val << ",";
  val = static_cast<int>(input->readULong(2));
  switch (val&3) {
  case 0:
    para.m_justify = MWAWParagraph::JustificationLeft;
    break;
  case 1:
    para.m_justify = MWAWParagraph::JustificationRight;
    break;
  case 2:
    para.m_justify = MWAWParagraph::JustificationCenter;
    break;
  case 3:
    para.m_justify = MWAWParagraph::JustificationFull;
    break;
  default:
    break;
  }
  if (val&0xFFFC) f << "#f1=" << val << ",";
  val = static_cast<int>(input->readLong(1));
  if (val) f << "#f2=" << val << ",";
  para.m_type = static_cast<int>(input->readLong(2));

  float dim[3];
  for (auto &d : dim) d = float(input->readLong(4))/65536.0f;
  para.m_marginsUnit = librevenge::RVNG_POINT;
  para.m_margins[0]=double(dim[1]);
  para.m_margins[1]=double(dim[0]);
  para.m_margins[2]=double(dim[2]); // ie. distance to right border - ...

  for (auto &spacing : para.m_spacings)
    spacing = double(input->readLong(4))/65536.0;
  int spacingsUnit[3]; // 1=mm, ..., 4=pt, b=line
  for (auto &unit : spacingsUnit) unit = static_cast<int>(input->readULong(1));
  if (spacingsUnit[0]==0xb)
    para.m_spacingsInterlineUnit = librevenge::RVNG_PERCENT;
  else
    para.m_spacingsInterlineUnit = librevenge::RVNG_POINT;
  for (int i = 1; i < 3; i++) // convert point|line -> inches
    para.m_spacings[i]= ((spacingsUnit[i]==0xb) ? 12.0 : 1.0)*(para.m_spacings[i].get())/72.0;

  val = static_cast<int>(input->readLong(1));
  if (val) f << "#f3=" << val << ",";
  for (int i = 0;  i< 2; i++) { // one time f4=8000
    val = static_cast<int>(input->readULong(2));
    if (val) f << "#f" << i+4 << "=" << std::hex << val << std::dec << ",";
  }
  // the borders
  char const *wh[5] = { "T", "L", "B", "R", "VSep" };
  MWAWBorder borders[5];
  for (auto &border : borders)
    border.m_width = double(input->readLong(4))/65536.;
  for (int d=0; d < 5; d++) {
    val = static_cast<int>(input->readULong(1));
    switch (val) {
    case 0: // normal
      break;
    case 1:
      borders[d].m_type = MWAWBorder::Double;
      break;
    case 2:
      borders[d].m_type = MWAWBorder::Double;
      f << "bord" << wh[d] << "[ext=2],";
      break;
    case 3:
      borders[d].m_type = MWAWBorder::Double;
      f << "bord" << wh[d] << "[int=2],";
      break;
    default:
      f << "#bord" << wh[d] << "[style=" << val << "],";
      break;
    }
  }
  int color[5], pattern[5];
  for (auto &c : color) c = static_cast<int>(input->readULong(1));
  for (auto &p : pattern) p = static_cast<int>(input->readULong(2));
  for (int d=0; d < 5; d++) {
    if (!pattern[d]) {
      borders[d].m_style=MWAWBorder::None;
      continue;
    }
    if (!color[d] && !pattern[d])
      continue;
    MWAWColor col;
    if (m_mainParser->getColor(color[d], pattern[d], col))
      borders[d].m_color = col;
    else
      f << "#bord" << wh[d] << "[col=" << color[d] << ",pat=" << pattern[d] << "],";
  }
  // update the paragraph
  para.resizeBorders(6);
  libmwaw::Position const which[5] = {
    libmwaw::Top, libmwaw::Left, libmwaw::Bottom, libmwaw::Right,
    libmwaw::VMiddle
  };
  for (int d=0; d < 5; d++) {
    if (borders[d].m_width <= 0)
      continue;
    para.m_borders[which[d]]=borders[d];
  }
  val = static_cast<int>(input->readLong(1));
  if (val) f << "#f6=" << val << ",";
  double bMargins[4]= {0,0,0,0};
  for (int d = 0; d < 4; d++) {
    bMargins[d] =  double(input->readLong(4))/256./65536./72.;
    if (bMargins[d] > 0 || bMargins[d] < 0)
      f << "bordMarg" << wh[d] << "=" << bMargins[d] << ",";
  }
  auto nTabs = static_cast<int>(input->readULong(1));
  if (pos+102+nTabs*12 > dataSz) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readParagraph: can not read numbers of tab\n"));
    return false;
  }
  val = static_cast<int>(input->readULong(2)); // always 0
  if (val) f << "#h0=" << val << ",";
  para.m_extra=f.str();
  static bool first=true;
  f.str("");
  if (first) {
    f << "Entries(Ruler):";
    first = false;
  }
  else
    f << "Ruler:";
  f << para;

  asciiFile.addPos(pos-4);
  asciiFile.addNote(f.str().c_str());
  for (int i = 0; i < nTabs; i++) {
    pos = input->tell();
    f.str("");
    f << "Ruler[Tabs-" << i << "]:";

    MWAWTabStop tab;
    val = static_cast<int>(input->readULong(1));
    switch (val) {
    case 0:
      break;
    case 1:
      tab.m_alignment = MWAWTabStop::CENTER;
      break;
    case 2:
      tab.m_alignment = MWAWTabStop::RIGHT;
      break;
    case 3:
      tab.m_alignment = MWAWTabStop::DECIMAL;
      break;
    case 4:
      tab.m_alignment = MWAWTabStop::BAR;
      break;
    default:
      f << "#type=" << val << ",";
      break;
    }
    val = static_cast<int>(input->readULong(1));
    if (val) f << "barType=" << val << ",";
    val = static_cast<int>(input->readULong(2));
    if (val) {
      int unicode= m_parserState->m_fontConverter->unicode(3, static_cast<unsigned char>(val));
      if (unicode==-1)
        tab.m_decimalCharacter = uint16_t(val);
      else
        tab.m_decimalCharacter = uint16_t(unicode);
    }
    val = static_cast<int>(input->readULong(2));
    if (val) {
      int unicode= m_parserState->m_fontConverter->unicode(3, static_cast<unsigned char>(val));
      if (unicode==-1)
        tab.m_leaderCharacter = uint16_t(val);
      else
        tab.m_leaderCharacter = uint16_t(unicode);
    }
    val = static_cast<int>(input->readULong(2)); // 0|73|74|a044|f170|f1e0|f590
    if (val) f << "f0=" << std::hex << val << std::dec << ",";

    tab.m_position = double(input->readLong(4))/65536./72.;
    para.m_tabs->push_back(tab);
    f << tab;

    asciiFile.addPos(pos);
    asciiFile.addNote(f.str().c_str());
    input->seek(pos+12, librevenge::RVNG_SEEK_SET);
  }
  return true;
}

////////////////////////////////////////////////////////////
//     the token
////////////////////////////////////////////////////////////
bool HanMacWrdKText::readToken(HanMacWrdKZone const &zone, HanMacWrdKTextInternal::Token &token) const
{
  token = HanMacWrdKTextInternal::Token();

  MWAWInputStreamPtr input = zone.m_input;
  long pos = input->tell();
  long dataSz = zone.length();
  if (pos+10 > dataSz) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readToken: the zone is too short\n"));
    return false;
  }

  libmwaw::DebugStream f;
  libmwaw::DebugFile &asciiFile = zone.ascii();
  auto val = static_cast<int>(input->readLong(2));
  if (val!=8) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readToken: the data size seems bad\n"));
    f << "##dataSz=" << val << ",";
  }

  token.m_type = static_cast<int>(input->readLong(1));
  val = static_cast<int>(input->readLong(1));
  if (val) f << "f0=" << val << ",";
  val = static_cast<int>(input->readLong(2));
  if (val) f << "f1=" << val << ",";
  token.m_id = long(input->readULong(4));
  token.m_extra = f.str();
  f.str("");
  static bool first=true;
  f.str("");
  if (first) {
    f << "Entries(Token):";
    first = false;
  }
  else
    f << "Token:";
  f << token;
  asciiFile.addPos(pos-4);
  asciiFile.addNote(f.str().c_str());
  return true;
}

////////////////////////////////////////////////////////////
//     the sections
////////////////////////////////////////////////////////////
bool HanMacWrdKText::readSections(std::shared_ptr<HanMacWrdKZone> zone)
{
  if (!zone) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readSections: called without any zone\n"));
    return false;
  }
  long dataSz = zone->length();
  if (dataSz < 160) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readSections: the zone seems too short\n"));
    return false;
  }

  MWAWInputStreamPtr input = zone->m_input;
  libmwaw::DebugFile &asciiFile = zone->ascii();
  libmwaw::DebugStream f;
  zone->m_parsed = true;

  HanMacWrdKTextInternal::Section sec;
  long pos=0;
  input->seek(pos, librevenge::RVNG_SEEK_SET);
  long val = input->readLong(2);
  if (val != 1) // always 1
    f << "f0=" << val << ",";
  auto numColumns = static_cast<int>(input->readLong(2));
  if (numColumns <= 0 || numColumns > 8) {
    MWAW_DEBUG_MSG(("HanMacWrdKText::readSections: numColumns seems bad\n"));
    f << "###nCols=" << numColumns << ",";
    numColumns = 1;
  }
  else
    sec.m_numCols=numColumns;
  val = long(input->readLong(1));
  bool diffWidth=val==0;
  if (val==1)
    f << "sameWidth,";
  else if (val)
    f << "#width=" << val << ",";
  val = long(input->readLong(1)); // always 0
  if (val) f << "f1=" << val << ",";
  for (int i = 0; i < 19; i++) { // always 0
    val = long(input->readLong(2));
    if (val) f << "g" << i << "=" << val << ",";
  }

  if (diffWidth) {
    for (int i = 0; i < numColumns; i++) {
      sec.m_colWidth.push_back(double(input->readLong(4))/65536);
      sec.m_colSep.push_back(double(input->readLong(4))/65536);
    }
  }
  else {
    sec.m_colWidth.push_back(double(input->readLong(4))/65536);
    sec.m_colSep.push_back(double(input->readLong(4))/65536);
  }
  sec.m_extra = f.str();

  f.str("");
  f << zone->name() << "(A):PTR=" << std::hex << zone->fileBeginPos() << std::dec << "," << sec;

  asciiFile.addDelimiter(input->tell(),'|');
  asciiFile.addPos(pos);
  asciiFile.addNote(f.str().c_str());
  input->seek(pos+0x6c, librevenge::RVNG_SEEK_SET);

  pos = input->tell();
  f.str("");
  f << zone->name() << "(B):";
  for (int i = 0; i < 4; i++) {
    long id = input->readLong(4);
    if (!id) continue;
    if (i < 2) {
      if (!m_state->m_headerId)
        m_state->m_headerId = id;
      else if (m_state->m_headerId != id) {
        MWAW_DEBUG_MSG(("HanMacWrdKText::readSections: headerid is already defined\n"));
        f << "###";
      }
      f << "headerId=" << std::hex << id << std::dec << ",";
    }
    else {
      if (!m_state->m_footerId)
        m_state->m_footerId = id;
      else if (m_state->m_footerId != id) {
        MWAW_DEBUG_MSG(("HanMacWrdKText::readSections: footerid is already defined\n"));
        f << "###";
      }
      f << "footerId=" << std::hex << id << std::dec << ",";
    }
  }
  for (int i = 0; i < 8; i++) {
    val = input->readLong(2);
    if (val) f << "f" << i << "=" << val << ",";
  }
  asciiFile.addDelimiter(input->tell(),'|');
  asciiFile.addPos(pos);
  asciiFile.addNote(f.str().c_str());
  if (zone->m_id >= 0) {
    m_state->m_sectionList[long(zone->m_id)]=sec;
  }
  return true;
}

//! send data to the listener
void HanMacWrdKText::flushExtra()
{
  if (!m_parserState->m_textListener) return;
  for (auto tIt : m_state->m_IdTextMaps) {
    if (!tIt.second) continue;
    HanMacWrdKZone &zone = *tIt.second;
    if (zone.m_parsed) continue;
    sendText(zone);
  }
}

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