Coverage Report

Created: 2026-07-20 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/RagTime5Text.cxx
Line
Count
Source
1
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2
3
/* libmwaw
4
* Version: MPL 2.0 / LGPLv2+
5
*
6
* The contents of this file are subject to the Mozilla Public License Version
7
* 2.0 (the "License"); you may not use this file except in compliance with
8
* the License or as specified alternatively below. You may obtain a copy of
9
* the License at http://www.mozilla.org/MPL/
10
*
11
* Software distributed under the License is distributed on an "AS IS" basis,
12
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
* for the specific language governing rights and limitations under the
14
* License.
15
*
16
* Major Contributor(s):
17
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20
* Copyright (C) 2006, 2007 Andrew Ziem
21
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22
*
23
*
24
* All Rights Reserved.
25
*
26
* For minor contributions see the git repository.
27
*
28
* Alternatively, the contents of this file may be used under the terms of
29
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30
* in which case the provisions of the LGPLv2+ are applicable
31
* instead of those above.
32
*/
33
34
#include <cmath>
35
#include <iomanip>
36
#include <iostream>
37
#include <limits>
38
#include <map>
39
#include <set>
40
#include <sstream>
41
#include <stack>
42
43
#include <librevenge/librevenge.h>
44
45
#include "MWAWFont.hxx"
46
#include "MWAWFontConverter.hxx"
47
#include "MWAWListener.hxx"
48
#include "MWAWGraphicEncoder.hxx"
49
#include "MWAWGraphicListener.hxx"
50
#include "MWAWParagraph.hxx"
51
#include "MWAWPosition.hxx"
52
#include "MWAWSection.hxx"
53
#include "MWAWSpreadsheetEncoder.hxx"
54
#include "MWAWSpreadsheetListener.hxx"
55
#include "MWAWSubDocument.hxx"
56
57
#include "RagTime5ClusterManager.hxx"
58
#include "RagTime5Document.hxx"
59
#include "RagTime5StructManager.hxx"
60
#include "RagTime5StyleManager.hxx"
61
62
#include "RagTime5Text.hxx"
63
64
#include "libmwaw_internal.hxx"
65
66
/** Internal: the structures of a RagTime5Text */
67
namespace RagTime5TextInternal
68
{
69
//! a PLC of a RagTime5Text
70
struct PLC {
71
  //! constructor
72
  PLC()
73
9.72M
    : m_position(-1)
74
9.72M
    , m_fileType(0)
75
9.72M
    , m_value(-1)
76
9.72M
  {
77
9.72M
  }
78
  //! operator<<
79
  friend std::ostream &operator<<(std::ostream &o, PLC const &plc)
80
0
  {
81
0
    if (plc.m_fileType==0) {
82
0
      o << "free[next]";
83
0
      if (plc.m_position>0)
84
0
        o << "=PLC" << plc.m_position;
85
0
      o << ",";
86
0
      return o;
87
0
    }
88
0
    if (plc.m_position>=0) o << "pos=" << plc.m_position << ",";
89
0
    switch (plc.m_fileType) {
90
0
    case 0:
91
0
      break;
92
0
    case 0x1001:
93
0
      o << "para,";
94
0
      break;
95
0
    case 0x1801: // soft?
96
0
      o << "line[beg],";
97
0
      break;
98
0
    case 0x3001:
99
0
      o << "index[end],";
100
0
      break;
101
0
    // 0x4001: related to footnote?
102
0
    case 0x5001:
103
0
      o << "char,";
104
0
      break;
105
0
    case 0x7001:
106
0
      o << "index[beg],";
107
0
      break;
108
0
    default:
109
0
      if (plc.m_fileType&0xfe) o << "#";
110
0
      o << "type=" << std::hex << plc.m_fileType << std::dec << ",";
111
0
    }
112
0
    if (plc.m_value!=-1) o << "f0=" << plc.m_value << ",";
113
0
    return o;
114
0
  }
115
  //! the position in the text
116
  int m_position;
117
  //! the file type
118
  int m_fileType;
119
  //! an unknown value
120
  int m_value;
121
};
122
123
//! a small struct use to define a block of a RagTime5Text
124
struct Block {
125
  //! constructor
126
  Block()
127
23.4k
    : m_id(0)
128
23.4k
    , m_subId(0)
129
23.4k
    , m_dimension()
130
23.4k
    , m_extra("")
131
23.4k
  {
132
46.8k
    for (auto &plc : m_plc) plc=0;
133
23.4k
  }
134
  //! operator<<
135
  friend std::ostream &operator<<(std::ostream &o, Block const &block)
136
0
  {
137
0
    o << "id=" << block.m_id << ",";
138
0
    if (block.m_subId) o << "id[sub]=" << block.m_subId << ",";
139
0
    o << "PLC" << block.m_plc[0] << "<->" << block.m_plc[1] << ",";
140
0
    o << block.m_dimension << ",";
141
0
    o << block.m_extra;
142
0
    return o;
143
0
  }
144
  //! the block id
145
  int m_id;
146
  //! the block sub id
147
  int m_subId;
148
  //! the block dimension
149
  MWAWBox2f m_dimension;
150
  //! the list of zone plc (first-end)
151
  int m_plc[2];
152
  //! extra data
153
  std::string m_extra;
154
};
155
156
//! a small struct used to store link plc data: footnote, index, ...
157
struct LinkPLC {
158
  //! constructor
159
  LinkPLC()
160
12.3k
    : m_what(0)
161
12.3k
    , m_type(0)
162
12.3k
    , m_positions(-1, -1)
163
12.3k
    , m_id(0)
164
12.3k
    , m_dimensions()
165
12.3k
    , m_footnotePositions()
166
12.3k
  {
167
12.3k
  }
168
  //! the plc type 0:attachment, 1:item(list item), 2:unknown, 3:index, 4:formula(page number, ...), 5:footnote
169
  int m_what;
170
  //! the file type
171
  int m_type;
172
  //! the position in the text
173
  MWAWVec2i m_positions;
174
  //! an identifier
175
  int m_id;
176
  //! the attachment box
177
  MWAWVec2f m_dimensions;
178
  //! the footnote data
179
  MWAWVec2i m_footnotePositions;
180
};
181
182
//! low level: the text cluster of a RagTime5Text
183
struct ClusterText final : public RagTime5ClusterManager::Cluster {
184
  //! constructor
185
  ClusterText()
186
17.8k
    : RagTime5ClusterManager::Cluster(C_TextZone)
187
17.8k
    , m_contentLink()
188
17.8k
    , m_plcDefLink()
189
17.8k
    , m_plcDefFreeBegin(0)
190
17.8k
    , m_plcDefNumFree(-1)
191
17.8k
    , m_plcToStyleLink()
192
17.8k
    , m_blockCellToPlcLink()
193
17.8k
    , m_separatorLink()
194
17.8k
    , m_footnoteLink()
195
17.8k
    , m_indexLink()
196
17.8k
    , m_textIntListLink()
197
17.8k
    , m_unknownLinks1()
198
199
17.8k
    , m_blockList()
200
17.8k
    , m_blockCellList()
201
17.8k
    , m_childList()
202
17.8k
    , m_PLCList()
203
17.8k
    , m_separators()
204
17.8k
    , m_posToStyleIdMap()
205
17.8k
    , m_linkPLCList()
206
17.8k
    , m_posToLinkIdMap()
207
17.8k
  {
208
17.8k
  }
209
  //! destructor
210
  ~ClusterText() final;
211
  //! the main content
212
  RagTime5ClusterManager::Link m_contentLink;
213
  //! the plc definition link
214
  RagTime5ClusterManager::Link m_plcDefLink;
215
  //! the plc first free block in the plc definition list
216
  int m_plcDefFreeBegin;
217
  //! the number of free block in the plc definition list
218
  int m_plcDefNumFree;
219
  //! the plc to text style link
220
  RagTime5ClusterManager::Link m_plcToStyleLink;
221
  //! the blockCell to plc link
222
  RagTime5ClusterManager::Link m_blockCellToPlcLink;
223
  //! the word/separator link
224
  RagTime5ClusterManager::Link m_separatorLink;
225
  //! the footnote link
226
  RagTime5ClusterManager::Link m_footnoteLink;
227
  //! the index link
228
  RagTime5ClusterManager::Link m_indexLink;
229
  //! the list of link zone
230
  RagTime5ClusterManager::Link m_linkDefs[5];
231
  //! list of a int link with size 2(only v6.6)
232
  RagTime5ClusterManager::Link m_textIntListLink;
233
  //! list of unkndata1 links
234
  std::vector<RagTime5ClusterManager::Link> m_unknownLinks1;
235
  //! list of unknown link: the three unkndata+2-3 links and the header link3 link
236
  RagTime5ClusterManager::Link m_unknownLink[3];
237
238
  // final data
239
240
  //! list of block (defined in header)
241
  std::vector<std::vector<Block> > m_blockList;
242
  //! list of block (defined in blockCell list)
243
  std::vector<Block> m_blockCellList;
244
  //! list of child
245
  std::vector<RagTime5StructManager::ZoneLink> m_childList;
246
  //! the PLC list
247
  std::vector<PLC> m_PLCList;
248
  //! the separators
249
  std::vector<int> m_separators;
250
  //! position to plc map
251
  std::multimap<int, int> m_posToStyleIdMap;
252
  //! the link plc list
253
  std::vector<LinkPLC> m_linkPLCList;
254
  //! position to link data map
255
  std::multimap<int, size_t> m_posToLinkIdMap;
256
};
257
258
ClusterText::~ClusterText()
259
17.8k
{
260
17.8k
}
261
262
////////////////////////////////////////
263
//! Internal: the state of a RagTime5Text
264
struct State {
265
  //! constructor
266
  State()
267
77.2k
    : m_numPages(0)
268
77.2k
    , m_idTextMap()
269
77.2k
    , m_uniqueIndexId(0)
270
77.2k
  {
271
77.2k
  }
272
  //! the number of pages
273
  int m_numPages;
274
  //! map data id to text zone
275
  std::map<int, std::shared_ptr<ClusterText> > m_idTextMap;
276
  //! an int used to create unique index id
277
  int m_uniqueIndexId;
278
};
279
280
//! Internal: the subdocument of a RagTime5Text
281
class SubDocument final : public MWAWSubDocument
282
{
283
public:
284
  // constructor
285
  SubDocument(RagTime5Text &parser, MWAWInputStreamPtr const &input,
286
              RagTime5TextInternal::ClusterText &cluster, RagTime5Zone &dataZone, size_t firstChar, size_t lastChar)
287
0
    : MWAWSubDocument(&parser.m_document.getMainParser(), input, MWAWEntry())
288
0
    , m_ragtimeParser(parser)
289
0
    , m_cluster(cluster)
290
0
    , m_dataZone(dataZone)
291
0
    , m_firstChar(firstChar)
292
0
    , m_lastChar(lastChar)
293
0
  {
294
0
  }
295
296
  //! destructor
297
0
  ~SubDocument() final {}
298
299
  //! operator!=
300
  bool operator!=(MWAWSubDocument const &doc) const final;
301
302
  //! the parser function
303
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
304
305
protected:
306
  //! the main parser
307
  RagTime5Text &m_ragtimeParser;
308
  //! the cluster
309
  ClusterText &m_cluster;
310
  //! the data zone
311
  RagTime5Zone &m_dataZone;
312
  //! the first char
313
  size_t m_firstChar;
314
  //! the last char
315
  size_t m_lastChar;
316
};
317
318
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
319
0
{
320
0
  if (!listener.get()) {
321
0
    MWAW_DEBUG_MSG(("RagTime5TextInternal::SubDocument::parse: no listener\n"));
322
0
    return;
323
0
  }
324
325
0
  long pos = m_input->tell();
326
0
  m_ragtimeParser.send(m_cluster, m_dataZone, listener, m_firstChar, m_lastChar, true, -1);
327
0
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
328
0
}
329
330
bool SubDocument::operator!=(MWAWSubDocument const &doc) const
331
0
{
332
0
  if (MWAWSubDocument::operator!=(doc)) return true;
333
0
  auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
334
0
  if (!sDoc) return true;
335
0
  if (&m_ragtimeParser != &sDoc->m_ragtimeParser) return true;
336
0
  if (&m_cluster != &sDoc->m_cluster) return true;
337
0
  if (&m_dataZone != &sDoc->m_dataZone) return true;
338
0
  if (m_firstChar != sDoc->m_firstChar) return true;
339
0
  if (m_lastChar != sDoc->m_lastChar) return true;
340
0
  return false;
341
0
}
342
343
}
344
345
////////////////////////////////////////////////////////////
346
// constructor/destructor, ...
347
////////////////////////////////////////////////////////////
348
RagTime5Text::RagTime5Text(RagTime5Document &doc)
349
77.2k
  : m_document(doc)
350
77.2k
  , m_structManager(m_document.getStructManager())
351
77.2k
  , m_styleManager(m_document.getStyleManager())
352
77.2k
  , m_parserState(doc.getParserState())
353
77.2k
  , m_state(new RagTime5TextInternal::State)
354
77.2k
{
355
77.2k
}
356
357
RagTime5Text::~RagTime5Text()
358
77.2k
{ }
359
360
int RagTime5Text::version() const
361
0
{
362
0
  return m_parserState->m_version;
363
0
}
364
365
int RagTime5Text::numPages() const
366
0
{
367
  // TODO IMPLEMENT ME
368
0
  MWAW_DEBUG_MSG(("RagTime5Text::numPages: is not implemented\n"));
369
0
  return 0;
370
0
}
371
372
bool RagTime5Text::send(int zoneId, MWAWListenerPtr listener, int partId, int cellId, double totalWidth)
373
9.08k
{
374
9.08k
  auto it=m_state->m_idTextMap.find(zoneId);
375
9.08k
  if (it==m_state->m_idTextMap.end() || !it->second) {
376
0
    MWAW_DEBUG_MSG(("RagTime5Text::send: can not find zone %d\n", zoneId));
377
0
    return false;
378
0
  }
379
9.08k
  return send(*it->second, listener, partId, cellId, totalWidth);
380
9.08k
}
381
382
////////////////////////////////////////////////////////////
383
//
384
// Intermediate level
385
//
386
////////////////////////////////////////////////////////////
387
388
////////////////////////////////////////////////////////////
389
// text separator position
390
////////////////////////////////////////////////////////////
391
bool RagTime5Text::readTextSeparators(RagTime5Zone &zone, std::vector<int> &separators)
392
4.50k
{
393
4.50k
  if (!zone.m_entry.valid() || zone.getKindLastPart(zone.m_kinds[1].empty())!="ItemData") {
394
213
    MWAW_DEBUG_MSG(("RagTime5Text::readTextSeparators: can not find the text position zone\n"));
395
213
    return false;
396
213
  }
397
398
4.28k
  zone.m_isParsed=true;
399
4.28k
  MWAWEntry entry=zone.m_entry;
400
4.28k
  MWAWInputStreamPtr input=zone.getInput();
401
4.28k
  libmwaw::DebugFile &ascFile=zone.ascii();
402
4.28k
  libmwaw::DebugStream f;
403
4.28k
  f << "Entries(TextSep)[" << zone << "]:";
404
405
4.28k
  input->seek(zone.m_entry.begin(), librevenge::RVNG_SEEK_SET);
406
4.28k
  separators.resize(size_t(2*entry.length()));
407
408
4.28k
  int lastSeen=0, numSeen=0;
409
101k
  for (long i=0; i<entry.length(); ++i) {
410
97.2k
    auto c=static_cast<int>(input->readULong(1));
411
291k
    for (int j=0; j<2; ++j) {
412
194k
      int v=(j==0 ? (c>>4) : c)&0xf;
413
194k
      if (v!=lastSeen) {
414
68.7k
        if (numSeen==1) f << lastSeen << ",";
415
43.1k
        else if (numSeen) f << lastSeen << "x" << numSeen << ",";
416
68.7k
        numSeen=0;
417
68.7k
        lastSeen=v;
418
68.7k
      }
419
194k
      ++numSeen;
420
194k
      separators[size_t(2*i+j)]=v;
421
194k
    }
422
97.2k
  }
423
4.28k
  if (numSeen==1) f << lastSeen << ",";
424
3.80k
  else if (numSeen) f << lastSeen << "x" << numSeen << ",";
425
426
4.28k
  ascFile.addPos(entry.end());
427
4.28k
  ascFile.addNote("_");
428
4.28k
  ascFile.addPos(entry.begin());
429
4.28k
  ascFile.addNote(f.str().c_str());
430
4.28k
  return true;
431
4.50k
}
432
433
////////////////////////////////////////////////////////////
434
// link/list definition
435
////////////////////////////////////////////////////////////
436
bool RagTime5Text::readLinkZones(RagTime5TextInternal::ClusterText &cluster, RagTime5ClusterManager::Link const &link, int what)
437
17.0k
{
438
17.0k
  if (link.m_ids.empty()) {
439
0
    MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: can not find the first zone id\n"));
440
0
    return false;
441
0
  }
442
17.0k
  if (what!=1 && link.m_ids.size()>=3 && link.m_ids[2]) {
443
0
    MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: find unexpected link2\n"));
444
0
  }
445
17.0k
  else if (link.m_ids.size()>=3 && link.m_ids[2]) {
446
0
    std::vector<long> decal;
447
0
    if (link.m_ids[1])
448
0
      m_document.readPositions(link.m_ids[1], decal);
449
0
    if (decal.empty())
450
0
      decal=link.m_longList;
451
0
    int const dataId=link.m_ids[2];
452
0
    auto dataZone=m_document.getDataZone(dataId);
453
0
    if (!dataZone) {
454
0
      MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: can not find the data zone %d\n", dataId));
455
0
      return false;
456
0
    }
457
0
    else if (!dataZone->m_entry.valid() || dataZone->getKindLastPart(dataZone->m_kinds[1].empty())!="ItemData") {
458
0
      if (decal.size()==1) {
459
        // a graphic zone with 0 zone is ok...
460
0
        dataZone->m_isParsed=true;
461
0
      }
462
0
      MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: the data zone %d seems bad\n", dataId));
463
0
    }
464
0
    else {
465
0
      MWAWEntry entry=dataZone->m_entry;
466
0
      dataZone->m_isParsed=true;
467
468
0
      libmwaw::DebugFile &ascFile=dataZone->ascii();
469
0
      libmwaw::DebugStream f;
470
0
      f << "Entries(" << link.m_name << "Def)[" << *dataZone << "]:";
471
0
      ascFile.addPos(entry.end());
472
0
      ascFile.addNote("_");
473
474
0
      if (decal.size() <= 1) {
475
0
        MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: can not find position for the data zone %d\n", dataId));
476
0
        f << "###";
477
0
        ascFile.addPos(entry.begin());
478
0
        ascFile.addNote(f.str().c_str());
479
0
      }
480
0
      else {
481
0
        auto N=int(decal.size());
482
0
        MWAWInputStreamPtr input=dataZone->getInput();
483
0
        input->setReadInverted(!cluster.m_hiLoEndian); // checkme maybe zone
484
485
0
        ascFile.addPos(entry.begin());
486
0
        ascFile.addNote(f.str().c_str());
487
488
0
        for (int i=0; i<N-1; ++i) {
489
0
          long pos=decal[size_t(i)], nextPos=decal[size_t(i+1)];
490
0
          if (pos==nextPos) continue;
491
0
          if (pos<0 || pos>entry.length()) {
492
0
            MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: can not read the data zone %d-%d seems bad\n", dataId, i));
493
0
            continue;
494
0
          }
495
0
          f.str("");
496
0
          f << link.m_name << "Def-" << i+1 << ":";
497
0
          librevenge::RVNGString string;
498
0
          input->seek(pos+entry.begin(), librevenge::RVNG_SEEK_SET);
499
0
          if (nextPos>entry.length() || !m_structManager->readUnicodeString(input, entry.begin()+nextPos, string)) {
500
0
            MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: can not read a string\n"));
501
0
            f << "###";
502
0
          }
503
0
          else if (!string.empty() && string.cstr()[0]=='\0')
504
0
            f << "\"" << string.cstr()+1 << "\",";
505
0
          else
506
0
            f << "\"" << string.cstr() << "\",";
507
0
          ascFile.addPos(entry.begin()+pos);
508
0
          ascFile.addNote(f.str().c_str());
509
0
        }
510
0
      }
511
0
    }
512
0
  }
513
  // ok no list
514
17.0k
  if (!link.m_ids[0])
515
58
    return true;
516
16.9k
  auto dataZone=m_document.getDataZone(link.m_ids[0]);
517
16.9k
  if (!dataZone || dataZone->getKindLastPart()!="ItemData") {
518
3.32k
    MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: can not find the first zone %d\n", link.m_ids[0]));
519
3.32k
    return false;
520
3.32k
  }
521
522
  // ok no list
523
13.6k
  if (!dataZone->m_entry.valid())
524
1.03k
    return true;
525
526
12.6k
  MWAWInputStreamPtr input=dataZone->getInput();
527
12.6k
  bool const hiLo=dataZone->m_hiLoEndian;
528
12.6k
  input->setReadInverted(!hiLo);
529
12.6k
  input->seek(dataZone->m_entry.begin(), librevenge::RVNG_SEEK_SET);
530
12.6k
  dataZone->m_isParsed=true;
531
532
12.6k
  libmwaw::DebugFile &ascFile=dataZone->ascii();
533
12.6k
  libmwaw::DebugStream f;
534
12.6k
  ascFile.addPos(dataZone->m_entry.end());
535
12.6k
  ascFile.addNote("_");
536
12.6k
  int const expectedSize[]= {32, 14/* or 24 in 6.5*/, 16, 12, 16, 24};
537
  // CHANGEME: find where the version is stored and use it to decide if the fieldSize is ok or not
538
12.6k
  if (what<0 || what>=6 || (link.m_fieldSize!=expectedSize[what] && (what!=1 || link.m_fieldSize!=24))) {
539
90
    MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: find unexpected size for zone %d\n", link.m_ids[0]));
540
90
    f << "###";
541
90
    ascFile.addPos(dataZone->m_entry.begin());
542
90
    ascFile.addNote(f.str().c_str());
543
90
    return false;
544
90
  }
545
12.5k
  if (link.m_fieldSize<12 || dataZone->m_entry.length()/link.m_fieldSize<link.m_N || link.m_N<=0) {
546
130
    MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: the position zone %d seems bad\n", dataZone->m_ids[0]));
547
130
    f << "Entries(" << link.m_name << ")[" << *dataZone << "]:" << link << "###,";
548
130
    ascFile.addPos(dataZone->m_entry.begin());
549
130
    ascFile.addNote(f.str().c_str());
550
130
    return false;
551
130
  }
552
12.3k
  size_t numPLC=cluster.m_PLCList.size();
553
25.0k
  for (int i=0; i<link.m_N; ++i) {
554
12.6k
    long pos=input->tell();
555
12.6k
    f.str("");
556
12.6k
    if (i==0)
557
12.3k
      f << "Entries(" << link.m_name << "Pos)[" << *dataZone << "]:";
558
289
    else
559
289
      f << link.m_name << "Pos-" << i << ":";
560
12.6k
    size_t ids[2];
561
25.3k
    for (auto &id : ids) id=size_t(input->readULong(4));
562
12.6k
    if (ids[0] && ids[1]) {
563
12.3k
      bool ok=true;
564
12.3k
      RagTime5TextInternal::LinkPLC linkPLC;
565
12.3k
      linkPLC.m_what=what;
566
36.9k
      for (int j=0; j<2; ++j) {
567
24.6k
        if (ids[j]>numPLC) {
568
9.11k
          MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: a plc position in zone %d seems bad\n", dataZone->m_ids[0]));
569
9.11k
          f << "###PLC" << ids[j];
570
9.11k
          ok=false;
571
9.11k
          continue;
572
9.11k
        }
573
15.5k
        linkPLC.m_positions[j]=cluster.m_PLCList[ids[j]-1].m_position;
574
15.5k
        f << linkPLC.m_positions[j];
575
15.5k
        if (j==0)
576
7.75k
          f << "<->";
577
7.74k
        else
578
7.74k
          f << ",";
579
15.5k
      }
580
12.3k
      if (ok && linkPLC.m_positions[0]>linkPLC.m_positions[1]) {
581
117
        MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: the plc orders in zone %d seems bad\n", dataZone->m_ids[0]));
582
117
        f << "###";
583
117
        ok=false;
584
117
      }
585
12.3k
      int val=int(input->readLong(2)); // always 0?
586
12.3k
      if (val) f << "f0=" << val << ",";
587
12.3k
      linkPLC.m_type=int(input->readLong(2));
588
12.3k
      f << "type=" << linkPLC.m_type << ","; // 8: graph/footnote/index, 10: ref1 and ref4, 14: ref3
589
12.3k
      if (link.m_fieldSize==16) {
590
12.3k
        linkPLC.m_id=int(input->readLong(4));
591
12.3k
        if (what==4)
592
0
          f << "FD" << linkPLC.m_id << ",";
593
12.3k
        else
594
12.3k
          f << "id=" << linkPLC.m_id << ",";
595
12.3k
      }
596
0
      else if (what==3) // index
597
0
        linkPLC.m_id=++m_state->m_uniqueIndexId;
598
0
      else if (what==5) { // footnote
599
0
        for (auto &id : ids) id=size_t(input->readULong(4));
600
0
        for (int j=0; j<2; ++j) {
601
0
          if (ids[j]>numPLC) {
602
0
            MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: a plc position in zone %d seems bad\n", dataZone->m_ids[0]));
603
0
            f << "###PLC" << ids[j];
604
0
            ok=false;
605
0
            continue;
606
0
          }
607
0
          linkPLC.m_footnotePositions[j]=cluster.m_PLCList[ids[j]-1].m_position;
608
0
          f << linkPLC.m_footnotePositions[j];
609
0
          if (j==0)
610
0
            f << "<->";
611
0
          else
612
0
            f << ",";
613
0
        }
614
0
        if (ok && linkPLC.m_footnotePositions[0]>linkPLC.m_footnotePositions[1]) {
615
0
          MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: the plc orders in zone %d seems bad\n", dataZone->m_ids[0]));
616
0
          f << "###";
617
0
          ok=false;
618
0
        }
619
0
        linkPLC.m_id=int(input->readLong(4));
620
0
        f << "id=" << linkPLC.m_id << ",";
621
0
      }
622
0
      else if (what==0) { // attachment
623
0
        float dim[2];
624
0
        for (auto &d : dim) d=float(input->readLong(4))/65536.f;
625
0
        linkPLC.m_dimensions=MWAWVec2f(dim[0],dim[1]);
626
0
        f << "dim=" << linkPLC.m_dimensions << ",";
627
0
        linkPLC.m_id=int(input->readLong(4));
628
0
        f << "id=" << linkPLC.m_id << ",";
629
0
      }
630
12.3k
      if (input->tell()!=pos+link.m_fieldSize)
631
0
        ascFile.addDelimiter(input->tell(),'|');
632
12.3k
      if (ok) {
633
7.62k
        size_t id=cluster.m_linkPLCList.size();
634
7.62k
        cluster.m_linkPLCList.push_back(linkPLC);
635
7.62k
        cluster.m_posToLinkIdMap.insert(std::pair<int, size_t>(linkPLC.m_positions[0],id));
636
7.62k
        if (linkPLC.m_positions[0]!=linkPLC.m_positions[1])
637
7.55k
          cluster.m_posToLinkIdMap.insert(std::pair<int, size_t>(linkPLC.m_positions[1],id));
638
7.62k
      }
639
12.3k
    }
640
12.6k
    ascFile.addPos(pos);
641
12.6k
    ascFile.addNote(f.str().c_str());
642
12.6k
    input->seek(pos+link.m_fieldSize, librevenge::RVNG_SEEK_SET);
643
12.6k
  }
644
12.3k
  if (input->tell()<dataZone->m_entry.end()) {
645
1.79k
    f.str("");
646
1.79k
    f << link.m_name << "Pos-:end";
647
    // check me: the size seems always a multiple of 16, so maybe reserved data...
648
1.79k
    if (dataZone->m_entry.length()%link.m_fieldSize) {
649
1.74k
      f << "###";
650
1.74k
      static bool first=true;
651
1.74k
      if (first) {
652
25
        MWAW_DEBUG_MSG(("RagTime5Text::readLinkZones: find some extra data\n"));
653
25
        first=false;
654
25
      }
655
1.74k
    }
656
1.79k
    ascFile.addPos(input->tell());
657
1.79k
    ascFile.addNote(f.str().c_str());
658
1.79k
  }
659
12.3k
  return true;
660
12.5k
}
661
662
663
////////////////////////////////////////////////////////////
664
// PLC
665
////////////////////////////////////////////////////////////
666
bool RagTime5Text::readPLC(RagTime5TextInternal::ClusterText &cluster, int zoneId)
667
15.5k
{
668
15.5k
  auto zone=m_document.getDataZone(zoneId);
669
15.5k
  if (!zone || !zone->m_entry.valid() || (zone->m_entry.length()%6) ||
670
9.21k
      zone->getKindLastPart(zone->m_kinds[1].empty())!="ItemData") {
671
6.60k
    MWAW_DEBUG_MSG(("RagTime5Text::readPLC: the entry of zone %d seems bad\n", zoneId));
672
6.60k
    return false;
673
6.60k
  }
674
8.98k
  MWAWEntry entry=zone->m_entry;
675
8.98k
  MWAWInputStreamPtr input=zone->getInput();
676
8.98k
  bool const hiLo=cluster.m_hiLoEndian;
677
8.98k
  input->setReadInverted(!hiLo);
678
8.98k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
679
680
8.98k
  libmwaw::DebugFile &ascFile=zone->ascii();
681
8.98k
  libmwaw::DebugStream f;
682
8.98k
  zone->m_isParsed=true;
683
8.98k
  ascFile.addPos(entry.end());
684
8.98k
  ascFile.addNote("_");
685
686
8.98k
  auto N=size_t(entry.length()/6);
687
8.98k
  f << "Entries(TextPLCDef)[" << *zone << "]:";
688
  // first check the free list
689
8.98k
  int freeId=cluster.m_plcDefFreeBegin;
690
8.98k
  std::set<int> listFreeIds;
691
8.98k
  bool ok=true;
692
471k
  for (int i=0; i<cluster.m_plcDefNumFree; ++i) {
693
463k
    if (freeId<=0 || freeId>int(N) || listFreeIds.find(freeId)!=listFreeIds.end()) {
694
945
      MWAW_DEBUG_MSG(("RagTime5Text::readPLC: find a bad freeId=%d\n", freeId));
695
945
      ok=false;
696
945
      break;
697
945
    }
698
462k
    listFreeIds.insert(freeId);
699
462k
    input->seek(entry.begin()+(freeId-1)*6, librevenge::RVNG_SEEK_SET);
700
462k
    freeId=static_cast<int>(input->readLong(4));
701
462k
  }
702
8.98k
  if (ok && freeId) {
703
28
    MWAW_DEBUG_MSG(("RagTime5Text::readPLC: last free Id=%d seems bad\n", freeId));
704
28
  }
705
8.98k
  if (!ok) {
706
945
    listFreeIds.clear();
707
945
    f << "###badFreeList,";
708
945
  }
709
8.98k
  ascFile.addPos(entry.begin());
710
8.98k
  ascFile.addNote(f.str().c_str());
711
712
8.98k
  cluster.m_PLCList.resize(N);
713
8.98k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
714
5.09M
  for (size_t i=0; i<N; ++i) {
715
5.08M
    long pos=input->tell();
716
5.08M
    if (listFreeIds.find(int(i+1))!=listFreeIds.end()) {
717
449k
      ascFile.addPos(pos);
718
449k
      ascFile.addNote("_");
719
449k
      input->seek(6, librevenge::RVNG_SEEK_CUR);
720
449k
      continue;
721
449k
    }
722
4.63M
    f.str("");
723
4.63M
    f << "TextPLCDef-PLC" << i+1 << ":";
724
4.63M
    RagTime5TextInternal::PLC plc;
725
4.63M
    if (hiLo) {
726
4.63M
      plc.m_fileType=static_cast<int>(input->readULong(2));
727
4.63M
      plc.m_position=static_cast<int>(input->readULong(2));
728
4.63M
      plc.m_value=static_cast<int>(input->readLong(2));
729
4.63M
    }
730
0
    else {
731
0
      plc.m_value=static_cast<int>(input->readLong(2));
732
0
      plc.m_position=static_cast<int>(input->readULong(2));
733
0
      plc.m_fileType=static_cast<int>(input->readULong(2));
734
0
    }
735
736
4.63M
    f << plc;
737
4.63M
    cluster.m_PLCList[i]=plc;
738
4.63M
    ascFile.addPos(pos);
739
4.63M
    ascFile.addNote(f.str().c_str());
740
4.63M
  }
741
8.98k
  input->setReadInverted(false);
742
8.98k
  return true;
743
15.5k
}
744
745
bool RagTime5Text::readPLCToCharStyle(RagTime5TextInternal::ClusterText &cluster)
746
17.8k
{
747
17.8k
  if (cluster.m_plcToStyleLink.m_ids.empty())
748
679
    return true;
749
17.1k
  int const zoneId=cluster.m_plcToStyleLink.m_ids[0];
750
17.1k
  if (!zoneId)
751
8
    return false;
752
753
17.1k
  auto zone=m_document.getDataZone(zoneId);
754
17.1k
  if (!zone || !zone->m_entry.valid() || (zone->m_entry.length()%6) ||
755
12.5k
      zone->getKindLastPart(zone->m_kinds[1].empty())!="ItemData") {
756
5.04k
    MWAW_DEBUG_MSG(("RagTime5Text::readPLCToCharStyle: the entry of zone %d seems bad\n", zoneId));
757
5.04k
    return false;
758
5.04k
  }
759
12.0k
  MWAWEntry entry=zone->m_entry;
760
12.0k
  MWAWInputStreamPtr input=zone->getInput();
761
12.0k
  input->setReadInverted(!cluster.m_hiLoEndian); // checkme: can also be zone->m_hiLoEndian
762
12.0k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
763
764
12.0k
  libmwaw::DebugFile &ascFile=zone->ascii();
765
12.0k
  libmwaw::DebugStream f;
766
12.0k
  zone->m_isParsed=true;
767
12.0k
  ascFile.addPos(entry.end());
768
12.0k
  ascFile.addNote("_");
769
770
12.0k
  f << "Entries(TextPLCToCStyle)[" << *zone << "]:";
771
772
12.0k
  auto N=int(entry.length()/6);
773
12.0k
  if (N>cluster.m_plcToStyleLink.m_N) // rare but can happens
774
1.02k
    N=cluster.m_plcToStyleLink.m_N;
775
11.0k
  else if (N<cluster.m_plcToStyleLink.m_N) {
776
59
    MWAW_DEBUG_MSG(("RagTime5Text::readPLCToCharStyle: N value seems too short\n"));
777
59
    f << "##N=" << N << ",";
778
59
  }
779
12.0k
  ascFile.addPos(entry.begin());
780
12.0k
  ascFile.addNote(f.str().c_str());
781
12.0k
  size_t numPLC=cluster.m_PLCList.size();
782
12.0k
  long lastFindPos=-1;
783
45.9k
  for (int i=0; i<N; ++i) {
784
33.8k
    long pos=input->tell();
785
33.8k
    f.str("");
786
33.8k
    f << "TextPLCToCStyle-" << i << ":";
787
33.8k
    auto id=size_t(input->readULong(4));
788
33.8k
    auto styleId=static_cast<int>(input->readULong(2));
789
33.8k
    f << "PLC" << id;
790
33.8k
    if (id==0 || id>numPLC) {
791
11.2k
      MWAW_DEBUG_MSG(("RagTime5Text::readPLCToCharStyle: find bad PLC id\n"));
792
11.2k
      f << "###";
793
11.2k
    }
794
22.5k
    else {
795
22.5k
      auto const plc=cluster.m_PLCList[size_t(id-1)];
796
22.5k
      if ((i==0 && plc.m_position!=0) || (i && plc.m_position<lastFindPos)) {
797
1.42k
        MWAW_DEBUG_MSG(("RagTime5Text::readPLCToCharStyle: the PLC position seems bad\n"));
798
1.42k
        f << "###";
799
1.42k
      }
800
21.1k
      else
801
21.1k
        cluster.m_posToStyleIdMap.insert(std::multimap<int, int>::value_type(plc.m_position, styleId));
802
22.5k
      lastFindPos=plc.m_position;
803
22.5k
      f << "[" << plc << "]";
804
22.5k
    }
805
33.8k
    f << "->TS" << styleId << ",";
806
33.8k
    ascFile.addPos(pos);
807
33.8k
    ascFile.addNote(f.str().c_str());
808
33.8k
  }
809
12.0k
  if (input->tell()!=entry.end()) {
810
1.02k
    ascFile.addPos(input->tell());
811
1.02k
    ascFile.addNote("TextPLCToCStyle:#extra");
812
1.02k
  }
813
12.0k
  input->setReadInverted(false);
814
12.0k
  return true;
815
17.1k
}
816
817
////////////////////////////////////////////////////////////
818
//
819
// Low level
820
//
821
////////////////////////////////////////////////////////////
822
823
////////////////////////////////////////////////////////////
824
// interface send function
825
////////////////////////////////////////////////////////////
826
827
void RagTime5Text::flushExtra(bool onlyCheck)
828
0
{
829
0
  for (auto const &it : m_state->m_idTextMap) {
830
0
    if (!it.second || it.second->m_isSent)
831
0
      continue;
832
0
    static bool first=true;
833
0
    if (first) {
834
0
      MWAW_DEBUG_MSG(("RagTime5Text::flushExtra: find some unsent zones: %d...\n", it.first));
835
0
      first=false;
836
0
    }
837
0
    if (!onlyCheck)
838
0
      send(*it.second, MWAWListenerPtr());
839
0
  }
840
0
}
841
842
bool RagTime5Text::send(RagTime5TextInternal::ClusterText &cluster, RagTime5Zone &dataZone, MWAWListenerPtr listener, size_t firstChar, size_t lastChar,
843
                        bool isLastZone, double totalWidth)
844
4.78k
{
845
4.78k
  if (!listener)
846
0
    listener=m_parserState->getMainListener();
847
4.78k
  if (!listener) {
848
0
    MWAW_DEBUG_MSG(("RagTime5Text::send: can not find the listener\n"));
849
0
    return false;
850
0
  }
851
4.78k
  if (cluster.m_separators.empty()) {
852
4.52k
    std::shared_ptr<RagTime5Zone> sepZone;
853
4.52k
    int cId=!cluster.m_separatorLink.m_ids.empty() ? cluster.m_separatorLink.m_ids[0] : -1;
854
4.52k
    if (cId>0)
855
4.50k
      sepZone=m_document.getDataZone(cId);
856
4.52k
    std::vector<int> separators;
857
4.52k
    if (!sepZone) {
858
24
      MWAW_DEBUG_MSG(("RagTime5Text::send: can not find the text separator zone %d\n", cId));
859
24
    }
860
4.50k
    else
861
4.50k
      readTextSeparators(*sepZone, cluster.m_separators);
862
4.52k
  }
863
4.78k
  auto input=dataZone.getInput();
864
4.78k
  auto const &entry=dataZone.m_entry;
865
4.78k
  auto &ascFile=dataZone.ascii();
866
4.78k
  if (!input || long(firstChar)>=entry.length()/2 || firstChar>=cluster.m_separators.size()) {
867
253
    MWAW_DEBUG_MSG(("RagTime5Text::send: can not find the text\n"));
868
253
    return false;
869
253
  }
870
4.52k
  if (long(lastChar)>entry.length()/2 || lastChar>cluster.m_separators.size()) {
871
298
    MWAW_DEBUG_MSG(("RagTime5Text::send: last char seems to big\n"));
872
298
    lastChar=std::min<size_t>(size_t(entry.length()/2),cluster.m_separators.size());
873
298
  }
874
4.52k
  input->seek(entry.begin()+2*long(firstChar), librevenge::RVNG_SEEK_SET);
875
4.52k
  long pos=input->tell();
876
4.52k
  libmwaw::DebugStream f;
877
4.52k
  f << "TextUnicode:";
878
4.52k
  bool newLine=true;
879
4.52k
  auto numLinks=cluster.m_linkPLCList.size();
880
145k
  for (size_t i=firstChar; i<lastChar; ++i) {
881
141k
    auto plcIt=cluster.m_posToStyleIdMap.lower_bound(int(i));
882
150k
    while (plcIt!=cluster.m_posToStyleIdMap.end() && plcIt->first==static_cast<int>(i)) {
883
9.33k
      int const styleId=plcIt++->second;
884
9.33k
      f << "[TS" << styleId << "]";
885
9.33k
      MWAWFont font;
886
9.33k
      MWAWParagraph para;
887
9.33k
      MWAWSection section;
888
9.33k
      if (!m_styleManager->updateTextStyles(styleId, font, para, section, totalWidth)) {
889
5.50k
        MWAW_DEBUG_MSG(("RagTime5Text::send: the style seems bad\n"));
890
5.50k
        f << "###";
891
5.50k
      }
892
3.83k
      else {
893
3.83k
        if (newLine && listener->canOpenSectionAddBreak() && section!=listener->getSection()) {
894
0
          if (listener->isSectionOpened())
895
0
            listener->closeSection();
896
0
          listener->openSection(section);
897
0
        }
898
3.83k
        listener->setParagraph(para);
899
3.83k
        listener->setFont(font);
900
3.83k
      }
901
9.33k
    }
902
903
141k
    switch (cluster.m_separators[i]) {
904
81.0k
    case 0: // none
905
82.1k
    case 2: // sign separator: .,/-(x)
906
130k
    case 3: // word separator
907
132k
    case 4: // potential hyphenate
908
132k
      break;
909
8.97k
    default: // find also 1 and 7:link?, 8, 12
910
8.97k
      f << "[m" << cluster.m_separators[i] << "]";
911
141k
    }
912
141k
    newLine=false;
913
141k
    auto linkIt=cluster.m_posToLinkIdMap.find(int(i));
914
145k
    while (linkIt!=cluster.m_posToLinkIdMap.end() && linkIt->first==int(i)) {
915
4.12k
      if (linkIt->second>=numLinks) {
916
0
        MWAW_DEBUG_MSG(("RagTime5Text::send: find a bad link\n"));
917
0
        ++linkIt;
918
0
        continue;
919
0
      }
920
4.12k
      auto const &plc=cluster.m_linkPLCList[linkIt++->second];
921
4.12k
      if (plc.m_what==3 && plc.m_positions[0]!=plc.m_positions[1]) { // index
922
0
        MWAWField field(int(i)==plc.m_positions[0] ? MWAWField::BookmarkStart : MWAWField::BookmarkEnd);
923
0
        std::stringstream s;
924
0
        s << "Index" << plc.m_id;
925
0
        field.m_data=s.str();
926
0
        listener->insertField(field);
927
0
      }
928
      // TODO when m_what==4, check if we can retrieve the formula, to decide if this is a pagenumber, ...
929
4.12k
    }
930
141k
    auto unicode=uint32_t(input->readULong(2));
931
141k
    switch (unicode) {
932
514
    case 0:
933
514
      f << "###[0]";
934
514
      break;
935
1.95k
    case 9:
936
1.95k
      listener->insertTab();
937
1.95k
      f << "\t";
938
1.95k
      break;
939
89
    case 0xb:
940
10.9k
    case 0xd:
941
10.9k
      if (i+1==lastChar && !isLastZone && unicode==0xd)
942
0
        break;
943
10.9k
      newLine=unicode==0xd;
944
10.9k
      listener->insertEOL(unicode==0xb);
945
10.9k
      ascFile.addPos(pos);
946
10.9k
      ascFile.addNote(f.str().c_str());
947
10.9k
      pos=input->tell();
948
10.9k
      f.str("");
949
10.9k
      f << "TextUnicode:";
950
10.9k
      break;
951
0
    case 0xe820: // attachment
952
0
    case 0xe824: { // footnote
953
0
      linkIt=cluster.m_posToLinkIdMap.find(int(i));
954
0
      bool find=false;
955
0
      int expectedType=unicode==0xe820 ? 0 : 5;
956
0
      while (linkIt!=cluster.m_posToLinkIdMap.end() && linkIt->first==int(i)) {
957
0
        if (linkIt->second>=numLinks) {
958
0
          MWAW_DEBUG_MSG(("RagTime5Text::send: find a bad link\n"));
959
0
          ++linkIt;
960
0
          continue;
961
0
        }
962
0
        auto const &plc=cluster.m_linkPLCList[linkIt++->second];
963
0
        if (plc.m_what!=expectedType) continue;
964
0
        find=true;
965
0
        if (unicode==0xe824) {
966
0
          if (plc.m_footnotePositions[0]>0 && plc.m_footnotePositions[0]<plc.m_footnotePositions[1]) {
967
            // add a note as comment (we are in a textbox)
968
0
            std::shared_ptr<MWAWSubDocument> doc=std::make_shared<RagTime5TextInternal::SubDocument>(*this, input, cluster, dataZone, plc.m_footnotePositions[0], plc.m_footnotePositions[1]);
969
0
            listener->insertComment(doc);
970
0
          }
971
0
        }
972
0
        else {
973
0
          if (plc.m_id<0 || plc.m_id>=int(cluster.m_childList.size())) {
974
0
            MWAW_DEBUG_MSG(("RagTime5Text::send: oops, unknown child %d\n", plc.m_id));
975
0
          }
976
0
          else {
977
0
            auto lnk=cluster.m_childList[size_t(plc.m_id)];
978
0
            auto lnkType=m_document.getClusterManager()->getClusterType(lnk.m_dataId);
979
980
0
            MWAWPosition position(MWAWVec2f(0,0), plc.m_dimensions, librevenge::RVNG_POINT);
981
0
            position.setRelativePosition(MWAWPosition::CharBaseLine, MWAWPosition::XLeft, MWAWPosition::YCenter);
982
0
            if (lnkType==RagTime5ClusterManager::Cluster::C_Unknown) {
983
0
              MWAW_DEBUG_MSG(("RagTime5Text::send: oops, child has no dataId\n"));
984
0
            }
985
0
            else if (lnkType==RagTime5ClusterManager::Cluster::C_PictureZone)
986
0
              m_document.send(lnk.m_dataId, listener, position);
987
0
            else if (lnkType==RagTime5ClusterManager::Cluster::C_SpreadsheetZone) {
988
              // let try to create a graphic object to represent the content
989
0
              MWAWBox2f box(MWAWVec2f(0,0), position.size());
990
0
              MWAWSpreadsheetEncoder spreadsheetEncoder;
991
0
              MWAWSpreadsheetListenerPtr spreadsheetListener(new MWAWSpreadsheetListener(*m_parserState, box, &spreadsheetEncoder));
992
0
              spreadsheetListener->startDocument();
993
0
              MWAWPosition spreadsheetPos;
994
0
              spreadsheetPos.m_anchorTo = MWAWPosition::Page;
995
0
              m_document.send(lnk.m_dataId, spreadsheetListener, spreadsheetPos);
996
0
              spreadsheetListener->endDocument();
997
998
0
              MWAWEmbeddedObject picture;
999
0
              if (spreadsheetEncoder.getBinaryResult(picture))
1000
0
                listener->insertPicture(position, picture);
1001
0
            }
1002
0
            else {
1003
              // let try to create a graphic object to represent the content
1004
0
              MWAWBox2f box(MWAWVec2f(0,0), position.size());
1005
0
              MWAWGraphicEncoder graphicEncoder;
1006
0
              MWAWGraphicListenerPtr graphicListener(new MWAWGraphicListener(*m_parserState, box, &graphicEncoder));
1007
0
              graphicListener->startDocument();
1008
0
              MWAWPosition graphicPos;
1009
0
              graphicPos.m_anchorTo = MWAWPosition::Page;
1010
0
              m_document.send(lnk.m_dataId, graphicListener, graphicPos);
1011
0
              graphicListener->endDocument();
1012
1013
0
              MWAWEmbeddedObject picture;
1014
0
              if (graphicEncoder.getBinaryResult(picture))
1015
0
                listener->insertPicture(position, picture);
1016
0
            }
1017
0
          }
1018
0
        }
1019
0
        break;
1020
0
      }
1021
1022
0
      if (!find) {
1023
0
        MWAW_DEBUG_MSG(("RagTime5Text::send: can not find the corresponding link\n"));
1024
0
      }
1025
0
      f << "[" << std::hex << unicode << std::dec << "]";
1026
0
      break;
1027
0
    }
1028
935
    case 0xe834: // end sub zone<
1029
4.70k
    case 0xe835: // end zone
1030
4.70k
      f << "[" << std::hex << unicode << std::dec << "]";
1031
4.70k
      break;
1032
123k
    default:
1033
123k
      if (unicode<=0x1f) {
1034
446
        MWAW_DEBUG_MSG(("RagTime5Text::send:  find an odd char %x\n", static_cast<unsigned int>(unicode)));
1035
446
        f << "[#" << std::hex << unicode << std::dec << "]";
1036
446
        break;
1037
446
      }
1038
122k
      listener->insertUnicode(unicode);
1039
122k
      if (unicode<0x80)
1040
120k
        f << char(unicode);
1041
2.45k
      else
1042
2.45k
        f << "[" << std::hex << unicode << std::dec << "]";
1043
122k
      break;
1044
141k
    }
1045
141k
  }
1046
4.52k
  if (pos!=input->tell()||firstChar==lastChar) {
1047
2.52k
    ascFile.addPos(pos);
1048
2.52k
    ascFile.addNote(f.str().c_str());
1049
2.52k
  }
1050
4.52k
  return true;
1051
4.52k
}
1052
1053
bool RagTime5Text::send(RagTime5TextInternal::ClusterText &cluster, MWAWListenerPtr listener, int blockId, int cellId, double totalWidth)
1054
9.08k
{
1055
9.08k
  if (!listener)
1056
3.61k
    listener=m_parserState->getMainListener();
1057
9.08k
  if (!listener) {
1058
0
    MWAW_DEBUG_MSG(("RagTime5Text::send: can not find the listener\n"));
1059
0
    return false;
1060
0
  }
1061
9.08k
  cluster.m_isSent=true;
1062
9.08k
  std::vector<RagTime5TextInternal::Block> const *blockZones=nullptr;
1063
9.08k
  std::vector<RagTime5TextInternal::Block> blockCell;
1064
9.08k
  if (cellId>0 && cellId<=static_cast<int>(cluster.m_blockCellList.size())) {
1065
640
    blockCell.push_back(cluster.m_blockCellList[size_t(cellId-1)]);
1066
640
    blockZones=&blockCell;
1067
640
  }
1068
8.44k
  else if (cellId>0) {
1069
92
    MWAW_DEBUG_MSG(("RagTime5Text::send: can not find the block %d in zone %d\n", cellId, cluster.m_zoneId));
1070
92
  }
1071
8.35k
  else if (blockId>0 && blockId<=static_cast<int>(cluster.m_blockList.size()) && !cluster.m_blockList[size_t(blockId-1)].empty())
1072
4.08k
    blockZones=&cluster.m_blockList[size_t(blockId-1)];
1073
4.26k
  else if (blockId>0) {
1074
522
    MWAW_DEBUG_MSG(("RagTime5Text::send: can not find the block %d in zone %d\n", blockId, cluster.m_zoneId));
1075
522
  }
1076
1077
9.08k
  std::shared_ptr<RagTime5Zone> dataZone;
1078
9.08k
  int cId=!cluster.m_contentLink.m_ids.empty() ? cluster.m_contentLink.m_ids[0] : -1;
1079
9.08k
  if (cId>0)
1080
8.21k
    dataZone=m_document.getDataZone(cId);
1081
874
  else
1082
874
    dataZone.reset();
1083
9.08k
  if (!dataZone || !dataZone->m_entry.valid() ||
1084
7.28k
      dataZone->getKindLastPart(dataZone->m_kinds[1].empty())!="Unicode") {
1085
3.09k
    MWAW_DEBUG_MSG(("RagTime5Text::send: can not find the text contents zone %d\n", cId));
1086
3.09k
    return false;
1087
3.09k
  }
1088
1089
5.99k
  dataZone->m_isParsed=true;
1090
5.99k
  if (dataZone->m_entry.length()==0) return true;
1091
1092
5.99k
  MWAWInputStreamPtr input=dataZone->getInput();
1093
5.99k
  libmwaw::DebugFile &ascFile=dataZone->ascii();
1094
5.99k
  libmwaw::DebugStream f;
1095
5.99k
  f << "Entries(TextUnicode)[" << *dataZone << "]:";
1096
5.99k
  if (dataZone->m_entry.length()%2) {
1097
325
    MWAW_DEBUG_MSG(("RagTime5Text::send: bad length for zone %d\n", cId));
1098
325
    f << "###";
1099
325
    ascFile.addPos(dataZone->m_entry.begin());
1100
325
    ascFile.addNote(f.str().c_str());
1101
325
    ascFile.addPos(dataZone->m_entry.end());
1102
325
    ascFile.addNote("_");
1103
325
    return false;
1104
325
  }
1105
5.66k
  input->setReadInverted(!cluster.m_hiLoEndian);
1106
5.66k
  input->seek(dataZone->m_entry.end()-2, librevenge::RVNG_SEEK_SET);
1107
5.66k
  if (input->readULong(2)==0xd00) {
1108
0
    static bool first=true;
1109
0
    if (first) {
1110
0
      MWAW_DEBUG_MSG(("RagTime5Text::send: must change some hiLo\n"));
1111
0
      first=false;
1112
0
    }
1113
0
    f << "###hiLo,";
1114
0
    input->setReadInverted(cluster.m_hiLoEndian);
1115
0
  }
1116
1117
5.66k
  auto N=size_t(dataZone->m_entry.length()/2);
1118
5.66k
  size_t numZones=blockZones ? blockZones->size() : 1;
1119
5.66k
  ascFile.addPos(dataZone->m_entry.begin());
1120
5.66k
  ascFile.addNote(f.str().c_str());
1121
5.66k
  ascFile.addPos(dataZone->m_entry.end());
1122
5.66k
  ascFile.addNote("_");
1123
11.3k
  for (size_t z=0; z<numZones; ++z) {
1124
5.66k
    RagTime5TextInternal::Block block;
1125
5.66k
    bool checkBlock=false;
1126
5.66k
    if (blockZones) {
1127
3.41k
      block=(*blockZones)[z];
1128
3.41k
      checkBlock=true;
1129
3.41k
    }
1130
2.25k
    else if (blockId<0) {
1131
128
      if (-blockId>static_cast<int>(cluster.m_blockCellList.size())) {
1132
2
        MWAW_DEBUG_MSG(("RagTime5Text::send: can not find blockCell %d zone\n", -blockId));
1133
2
        return true;
1134
2
      }
1135
126
      block=cluster.m_blockCellList[size_t(-blockId-1)];
1136
126
      if (block.m_plc[0]==0 && block.m_plc[1]==0)
1137
0
        return true;
1138
126
      checkBlock=true;
1139
126
    }
1140
1141
5.66k
    size_t firstChar=0, lastChar=N;
1142
5.66k
    if (checkBlock) {
1143
3.53k
      bool ok=true;
1144
10.6k
      for (int i=0; i<2; ++i) {
1145
7.07k
        if (block.m_plc[i]==0) continue;
1146
7.07k
        if (block.m_plc[i]<0 || block.m_plc[i]>static_cast<int>(cluster.m_PLCList.size())) {
1147
1.52k
          MWAW_DEBUG_MSG(("RagTime5Text::send: find bad plc id for block %d\n", cluster.m_zoneId));
1148
1.52k
          ok=false;
1149
1.52k
          continue;
1150
1.52k
        }
1151
5.55k
        if (i==0)
1152
2.73k
          firstChar=size_t(cluster.m_PLCList[size_t(block.m_plc[i]-1)].m_position);
1153
2.82k
        else
1154
2.82k
          lastChar=size_t(cluster.m_PLCList[size_t(block.m_plc[i]-1)].m_position);
1155
5.55k
      }
1156
3.53k
      if (lastChar<firstChar) {
1157
64
        MWAW_DEBUG_MSG(("RagTime5Text::send: find bad plc positions for block %d\n", cluster.m_zoneId));
1158
64
        continue;
1159
64
      }
1160
3.47k
      if (!ok) continue;
1161
2.65k
      if (lastChar>N) {
1162
49
        MWAW_DEBUG_MSG(("RagTime5Text::send: last char seems too big for block %d\n", cluster.m_zoneId));
1163
49
        lastChar=N;
1164
49
      }
1165
1166
2.65k
      auto plcIt=cluster.m_posToStyleIdMap.upper_bound(int(firstChar));
1167
2.65k
      MWAWFont font;
1168
2.65k
      MWAWParagraph para;
1169
2.65k
      MWAWSection section;
1170
2.65k
      if ((plcIt==cluster.m_posToStyleIdMap.end() || plcIt->first>static_cast<int>(firstChar)) &&
1171
2.65k
          plcIt !=cluster.m_posToStyleIdMap.begin() && m_styleManager->updateTextStyles((--plcIt)->second, font, para, section, totalWidth)) {
1172
982
        if (listener->canOpenSectionAddBreak() && section!=listener->getSection()) {
1173
42
          if (listener->isSectionOpened())
1174
0
            listener->closeSection();
1175
42
          listener->openSection(section);
1176
42
        }
1177
982
        listener->setParagraph(para);
1178
982
        listener->setFont(font);
1179
982
      }
1180
2.65k
    }
1181
1182
4.78k
    send(cluster, *dataZone, listener, firstChar, lastChar, z+1==numZones, totalWidth);
1183
4.78k
  }
1184
5.66k
  ascFile.addPos(dataZone->m_entry.end());
1185
5.66k
  ascFile.addNote("_");
1186
5.66k
  input->setReadInverted(false);
1187
5.66k
  return true;
1188
5.66k
}
1189
1190
////////////////////////////////////////////////////////////
1191
// cluster parser
1192
////////////////////////////////////////////////////////////
1193
1194
namespace RagTime5TextInternal
1195
{
1196
1197
1198
//! Internal: the helper to read a clustList
1199
struct ClustListParser final : public RagTime5StructManager::DataParser {
1200
  //! constructor
1201
  ClustListParser(RagTime5ClusterManager &clusterManager, std::string const &zoneName)
1202
16.3k
    : RagTime5StructManager::DataParser(zoneName)
1203
16.3k
    , m_clusterList()
1204
16.3k
    , m_clusterManager(clusterManager)
1205
16.3k
  {
1206
16.3k
  }
1207
  //! destructor
1208
  ~ClustListParser() final;
1209
  //! returns a name which can be used to debugging
1210
  std::string getClusterDebugName(int id) const
1211
7.41k
  {
1212
7.41k
    return m_clusterManager.getClusterDebugName(id);
1213
7.41k
  }
1214
1215
  //! try to parse a data
1216
  bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int /*n*/, libmwaw::DebugStream &f) final
1217
9.02k
  {
1218
9.02k
    long pos=input->tell();
1219
9.02k
    long fSz=endPos-pos;
1220
9.02k
    if (fSz!=10 && fSz!=12 && fSz!=14) {
1221
44
      MWAW_DEBUG_MSG(("RagTime5TextInternal::ClustListParser::parse: bad data size\n"));
1222
44
      return false;
1223
44
    }
1224
1225
8.97k
    std::vector<int> listIds;
1226
8.97k
    if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
1227
1.41k
      MWAW_DEBUG_MSG(("RagTime5TextInternal::ClustListParser::parse: can not read an cluster id\n"));
1228
1.41k
      f << "##clusterIds,";
1229
1.41k
      return false;
1230
1.41k
    }
1231
7.56k
    if (listIds[0]) {
1232
7.41k
      m_clusterList.push_back(listIds[0]);
1233
7.41k
      f << getClusterDebugName(listIds[0]) << ",";
1234
7.41k
    }
1235
7.56k
    if (fSz==12 || fSz==14) {
1236
1.03k
      unsigned long lVal=input->readULong(4); // c00..small number
1237
1.03k
      f << "f0=" << (lVal&0x3fffffff);
1238
1.03k
      if ((lVal&0xc0000000)==0xc0000000) f << "*";
1239
1.03k
      else if (lVal&0xc0000000) f << ":" << (lVal>>30);
1240
1.03k
      f << ",";
1241
1.03k
    }
1242
7.56k
    int num=fSz==12 ? 2 : 3;
1243
30.2k
    for (int i=0; i<num; ++i) { // f3=1 if fSz==14, f1=0x200, f2=1 if fSz==12
1244
22.6k
      auto val=static_cast<int>(input->readLong(2));
1245
22.6k
      if (val) f << "f" << i+1 << "=" << val << ",";
1246
22.6k
    }
1247
7.56k
    return true;
1248
8.97k
  }
1249
1250
  //! the list of read cluster
1251
  std::vector<int> m_clusterList;
1252
private:
1253
  //! the main zone manager
1254
  RagTime5ClusterManager &m_clusterManager;
1255
  //! copy constructor, not implemented
1256
  ClustListParser(ClustListParser &orig) = delete;
1257
  //! copy operator, not implemented
1258
  ClustListParser &operator=(ClustListParser &orig) = delete;
1259
};
1260
1261
ClustListParser::~ClustListParser()
1262
16.3k
{
1263
16.3k
}
1264
1265
//! Internal: the helper to read a block 2 list
1266
struct BlockCellListParser final : public RagTime5StructManager::DataParser {
1267
  //! constructor
1268
  BlockCellListParser()
1269
5.32k
    : RagTime5StructManager::DataParser("TextBlockCell")
1270
5.32k
    , m_blockList()
1271
5.32k
  {
1272
5.32k
  }
1273
  //! destructor
1274
  ~BlockCellListParser() final;
1275
  //! try to parse a data
1276
  bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int /*n*/, libmwaw::DebugStream &f) final
1277
4.11k
  {
1278
4.11k
    long pos=input->tell();
1279
4.11k
    long fSz=endPos-pos;
1280
4.11k
    if (fSz!=20) {
1281
0
      MWAW_DEBUG_MSG(("RagTime5TextInternal::BlockCellListParser::parse: bad data size\n"));
1282
0
      return false;
1283
0
    }
1284
1285
4.11k
    Block block;
1286
8.23k
    for (int &i : block.m_plc) i=static_cast<int>(input->readLong(4));
1287
4.11k
    if (block.m_plc[0]==0 && block.m_plc[1]==0) {
1288
49
      f << "empty,";
1289
49
      m_blockList.push_back(block);
1290
49
      return true;
1291
49
    }
1292
4.06k
    f << "PLC" << block.m_plc[0] << "<->" << block.m_plc[1] << ",";
1293
4.06k
    libmwaw::DebugStream f2;
1294
4.06k
    auto val=static_cast<int>(input->readULong(2));
1295
4.06k
    if (val) f2 << "fl=" << std::hex << val << std::dec << ",";
1296
12.2k
    for (int i=0; i<2; ++i) { // f0=a|1e, f1=1-e, f2=[02][145]
1297
8.13k
      val=static_cast<int>(input->readLong(2));
1298
8.13k
      if (val) f2 << "f" << i << "=" << val << ",";
1299
8.13k
    }
1300
4.06k
    auto fl=input->readULong(2);
1301
4.06k
    if (fl)
1302
4.06k
      f2 << "fl=" << std::hex << fl << std::dec << ",";
1303
20.3k
    for (int i=0; i<4; ++i) { // f3=1-30, f6=1-5c
1304
16.2k
      val=static_cast<int>(input->readLong(1));
1305
16.2k
      if (val) f2 << "f" << i+3 << "=" << val << ",";
1306
16.2k
    }
1307
4.06k
    f << f2.str();
1308
4.06k
    block.m_extra=f2.str();
1309
4.06k
    m_blockList.push_back(block);
1310
4.06k
    return true;
1311
4.11k
  }
1312
1313
  //! the list of block
1314
  std::vector<Block> m_blockList;
1315
private:
1316
  //! copy constructor, not implemented
1317
  BlockCellListParser(BlockCellListParser &orig) = delete;
1318
  //! copy operator, not implemented
1319
  BlockCellListParser &operator=(BlockCellListParser &orig) = delete;
1320
};
1321
1322
BlockCellListParser::~BlockCellListParser()
1323
5.32k
{
1324
5.32k
}
1325
1326
//
1327
//! low level: parser of text cluster
1328
//
1329
struct TextCParser final : public RagTime5ClusterManager::ClusterParser {
1330
  enum { F_block, F_indexList, F_footnote, F_linkDefs=F_footnote+2, F_parentLink=F_linkDefs+5, F_nextId, F_plc, F_plcToStyle, F_text, F_textDefs, F_textRoot, F_textList, F_unknLongs=F_textList+3, F_unknData };
1331
1332
  //! constructor
1333
  TextCParser(RagTime5ClusterManager &parser, int type, libmwaw::DebugFile &ascii)
1334
17.8k
    : ClusterParser(parser, type, "ClustText")
1335
17.8k
    , m_cluster(new ClusterText)
1336
17.8k
    , m_expectedIdToType()
1337
17.8k
    , m_NToBlockIdMap()
1338
17.8k
    , m_fieldName("")
1339
17.8k
    , m_asciiFile(ascii)
1340
17.8k
  {
1341
17.8k
  }
1342
  //! destructor
1343
  ~TextCParser() final;
1344
  //! return the current cluster
1345
  std::shared_ptr<RagTime5ClusterManager::Cluster> getCluster() final
1346
17.8k
  {
1347
17.8k
    return m_cluster;
1348
17.8k
  }
1349
  //! return the text cluster
1350
  std::shared_ptr<ClusterText> getTextCluster()
1351
35.6k
  {
1352
35.6k
    return m_cluster;
1353
35.6k
  }
1354
  //! end of a start zone call
1355
  void endZone() final
1356
135k
  {
1357
135k
    if (m_link.empty())
1358
69.0k
      return;
1359
66.1k
    auto const &it=m_expectedIdToType.find(m_dataId);
1360
66.1k
    int expected=(it!=m_expectedIdToType.end())?it->second : -1;
1361
66.1k
    switch (expected) {
1362
111
    case F_linkDefs:
1363
111
    case F_linkDefs+1:
1364
17.0k
    case F_linkDefs+2:
1365
17.0k
    case F_linkDefs+3:
1366
17.0k
    case F_linkDefs+4:
1367
17.0k
      if (m_cluster->m_linkDefs[expected-F_linkDefs].empty())
1368
17.0k
        m_cluster->m_linkDefs[expected-F_linkDefs]=m_link;
1369
0
      else {
1370
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::endZone: parent link pos %d is already set\n", expected));
1371
0
        m_cluster->m_linksList.push_back(m_link);
1372
0
      }
1373
17.0k
      break;
1374
6
    case F_footnote:
1375
6
      if (m_cluster->m_footnoteLink.empty())
1376
6
        m_cluster->m_footnoteLink=m_link;
1377
0
      else {
1378
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::endZone: footnote link is already set\n"));
1379
0
        m_cluster->m_linksList.push_back(m_link);
1380
0
      }
1381
6
      break;
1382
0
    case F_indexList:
1383
0
      if (m_cluster->m_indexLink.empty())
1384
0
        m_cluster->m_indexLink=m_link;
1385
0
      else {
1386
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::endZone: index link is already set\n"));
1387
0
        m_cluster->m_linksList.push_back(m_link);
1388
0
      }
1389
0
      break;
1390
16.3k
    case F_parentLink:
1391
16.3k
      if (m_cluster->m_parentLink.empty())
1392
16.3k
        m_cluster->m_parentLink=m_link;
1393
0
      else {
1394
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::endZone: parent link is already set\n"));
1395
0
        m_cluster->m_linksList.push_back(m_link);
1396
0
      }
1397
16.3k
      break;
1398
17.1k
    case F_plcToStyle:
1399
17.1k
      if (m_cluster->m_plcToStyleLink.empty())
1400
17.1k
        m_cluster->m_plcToStyleLink=m_link;
1401
0
      else {
1402
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::endZone: link plcToTextStyle is already set\n"));
1403
0
        m_cluster->m_linksList.push_back(m_link);
1404
0
      }
1405
17.1k
      break;
1406
15.5k
    case F_plc:
1407
15.5k
      if (m_cluster->m_plcDefLink.empty())
1408
15.5k
        m_cluster->m_plcDefLink=m_link;
1409
0
      else {
1410
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::endZone: link plcDef is already set\n"));
1411
0
        m_cluster->m_linksList.push_back(m_link);
1412
0
      }
1413
15.5k
      break;
1414
1
    case F_textList+2:
1415
1
      if (m_cluster->m_textIntListLink.empty())
1416
1
        m_cluster->m_textIntListLink=m_link;
1417
0
      else {
1418
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::endZone: link text int list is already set\n"));
1419
0
        m_cluster->m_linksList.push_back(m_link);
1420
0
      }
1421
1
      break;
1422
0
    case F_unknData+1:
1423
0
      m_cluster->m_unknownLinks1.push_back(m_link);
1424
0
      break;
1425
0
    case F_unknData+2:
1426
0
    case F_unknData+3:
1427
0
      if (m_cluster->m_unknownLink[expected-F_unknData-2].empty())
1428
0
        m_cluster->m_unknownLink[expected-F_unknData-2]=m_link;
1429
0
      else {
1430
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::endZone: unknown link %d is already set\n", expected));
1431
0
        m_cluster->m_linksList.push_back(m_link);
1432
0
      }
1433
0
      break;
1434
21
    default:
1435
21
      m_cluster->m_linksList.push_back(m_link);
1436
21
      break;
1437
66.1k
    }
1438
66.1k
  }
1439
  //! parse a zone
1440
  bool parseZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f) final
1441
135k
  {
1442
135k
    m_fieldName="";
1443
135k
    if (m_dataId==0)
1444
17.8k
      return parseHeaderZone(input,fSz,N,flag,f);
1445
1446
117k
    auto const &it=m_expectedIdToType.find(m_dataId);
1447
117k
    int expected=(it!=m_expectedIdToType.end())?it->second : -1;
1448
117k
    if (expected!=-1)
1449
113k
      f << "[F" << m_dataId << "]";
1450
    /*
1451
       normally the header is followed by num[zones] or less but sometimes block zone happens after other zones,
1452
       so just test also fSz.
1453
     */
1454
117k
    if (expected==F_block || fSz==80) {
1455
13.8k
      return parseZoneBlock(input,fSz,N,flag,f);
1456
13.8k
    }
1457
103k
    if (N<0) {
1458
158
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseZone: expected N value\n"));
1459
158
      f << "###N=" << N << ",";
1460
158
      return true;
1461
158
    }
1462
103k
    return parseDataZone(input, fSz, N, flag, f);
1463
103k
  }
1464
  //! parse a field
1465
  bool parseField(RagTime5StructManager::Field const &field, int /*m*/, libmwaw::DebugStream &f) final
1466
68.9k
  {
1467
68.9k
    if (!m_fieldName.empty())
1468
66.1k
      f << m_fieldName << ",";
1469
68.9k
    if (m_dataId==0) {
1470
17.6k
      if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==0x15e0825) {
1471
17.1k
        for (auto const &child : field.m_fieldList) {
1472
17.0k
          if (child.m_type==RagTime5StructManager::Field::T_LongList && child.m_fileType==0xd7842) {
1473
16.8k
            if ((child.m_longList.size()%3)!=0) {
1474
0
              MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: block def child seen bad\n"));
1475
0
              f << "###blockDef[sz]=" << child.m_longList.size() << ",";
1476
0
            }
1477
16.8k
            size_t N=child.m_longList.size()/3;
1478
16.8k
            m_cluster->m_blockList.resize(N);
1479
16.8k
            f << "blockDef=[";
1480
41.2k
            for (size_t b=0; b<N; ++b) {
1481
24.3k
              if (child.m_longList[3*b]==0) {
1482
11.2k
                f << "_,";
1483
11.2k
                continue;
1484
11.2k
              }
1485
13.1k
              auto id=int(child.m_longList[3*b]-1);
1486
13.1k
              m_expectedIdToType[id]=F_block;
1487
13.1k
              if (m_NToBlockIdMap.find(id)!=m_NToBlockIdMap.end()) {
1488
0
                MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: block pos is already set\n"));
1489
0
                f << "#";
1490
0
              }
1491
13.1k
              else
1492
13.1k
                m_NToBlockIdMap[id]=b;
1493
13.1k
              f << "F" << child.m_longList[3*b]-1;
1494
39.4k
              for (size_t j=1; j<3; ++j) {
1495
26.2k
                if (child.m_longList[3*b+j]) f << ":" << child.m_longList[3*b+j];
1496
13.1k
                else f << ":_";
1497
26.2k
              }
1498
13.1k
              f << ",";
1499
13.1k
            }
1500
16.8k
            f << "],";
1501
16.8k
            continue;
1502
16.8k
          }
1503
199
          MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: find unexpected unkn child[header]\n"));
1504
199
          f << "####[" << child << "],";
1505
199
        }
1506
17.1k
      }
1507
431
      else if (field.m_type==RagTime5StructManager::Field::T_LongList && field.m_fileType==0x3c057) {
1508
4
        for (auto const &id : field.m_longList) {
1509
4
          if (id) {
1510
4
            m_expectedIdToType[int(id-1)]=F_unknData;
1511
4
            f << "unknData=F" << id-1 << ",";
1512
4
          }
1513
0
          else
1514
0
            f << "unkn0=" << id << ",";
1515
4
        }
1516
4
      }
1517
      // extended header
1518
427
      else if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==0x15f9015) {
1519
0
        f << "unknExt=[";
1520
0
        for (auto const &child : field.m_fieldList) {
1521
0
          if (child.m_type==RagTime5StructManager::Field::T_Unstructured && child.m_fileType==0xce017) {
1522
0
            f << "unkn="<<child.m_extra << ",";
1523
0
            continue;
1524
0
          }
1525
0
          if (child.m_type==RagTime5StructManager::Field::T_FieldList && child.m_fileType==0x15f6815) {
1526
0
            for (auto const &child2 : child.m_fieldList) {
1527
0
              if (child2.m_type==RagTime5StructManager::Field::T_Unstructured && child2.m_fileType==0xce017) {
1528
0
                f << "unkn15f6815="<<child2.m_extra << ",";
1529
0
                continue;
1530
0
              }
1531
0
              MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: find unexpected unkn child2[header]\n"));
1532
0
              f << "###"<<child2.m_extra << ",";
1533
0
            }
1534
0
            continue;
1535
0
          }
1536
0
          MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: find unexpected unkn child[header]\n"));
1537
0
          f << "####[" << child << "],";
1538
0
        }
1539
0
        f << "],";
1540
0
      }
1541
427
      else {
1542
427
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: find unexpected header field\n"));
1543
427
        f << "###" << field;
1544
427
      }
1545
17.6k
      return true;
1546
17.6k
    }
1547
51.3k
    auto const &it=m_expectedIdToType.find(m_dataId);
1548
51.3k
    int expected=(it!=m_expectedIdToType.end())?it->second : -1;
1549
51.3k
    switch (expected) {
1550
15.8k
    case F_plc:
1551
15.8k
      if (field.m_type==RagTime5StructManager::Field::T_2Long && field.m_fileType==0x15e3017) {
1552
15.7k
        f << "unk=" << field.m_longValue[0] << "x" << field.m_longValue[1] << ",";
1553
15.7k
        break;
1554
15.7k
      }
1555
101
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: find unexpected plc link field\n"));
1556
101
      f << "###" << field;
1557
101
      break;
1558
0
    case F_linkDefs:
1559
0
    case F_linkDefs+1:
1560
149
    case F_linkDefs+2:
1561
151
    case F_linkDefs+3:
1562
151
    case F_linkDefs+4:
1563
151
      if (field.m_type==RagTime5StructManager::Field::T_FieldList &&
1564
0
          (field.m_fileType==0x15f4815 /* v5?*/ || field.m_fileType==0x160f815 /* v6? */)) {
1565
0
        f << "decal=[";
1566
0
        for (auto const &child : field.m_fieldList) {
1567
0
          if (child.m_type==RagTime5StructManager::Field::T_LongList && child.m_fileType==0xce842) {
1568
0
            for (auto val : child.m_longList)
1569
0
              f << val << ",";
1570
0
            m_link.m_longList=child.m_longList;
1571
0
            continue;
1572
0
          }
1573
0
          MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: find unexpected decal child[linkDefs]\n"));
1574
0
          f << "#[" << child << "],";
1575
0
        }
1576
0
        f << "],";
1577
0
        break;
1578
0
      }
1579
151
      else if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==0x15f4015) {
1580
0
        f << "id=[";
1581
0
        for (auto const &child : field.m_fieldList) {
1582
0
          if (child.m_type==RagTime5StructManager::Field::T_Unstructured && child.m_fileType==0xce017) {
1583
0
            f << "unkn0=" << field.m_longValue[0] << field.m_extra; // id to ?
1584
0
            continue;
1585
0
          }
1586
0
          MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: find unexpected id child[linkDefs]\n"));
1587
0
          f << "#[" << child << "],";
1588
0
        }
1589
0
        f << "],";
1590
0
        break;
1591
0
      }
1592
151
      else if (field.m_type==RagTime5StructManager::Field::T_Unstructured && field.m_fileType==0xce017) {
1593
0
        f << "unkn0=" << field.m_longValue[0] << field.m_extra; // id to a fSz=0x31 zone
1594
0
        break;
1595
0
      }
1596
151
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: find unexpected child[linkDefs]\n"));
1597
151
      f << "###" << field;
1598
151
      break;
1599
0
    case F_indexList:
1600
16.5k
    case F_parentLink:
1601
16.5k
    case F_textList:
1602
16.5k
    case F_textList+1:
1603
16.5k
    case F_unknData+2:
1604
16.5k
    case F_unknData+3:
1605
16.5k
      if (field.m_type==RagTime5StructManager::Field::T_LongList && field.m_fileType==0xce842) {
1606
16.4k
        f << "pos=[";
1607
16.4k
        for (auto val : field.m_longList)
1608
29.2k
          f << val << ",";
1609
16.4k
        f << "],";
1610
16.4k
        m_link.m_longList=field.m_longList;
1611
16.4k
        break;
1612
16.4k
      }
1613
95
      if (field.m_type==RagTime5StructManager::Field::T_Unstructured && field.m_fileType==0xce017) {
1614
        // 1,2
1615
0
        f << "unkn=" << field.m_longValue[0] <<field.m_extra << ",";
1616
0
        break;
1617
0
      }
1618
95
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: find unexpected child[clustList]\n"));
1619
95
      f << "###" << field;
1620
95
      break;
1621
17.1k
    case F_textDefs: // list of id
1622
17.1k
    case F_unknLongs:
1623
17.1k
      if (field.m_type==RagTime5StructManager::Field::T_LongList && field.m_fileType==0xcf042) {
1624
16.8k
        f << "unkn=[";
1625
16.9k
        for (auto val : field.m_longList) {
1626
16.9k
          if (val==0)
1627
38
            f << "_,";
1628
16.9k
          else
1629
16.9k
            f << val << ",";
1630
16.9k
        }
1631
16.8k
        break;
1632
16.8k
      }
1633
314
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: find unexpected child[textDefs]\n"));
1634
314
      f << "###" << field;
1635
314
      break;
1636
1.63k
    default:
1637
1.63k
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseField: find unexpected field %d\n", expected));
1638
1.63k
      f << "###" << field;
1639
1.63k
      break;
1640
51.3k
    }
1641
51.3k
    return true;
1642
51.3k
  }
1643
protected:
1644
  //! parse a data block
1645
  bool parseDataZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f)
1646
103k
  {
1647
103k
    f << "fl=" << std::hex << flag << std::dec << ",";
1648
103k
    auto const &it=m_expectedIdToType.find(m_dataId);
1649
103k
    int expected=(it!=m_expectedIdToType.end())?it->second : -1;
1650
1651
103k
    m_link.m_N=N;
1652
103k
    int val;
1653
103k
    long linkValues[4];
1654
103k
    std::string mess("");
1655
1656
103k
    switch (expected) {
1657
10
    case F_footnote:
1658
17.0k
    case F_linkDefs+2:
1659
17.0k
    case F_linkDefs+3:
1660
17.0k
    case F_linkDefs+4:
1661
33.8k
    case F_parentLink:
1662
49.6k
    case F_plc:
1663
67.0k
    case F_plcToStyle:
1664
84.3k
    case F_textDefs:
1665
84.3k
    case F_textList:
1666
84.3k
    case F_textList+1:
1667
84.3k
    case F_textList+2:
1668
84.3k
    case F_unknData+1:
1669
84.3k
    case F_unknData+2: // related to column
1670
84.3k
    case F_unknData+3: { // related to colum ?
1671
84.3k
      if (fSz<28 || !readLinkHeader(input, fSz, m_link, linkValues, mess)) {
1672
2.05k
        f << "###fType=" << RagTime5Text::printType(m_link.m_fileType[0]) << ",";
1673
2.05k
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseZone: the expected field[%d] seems bad\n", expected));
1674
2.05k
        return true;
1675
2.05k
      }
1676
82.2k
      f << m_link << "," << mess;
1677
82.2k
      long expectedFileType1=0, expectedFieldSize=0;
1678
82.2k
      if (expected==F_parentLink && fSz>=36) {
1679
16.3k
        if (m_link.m_fileType[0]) {
1680
7
          MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: unexpected file type0\n"));
1681
7
          f << "###type0";
1682
7
        }
1683
16.3k
        expectedFileType1=0x10;
1684
16.3k
        m_link.m_name="textParentLst";
1685
16.3k
        m_link.m_type=RagTime5ClusterManager::Link::L_ClusterLink;
1686
48.9k
        for (int i=0; i<2; ++i) { // small value between 3e and 74 some data id ?
1687
32.6k
          val=static_cast<int>(input->readLong(2));
1688
32.6k
          if (val) f << "g" << i << "=" << val << ",";
1689
32.6k
        }
1690
16.3k
      }
1691
65.9k
      else if (((expected>=F_linkDefs+2 && expected<=F_linkDefs+4) || expected==F_footnote) && fSz>=39) {
1692
16.7k
        if (linkValues[3]!=0x15f3817) { // fSz=39
1693
104
          MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: unexpected link type\n"));
1694
104
          f << "###values3";
1695
104
        }
1696
        // fieldSize=12|16
1697
16.7k
        expectedFileType1=expected==F_footnote ? 0x43 : 0x50;
1698
16.7k
        if (expected==F_footnote)
1699
4
          m_link.m_name="footnote";
1700
16.7k
        else
1701
16.7k
          m_link.m_name=expected==F_linkDefs+2 ? "linkDef2" : expected==F_linkDefs+3 ? "linkIndex" : "linkField";
1702
16.7k
        val=static_cast<int>(input->readLong(4)); // 1|8
1703
16.7k
        if (val) f << "g0=" << val << ",";
1704
66.9k
        for (int i=0; i<3; ++i) { // g1=language[0:US,7:UK,9:croatian...], g2=1, linkIndex: g2=9
1705
50.2k
          val=static_cast<int>(input->readLong(i==2 ? 1 : 2));
1706
50.2k
          if (!val)
1707
33.4k
            continue;
1708
16.7k
          f << "g" << i+1 << "=" << val << ",";
1709
16.7k
        }
1710
16.7k
      }
1711
49.2k
      else if (expected==F_plc && fSz>=52) {
1712
15.5k
        if (m_link.m_fileType[0]!=0 || m_link.m_fieldSize!=6) {
1713
81
          MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: unexpected plc file type\n"));
1714
81
          f << "###plc";
1715
81
        }
1716
15.5k
        expectedFileType1=0;
1717
15.5k
        m_link.m_name="plc";
1718
93.2k
        for (int i=0; i<5; ++i) { // g2=0 maybe an 2xint other small number
1719
77.7k
          val=static_cast<int>(input->readLong(4));
1720
77.7k
          switch (i) {
1721
15.5k
          case 0:
1722
15.5k
            if (m_cluster->m_plcDefFreeBegin) {
1723
0
              MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: the plc root is already set\n"));
1724
0
              f << "###";
1725
0
            }
1726
15.5k
            else
1727
15.5k
              m_cluster->m_plcDefFreeBegin=val;
1728
15.5k
            f << "free[rootId]=" << val << ",";
1729
15.5k
            break;
1730
15.5k
          case 4:
1731
15.5k
            if (m_cluster->m_plcDefNumFree<0)
1732
15.5k
              m_cluster->m_plcDefNumFree=val;
1733
15.5k
            f << "free[num]=" << val << ",";
1734
15.5k
            break;
1735
46.6k
          default:
1736
46.6k
            if (val) f << "g" << i << "=" << val << ",";
1737
46.6k
            break;
1738
77.7k
          }
1739
77.7k
        }
1740
15.5k
        val=static_cast<int>(input->readLong(2)); // always 1
1741
15.5k
        if (val!=1)
1742
114
          f << "g5=" << val << ",";
1743
15.5k
      }
1744
33.6k
      else if (expected==F_plcToStyle && fSz==34) {
1745
17.1k
        if (m_link.m_fieldSize!=6 || linkValues[3]!=0x15e4817) {
1746
264
          MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: unexpected file type1\n"));
1747
264
          f << "###type";
1748
264
        }
1749
17.1k
        m_link.m_name="plcToCStyle";
1750
17.1k
        expectedFileType1=0x47;
1751
17.1k
        val=static_cast<int>(input->readLong(4)); // always 1
1752
17.1k
        if (val!=1) f << "g0=" << val << ",";
1753
17.1k
      }
1754
16.5k
      else if (expected==F_textDefs && m_link.m_fileType[0]==0x3c052 && fSz>=41) { // fSz==41|46
1755
16.3k
        m_link.m_name="textDefs";
1756
16.3k
        expectedFileType1=0x40;
1757
16.3k
        val=static_cast<int>(input->readLong(1)); // always 1
1758
16.3k
        if (val!=1) f << "g0=" << val << ",";
1759
16.3k
        if (fSz>41) {
1760
103
          f << "#extra,";
1761
103
          input->seek(fSz-41, librevenge::RVNG_SEEK_CUR);
1762
103
        }
1763
        // first g2, g3 are ids to textZone and plc
1764
65.5k
        for (int i=0; i<3; ++i) { // g1=1, g3=g2+1
1765
49.1k
          val=static_cast<int>(input->readLong(4));
1766
49.1k
          if (!val)
1767
51
            continue;
1768
49.0k
          if (i==1) {
1769
16.3k
            m_expectedIdToType[int(val-1)]=F_text;
1770
16.3k
            f << "textZone=F" << val-1 << ",";
1771
16.3k
          }
1772
32.7k
          else if (i==2) {
1773
16.3k
            m_expectedIdToType[int(val-1)]=F_plc;
1774
16.3k
            f << "plc=F" << val-1 << ",";
1775
16.3k
          }
1776
16.3k
          else
1777
16.3k
            f << "g" << i+1 << "=" << val << ",";
1778
49.0k
        }
1779
        // in unkn0, id to textZone
1780
16.3k
      }
1781
220
      else if (expected==F_unknData+1 && fSz>=39) {
1782
0
        expectedFileType1=0x47;
1783
0
        m_link.m_name="unknData1";
1784
0
        for (int i=0; i<3; ++i) { // g0=probably previous
1785
0
          val=static_cast<int>(input->readLong(i==2 ? 1 : 4));
1786
0
          if (val) f << "g" << i << "=" << val << ",";
1787
0
        }
1788
0
      }
1789
220
      else if (expected==F_unknData+2 && fSz==32) {
1790
0
        expectedFileType1=0x210;
1791
0
        m_link.m_name="TextUnknData2";
1792
0
      }
1793
220
      else if (expected==F_unknData+3 && fSz==32) {
1794
0
        expectedFileType1=0x10;
1795
0
        m_link.m_name="TextUnknData3";
1796
0
      }
1797
      // v6.5
1798
220
      else if (expected==F_textList && m_link.m_fileType[0]==0x3e800)
1799
1
        m_link.m_name="textList0";
1800
219
      else if (expected==F_textList+1 && m_link.m_fileType[0]==0x35800)
1801
1
        m_link.m_name="textList1";
1802
218
      else if (expected==F_textList+2 && m_link.m_fileType[0]==0x45080) {
1803
0
        m_link.m_name="textListInt";
1804
0
        expectedFieldSize=2;
1805
0
      }
1806
218
      else {
1807
218
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseZone: the expected field[%d] seems bad\n", expected));
1808
218
        f << "###";
1809
218
      }
1810
82.2k
      if (!m_link.m_name.empty()) {
1811
82.0k
        m_fieldName=m_link.m_name;
1812
82.0k
        f << m_link.m_name << ",";
1813
82.0k
      }
1814
82.2k
      if (expectedFileType1>0 && long(m_link.m_fileType[1]&0xFFD7)!=expectedFileType1) {
1815
295
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: the expected field[%d] fileType1 seems odd\n", expected));
1816
295
        f << "###fileType1=" << std::hex << m_link.m_fileType[1] << std::dec << ",";
1817
295
      }
1818
82.2k
      if (expectedFieldSize>0 && m_link.m_fieldSize!=expectedFieldSize) {
1819
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: fieldSize seems odd[%d]\n", expected));
1820
0
        f << "###fieldSize,";
1821
0
      }
1822
82.2k
      return true;
1823
82.2k
    }
1824
111
    case F_linkDefs: // fSz=69 attachment
1825
111
    case F_linkDefs+1: { // fSz=71
1826
111
      m_fieldName=(expected==F_linkDefs ? "attachmentLink" : "itemLink");
1827
111
      f << m_fieldName << ",";
1828
111
      if (fSz<69) {
1829
0
        f << "##fSz,";
1830
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: unexpected size\n"));
1831
0
        return true;
1832
0
      }
1833
111
      if (!readLinkHeader(input, fSz, m_link, linkValues, mess)) {
1834
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: can not read the link position field\n"));
1835
0
        f << "###link";
1836
0
        return true;
1837
0
      }
1838
111
      if (linkValues[3]==0x15f3817) {
1839
111
        if ((m_link.m_fileType[1]&0xFFF7)!=0x43 && (m_link.m_fileType[1]&0xFFF7)!=0x50) {
1840
0
          MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: fileType1 seems odd\n"));
1841
0
          f << "###fileType1,";
1842
0
        }
1843
111
        m_link.m_name="linkDef";
1844
111
      }
1845
0
      else {
1846
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: unexpected field\n"));
1847
0
        m_fieldName="##unknown";
1848
0
      }
1849
111
      f << m_link << "," << mess;
1850
333
      for (int i=0; i<2; ++i) { // g0=1, g1=2,b,c
1851
222
        val=static_cast<int>(input->readLong(4));
1852
222
        if (val) f << "g" << i << "=" << val << ",";
1853
222
      }
1854
111
      val=static_cast<int>(input->readLong(1)); // always 0
1855
111
      if (val) f << "g2=" << val << ",";
1856
111
      val=static_cast<int>(input->readLong(2)); // always 0
1857
111
      if (val!=0x10) f << "g3=" << val << ",";
1858
111
      val=static_cast<int>(input->readLong(4)); // 1,3, 5
1859
111
      if (val) f << "g3=" << val << ",";
1860
111
      RagTime5ClusterManager::Link link2;
1861
111
      mess="";
1862
111
      if (!readLinkHeader(input, fSz, link2, linkValues, mess)) {
1863
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: can not read the link second field\n"));
1864
0
        f << "###link2";
1865
0
        return true;
1866
0
      }
1867
111
      if (fSz==69 && link2.m_fieldSize==12)
1868
111
        m_cluster->m_childLink=link2;
1869
0
      else if (fSz==71 && link2.m_ids.size()==2) {
1870
        // FIXME: store directly the field pos and set link2 as main link
1871
0
        m_link.m_ids.push_back(link2.m_ids[0]);
1872
0
        m_link.m_ids.push_back(link2.m_ids[1]);
1873
0
      }
1874
0
      else if (!link2.empty()) {
1875
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: can not find the second link field\n"));
1876
0
        f << "###";
1877
0
        m_cluster->m_linksList.push_back(link2);
1878
0
      }
1879
111
      f << "link2=[" << link2 << "]," << mess;
1880
111
      return true;
1881
111
    }
1882
15.9k
    case F_text: {
1883
15.9k
      f << "textZone,";
1884
15.9k
      if (fSz<28) {
1885
23
        f << "##fSz,";
1886
23
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: unexpected size\n"));
1887
23
        return true;
1888
23
      }
1889
15.9k
      val=static_cast<int>(input->readULong(2));
1890
15.9k
      if (val!=0x10) {
1891
112
        f << "##fType=" << std::hex << val << std::dec << ",";
1892
112
      }
1893
15.9k
      m_fieldName="textZone";
1894
15.9k
      val=static_cast<int>(input->readULong(2));
1895
15.9k
      if (val!=4) {
1896
96
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: the first value\n"));
1897
96
        f << "##f0=" << val << ",";
1898
96
      }
1899
15.9k
      val=static_cast<int>(input->readLong(2)); // always 0?
1900
15.9k
      if (val) f << "f1=" << val << ",";
1901
15.9k
      val=static_cast<int>(input->readLong(2)); // always f?
1902
15.9k
      if (val!=15) f << "f2=" << val << ",";
1903
15.9k
      std::vector<int> listIds;
1904
15.9k
      if (RagTime5StructManager::readDataIdList(input, 1, listIds) && listIds[0]) {
1905
15.6k
        if (!m_cluster->m_separatorLink.m_ids.empty()) {
1906
0
          MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: oops the text separator is already set\n"));
1907
0
          f << "###";
1908
0
        }
1909
15.6k
        m_cluster->m_separatorLink.m_ids.push_back(static_cast<int>(listIds[0]));
1910
15.6k
        f << "textSep=data" << listIds[0] << "A,";
1911
15.6k
      }
1912
278
      else {
1913
278
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: can not read the text separator\n"));
1914
278
        f << "##textSeparator,";
1915
278
      }
1916
15.9k
      m_link.m_N=static_cast<int>(input->readULong(4));
1917
15.9k
      val=static_cast<int>(input->readLong(1)); // always 0?
1918
15.9k
      if (val) f << "f3=" << val << ",";
1919
15.9k
      listIds.clear();
1920
15.9k
      if (RagTime5StructManager::readDataIdList(input, 1, listIds) && listIds[0]) {
1921
15.7k
        if (!m_cluster->m_contentLink.m_ids.empty()) {
1922
0
          MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: oops the text content is already set\n"));
1923
0
          f << "###";
1924
0
        }
1925
15.7k
        m_cluster->m_contentLink.m_ids.push_back(listIds[0]);
1926
15.7k
        f << "content=data" << listIds[0] << "A,";
1927
15.7k
      }
1928
240
      else {
1929
240
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: can not read the text content\n"));
1930
240
        f << "##textContent,";
1931
240
      }
1932
15.9k
      val=static_cast<int>(input->readLong(1)); // always 1?
1933
15.9k
      if (val) f << "f4=" << val << ",";
1934
15.9k
      f << m_link;
1935
15.9k
      return true;
1936
15.9k
    }
1937
0
    case F_footnote+1: { // checkme, seens rarely with no data...
1938
0
      f << "footnote1,";
1939
0
      if (fSz<106) {
1940
0
        f << "###fSz,";
1941
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseZone: the expected field[%d] seems bad\n", expected));
1942
0
        return true;
1943
0
      }
1944
0
      for (int i=0; i<8; ++i) { // f1=1, f2=17, f4=2048,
1945
0
        val=static_cast<int>(input->readLong(2));
1946
0
        if (val) f << "f" << i << "=" << val << ",";
1947
0
      }
1948
0
      for (int i=0; i<6; ++i) { //some dim ? dim0=dim4=712,dim1=dim5=532,dim3=517,
1949
0
        val=static_cast<int>(input->readLong(4));
1950
0
        if (val) f << "dim" << i << "=" << val << ",";
1951
0
      }
1952
0
      for (int i=0; i<7; ++i) { // f2=2
1953
0
        val=static_cast<int>(input->readLong(2));
1954
0
        if (val) f << "g" << i << "=" << val << ",";
1955
0
      }
1956
0
      for (int i=0; i<2; ++i) { //some dim ? dim6=121,dim7=74
1957
0
        val=static_cast<int>(input->readLong(4));
1958
0
        if (val) f << "dim" << i+6 << "=" << val << ",";
1959
0
      }
1960
0
      for (int i=0; i<9; ++i) { // h7=1,h8=1,
1961
0
        val=static_cast<int>(input->readLong(2));
1962
0
        if (val) f << "h" << i << "=" << val << ",";
1963
0
      }
1964
      // then 02050000000e4000020500000205000000000000
1965
0
      return true;
1966
0
    }
1967
4
    case F_unknData: { // checkme, seens rarely with no data...
1968
4
      if (fSz<49) {
1969
4
        f << "###fSz,";
1970
4
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseZone: the expected field[%d] seems bad\n", expected));
1971
4
        return true;
1972
4
      }
1973
0
      f << "unknData0,";
1974
0
      for (int i=0; i<6; ++i) { // f3=1, f4=1c
1975
0
        val=static_cast<int>(input->readLong(2));
1976
0
        if (val) f << "f" << i << "=" << val << ",";
1977
0
      }
1978
0
      auto type=input->readULong(4);
1979
0
      if (type!=0x15e0842) {
1980
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: fileType0 seems odd\n"));
1981
0
        f << "###fileType0=" << RagTime5Text::printType(type) << ",";
1982
0
      }
1983
0
      for (int i=0; i<4; ++i) { // f6=1, f7=1|2
1984
0
        val=static_cast<int>(input->readLong(2));
1985
0
        if (val) f << "f" << i+6 << "=" << val << ",";
1986
0
      }
1987
0
      for (int i=0; i<3; ++i) {
1988
0
        val=static_cast<int>(input->readLong(4));
1989
0
        if (!val) continue;
1990
0
        if (i==0) {
1991
0
          m_expectedIdToType[int(val-1)]=F_unknData+1;
1992
0
          f << "unknData1=F" << val-1 << ",";
1993
0
        }
1994
0
        else
1995
0
          f << "g" << i << "=" << val << ",";
1996
0
      }
1997
0
      for (int i=0; i<3; ++i) { // g3: big number, g5=2|3
1998
0
        val=static_cast<int>(input->readLong(2));
1999
0
        if (val) f << "g" << i+3 << "=" << val << ",";
2000
0
      }
2001
0
      val=static_cast<int>(input->readLong(1)); // always 0?
2002
0
      if (val) f << "g7=" << val << ",";
2003
0
      return true;
2004
4
    }
2005
0
    case F_block:
2006
0
    case F_nextId:
2007
0
    case F_indexList:
2008
0
    case F_textRoot:
2009
0
    case F_unknLongs:
2010
2.92k
    default:
2011
2.92k
      break;
2012
103k
    }
2013
2.92k
    if (expected==-1) {
2014
2.92k
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::PictCParser::parseDataZone: find unexpected field\n"));
2015
2.92k
      f << "###field,";
2016
2.92k
    }
2017
2.92k
    switch (fSz) {
2018
28
    case 29: // unknLong0
2019
28
    case 44: { // indexlist
2020
28
      if (!readLinkHeader(input, fSz, m_link, linkValues, mess)) {
2021
28
        f << "###fType=" << RagTime5Text::printType(m_link.m_fileType[0]) << ",";
2022
28
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseZone: the expected field[%d] seems bad\n", m_dataId));
2023
28
        return true;
2024
28
      }
2025
0
      f << m_link << "," << mess;
2026
0
      long expectedFileType1=0;
2027
0
      if (fSz==44 && linkValues[0]==0x1484017) {
2028
0
        if (m_link.m_fileType[0]) {
2029
0
          MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: unexpected file type0\n"));
2030
0
          f << "###type0";
2031
0
        }
2032
0
        expectedFileType1=0x10;
2033
0
        m_link.m_name="textIndexData";
2034
0
        m_expectedIdToType[m_dataId]=F_indexList;
2035
0
        for (int i=0; i<2; ++i) { // g0=1
2036
0
          val=static_cast<int>(input->readLong(2));
2037
0
          if (val) f << "g" << i << "=" << val << ",";
2038
0
        }
2039
0
        for (int i=0; i<2; ++i) {
2040
0
          val=static_cast<int>(input->readLong(4));
2041
0
          if (!val) continue;
2042
0
          m_expectedIdToType[int(val-1)]=F_unknData+2+i;
2043
0
          f << "unknData" << i+2 << "=F" << val-1 << ",";
2044
0
        }
2045
0
      }
2046
0
      else if (fSz==29 && m_link.m_fileType[0]==0x3c052) { // v5-v6.5
2047
0
        m_expectedIdToType[m_dataId]=F_unknLongs;
2048
0
        m_link.m_name="unknLongs0";
2049
0
        expectedFileType1=0x50;
2050
0
        val=int(input->readLong(1));
2051
0
        if (val!=1) f << "g0=" << val << ",";
2052
0
      }
2053
0
      else {
2054
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: unknow field[%d]\n", m_dataId));
2055
0
        f << "###field,";
2056
0
      }
2057
0
      if (linkValues[2]) {
2058
0
        m_expectedIdToType[int(linkValues[2]-1)]=F_nextId;
2059
0
        f << "nextId=F" << linkValues[2]-1 << ",";
2060
0
      }
2061
0
      if (!m_link.m_name.empty()) {
2062
0
        m_fieldName=m_link.m_name;
2063
0
        f << m_link.m_name << ",";
2064
0
      }
2065
0
      if (expectedFileType1>0 && long(m_link.m_fileType[1]&0xFFD7)!=expectedFileType1) {
2066
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: the expected field[%d] fileType1 seems odd\n", expected));
2067
0
        f << "###fileType1=" << std::hex << m_link.m_fileType[1] << std::dec << ",";
2068
0
      }
2069
0
      return true;
2070
28
    }
2071
162
    case 36: { // v6.6
2072
162
      m_fieldName="textList[root]";
2073
162
      m_expectedIdToType[m_dataId]=F_textRoot;
2074
162
      f << m_fieldName << ",";
2075
162
      val=static_cast<int>(input->readLong(4));
2076
162
      if (val) f << "#f0=" << val << ",";
2077
162
      val=static_cast<int>(input->readLong(4));
2078
162
      if (val!=0x17db042) {
2079
162
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseZone: find unexpected type0\n"));
2080
162
        f << "#fileType0=" << std::hex << val << std::dec << ",";
2081
162
      }
2082
486
      for (int i=0; i<2; ++i) {
2083
324
        val=static_cast<int>(input->readLong(4));
2084
324
        if (val) f << "f" << i+1 << "=" << val << ",";
2085
324
      }
2086
162
      val=static_cast<int>(input->readULong(2));
2087
162
      if ((val&0xFFD7)!=0x10) {
2088
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseZone: find unexpected type1[fSz36]\n"));
2089
0
        f << "#fileType1=" << std::hex << val << std::dec << ",";
2090
0
      }
2091
162
      f << "ids=[";
2092
648
      for (int i=0; i<3; ++i) {
2093
486
        val=static_cast<int>(input->readLong(4));
2094
486
        if (!val) {
2095
151
          f << "_,";
2096
151
          continue;
2097
151
        }
2098
335
        m_expectedIdToType[val-1]=F_textList+i;
2099
335
        f << "F" << val-1 << ",";
2100
335
      }
2101
162
      f << "],";
2102
162
      return true;
2103
28
    }
2104
2.73k
    default:
2105
2.73k
      break;
2106
2.92k
    }
2107
2.73k
    f << "###fSz=" << fSz;
2108
2.73k
    MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseDataZone: find unexpected field size\n"));
2109
2.73k
    return true;
2110
2.92k
  }
2111
  //! parse the header zone
2112
  bool parseHeaderZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f)
2113
17.8k
  {
2114
17.8k
    f << "header, fl=" << std::hex << flag << std::dec << ",";
2115
17.8k
    m_fieldName="header";
2116
17.8k
    if (N!=-5 || m_dataId!=0 || (fSz!=135 && fSz!=140 && fSz!=143 && fSz!=208 && fSz!=212 && fSz!=213 && fSz!=216)) {
2117
11
      f << "###N=" << N << ",fSz=" << fSz << ",";
2118
11
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseHeaderZone: find unexpected main field\n"));
2119
11
      return true;
2120
11
    }
2121
17.7k
    bool hasData1=(fSz==140||fSz==213);
2122
17.7k
    int numData2=(fSz==143||fSz==216) ? 2 : fSz==212 ? 1 : 0;
2123
17.7k
    int val;
2124
53.3k
    for (int i=0; i<2; ++i) { // always 0?
2125
35.5k
      val=static_cast<int>(input->readLong(2));
2126
35.5k
      if (val) f << "f" << i << "=" << val << ",";
2127
35.5k
    }
2128
17.7k
    val=static_cast<int>(input->readLong(2));
2129
17.7k
    f << "id=" << val << ",";
2130
17.7k
    val=static_cast<int>(input->readULong(2));
2131
17.7k
    if (m_type>0 && val!=m_type) {
2132
30
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseHeaderZone: unexpected zone type\n"));
2133
30
      f << "##zoneType=" << std::hex << val << std::dec << ",";
2134
30
    }
2135
    // f2,f3 are also some ids to listClust and to zone:longs2
2136
53.3k
    for (int i=0; i<2; ++i) { // f2=9-5d, f3=0
2137
35.5k
      val=static_cast<int>(input->readLong(4));
2138
35.5k
      if (!val) continue;
2139
17.7k
      if (i==0) {
2140
17.6k
        m_expectedIdToType[int(val-1)]=F_parentLink;
2141
17.6k
        f << "textParentLst=F" << val-1 << ",";
2142
17.6k
      }
2143
109
      else {
2144
109
        m_expectedIdToType[int(val-1)]=F_nextId;
2145
109
        f << "nextId=F" << val-1 << ",";
2146
109
      }
2147
17.7k
    }
2148
17.7k
    val=static_cast<int>(input->readLong(1)); // 0|1
2149
17.7k
    if (val)
2150
232
      f << "fl=" << val << ",";
2151
17.7k
    val=static_cast<int>(input->readULong(2));
2152
17.7k
    if (val&1)
2153
0
      f << "area[widest,only],";
2154
17.7k
    if (val&8)
2155
0
      f << "noShift[baseline,start],";
2156
17.7k
    if (val&0x10)
2157
18
      f << "recalculate[demand],";
2158
17.7k
    if (val&0x1000)
2159
15
      f << "vertical[writing],";
2160
17.7k
    val&=0xefe6;
2161
17.7k
    if (val) // [08]0[08][049]
2162
17.7k
      f << "fl2=" << std::hex << val << std::dec << ",";
2163
17.7k
    val=static_cast<int>(input->readLong(1)); // 1|1d
2164
17.7k
    if ((val&1)==0) f << "hyphen[end],";
2165
17.7k
    if (val&2) f << "column[balanced],";
2166
17.7k
    if ((val&4)==0) f << "space[between,para,sum],";
2167
17.7k
    if ((val&8)==0) f << "footnote[endComponent],";
2168
17.7k
    if (val&0x10) f << "footnote[number,restart],";
2169
17.7k
    if (val&0x20) f << "footnote[symbol,cycle],";
2170
17.7k
    val &= 0xc0;
2171
17.7k
    if (val)
2172
20
      f << "fl3=" << std::hex << val << std::dec << ",";
2173
17.7k
    val=static_cast<int>(input->readULong(2)); // alway 10
2174
17.7k
    if (val!=0x10)
2175
95
      f << "f4=" << val << ",";
2176
17.7k
    int numZones=static_cast<int>(input->readLong(4));
2177
17.7k
    if (numZones)
2178
17.6k
      f << "num[zones]=" << numZones << ",";
2179
213k
    for (int i=0; i<11; ++i) { // g8=40|60
2180
195k
      val=static_cast<int>(input->readLong(2));
2181
195k
      if (val)
2182
18.7k
        f << "g" << i << "=" << val << ",";
2183
195k
    }
2184
17.7k
    val=static_cast<int>(input->readLong(1)); // always 1
2185
17.7k
    if (val!=1)
2186
122
      f << "fl4=" << val << ",";
2187
17.7k
    if (hasData1) {
2188
42
      for (int i=0; i<5; ++i) { // unsure find only 0 here
2189
35
        val=static_cast<int>(input->readLong(1));
2190
35
        if (val)
2191
7
          f << "flA" << i << "=" << val << ",";
2192
35
      }
2193
7
    }
2194
2195
53.3k
    for (int i=0; i<2; ++i) { // always 1,2 checkme id?
2196
35.5k
      val=static_cast<int>(input->readLong(4));
2197
35.5k
      if (val!=i+1)
2198
377
        f << "h" << i << "=" << val << ",";
2199
35.5k
    }
2200
53.3k
    for (int i=0; i<2; ++i) { // always 0,4
2201
35.5k
      val=static_cast<int>(input->readLong(2));
2202
35.5k
      if (!val) continue;
2203
17.7k
      if (i==1)
2204
17.7k
        f << "column[line,style]=" << val << ","; // 5: contain border, ...
2205
22
      else
2206
22
        f << "h" << i+2 << "=" << val << ",";
2207
17.7k
    }
2208
88.9k
    for (int i=0; i<4; ++i) { // always h4=3, h5=id to plcToCStyle and zone:longs2
2209
71.1k
      val=static_cast<int>(input->readLong(4));
2210
71.1k
      if (!val) continue;
2211
53.5k
      if (i==1) {
2212
17.7k
        m_expectedIdToType[int(val-1)]=F_plcToStyle;
2213
17.7k
        f << "plcToStyle=F" << val-1 << ",";
2214
17.7k
      }
2215
35.7k
      else if (i==2) {
2216
17.7k
        m_expectedIdToType[int(val-1)]=F_textDefs;
2217
17.7k
        f << "textDefs=F" << val-1 << ",";
2218
17.7k
      }
2219
17.9k
      else if (i==3) {
2220
146
        m_expectedIdToType[int(val-1)]=F_footnote;
2221
146
        f << "footnote=F" << val-1 << ",";
2222
146
      }
2223
17.7k
      else
2224
17.7k
        f << "h" << i+4 << "=" << val << ",";
2225
53.5k
    }
2226
53.3k
    for (int i=0; i<2; ++i) {  // always 1,4
2227
35.5k
      val=static_cast<int>(input->readLong(2));
2228
35.5k
      if (!val) continue;
2229
29.9k
      if (i==1)
2230
17.7k
        f << "footnote[sep,style]=" << val << ",";
2231
12.2k
      else
2232
12.2k
        f << "h" << i+8 << "=" << val << ",";
2233
29.9k
    }
2234
17.7k
    auto sepLen=input->readULong(4);
2235
17.7k
    if (sepLen!=0x5555)
2236
186
      f << "footnote[len,separator]=" << 100*double(sepLen)/double(0x10000) << "%,";
2237
17.7k
    sepLen=input->readULong(4);
2238
17.7k
    if (sepLen!=0x18000)
2239
240
      f << "footnote[margins,vert]=" << 100*double(sepLen)/double(0x10000) << "%,";
2240
106k
    for (int i=0; i<5; ++i) { // always 0
2241
88.9k
      val=static_cast<int>(input->readLong(2));
2242
88.9k
      if (!val) continue;
2243
446
      f << "j" << i << "=" << val << ",";
2244
446
    }
2245
106k
    for (int i=0; i<5; ++i) { // j5=0|5, j6=0|5,
2246
88.9k
      val=static_cast<int>(input->readLong(4));
2247
88.9k
      if (!val) continue;
2248
18.5k
      m_expectedIdToType[int(val-1)]=F_linkDefs+i;
2249
18.5k
      char const *what[]= {"attachLink", "itemLink", "linkDef2", "indexLink", "fieldLink"};
2250
18.5k
      f << what[i] << "=F" << val-1 << ",";
2251
18.5k
    }
2252
17.7k
    f << "IDS=[";
2253
53.3k
    for (int i=0; i<2; ++i) // unsure, junk
2254
35.5k
      f << std::hex << input->readULong(4) << std::dec << ",";
2255
17.7k
    f << "],";
2256
17.7k
    val=static_cast<int>(input->readULong(2)); // c00|cef
2257
17.7k
    if (val)
2258
17.7k
      f << "fl5=" << std::hex << val << std::dec << ",";
2259
17.8k
    for (int i=0; i<numData2; ++i) { // always 0
2260
38
      val=static_cast<int>(input->readLong(4));
2261
38
      if (!val) continue;
2262
38
      if (i==1) {
2263
19
        m_expectedIdToType[int(val-1)]=F_linkDefs+1; // checkme: another link with fSz=47
2264
19
        f << "linkDef1[bis]=F" << val-1 << ",";
2265
19
      }
2266
19
      else
2267
19
        f << "k" << i << "=" << val << ",";
2268
38
    }
2269
17.7k
    if (fSz<=143)
2270
12.2k
      return true;
2271
2272
5.51k
    f << "link2=[";
2273
5.51k
    long linkValues[4];
2274
5.51k
    std::string mess("");
2275
5.51k
    val=static_cast<int>(input->readULong(2));
2276
5.51k
    if (val!=0x10) f << "fl=" << std::hex << val << std::dec << ",";
2277
5.51k
    RagTime5ClusterManager::Link link2;
2278
5.51k
    link2.m_N=static_cast<int>(input->readLong(4));
2279
5.51k
    mess="";
2280
5.51k
    if (!readLinkHeader(input, fSz, link2, linkValues, mess)) {
2281
24
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseHeaderZone: can not read the second link\n"));
2282
24
      f << "###link2";
2283
24
      return true;
2284
24
    }
2285
5.49k
    if (linkValues[3]==0x15f3817 && link2.m_fieldSize==20)
2286
5.46k
      m_cluster->m_blockCellToPlcLink=link2;
2287
34
    else {
2288
34
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseHeaderZone: blockCell to plc link\n"));
2289
34
      f << "###";
2290
34
    }
2291
5.49k
    f << link2 << "," << mess;
2292
16.4k
    for (int i=0; i<2; ++i) { // always 1 and 4
2293
10.9k
      val=static_cast<int>(input->readLong(4));
2294
10.9k
      if (val) f << "f" << i << "=" << val << ",";
2295
10.9k
    }
2296
5.49k
    val=static_cast<int>(input->readLong(1)); // always 1
2297
5.49k
    if (val) f << "f2=" << val << ",";
2298
5.49k
    f << "],";
2299
2300
5.49k
    f << "link3=[";
2301
5.49k
    val=static_cast<int>(input->readULong(2));
2302
5.49k
    if (val!=0x10) f << "fl=" << std::hex << val << std::dec << ",";
2303
5.49k
    RagTime5ClusterManager::Link link3;
2304
5.49k
    link3.m_N=static_cast<int>(input->readLong(4));
2305
5.49k
    mess="";
2306
5.49k
    if (!readLinkHeader(input, fSz, link3, linkValues, mess)) {
2307
5
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseHeaderZone: can not read the third link\n"));
2308
5
      f << "###link3";
2309
5
      return true;
2310
5
    }
2311
5.48k
    if (link3.m_fieldSize==12)
2312
5.48k
      m_cluster->m_unknownLink[2]=link3;
2313
0
    else {
2314
0
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseHeaderZone: third link seems bad\n"));
2315
0
      f << "###";
2316
0
    }
2317
5.48k
    f << link3 << "," << mess;
2318
5.48k
    f << "],";
2319
2320
5.48k
    std::vector<int> listIds;
2321
5.48k
    if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
2322
0
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseHeaderZone: can not read an cluster id\n"));
2323
0
      f << "##clusterIds,";
2324
0
      return false;
2325
0
    }
2326
5.48k
    if (listIds[0]) {
2327
5.47k
      m_cluster->m_clusterIdsList.push_back(listIds[0]);
2328
5.47k
      f << "cluster=" << getClusterDebugName(listIds[0]) << ",";
2329
5.47k
    }
2330
5.48k
    return true;
2331
5.48k
  }
2332
  //! parse a zone block
2333
  bool parseZoneBlock(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f)
2334
13.8k
  {
2335
13.8k
    if (N<0 || fSz!=80) {
2336
159
      f << "###N=" << N << ",fSz=" << fSz << ",";
2337
159
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseZoneBlock: find unexpected main field\n"));
2338
159
      return false;
2339
159
    }
2340
13.6k
    RagTime5TextInternal::Block block;
2341
13.6k
    m_fieldName="block";
2342
13.6k
    std::string debugHeader=f.str();
2343
13.6k
    f.str("");
2344
13.6k
    if (N!=1) {
2345
102
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseZoneBlock: zone N seems badA\n"));
2346
102
      f << "#N=" << N << ",";
2347
102
    }
2348
13.6k
    auto val=static_cast<int>(input->readULong(2)); // always 0?
2349
13.6k
    if (val) f << "f0=" << val << ",";
2350
13.6k
    block.m_id=static_cast<int>(input->readULong(2));
2351
13.6k
    val=static_cast<int>(input->readULong(2)); //[04][01248a][01][23]
2352
13.6k
    if (val) f << "fl=" << std::hex << val << std::dec << ",";
2353
13.6k
    block.m_subId=static_cast<int>(input->readULong(2));
2354
13.6k
    val=static_cast<int>(input->readULong(2)); //f1=0|3ffe
2355
13.6k
    if (val) f << "f1=" << val << ",";
2356
13.6k
    float dim[4];
2357
54.5k
    for (auto &d : dim) d=float(input->readLong(4))/65536.f;
2358
13.6k
    block.m_dimension=MWAWBox2f(MWAWVec2f(dim[0],dim[1]), MWAWVec2f(dim[2],dim[3]));
2359
54.5k
    for (float &i : dim) i=float(input->readLong(4))/65536.f;
2360
13.6k
    MWAWBox2f box2(MWAWVec2f(dim[0],dim[1]), MWAWVec2f(dim[2],dim[3]));
2361
13.6k
    if (block.m_dimension!=box2)
2362
367
      f << "boxA=" << box2 << ",";
2363
13.6k
    int nextId=0;
2364
68.2k
    for (int i=0; i<4; ++i) { // g1=0|2, g3=9|7
2365
54.5k
      val=static_cast<int>(input->readLong(i<2 ? 4 : 2));
2366
54.5k
      if (!val) continue;
2367
390
      switch (i) {
2368
293
      case 0: // prev
2369
293
        m_expectedIdToType[int(val-1)]=F_block;
2370
293
        f << "prev=F" << val-1 << ",";
2371
293
        break;
2372
6
      case 1: // next
2373
6
        m_expectedIdToType[int(val-1)]=F_block;
2374
6
        f << "next=F" << val-1 << ",";
2375
6
        nextId=val;
2376
6
        break;
2377
7
      case 3:
2378
7
        m_expectedIdToType[int(val-1)]=F_footnote+1;
2379
7
        f << "footnote1=F" << val-1 << ",";
2380
7
        break;
2381
84
      default:
2382
84
        f << "g" << i << "=" << val << ",";
2383
84
        break;
2384
390
      }
2385
390
    }
2386
27.2k
    for (int &i : block.m_plc) i=static_cast<int>(input->readULong(4));
2387
95.5k
    for (int i=0; i<6; ++i) { // h1=h2=0|-1
2388
81.8k
      val=static_cast<int>(input->readLong(2));
2389
81.8k
      if (!val) continue;
2390
395
      f << "h" << i << "=" << val << ",";
2391
395
    }
2392
13.6k
    block.m_extra=f.str();
2393
13.6k
    f.str("");
2394
13.6k
    f << debugHeader << "block,fl=" << std::hex << flag << std::dec << "," << block;
2395
2396
13.6k
    if (m_NToBlockIdMap.find(m_dataId)==m_NToBlockIdMap.end()) {
2397
867
      MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseZoneBlock: unknown block for N=%d\n", m_dataId));
2398
867
      f << "###unknown,";
2399
867
    }
2400
12.7k
    else {
2401
12.7k
      m_cluster->m_blockList[m_NToBlockIdMap.find(m_dataId)->second].push_back(block);
2402
12.7k
      if (nextId && m_NToBlockIdMap.find(nextId)!=m_NToBlockIdMap.end()) {
2403
0
        MWAW_DEBUG_MSG(("RagTime5TextInternal::TextCParser::parseZoneBlock: next id block for N=%d is already set\n", nextId));
2404
0
        f << "###nextId,";
2405
0
      }
2406
12.7k
      else if (nextId)
2407
4
        m_NToBlockIdMap[nextId-1]=m_NToBlockIdMap.find(m_dataId)->second;
2408
12.7k
    }
2409
13.6k
    return true;
2410
13.6k
  }
2411
2412
  //! the current cluster
2413
  std::shared_ptr<ClusterText> m_cluster;
2414
  //! the expected id
2415
  std::map<int, int> m_expectedIdToType;
2416
  //! the field pos to block map
2417
  std::map<int, size_t> m_NToBlockIdMap;
2418
  //! the actual field name
2419
  std::string m_fieldName;
2420
  //! the ascii file
2421
  libmwaw::DebugFile &m_asciiFile;
2422
private:
2423
  //! copy constructor (not implemented)
2424
  TextCParser(TextCParser const &orig) = delete;
2425
  //! copy operator (not implemented)
2426
  TextCParser &operator=(TextCParser const &orig) = delete;
2427
};
2428
2429
TextCParser::~TextCParser()
2430
17.8k
{
2431
17.8k
}
2432
2433
}
2434
2435
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Text::readTextCluster(RagTime5Zone &zone, int zoneType)
2436
17.8k
{
2437
17.8k
  auto clusterManager=m_document.getClusterManager();
2438
17.8k
  if (!clusterManager) {
2439
0
    MWAW_DEBUG_MSG(("RagTime5Text::readTextCluster: oops can not find the cluster manager\n"));
2440
0
    return std::shared_ptr<RagTime5ClusterManager::Cluster>();
2441
0
  }
2442
17.8k
  RagTime5TextInternal::TextCParser parser(*clusterManager, zoneType, zone.ascii());
2443
17.8k
  if (!clusterManager->readCluster(zone, parser) || !parser.getTextCluster()) {
2444
7
    MWAW_DEBUG_MSG(("RagTime5Text::readTextCluster: oops can not find the cluster\n"));
2445
7
    return std::shared_ptr<RagTime5ClusterManager::Cluster>();
2446
7
  }
2447
17.8k
  auto cluster=parser.getTextCluster();
2448
17.8k
  if (m_state->m_idTextMap.find(zone.m_ids[0])!=m_state->m_idTextMap.end()) {
2449
1.57k
    MWAW_DEBUG_MSG(("RagTime5Text::readTextCluster: oops text zone %d is already stored\n", zone.m_ids[0]));
2450
1.57k
  }
2451
16.2k
  else
2452
16.2k
    m_state->m_idTextMap[zone.m_ids[0]]=cluster;
2453
17.8k
  m_document.checkClusterList(cluster->m_clusterIdsList);
2454
2455
17.8k
  if (!cluster->m_dataLink.empty()) {
2456
0
    MWAW_DEBUG_MSG(("RagTime5Text::readTextCluster: oops do not know how to read the dataLink\n"));
2457
0
  }
2458
2459
  // the text<->separator zone cluster->m_separatorLink.m_ids[0] will be parsed when we send the cluster
2460
  // the textzone cluster->m_contentLink.m_ids[0] will be parsed when we send the cluster
2461
2462
17.8k
  if (!cluster->m_plcDefLink.m_ids.empty())
2463
15.5k
    readPLC(*cluster, cluster->m_plcDefLink.m_ids[0]);
2464
17.8k
  readPLCToCharStyle(*cluster); // read cluster->m_plcToStyleLink
2465
17.8k
  if (!cluster->m_blockCellToPlcLink.empty()) {
2466
5.32k
    RagTime5TextInternal::BlockCellListParser blockCellParser;
2467
5.32k
    m_document.readFixedSizeZone(cluster->m_blockCellToPlcLink, blockCellParser);
2468
5.32k
    cluster->m_blockCellList=blockCellParser.m_blockList;
2469
5.32k
  }
2470
17.8k
  for (auto const &link : cluster->m_unknownLinks1)
2471
0
    m_document.readFixedSizeZone(link, "TextUnkn0");
2472
17.8k
  if (!cluster->m_unknownLink[0].empty()) { // some unicode string related to index ?
2473
0
    std::map<int, librevenge::RVNGString> idToStringMap;
2474
0
    m_document.readUnicodeStringList(RagTime5ClusterManager::NameLink(cluster->m_unknownLink[0]), idToStringMap);
2475
0
  }
2476
17.8k
  if (!cluster->m_unknownLink[1].empty()) // related to column/section ?
2477
0
    m_document.readListZone(cluster->m_unknownLink[1]);
2478
17.8k
  if (!cluster->m_unknownLink[2].empty())
2479
5.36k
    m_document.readFixedSizeZone(cluster->m_unknownLink[2], "TextUnkn3");
2480
  // parent zones:  graphic or pipeline, ...
2481
17.8k
  if (!cluster->m_parentLink.empty()) {
2482
16.3k
    RagTime5TextInternal::ClustListParser linkParser(*clusterManager, "TextParentLst");
2483
16.3k
    m_document.readListZone(cluster->m_parentLink, linkParser);
2484
16.3k
    m_document.checkClusterList(linkParser.m_clusterList);
2485
16.3k
  }
2486
17.8k
  if (!cluster->m_indexLink.empty())
2487
0
    m_document.readListZone(cluster->m_indexLink);
2488
17.8k
  if (!cluster->m_childLink.empty()) {
2489
111
    cluster->m_childLink.m_name="TextChildLst";
2490
111
    m_document.readChildList(cluster->m_childLink, cluster->m_childList, true);
2491
111
  }
2492
106k
  for (int i=0; i<5; ++i) {
2493
    // 0: attachement, pos sz=32, id, dim, ??
2494
    // 1: item: list type in unicode pos sz=12 or v6 sz=24
2495
    // 2: maybe end doc or section, pos sz=16
2496
    // 3: index, pos sz=12
2497
    // 4: pos sz=16
2498
89.0k
    auto &lnk = cluster->m_linkDefs[i];
2499
89.0k
    if (lnk.empty()) continue;
2500
17.0k
    std::stringstream s;
2501
17.0k
    if (i==0)
2502
111
      lnk.m_name="TextLinkAttach";
2503
16.9k
    else if (i==1)
2504
0
      lnk.m_name="TextLinkItem";
2505
16.9k
    else if (i==3)
2506
2
      lnk.m_name="TextLinkIndex";
2507
16.9k
    else if (i==4)
2508
3
      lnk.m_name="TextLinkFormula";
2509
16.9k
    else {
2510
16.9k
      s << "TextLink" << i;
2511
16.9k
      lnk.m_name=s.str();
2512
16.9k
    }
2513
17.0k
    readLinkZones(*cluster, lnk, i);
2514
17.0k
  }
2515
17.8k
  if (!cluster->m_footnoteLink.empty()) {
2516
6
    cluster->m_footnoteLink.m_name="TextLinkFootnote";
2517
6
    readLinkZones(*cluster, cluster->m_footnoteLink, 5);
2518
6
  }
2519
17.8k
  if (!cluster->m_textIntListLink.empty()) { // only v6
2520
1
    std::vector<long> intList;
2521
1
    cluster->m_textIntListLink.m_name="TextListInt";
2522
1
    m_document.readLongList(cluster->m_textIntListLink, intList);
2523
1
  }
2524
17.8k
  for (auto const &link : cluster->m_linksList) {
2525
21
    if (link.m_type==RagTime5ClusterManager::Link::L_List) {
2526
6
      m_document.readListZone(link);
2527
6
      continue;
2528
6
    }
2529
15
    std::stringstream s;
2530
15
    s << "Text_Data" << link.m_fieldSize;
2531
15
    RagTime5StructManager::DataParser defaultParser(link.m_name.empty() ? s.str() : link.m_name);
2532
15
    m_document.readFixedSizeZone(link, defaultParser);
2533
15
  }
2534
17.8k
  return cluster;
2535
17.8k
}
2536
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: