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 "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWGraphicListener.hxx"
#include "MWAWListener.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWParser.hxx"
#include "MWAWPosition.hxx"
#include "MWAWSection.hxx"

#include "ClarisDrawParser.hxx"
#include "ClarisDrawStyleManager.hxx"

#include "ClarisDrawText.hxx"

/** Internal: the structures of a ClarisDrawText */
namespace ClarisDrawTextInternal
{
/** the different plc type */
enum PLCType { P_Font,  P_Ruler, P_Child, P_TextZone, P_Token, P_Unknown};

/** Internal : the different plc types: mainly for debugging */
struct PLC {
  /// the constructor
  PLC()
    : m_type(P_Unknown)
    , m_id(-1)
    , m_extra("")
  {
  }
  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, PLC const &plc);
  /** the PLC types */
  PLCType m_type;
  /** the id */
  int m_id;
  /** extra data */
  std::string m_extra;
};

std::ostream &operator<<(std::ostream &o, PLC const &plc)
{
  switch (plc.m_type) {
  case P_Font:
    o << "F";
    break;
  case P_Ruler:
    o << "R";
    break;
  case P_Child:
    o << "C";
    break;
  case P_TextZone:
    o << "TZ";
    break;
  case P_Token:
    o << "Tok";
    break;
  case P_Unknown:
#if !defined(__clang__)
  default:
#endif
    o << "#Unkn";
    break;
  }
  if (plc.m_id >= 0) o << plc.m_id;
  else o << "_";
  if (plc.m_extra.length()) o << ":" << plc.m_extra;
  return o;
}

/** Internal: class to store the paragraph properties */
struct Paragraph final : public MWAWParagraph {
  //! Constructor
  Paragraph()
    : MWAWParagraph()
    , m_labelType(0)
  {
  }
  //! destructor
  ~Paragraph() final;
  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, Paragraph const &ind)
  {
    o << static_cast<MWAWParagraph const &>(ind) << ",";
    static char const *labelNames[] = {
      "none", "diamond", "bullet", "checkbox", "hardvard", "leader", "legal",
      "upperalpha", "alpha", "numeric", "upperroman", "roman"
    };
    if (ind.m_labelType > 0 && ind.m_labelType < 12)
      o << "label=" << labelNames[ind.m_labelType] << ",";
    else if (ind.m_labelType)
      o << "#labelType=" << ind.m_labelType << ",";
    return o;
  }
  //! update the list level
  void updateListLevel();
  //! the label
  int m_labelType;
};

Paragraph::~Paragraph()
{
}

void Paragraph::updateListLevel()
{
  int extraLevel = m_labelType!=0 ? 1 : 0;
  if (*m_listLevelIndex+extraLevel<=0)
    return;
  int lev = *m_listLevelIndex+extraLevel;
  m_listLevelIndex = lev;
  MWAWListLevel theLevel;
  theLevel.m_labelWidth=0.2;
  switch (m_labelType) {
  case 0:
    theLevel.m_type = MWAWListLevel::NONE;
    break;
  case 1: // diamond
    theLevel.m_type = MWAWListLevel::BULLET;
    libmwaw::appendUnicode(0x25c7, theLevel.m_bullet);
    break;
  case 3: // checkbox
    theLevel.m_type = MWAWListLevel::BULLET;
    libmwaw::appendUnicode(0x2610, theLevel.m_bullet);
    break;
  case 4: {
    theLevel.m_suffix = (lev <= 3) ? "." : ")";
    if (lev == 1) theLevel.m_type = MWAWListLevel::UPPER_ROMAN;
    else if (lev == 2) theLevel.m_type = MWAWListLevel::UPPER_ALPHA;
    else if (lev == 3) theLevel.m_type = MWAWListLevel::DECIMAL;
    else if (lev == 4) theLevel.m_type =  MWAWListLevel::LOWER_ALPHA;
    else if ((lev%3)==2) {
      theLevel.m_prefix = "(";
      theLevel.m_type = MWAWListLevel::DECIMAL;
    }
    else if ((lev%3)==0) {
      theLevel.m_prefix = "(";
      theLevel.m_type = MWAWListLevel::LOWER_ALPHA;
    }
    else
      theLevel.m_type = MWAWListLevel::LOWER_ROMAN;
    break;
  }
  case 5: // leader
    theLevel.m_type = MWAWListLevel::BULLET;
    theLevel.m_bullet = "+"; // in fact + + and -
    break;
  case 6: // legal
    theLevel.m_type = MWAWListLevel::DECIMAL;
    theLevel.m_numBeforeLabels = lev-1;
    theLevel.m_suffix = ".";
    theLevel.m_labelWidth = 0.2*lev;
    break;
  case 7:
    theLevel.m_type = MWAWListLevel::UPPER_ALPHA;
    theLevel.m_suffix = ".";
    break;
  case 8:
    theLevel.m_type = MWAWListLevel::LOWER_ALPHA;
    theLevel.m_suffix = ".";
    break;
  case 9:
    theLevel.m_type = MWAWListLevel::DECIMAL;
    theLevel.m_suffix = ".";
    break;
  case 10:
    theLevel.m_type = MWAWListLevel::UPPER_ROMAN;
    theLevel.m_suffix = ".";
    break;
  case 11:
    theLevel.m_type = MWAWListLevel::LOWER_ROMAN;
    theLevel.m_suffix = ".";
    break;
  case 2: // bullet
  default:
    theLevel.m_type = MWAWListLevel::BULLET;
    libmwaw::appendUnicode(0x2022, theLevel.m_bullet);
    break;
  }
  m_margins[1]=m_margins[1].get()-theLevel.m_labelWidth;
  m_listLevel=theLevel;
}

//! paragraph plc
struct ParagraphPLC {
  //! constructor
  ParagraphPLC()
    : m_rulerId(-1)
    , m_flags(0)
    , m_extra("")
  {
  }
  //! return the label type
  int getLabelType() const
  {
    return int((m_flags>>3)&0xF);
  }
  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, ParagraphPLC const &info)
  {
    if (info.m_rulerId >= 0) o << "P" << info.m_rulerId <<",";
    switch (info.m_flags&3) {
    case 0: // normal
      break;
    case 1:
      o << "hidden,";
      break;
    case 2:
      o << "collapsed,";
      break;
    default:
      o<< "hidden/collapsed,";
      break;
    }
    if (info.m_flags&4)
      o << "flags4,";
    static char const *labelNames[] = {
      "none", "diamond", "bullet", "checkbox", "hardvard", "leader", "legal",
      "upperalpha", "alpha", "numeric", "upperroman", "roman"
    };

    auto listType=int((info.m_flags>>3)&0xF);
    if (listType>0 && listType < 12)
      o << labelNames[listType] << ",";
    else if (listType)
      o << "#listType=" << listType << ",";
    if (info.m_flags&0x80) o << "flags80,";
    auto listLevel=int((info.m_flags>>8)&0xF);
    if (listLevel) o << "level=" << listLevel+1;
    if (info.m_flags>>12) o << "flags=" << std::hex << (info.m_flags>>12) << std::dec << ",";
    if (info.m_extra.length()) o << info.m_extra;
    return o;
  }

  /** the ruler id */
  int m_rulerId;
  /** some flags */
  int m_flags;
  /** extra data */
  std::string m_extra;
};

/** internal class used to store a text zone */
struct TextZoneInfo {
  //! constructor
  TextZoneInfo()
    : m_pos(0)
    , m_N(0)
    , m_extra("")
  {
  }
  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, TextZoneInfo const &info)
  {
    o << "pos=" << info.m_pos << ",";
    if (info.m_N >= 0) o << "size=" << info.m_N <<",";
    if (info.m_extra.length()) o << info.m_extra;
    return o;
  }
  //! the position
  long m_pos;
  //! the number of character
  int m_N;
  //! extra data
  std::string m_extra;
};

//! token type
enum TokenType { TKN_UNKNOWN, TKN_FOOTNOTE, TKN_PAGENUMBER, TKN_GRAPHIC, TKN_FIELD };

/** Internal: class to store field definition: TOKN entry*/
struct Token {
  //! constructor
  Token()
    : m_type(TKN_UNKNOWN)
    , m_zoneId(-1), m_page(-1)
    , m_descent(0)
    , m_fieldEntry()
    , m_extra("")
  {
    for (auto &i : m_unknown) i = 0;
    for (auto &i : m_size) i = 0;
  }
  //! operator <<
  friend std::ostream &operator<<(std::ostream &o, Token const &tok);
  //! the type
  TokenType m_type;
  //! the zone id which correspond to this type
  int m_zoneId;
  //! the page
  int m_page;
  //! the size(?)
  int m_size[2];
  //! the descent
  int m_descent;
  //! the field name entry
  MWAWEntry m_fieldEntry;
  //! the unknown zone
  int m_unknown[3];
  //! a string used to store the parsing errors
  std::string m_extra;
};
std::ostream &operator<<(std::ostream &o, Token const &tok)
{
  switch (tok.m_type) {
  case TKN_FOOTNOTE:
    o << "footnoote,";
    break;
  case TKN_FIELD:
    o << "field[linked],";
    break;
  case TKN_PAGENUMBER:
    switch (tok.m_unknown[0]) {
    case 0:
      o << "field[pageNumber],";
      break;
    case 1:
      o << "field[sectionNumber],";
      break;
    case 2:
      o << "field[sectionInPageNumber],";
      break;
    case 3:
      o << "field[pageCount],";
      break;
    default:
      o << "field[pageNumber=#" << tok.m_unknown[0] << "],";
      break;
    }
    break;
  case TKN_GRAPHIC:
    o << "graphic,";
    break;
  case TKN_UNKNOWN:
#if !defined(__clang__)
  default:
#endif
    o << "##field[unknown]" << ",";
    break;
  }
  if (tok.m_zoneId != -1) o << "zoneId=" << tok.m_zoneId << ",";
  if (tok.m_page != -1) o << "page?=" << tok.m_page << ",";
  o << "pos?=" << tok.m_size[0] << "x" << tok.m_size[1] << ",";
  if (tok.m_descent) o << "descent=" << tok.m_descent << ",";
  for (int i = 0; i < 3; i++) {
    if (tok.m_unknown[i] == 0 || (i==0 && tok.m_type==TKN_PAGENUMBER))
      continue;
    o << "#unkn" << i << "=" << std::hex << tok.m_unknown[i] << std::dec << ",";
  }
  if (!tok.m_extra.empty()) o << "err=[" << tok.m_extra << "]";
  return o;
}

//! low level internal: main text zone
struct DSET final : public ClarisWksStruct::DSET {
  //! constructor
  explicit DSET(ClarisWksStruct::DSET const &dset = ClarisWksStruct::DSET())
    : ClarisWksStruct::DSET(dset)
    , m_zones()
    , m_numChar(0)
    , m_numTextZone(0)
    , m_numParagInfo(0)
    , m_numFont(0)
    , m_fatherId(0)
    , m_unknown(0)
    , m_subSectionPosList()
    , m_fontList()
    , m_paragraphList()
    , m_tokenList()
    , m_textZoneList()
    , m_plcMap()
  {
  }
  //! destructor
  ~DSET() final;
  //! true if the zone is outlined
  bool isOutlined() const
  {
    return m_flags[0]==1;
  }
  //! operator<<
  friend std::ostream &operator<<(std::ostream &o, DSET const &doc)
  {
    o << static_cast<ClarisWksStruct::DSET const &>(doc);
    if (doc.m_numChar) o << "numChar=" << doc.m_numChar << ",";
    if (doc.m_numTextZone) o << "numTextZone=" << doc.m_numTextZone << ",";
    if (doc.m_numParagInfo) o << "numParag=" << doc.m_numParagInfo << ",";
    if (doc.m_numFont) o << "numFont=" << doc.m_numFont << ",";
    if (doc.m_fatherId) o << "id[father]=" << doc.m_fatherId << ",";
    if (doc.m_unknown) o << "unkn=" << doc.m_unknown << ",";
    return o;
  }

  std::vector<MWAWEntry> m_zones; // the text zones
  int m_numChar /** the number of char in text zone */;
  int m_numTextZone /** the number of text zone ( ie. number of page ? ) */;
  int m_numParagInfo /** the number of paragraph info */;
  int m_numFont /** the number of font */;
  int m_fatherId /** the father id */;
  int m_unknown /** an unknown flags */;

  std::vector<long> m_subSectionPosList /** list of end of section position */;
  std::vector<MWAWFont> m_fontList /** the list of fonts */;
  std::vector<ParagraphPLC> m_paragraphList /** the list of paragraph */;
  std::vector<Token> m_tokenList /** the list of token */;
  std::vector<TextZoneInfo> m_textZoneList /** the list of zone */;
  std::multimap<long, PLC> m_plcMap /** the plc map */;
};

DSET::~DSET()
{
}

////////////////////////////////////////
//! Internal: the state of a ClarisDrawText
struct State {
  //! constructor
  State()
    : m_version(-1)
    , m_paragraphsList()
    , m_zoneMap()
  {
  }

  //! the file version
  mutable int m_version;
  //! the list of paragraph
  std::vector<Paragraph> m_paragraphsList;
  //! the list of text zone
  std::map<int, std::shared_ptr<DSET> > m_zoneMap;
};

}

////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
ClarisDrawText::ClarisDrawText(ClarisDrawParser &parser)
  : m_parserState(parser.getParserState())
  , m_state(new ClarisDrawTextInternal::State)
  , m_mainParser(&parser)
  , m_styleManager(parser.m_styleManager)
{
}

ClarisDrawText::~ClarisDrawText()
{ }

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

void ClarisDrawText::resetState()
{
  m_state.reset(new ClarisDrawTextInternal::State);
}

int ClarisDrawText::numPages() const
{
  // to do
  return 0;
}

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

////////////////////////////////////////////////////////////
// a document part
////////////////////////////////////////////////////////////
std::shared_ptr<ClarisWksStruct::DSET> ClarisDrawText::readDSETZone(ClarisWksStruct::DSET const &zone, MWAWEntry const &entry)
{
  if (!entry.valid() || zone.m_fileType != 1)
    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;
  f << "Entries(DSETT):";

  std::shared_ptr<ClarisDrawTextInternal::DSET> textZone(new ClarisDrawTextInternal::DSET(zone));
  textZone->m_unknown = static_cast<int>(input->readULong(2)); // alway 0 ?
  textZone->m_fatherId = static_cast<int>(input->readULong(2));
  textZone->m_numChar = static_cast<int>(input->readULong(4));
  textZone->m_numTextZone = static_cast<int>(input->readULong(2));
  textZone->m_numParagInfo = static_cast<int>(input->readULong(2));
  textZone->m_numFont = static_cast<int>(input->readULong(2));
  switch (textZone->m_textType >> 4) {
  case 2:
    textZone->m_position = ClarisWksStruct::DSET::P_Header;
    break;
  case 4:
    textZone->m_position = ClarisWksStruct::DSET::P_Footer;
    break;
  case 6:
    textZone->m_position = ClarisWksStruct::DSET::P_Footnote;
    break;
  case 8:
    textZone->m_position = ClarisWksStruct::DSET::P_Frame;
    break;
  case 0xe:
    textZone->m_position = ClarisWksStruct::DSET::P_Table;
    break;
  case 0:
    if (zone.m_id==1) {
      textZone->m_position = ClarisWksStruct::DSET::P_Main;
      break;
    }
    MWAW_FALLTHROUGH;
  default:
    MWAW_DEBUG_MSG(("ClarisDrawText::readDSETZone: find unknown position %d\n", (textZone->m_textType >> 4)));
    f << "#position="<< (textZone->m_textType >> 4) << ",";
    break;
  }
  // find 2,3,6,a,b,e,f
  if (textZone->m_textType != ClarisWksStruct::DSET::P_Unknown)
    textZone->m_textType &= 0xF;
  f << *textZone << ",";

  if (long(input->tell())%2)
    input->seek(1, librevenge::RVNG_SEEK_CUR);
  ascFile.addDelimiter(input->tell(), '|');
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  // read the last part
  int const data0Length = 28;

  auto N = int(zone.m_numData);
  if (N<0 || long(input->tell())+N*data0Length > entry.end()) {
    MWAW_DEBUG_MSG(("ClarisDrawText::readDSETZone: file is too short\n"));
    return std::shared_ptr<ClarisWksStruct::DSET>();
  }

  input->seek(entry.end()-N*data0Length, librevenge::RVNG_SEEK_SET);
  ClarisDrawTextInternal::PLC plc;
  plc.m_type = ClarisDrawTextInternal::P_Child;
  int numExtraHId=0;
  textZone->m_subSectionPosList.push_back(0);
  for (int i = 0; i < N; i++) { // normally 1
    /* definition of a list of text zone ( one by column and one by page )*/
    pos = input->tell();
    f.str("");
    f << "DSETT-" << i << ":";
    ClarisWksStruct::DSET::Child child;
    child.m_posC = long(input->readULong(4));
    if (child.m_posC>textZone->m_subSectionPosList.back())
      textZone->m_subSectionPosList.push_back(child.m_posC);
    child.m_type = ClarisWksStruct::DSET::C_SubText;
    int dim[2];
    for (int &j : dim) j = static_cast<int>(input->readLong(2));
    child.m_box = MWAWBox2f(MWAWVec2f(0,0), MWAWVec2f(float(dim[0]), float(dim[1])));
    textZone->m_childs.push_back(child);
    plc.m_id = i;
    textZone->m_plcMap.insert(std::map<long, ClarisDrawTextInternal::PLC>::value_type(child.m_posC, plc));
    f << child;
    f << "ptr=" << std::hex << input->readULong(4) << std::dec << ",";
    f << "f0=" << input->readLong(2) << ","; // a small number : number of line ?
    f << "y[real]=" << input->readLong(2) << ",";
    for (int j = 1; j < 4; j++) {
      auto val = static_cast<int>(input->readLong(2));
      if (val)
        f << "f" << j << "=" << val << ",";
    }
    auto order = static_cast<int>(input->readLong(2));
    // simple id or 0: main text ?, 1 : header/footnote ?, 2: footer => id or order?
    if (order)
      f << "order?=" << order << ",";

    auto id=static_cast<long>(input->readULong(4));
    if (id) {
      f << "ID=" << std::hex << id << std::dec << ",";
      ++numExtraHId;
    }

    long actPos = input->tell();
    if (actPos != pos && actPos != pos+data0Length)
      ascFile.addDelimiter(input->tell(),'|');
    input->seek(pos+data0Length, librevenge::RVNG_SEEK_SET);

    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
  }

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

  // now normally 4 zones: paragraph, font, token, text zone size + 1 zone for each text zone
  bool ok=true;
  for (int z = 0; z < 4+textZone->m_numTextZone; z++) {
    pos = input->tell();
    if (input->isEnd()) {
      MWAW_DEBUG_MSG(("ClarisDrawText::readDSETZone: can not find some zone\n"));
      if (z > 4)
        break;
      return textZone;
    }
    auto sz = long(input->readULong(4));
    if (!sz) {
      f.str("");
      f << "DSETT-Z" << z;
      ascFile.addPos(pos);
      ascFile.addNote(f.str().c_str());
      continue;
    }

    MWAWEntry zEntry;
    zEntry.setBegin(pos);
    zEntry.setLength(sz+4);

    if (!input->checkPosition(zEntry.end())) {
      MWAW_DEBUG_MSG(("ClarisDrawText::readDSETZone: entry for %d zone is too short\n", z));
      ascFile.addPos(pos);
      ascFile.addNote("###");
      input->seek(pos, librevenge::RVNG_SEEK_SET);
      if (z > 4)
        break;
      return textZone;
    }

    switch (z) {
    case 0:
      ok = readParagraphs(zEntry, *textZone);
      break;
    case 1:
      ok = readFonts(zEntry, *textZone);
      break;
    case 2:
      ok = readTokens(zEntry, *textZone);
      break;
    case 3:
      ok = readTextZoneSize(zEntry, *textZone);
      break;
    default:
      textZone->m_zones.push_back(zEntry);
      break;
    }
    if (!ok) {
      if (z >= 4) {
        input->seek(pos, librevenge::RVNG_SEEK_SET);
        MWAW_DEBUG_MSG(("ClarisDrawText::readDSETZone: can not find text %d zone\n", z-4));
        if (z > 4) break;
        return textZone;
      }
      f.str("");
      f << "DSETT-Z" << z << "#";
      ascFile.addPos(pos);
      ascFile.addNote(f.str().c_str());
    }
    input->seek(zEntry.end(), librevenge::RVNG_SEEK_SET);
  }
  // never seems
  for (int i=0; ok && i<numExtraHId; ++i) {
    pos=input->tell();
    auto sz=long(input->readULong(4));
    if (sz<10 || !input->checkPosition(pos+4+sz)) {
      MWAW_DEBUG_MSG(("ClarisDrawText::readDSETZone:: can not read an extra block\n"));
      ascFile.addPos(pos);
      ascFile.addNote("DSETT-extra:###");
      input->seek(pos, librevenge::RVNG_SEEK_SET);
      ok=false;
      break;
    }
    f.str("");
    f << "DSETT-extra:";
    /* Checkme: no sure how to read this unfrequent structures */
    auto val=static_cast<int>(input->readLong(2)); // 2 (with size=34 or 4c)|a(with size=a or e)|3c (with size 3c)
    f << "type?=" << val << ",";
    int dim[4];
    for (int &j : dim) j=static_cast<int>(input->readLong(2));
    f << "dim=" << dim[1] << "x" << dim[0] << "<->" << dim[3] << "x" << dim[2] << ",";
    if (sz!=10) ascFile.addDelimiter(input->tell(),'|');
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
  }

  if (m_state->m_zoneMap.find(textZone->m_id) != m_state->m_zoneMap.end()) {
    MWAW_DEBUG_MSG(("ClarisDrawText::readDSETZone: zone %d already exists!!!\n", textZone->m_id));
  }
  else
    m_state->m_zoneMap[textZone->m_id] = textZone;

  if (ok) {
    // look for unparsed zone
    pos=input->tell();
    auto sz=long(input->readULong(4));
    if (input->checkPosition(pos+4+sz)) {
      if (sz) {
        MWAW_DEBUG_MSG(("ClarisDrawText::readDSETZone:: find some extra block\n"));
        input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
        ascFile.addPos(pos);
        ascFile.addNote("Entries(TextEnd):###");
      }
      else {
        // 2 empty zone is ok, if not probably a problem, but...
        ascFile.addPos(pos);
        ascFile.addNote("_");
      }
    }
    else
      input->seek(pos, librevenge::RVNG_SEEK_SET);
  }

  return textZone;
}
////////////////////////////////////////////////////////////
//
// Low level
//
////////////////////////////////////////////////////////////
bool ClarisDrawText::readFonts(MWAWEntry const &entry, ClarisDrawTextInternal::DSET &zone)
{
  long pos = entry.begin();
  if ((entry.length()%12) != 4)
    return false;

  auto numElt = int((entry.length()-4)/12);
  MWAWInputStreamPtr &input= m_parserState->m_input;
  input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  ascFile.addPos(pos);
  ascFile.addNote("Entries(FontPLC)");

  ClarisDrawTextInternal::PLC plc;
  plc.m_type = ClarisDrawTextInternal::P_Font;
  for (int i = 0; i < numElt; i++) {
    MWAWFont font;
    int posChar;
    if (!readFont(i, posChar, font)) return false;
    zone.m_fontList.push_back(font);
    plc.m_id = i;
    zone.m_plcMap.insert(std::map<long, ClarisDrawTextInternal::PLC>::value_type(posChar, plc));
  }

  return true;
}


bool ClarisDrawText::readFont(int id, int &posC, MWAWFont &font)
{
  MWAWInputStreamPtr &input= m_parserState->m_input;
  long pos = input->tell();

  int const fontSize = 12;
  if (!input->checkPosition(pos+fontSize)) {
    MWAW_DEBUG_MSG(("ClarisDrawText::readFont: file is too short"));
    return false;
  }

  font = MWAWFont();
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  if (id >= 0)
    f << "FontPLC-F" << id << ":";
  else
    f << "Entries(FontDef):";
  posC = int(input->readULong(4));
  f << "pos=" << posC << ",";
  font.setId(static_cast<int>(input->readULong(2)));
  auto flag =static_cast<int>(input->readULong(2));
  uint32_t flags=0;
  if (flag&0x1) flags |= MWAWFont::boldBit;
  if (flag&0x2) flags |= MWAWFont::italicBit;
  if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
  if (flag&0x8) flags |= MWAWFont::embossBit;
  if (flag&0x10) flags |= MWAWFont::shadowBit;
  if (flag&0x20) font.setDeltaLetterSpacing(-1);
  if (flag&0x40) font.setDeltaLetterSpacing(1);
  if (flag&0x80) font.setStrikeOutStyle(MWAWFont::Line::Simple);
  if (flag&0x100) font.set(MWAWFont::Script::super100());
  if (flag&0x200) font.set(MWAWFont::Script::sub100());
  if (flag&0x400) font.set(MWAWFont::Script::super());
  if (flag&0x800) font.set(MWAWFont::Script::sub());
  if (flag&0x2000) {
    font.setUnderlineStyle(MWAWFont::Line::Simple);
    font.setUnderlineType(MWAWFont::Line::Double);
  }
  font.setSize(float(input->readULong(2)));
  auto colId = static_cast<int>(input->readULong(1));
  if (colId) f << "#backColor=" << colId << ",";
  colId = static_cast<int>(input->readULong(1));
  if (colId!=1) {
    MWAWColor col;
    if (m_styleManager->getColor(colId, col))
      font.setColor(col);
    else {
      MWAW_DEBUG_MSG(("ClarisDrawText::readFont: unknown color %d\n", colId));
      f << "##colId=" << colId << ",";
    }
  }
  font.setFlags(flags);
  f << font.getDebugString(m_parserState->m_fontConverter);
  if (long(input->tell()) != pos+fontSize)
    ascFile.addDelimiter(input->tell(), '|');
  input->seek(pos+fontSize, librevenge::RVNG_SEEK_SET);
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());
  return true;
}

////////////////////////////////////////////////////////////
// read a list of rulers
////////////////////////////////////////////////////////////
bool ClarisDrawText::readParagraphs()
{
  MWAWInputStreamPtr &input= m_parserState->m_input;
  long pos = input->tell();
  ClarisWksStruct::Struct header;
  if (!header.readHeader(input,true)) {
    MWAW_DEBUG_MSG(("ClarisDrawText::readParagraphs: can not reead the header\n"));
    return false;
  }

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  f << "Entries(RULR):" << 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 < int(header.m_numData); i++) {
    pos = input->tell();
    if (!readParagraph(i)) {
      input->seek(pos, librevenge::RVNG_SEEK_SET);
      return false;
    }
  }
  return true;
}

bool ClarisDrawText::readParagraphs(MWAWEntry const &entry, ClarisDrawTextInternal::DSET &zone)
{
  long pos = entry.begin();
  if ((entry.length()%8) != 4)
    return false;

  auto numElt = int((entry.length()-4)/8);

  MWAWInputStreamPtr &input= m_parserState->m_input;
  input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  pos = entry.begin();
  ascFile.addPos(pos);
  ascFile.addNote("Entries(ParaPLC)");

  libmwaw::DebugStream f;
  input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header
  ClarisDrawTextInternal::PLC plc;
  plc.m_type = ClarisDrawTextInternal::P_Ruler;
  for (int i = 0; i < numElt; i++) {
    pos = input->tell();
    ClarisDrawTextInternal::ParagraphPLC info;

    auto posC = long(input->readULong(4));
    f.str("");
    f << "ParaPLC-R" << i << ": pos=" << posC << ",";
    info.m_rulerId = static_cast<int>(input->readLong(2));
    info.m_flags = static_cast<int>(input->readLong(2));
    f << info;

    zone.m_paragraphList.push_back(info);
    plc.m_id = i;
    zone.m_plcMap.insert(std::map<long, ClarisDrawTextInternal::PLC>::value_type(posC, plc));
    input->seek(pos+8, librevenge::RVNG_SEEK_SET);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
  }

  return true;
}

////////////////////////////////////////////////////////////
// read a ruler zone
////////////////////////////////////////////////////////////
bool ClarisDrawText::readParagraph(int id)
{
  int const dataSize=96;
  ClarisDrawTextInternal::Paragraph ruler;
  MWAWInputStreamPtr &input= m_parserState->m_input;
  long pos = input->tell();
  long endPos = pos+dataSize;
  if (!input->checkPosition(endPos)) {
    MWAW_DEBUG_MSG(("ClarisDrawText::readParagraph: file is too short\n"));
    return false;
  }
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;

  int val;
  val = static_cast<int>(input->readLong(2));
  f << "num[used]=" << val << ",";
  val = static_cast<int>(input->readULong(2));
  int align = 0;
  align = (val >> 14);
  val &= 0x3FFF;
  switch (align) {
  case 0:
    break;
  case 1:
    ruler.m_justify = MWAWParagraph::JustificationCenter;
    break;
  case 2:
    ruler.m_justify = MWAWParagraph::JustificationRight ;
    break;
  case 3:
    ruler.m_justify = MWAWParagraph::JustificationFull;
    break;
  default:
    break;
  }

  bool inPoint = (val & 0x2000);
  int interline = val & 0xFF;
  if (interline) {
    if (inPoint)
      ruler.setInterline(interline, librevenge::RVNG_POINT);
    else
      ruler.setInterline(1.0+double(interline)/8, librevenge::RVNG_PERCENT);
  }
  if (val) f << "#flags=" << std::hex << val << std::dec << ",";
  for (auto &margin : ruler.m_margins)
    margin = double(input->readLong(2))/72.;
  *(ruler.m_margins[2]) -= 28./72.;
  if (ruler.m_margins[2].get() < 0.0) ruler.m_margins[2] = 0.0;
  for (int i = 0; i < 2; i++) {
    ruler.m_spacings[i+1] = double(input->readULong(1))/72.;
    input->seek(1, librevenge::RVNG_SEEK_CUR); // flags to define the printing unit
  }
  val = static_cast<int>(input->readLong(1));
  if (val) f << "unkn1=" << val << ",";
  auto numTabs = static_cast<int>(input->readULong(1));
  if (long(input->tell())+numTabs*4 > endPos) {
    if (numTabs != 255) { // 0xFF seems to be used in v1, v2
      MWAW_DEBUG_MSG(("ClarisDrawText::readParagraph: numTabs is too big\n"));
    }
    f << "numTabs*=" << numTabs << ",";
    numTabs = 0;
  }
  for (int i = 0; i < numTabs; i++) {
    MWAWTabStop tab;
    tab.m_position = double(input->readLong(2))/72.;
    val = static_cast<int>(input->readULong(1));
    switch ((val>>6)&3) {
    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 0: // left
    default:
      break;
    }
    switch (val&3) {
    case 1:
      tab.m_leaderCharacter = '.';
      break;
    case 2:
      tab.m_leaderCharacter = '-';
      break;
    case 3:
      tab.m_leaderCharacter = '_';
      break;
    case 0:
    default:
      break;
    }
    val &= 0x3C;
    auto decimalChar = char(input->readULong(1));
    if (decimalChar) {
      int unicode= m_parserState->m_fontConverter->unicode(3, static_cast<unsigned char>(decimalChar));
      if (unicode==-1)
        tab.m_decimalCharacter = uint16_t(decimalChar);
      else
        tab.m_decimalCharacter = uint16_t(unicode);
    }
    ruler.m_tabs->push_back(tab);
    if (val)
      f << "#unkn[tab" << i << "=" << std::hex << val << std::dec << "],";
  }
  ruler.m_extra = f.str();
  // save the style
  if (id >= 0) {
    if (int(m_state->m_paragraphsList.size()) <= id)
      m_state->m_paragraphsList.resize(size_t(id)+1);
    m_state->m_paragraphsList[size_t(id)]=ruler;
  }
  f.str("");
  if (id == 0)
    f << "Entries(RULR)-P0";
  else if (id < 0)
    f << "Entries(RULR)-P_";
  else
    f << "RULR-P" << id;
  f << ":" << ruler;

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

////////////////////////////////////////////////////////////
// zone which corresponds to the token (never seens data, so suppose that this is simillar to ClarisWorks)
////////////////////////////////////////////////////////////
bool ClarisDrawText::readTokens(MWAWEntry const &entry, ClarisDrawTextInternal::DSET &zone)
{
  long pos = entry.begin();

  if ((entry.length()%32) != 4)
    return false;

  MWAWInputStreamPtr &input= m_parserState->m_input;
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  ascFile.addPos(pos);
  ascFile.addNote("Entries(Token)");
  auto numElt = int((entry.length()-4)/32);
  input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header

  libmwaw::DebugStream f;
  ClarisDrawTextInternal::PLC plc;
  plc.m_type = ClarisDrawTextInternal::P_Token;
  int val;
  std::vector<int> fieldList;
  for (int i = 0; i < numElt; i++) {
    pos = input->tell();

    auto posC = static_cast<int>(input->readULong(4));
    ClarisDrawTextInternal::Token token;

    auto type = static_cast<int>(input->readLong(2));
    f.str("");
    switch (type) {
    case 0:
      token.m_type = ClarisDrawTextInternal::TKN_FOOTNOTE;
      break;
    case 1:
      token.m_type = ClarisDrawTextInternal::TKN_GRAPHIC;
      break;
    case 2:
      token.m_type = ClarisDrawTextInternal::TKN_PAGENUMBER;
      break;
    case 3:
      token.m_type = ClarisDrawTextInternal::TKN_FIELD;
      fieldList.push_back(i);
      break;
    default:
      f << "#type=" << type << ",";
      break;
    }

    token.m_unknown[0] = static_cast<int>(input->readLong(2));
    token.m_zoneId = static_cast<int>(input->readLong(2));
    token.m_unknown[1] = static_cast<int>(input->readLong(1));
    token.m_page = static_cast<int>(input->readLong(1));
    token.m_unknown[2] = static_cast<int>(input->readLong(2));
    for (int j = 0; j < 2; j++)
      token.m_size[1-j] = static_cast<int>(input->readLong(2));
    for (int j = 0; j < 3; j++) {
      val = static_cast<int>(input->readLong(2));
      if (val) f << "f" << j << "=" << val << ",";
    }
    val = static_cast<int>(input->readLong(2));
    if (val)
      f << "f3=" << val << ",";
    token.m_extra = f.str();
    f.str("");
    f << "Token-" << i << ": pos=" << posC << "," << token;
    zone.m_tokenList.push_back(token);
    plc.m_id = i;
    zone.m_plcMap.insert(std::map<long, ClarisDrawTextInternal::PLC>::value_type(posC, plc));

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

  input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
  int n=0;
  for (auto fl : fieldList) {
    pos=input->tell();
    auto sz=long(input->readULong(4));
    f.str("");
    f << "Token[field-" << n++ << "]:";
    if (!input->checkPosition(pos+sz+4) || long(input->readULong(1))+1!=sz) {
      MWAW_DEBUG_MSG(("ClarisDrawText::readTokens: can find token field name %d\n", n-1));
      input->seek(pos, librevenge::RVNG_SEEK_SET);
      f << "###";
      ascFile.addPos(pos);
      ascFile.addNote(f.str().c_str());
      return false;
    }
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    MWAWEntry fieldEntry;
    fieldEntry.setBegin(input->tell());
    fieldEntry.setEnd(pos+sz+4);
    zone.m_tokenList[size_t(fl)].m_fieldEntry=fieldEntry;
    input->seek(fieldEntry.end(), librevenge::RVNG_SEEK_SET);
  }
  return true;
}

////////////////////////////////////////////////////////////
// read the different size for the text
////////////////////////////////////////////////////////////
bool ClarisDrawText::readTextZoneSize(MWAWEntry const &entry, ClarisDrawTextInternal::DSET &zone)
{
  long pos = entry.begin();

  int dataSize = 10;
  if ((entry.length()%dataSize) != 4)
    return false;

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  ascFile.addPos(pos);
  ascFile.addNote("Entries(TextZoneSz)");

  auto numElt = int((entry.length()-4)/dataSize);

  MWAWInputStreamPtr &input= m_parserState->m_input;
  input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header

  ClarisDrawTextInternal::PLC plc;
  plc.m_type = ClarisDrawTextInternal::P_TextZone;
  for (int i = 0; i < numElt; i++) {
    pos = input->tell();
    f.str("");
    f << "TextZoneSz-" << i << ":";
    ClarisDrawTextInternal::TextZoneInfo info;
    info.m_pos = long(input->readULong(4));
    info.m_N = static_cast<int>(input->readULong(2));
    f << info;
    zone.m_textZoneList.push_back(info);
    plc.m_id = i;
    zone.m_plcMap.insert(std::map<long, ClarisDrawTextInternal::PLC>::value_type(info.m_pos, plc));

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

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

////////////////////////////////////////////////////////////
// send data
////////////////////////////////////////////////////////////
bool ClarisDrawText::sendText(ClarisDrawTextInternal::DSET const &zone, int subZone)
{
  zone.m_parsed=true;
  MWAWListenerPtr listener=m_parserState->getMainListener();
  if (!listener || !listener->canWriteText()) {
    MWAW_DEBUG_MSG(("ClarisDrawText::sendText: can not find a listener\n"));
    return false;
  }
  auto numParaPLC = int(zone.m_paragraphList.size());
  auto numParagraphs = int(m_state->m_paragraphsList.size());
  int numCols = 1;
  std::shared_ptr<MWAWList> actList;
  bool isOutline=zone.isOutlined();
  MWAWInputStreamPtr &input= m_parserState->m_input;
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;

  long firstChar=0, lastChar=zone.m_numChar;
  if (subZone>=0) {
    if (subZone>=static_cast<int>(zone.m_subSectionPosList.size()))
      return true;
    firstChar=zone.m_subSectionPosList[size_t(subZone)];
    if (subZone+1<static_cast<int>(zone.m_subSectionPosList.size()))
      lastChar=zone.m_subSectionPosList[size_t(subZone+1)];
  }

  long actC = 0;
  size_t numZones = zone.m_zones.size();
  for (size_t z = 0; z < numZones; z++) {
    MWAWEntry const &entry  =  zone.m_zones[z];
    long pos = entry.begin();
    libmwaw::DebugStream f, f2;

    auto numC = int(entry.length()-4);
    input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header

    for (int i = 0; i < numC; i++) {
      auto plcIt = zone.m_plcMap.find(actC);
      bool seeToken = false;
      while (actC>=firstChar && plcIt != zone.m_plcMap.end() && plcIt->first<=actC) {
        if (actC != plcIt->first) {
          MWAW_DEBUG_MSG(("ClarisDrawText::sendText: find a plc inside a complex char!!!\n"));
          f << "###";
        }
        auto const &plc = plcIt++->second;
        f << "[" << plc << "]";
        switch (plc.m_type) {
        case ClarisDrawTextInternal::P_Font:
          if (plc.m_id < 0 || plc.m_id >= int(zone.m_fontList.size())) {
            MWAW_DEBUG_MSG(("ClarisDrawText::sendText: can not find font %d\n", plc.m_id));
            f << "###";
            break;
          }
          listener->setFont(zone.m_fontList[size_t(plc.m_id)]);
          break;
        case ClarisDrawTextInternal::P_Ruler: {
          if (plc.m_id < 0 || plc.m_id >= numParaPLC)
            break;
          auto const &paraPLC = zone.m_paragraphList[size_t(plc.m_id)];
          f << "[" << paraPLC << "]";
          if (paraPLC.m_rulerId < 0 || paraPLC.m_rulerId >= numParagraphs)
            break;
          auto para = m_state->m_paragraphsList[size_t(paraPLC.m_rulerId)];
          if (isOutline) {
            para.m_labelType=paraPLC.getLabelType();
            para.m_listLevelIndex=0;
            para.updateListLevel();

            actList = m_parserState->m_listManager->getNewList(actList, 1, *para.m_listLevel);
            para.m_listId=actList->getId();
          }
          listener->setParagraph(para);
          break;
        }
        case ClarisDrawTextInternal::P_Token: {
          if (plc.m_id < 0 || plc.m_id >= int(zone.m_tokenList.size())) {
            MWAW_DEBUG_MSG(("ClarisDrawText::sendText: can not find the token %d\n", plc.m_id));
            f << "###";
            break;
          }
          auto const &token = zone.m_tokenList[size_t(plc.m_id)];
          switch (token.m_type) {
          case ClarisDrawTextInternal::TKN_FOOTNOTE:
            MWAW_DEBUG_MSG(("ClarisDrawText::sendText: can not send footnote in a paint file\n"));
            f << "###ftnote";
            break;
          case ClarisDrawTextInternal::TKN_PAGENUMBER:
            switch (token.m_unknown[0]) {
            case 1:
            case 2: {
              MWAW_DEBUG_MSG(("ClarisDrawText::sendText: find section token\n"));
              f << "##";
              listener->insertUnicodeString(librevenge::RVNGString("1"));
              break;
            }
            case 3:
              listener->insertField(MWAWField(MWAWField::PageCount));
              break;
            case 0:
            default:
              listener->insertField(MWAWField(MWAWField::PageNumber));
            }
            break;
          case ClarisDrawTextInternal::TKN_GRAPHIC:
            MWAW_DEBUG_MSG(("ClarisDrawText::sendText: can not send graphic\n"));
            f << "###";
            break;
          case ClarisDrawTextInternal::TKN_FIELD:
            listener->insertUnicode(0xab);
            if (token.m_fieldEntry.valid() &&
                input->checkPosition(token.m_fieldEntry.end())) {
              long actPos=input->tell();
              input->seek(token.m_fieldEntry.begin(), librevenge::RVNG_SEEK_SET);
              long endFPos=token.m_fieldEntry.end();
              while (!input->isEnd() && input->tell() < token.m_fieldEntry.end())
                listener->insertCharacter(static_cast<unsigned char>(input->readULong(1)), input, endFPos);
              input->seek(actPos, librevenge::RVNG_SEEK_SET);
            }
            else {
              MWAW_DEBUG_MSG(("ClarisDrawText::sendText: can not find field token data\n"));
              listener->insertCharacter(' ');
            }
            listener->insertUnicode(0xbb);
            break;
          case ClarisDrawTextInternal::TKN_UNKNOWN:
#if !defined(__clang__)
          default:
#endif
            break;
          }
          seeToken = true;
          break;
        }
        /* checkme: normally, this corresponds to the first
           character following a 0xb/0x1, so we do not need to a
           column/page break here */
        case ClarisDrawTextInternal::P_Child:
        case ClarisDrawTextInternal::P_TextZone:
        case ClarisDrawTextInternal::P_Unknown:
#if !defined(__clang__)
        default:
#endif
          break;
        }
      }
      auto c = char(input->readULong(1));
      if (actC++<firstChar)
        continue;
      if (actC>lastChar) break;
      if (c == '\0') {
        if (i == numC-1) break;
        MWAW_DEBUG_MSG(("ClarisDrawText::sendText: OOPS, find 0 reading the text\n"));
        f << "###0x0";
        continue;
      }
      f << c;
      if (seeToken && static_cast<unsigned char>(c) < 32) continue;
      switch (c) {
      case 0x1: // fixme: column break
        if (numCols) {
          listener->insertBreak(MWAWListener::ColumnBreak);
          break;
        }
        MWAW_DEBUG_MSG(("ClarisDrawText::sendText: Find unexpected char 1\n"));
        f << "###";
        MWAW_FALLTHROUGH;
      case 0xb: // page break
        MWAW_DEBUG_MSG(("ClarisDrawText::sendText: Find unexpected page break\n"));
        f << "###pb";
        break;
      case 0x2: // token footnote ( normally already done)
        break;
      case 0x3: // token graphic
        break;
      case 0x4:
        listener->insertField(MWAWField(MWAWField::Date));
        break;
      case 0x5: {
        MWAWField field(MWAWField::Time);
        field.m_DTFormat="%H:%M";
        listener->insertField(field);
        break;
      }
      case 0x6: // normally already done, but if we do not find the token, ...
        listener->insertField(MWAWField(MWAWField::PageNumber));
        break;
      case 0x7: // footnote index (ok to ignore : index of the footnote )
        break;
      case 0x8: // potential breaking <<hyphen>>
        break;
      case 0x9:
        listener->insertTab();
        break;
      case 0xa:
        listener->insertEOL(true);
        break;
      case 0xc: // new section: this is treated after, at the beginning of the for loop
        break;
      case 0xd:
        f2.str("");
        f2 << "Entries(TextContent):" << f.str();
        ascFile.addPos(pos);
        ascFile.addNote(f2.str().c_str());
        f.str("");
        pos = input->tell();

        // ignore last end of line returns
        if (z != numZones-1 || i != numC-2)
          listener->insertEOL();
        break;

      default: {
        int extraChar = listener->insertCharacter
                        (static_cast<unsigned char>(c), input, input->tell()+(numC-1-i));
        if (extraChar) {
          i += extraChar;
          actC += extraChar;
        }
      }
      }
    }
    if (f.str().length()) {
      f2.str("");
      f2 << "Entries(TextContent):" << f.str();
      ascFile.addPos(pos);
      ascFile.addNote(f2.str().c_str());
    }
  }

  return true;
}

bool ClarisDrawText::sendZone(int number, int subZone)
{
  auto iter = m_state->m_zoneMap.find(number);
  if (iter == m_state->m_zoneMap.end())
    return false;
  auto zone = iter->second;
  if (zone)
    sendText(*zone, subZone);
  return true;
}

void ClarisDrawText::flushExtra()
{
  MWAW_DEBUG_MSG(("ClarisDrawText::flushExtra: not implemented\n"));
}

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