Coverage Report

Created: 2026-03-12 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/ClarisWksText.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 <iomanip>
35
#include <iostream>
36
#include <limits>
37
#include <map>
38
#include <sstream>
39
40
#include <librevenge/librevenge.h>
41
42
#include "MWAWFont.hxx"
43
#include "MWAWFontConverter.hxx"
44
#include "MWAWGraphicListener.hxx"
45
#include "MWAWListener.hxx"
46
#include "MWAWParagraph.hxx"
47
#include "MWAWParser.hxx"
48
#include "MWAWPosition.hxx"
49
#include "MWAWSection.hxx"
50
51
#include "ClarisWksDocument.hxx"
52
#include "ClarisWksStruct.hxx"
53
#include "ClarisWksStyleManager.hxx"
54
55
#include "ClarisWksText.hxx"
56
57
/** Internal: the structures of a ClarisWksText */
58
namespace ClarisWksTextInternal
59
{
60
/** the different plc type */
61
enum PLCType { P_Font,  P_Ruler, P_Child, P_Section, P_TextZone, P_Token, P_Unknown};
62
63
/** Internal : the different plc types: mainly for debugging */
64
struct PLC {
65
  /// the constructor
66
  PLC()
67
2.50M
    : m_type(P_Unknown)
68
2.50M
    , m_id(-1)
69
2.50M
    , m_extra("")
70
2.50M
  {
71
2.50M
  }
72
  //! operator<<
73
  friend std::ostream &operator<<(std::ostream &o, PLC const &plc);
74
  /** the PLC types */
75
  PLCType m_type;
76
  /** the id */
77
  int m_id;
78
  /** extra data */
79
  std::string m_extra;
80
};
81
82
std::ostream &operator<<(std::ostream &o, PLC const &plc)
83
0
{
84
0
  switch (plc.m_type) {
85
0
  case P_Font:
86
0
    o << "F";
87
0
    break;
88
0
  case P_Ruler:
89
0
    o << "R";
90
0
    break;
91
0
  case P_Child:
92
0
    o << "C";
93
0
    break;
94
0
  case P_Section:
95
0
    o << "S";
96
0
    break;
97
0
  case P_TextZone:
98
0
    o << "TZ";
99
0
    break;
100
0
  case P_Token:
101
0
    o << "Tok";
102
0
    break;
103
0
  case P_Unknown:
104
#if !defined(__clang__)
105
  default:
106
#endif
107
0
    o << "#Unkn";
108
0
    break;
109
0
  }
110
0
  if (plc.m_id >= 0) o << plc.m_id;
111
0
  else o << "_";
112
0
  if (plc.m_extra.length()) o << ":" << plc.m_extra;
113
0
  return o;
114
0
}
115
116
/** Internal: class to store the paragraph properties */
117
struct Paragraph final : public MWAWParagraph {
118
  //! constructor
119
  Paragraph()
120
3.90M
    : MWAWParagraph()
121
3.90M
    , m_labelType(0)
122
3.90M
  {
123
3.90M
  }
124
2.74M
  Paragraph(Paragraph const &)=default;
125
1.85M
  Paragraph &operator=(Paragraph const &)=default;
126
  //! destructor
127
  ~Paragraph() final;
128
  //! operator<<
129
  friend std::ostream &operator<<(std::ostream &o, Paragraph const &ind)
130
0
  {
131
0
    o << static_cast<MWAWParagraph const &>(ind) << ",";
132
0
    if (ind.m_labelType > 0 && ind.m_labelType < 12) {
133
0
      static char const* const labelNames[] = {
134
0
        "none", "diamond", "bullet", "checkbox", "hardvard", "leader", "legal",
135
0
        "upperalpha", "alpha", "numeric", "upperroman", "roman"
136
0
      };
137
0
      o << "label=" << labelNames[ind.m_labelType] << ",";
138
0
    }
139
0
    else if (ind.m_labelType)
140
0
      o << "#labelType=" << ind.m_labelType << ",";
141
0
    return o;
142
0
  }
143
  //! update the list level
144
  void updateListLevel();
145
  //! the label
146
  int m_labelType;
147
};
148
149
Paragraph::~Paragraph()
150
6.64M
{
151
6.64M
}
152
153
void Paragraph::updateListLevel()
154
2.04M
{
155
2.04M
  int extraLevel = m_labelType!=0 ? 1 : 0;
156
2.04M
  if (*m_listLevelIndex+extraLevel<=0)
157
2.04M
    return;
158
1.54k
  int lev = *m_listLevelIndex+extraLevel;
159
1.54k
  m_listLevelIndex = lev;
160
1.54k
  MWAWListLevel theLevel;
161
1.54k
  theLevel.m_labelWidth=0.2;
162
1.54k
  switch (m_labelType) {
163
50
  case 0:
164
50
    theLevel.m_type = MWAWListLevel::NONE;
165
50
    break;
166
174
  case 1: // diamond
167
174
    theLevel.m_type = MWAWListLevel::BULLET;
168
174
    libmwaw::appendUnicode(0x25c7, theLevel.m_bullet);
169
174
    break;
170
176
  case 3: // checkbox
171
176
    theLevel.m_type = MWAWListLevel::BULLET;
172
176
    libmwaw::appendUnicode(0x2610, theLevel.m_bullet);
173
176
    break;
174
24
  case 4: {
175
24
    theLevel.m_suffix = (lev <= 3) ? "." : ")";
176
24
    if (lev == 1) theLevel.m_type = MWAWListLevel::UPPER_ROMAN;
177
0
    else if (lev == 2) theLevel.m_type = MWAWListLevel::UPPER_ALPHA;
178
0
    else if (lev == 3) theLevel.m_type = MWAWListLevel::DECIMAL;
179
0
    else if (lev == 4) theLevel.m_type =  MWAWListLevel::LOWER_ALPHA;
180
0
    else if ((lev%3)==2) {
181
0
      theLevel.m_prefix = "(";
182
0
      theLevel.m_type = MWAWListLevel::DECIMAL;
183
0
    }
184
0
    else if ((lev%3)==0) {
185
0
      theLevel.m_prefix = "(";
186
0
      theLevel.m_type = MWAWListLevel::LOWER_ALPHA;
187
0
    }
188
0
    else
189
0
      theLevel.m_type = MWAWListLevel::LOWER_ROMAN;
190
24
    break;
191
0
  }
192
26
  case 5: // leader
193
26
    theLevel.m_type = MWAWListLevel::BULLET;
194
26
    theLevel.m_bullet = "+"; // in fact + + and -
195
26
    break;
196
138
  case 6: // legal
197
138
    theLevel.m_type = MWAWListLevel::DECIMAL;
198
138
    theLevel.m_numBeforeLabels = lev-1;
199
138
    theLevel.m_suffix = ".";
200
138
    theLevel.m_labelWidth = 0.2*lev;
201
138
    break;
202
4
  case 7:
203
4
    theLevel.m_type = MWAWListLevel::UPPER_ALPHA;
204
4
    theLevel.m_suffix = ".";
205
4
    break;
206
14
  case 8:
207
14
    theLevel.m_type = MWAWListLevel::LOWER_ALPHA;
208
14
    theLevel.m_suffix = ".";
209
14
    break;
210
329
  case 9:
211
329
    theLevel.m_type = MWAWListLevel::DECIMAL;
212
329
    theLevel.m_suffix = ".";
213
329
    break;
214
3
  case 10:
215
3
    theLevel.m_type = MWAWListLevel::UPPER_ROMAN;
216
3
    theLevel.m_suffix = ".";
217
3
    break;
218
1
  case 11:
219
1
    theLevel.m_type = MWAWListLevel::LOWER_ROMAN;
220
1
    theLevel.m_suffix = ".";
221
1
    break;
222
24
  case 2: // bullet
223
603
  default:
224
603
    theLevel.m_type = MWAWListLevel::BULLET;
225
603
    libmwaw::appendUnicode(0x2022, theLevel.m_bullet);
226
603
    break;
227
1.54k
  }
228
1.54k
  m_margins[1]=m_margins[1].get()-theLevel.m_labelWidth;
229
1.54k
  m_listLevel=theLevel;
230
1.54k
}
231
232
struct ParagraphPLC {
233
  ParagraphPLC()
234
780k
    : m_rulerId(-1)
235
780k
    , m_styleId(-1)
236
780k
    , m_flags(0)
237
780k
    , m_extra("")
238
780k
  {
239
780k
  }
240
241
  friend std::ostream &operator<<(std::ostream &o, ParagraphPLC const &info)
242
0
  {
243
0
    if (info.m_rulerId >= 0) o << "P" << info.m_rulerId <<",";
244
0
    if (info.m_styleId >= 0) o << "LK" << info.m_styleId <<",";
245
0
    switch (info.m_flags&3) {
246
0
    case 0: // normal
247
0
      break;
248
0
    case 1:
249
0
      o << "hidden,";
250
0
      break;
251
0
    case 2:
252
0
      o << "collapsed,";
253
0
      break;
254
0
    default:
255
0
      o<< "hidden/collapsed,";
256
0
      break;
257
0
    }
258
0
    if (info.m_flags&4)
259
0
      o << "flags4,";
260
0
261
0
    auto listType=int((info.m_flags>>3)&0xF);
262
0
    if (listType>0 && listType < 12) {
263
0
      static char const* const labelNames[] = {
264
0
        "none", "diamond", "bullet", "checkbox", "hardvard", "leader", "legal",
265
0
        "upperalpha", "alpha", "numeric", "upperroman", "roman"
266
0
      };
267
0
      o << labelNames[listType] << ",";
268
0
    }
269
0
    else if (listType)
270
0
      o << "#listType=" << listType << ",";
271
0
    if (info.m_flags&0x80) o << "flags80,";
272
0
    auto listLevel=int((info.m_flags>>8)&0xF);
273
0
    if (listLevel) o << "level=" << listLevel+1;
274
0
    if (info.m_flags>>12) o << "flags=" << std::hex << (info.m_flags>>12) << std::dec << ",";
275
0
    if (info.m_extra.length()) o << info.m_extra;
276
0
    return o;
277
0
  }
278
279
  /** the ruler id */
280
  int m_rulerId;
281
  /** the style id ( via the style lookup table )*/
282
  int m_styleId;
283
  /** some flags */
284
  int m_flags;
285
  /** extra data */
286
  std::string m_extra;
287
};
288
289
/** internal class used to store a section */
290
struct Section {
291
  //! the constructor
292
  Section()
293
278k
    : m_pos(0)
294
278k
    , m_numColumns(1)
295
278k
    , m_columnsWidth()
296
278k
    , m_columnsSep()
297
278k
    , m_startOnNewPage(false)
298
278k
    , m_firstPage(0)
299
278k
    , m_hasTitlePage(false)
300
278k
    , m_continuousHF(true)
301
278k
    , m_leftRightHF(false)
302
278k
    , m_extra("")
303
278k
  {
304
1.11M
    for (auto &id : m_HFId) id=0;
305
278k
  }
306
  //! returns a section
307
  MWAWSection getSection() const
308
148k
  {
309
148k
    MWAWSection sec;
310
148k
    if (m_numColumns <= 1)
311
38.1k
      return sec;
312
110k
    size_t numCols = m_columnsWidth.size();
313
110k
    if (m_numColumns != int(numCols)) {
314
0
      MWAW_DEBUG_MSG(("ClarisWksTextInternal::Section::getSection: unexpected number of columns\n"));
315
0
      return sec;
316
0
    }
317
110k
    bool hasSep = numCols==m_columnsSep.size();
318
110k
    if (!hasSep && m_columnsSep.size()) {
319
0
      MWAW_DEBUG_MSG(("ClarisWksTextInternal::Section::getSection: can not used column separator\n"));
320
0
      return sec;
321
0
    }
322
110k
    sec.m_columns.resize(size_t(numCols));
323
460k
    for (size_t c=0; c < numCols; c++) {
324
349k
      sec.m_columns[c].m_width = double(m_columnsWidth[c]);
325
349k
      sec.m_columns[c].m_widthUnit = librevenge::RVNG_POINT;
326
349k
      if (!hasSep) continue;
327
349k
      sec.m_columns[c].m_margins[libmwaw::Left]=
328
349k
        double(m_columnsSep[c])/72.*(c==0 ? 1. : 0.5);
329
349k
      if (c+1!=numCols)
330
238k
        sec.m_columns[c].m_margins[libmwaw::Right]=double(m_columnsSep[c+1])/2.0/72.;
331
349k
    }
332
110k
    return sec;
333
110k
  }
334
  //! operator <<
335
  friend std::ostream &operator<<(std::ostream &o, Section const &sec)
336
0
  {
337
0
    o << "pos=" << sec.m_pos << ",";
338
0
    if (sec.m_numColumns != 1) o << "numCols=" << sec.m_numColumns << ",";
339
0
    o << "col[width]=[";
340
0
    for (auto const &w : sec.m_columnsWidth)
341
0
      o << w << ",";
342
0
    o << "],";
343
0
    if (sec.m_columnsSep.size()) {
344
0
      o << "col[sepW]=[";
345
0
      for (auto const &sep : sec.m_columnsSep)
346
0
        o << sep << ",";
347
0
      o << "],";
348
0
    }
349
0
    if (sec.m_firstPage) o << "first[page]=" << sec.m_firstPage << ",";
350
0
    if (sec.m_hasTitlePage) o << "title[page],";
351
0
    if (sec.m_continuousHF) o << "continuousHF,";
352
0
    if (sec.m_leftRightHF) o << "leftRightHF,";
353
0
    if (sec.m_HFId[0]) o << "id[header]=" << sec.m_HFId[0] << ",";
354
0
    if (sec.m_HFId[1] || sec.m_HFId[0]!=sec.m_HFId[1]) o << "id[header2]=" << sec.m_HFId[1] << ",";
355
0
    if (sec.m_HFId[2]) o << "id[footer]=" << sec.m_HFId[2] << ",";
356
0
    if (sec.m_HFId[3] || sec.m_HFId[2]!=sec.m_HFId[3]) o << "id[footer2]=" << sec.m_HFId[3] << ",";
357
0
    if (sec.m_extra.length()) o << sec.m_extra;
358
0
    return o;
359
0
  }
360
  /** the character position */
361
  long m_pos;
362
  /** the number of column */
363
  int m_numColumns;
364
  /** the columns width */
365
  std::vector<int> m_columnsWidth;
366
  /** the columns separator */
367
  std::vector<int> m_columnsSep;
368
  /** a new section generates a page break */
369
  bool m_startOnNewPage;
370
  /** the first page */
371
  int m_firstPage;
372
  /** true if the first page is a title page(ie. no header/footer) */
373
  bool m_hasTitlePage;
374
  /** true if the header/footer are shared with previous sections */
375
  bool m_continuousHF;
376
  /** true if the left/right header/footer are different */
377
  bool m_leftRightHF;
378
  /** the header/footer id*/
379
  int m_HFId[4];
380
  /** a string to store unparsed data */
381
  std::string m_extra;
382
};
383
384
/** internal class used to store a text zone */
385
struct TextZoneInfo {
386
  //! constructor
387
  TextZoneInfo()
388
1.07M
    : m_pos(0)
389
1.07M
    , m_N(0)
390
1.07M
    , m_extra("")
391
1.07M
  {
392
1.07M
  }
393
  //! operator<<
394
  friend std::ostream &operator<<(std::ostream &o, TextZoneInfo const &info)
395
0
  {
396
0
    o << "pos=" << info.m_pos << ",";
397
0
    if (info.m_N >= 0) o << "size=" << info.m_N <<",";
398
0
    if (info.m_extra.length()) o << info.m_extra;
399
0
    return o;
400
0
  }
401
  long m_pos;
402
  //! the number of character
403
  int m_N;
404
  //! extra data
405
  std::string m_extra;
406
};
407
408
enum TokenType { TKN_UNKNOWN, TKN_FOOTNOTE, TKN_PAGENUMBER, TKN_GRAPHIC, TKN_FIELD };
409
410
/** Internal: class to store field definition: TOKN entry*/
411
struct Token {
412
  //! constructor
413
  Token()
414
8.88M
    : m_type(TKN_UNKNOWN)
415
8.88M
    , m_zoneId(-1)
416
8.88M
    , m_page(-1)
417
8.88M
    , m_descent(0)
418
8.88M
    , m_fieldEntry()
419
8.88M
    , m_extra("")
420
8.88M
  {
421
26.6M
    for (auto &unkn : m_unknown) unkn=0;
422
17.7M
    for (auto &size : m_size) size=0;
423
8.88M
  }
424
  //! operator <<
425
  friend std::ostream &operator<<(std::ostream &o, Token const &tok);
426
  //! the type
427
  TokenType m_type;
428
  //! the zone id which correspond to this type
429
  int m_zoneId;
430
  //! the page
431
  int m_page;
432
  //! the size(?)
433
  int m_size[2];
434
  //! the descent
435
  int m_descent;
436
  //! the field name entry
437
  MWAWEntry m_fieldEntry;
438
  //! the unknown zone
439
  int m_unknown[3];
440
  //! a string used to store the parsing errors
441
  std::string m_extra;
442
};
443
//! operator<< for Token
444
std::ostream &operator<<(std::ostream &o, Token const &tok)
445
0
{
446
0
  switch (tok.m_type) {
447
0
  case TKN_FOOTNOTE:
448
0
    o << "footnoote,";
449
0
    break;
450
0
  case TKN_FIELD:
451
0
    o << "field[linked],";
452
0
    break;
453
0
  case TKN_PAGENUMBER:
454
0
    switch (tok.m_unknown[0]) {
455
0
    case 0:
456
0
      o << "field[pageNumber],";
457
0
      break;
458
0
    case 1:
459
0
      o << "field[sectionNumber],";
460
0
      break;
461
0
    case 2:
462
0
      o << "field[sectionInPageNumber],";
463
0
      break;
464
0
    case 3:
465
0
      o << "field[pageCount],";
466
0
      break;
467
0
    default:
468
0
      o << "field[pageNumber=#" << tok.m_unknown[0] << "],";
469
0
      break;
470
0
    }
471
0
    break;
472
0
  case TKN_GRAPHIC:
473
0
    o << "graphic,";
474
0
    break;
475
0
  case TKN_UNKNOWN:
476
#if !defined(__clang__)
477
  default:
478
#endif
479
0
    o << "##field[unknown]" << ",";
480
0
    break;
481
0
  }
482
0
  if (tok.m_zoneId != -1) o << "zoneId=" << tok.m_zoneId << ",";
483
0
  if (tok.m_page != -1) o << "page?=" << tok.m_page << ",";
484
0
  o << "pos?=" << tok.m_size[0] << "x" << tok.m_size[1] << ",";
485
0
  if (tok.m_descent) o << "descent=" << tok.m_descent << ",";
486
0
  for (int i = 0; i < 3; i++) {
487
0
    if (tok.m_unknown[i] == 0 || (i==0 && tok.m_type==TKN_PAGENUMBER))
488
0
      continue;
489
0
    o << "#unkn" << i << "=" << std::hex << tok.m_unknown[i] << std::dec << ",";
490
0
  }
491
0
  if (!tok.m_extra.empty()) o << "err=[" << tok.m_extra << "]";
492
0
  return o;
493
0
}
494
495
struct Zone final : public ClarisWksStruct::DSET {
496
  //! constructor
497
  explicit Zone(ClarisWksStruct::DSET const &dset = ClarisWksStruct::DSET())
498
1.15M
    : ClarisWksStruct::DSET(dset)
499
1.15M
    , m_zones()
500
1.15M
    , m_numChar(0)
501
1.15M
    , m_numTextZone(0)
502
1.15M
    , m_numParagInfo(0)
503
1.15M
    , m_numFont(0)
504
1.15M
    , m_fatherId(0)
505
1.15M
    , m_unknown(0)
506
1.15M
    , m_fontList()
507
1.15M
    , m_paragraphList()
508
1.15M
    , m_sectionList()
509
1.15M
    , m_tokenList()
510
1.15M
    , m_textZoneList()
511
1.15M
    , m_plcMap()
512
1.15M
  {
513
1.15M
  }
514
  //!destructor
515
  ~Zone() final;
516
  //! operator<<
517
  friend std::ostream &operator<<(std::ostream &o, Zone const &doc)
518
0
  {
519
0
    o << static_cast<ClarisWksStruct::DSET const &>(doc);
520
0
    if (doc.m_numChar) o << "numChar=" << doc.m_numChar << ",";
521
0
    if (doc.m_numTextZone) o << "numTextZone=" << doc.m_numTextZone << ",";
522
0
    if (doc.m_numParagInfo) o << "numParag=" << doc.m_numParagInfo << ",";
523
0
    if (doc.m_numFont) o << "numFont=" << doc.m_numFont << ",";
524
0
    if (doc.m_fatherId) o << "id[father]=" << doc.m_fatherId << ",";
525
0
    if (doc.m_unknown) o << "unkn=" << doc.m_unknown << ",";
526
0
    return o;
527
0
  }
528
529
  /** remove a child from a list.
530
531
      Normally, this function is not called, so optimizing it is not usefull
532
   */
533
  void removeChild(int cId, bool normalChild) final
534
2.01M
  {
535
2.01M
    DSET::removeChild(cId, normalChild);
536
4.24G
    for (auto &token : m_tokenList) {
537
4.24G
      if (token.m_zoneId!=cId) continue;
538
1.95M
      token.m_zoneId=0;
539
1.95M
      return;
540
4.24G
    }
541
    // normally, section list point only to the text zone (ie. the
542
    // child of the header/footer group), so remove child is not
543
    // called on it
544
63.3k
    MWAW_DEBUG_MSG(("ClarisWksTextInternal::Zone can not detach %d\n", cId));
545
63.3k
  }
546
547
  std::vector<MWAWEntry> m_zones; // the text zones
548
  int m_numChar /** the number of char in text zone */;
549
  int m_numTextZone /** the number of text zone ( ie. number of page ? ) */;
550
  int m_numParagInfo /** the number of paragraph info */;
551
  int m_numFont /** the number of font */;
552
  int m_fatherId /** the father id */;
553
  int m_unknown /** an unknown flags */;
554
555
  std::vector<MWAWFont> m_fontList /** the list of fonts */;
556
  std::vector<ParagraphPLC> m_paragraphList /** the list of paragraph */;
557
  std::vector<Section> m_sectionList /** the list of section */;
558
  std::vector<Token> m_tokenList /** the list of token */;
559
  std::vector<TextZoneInfo> m_textZoneList /** the list of zone */;
560
  std::multimap<long, PLC> m_plcMap /** the plc map */;
561
};
562
563
Zone::~Zone()
564
1.15M
{
565
1.15M
}
566
////////////////////////////////////////
567
//! Internal: the state of a ClarisWksText
568
struct State {
569
  //! constructor
570
  State()
571
402k
    : m_version(-1)
572
402k
    , m_paragraphsList()
573
402k
    , m_zoneMap()
574
402k
  {
575
402k
  }
576
577
  //! the file version
578
  mutable int m_version;
579
  //! the list of paragraph
580
  std::vector<Paragraph> m_paragraphsList;
581
  //! the list of text zone
582
  std::map<int, std::shared_ptr<Zone> > m_zoneMap;
583
};
584
585
////////////////////////////////////////
586
//! Internal: the subdocument of a ClarisWksDocument
587
class SubDocument final : public MWAWSubDocument
588
{
589
public:
590
  SubDocument(ClarisWksText &parser, MWAWInputStreamPtr const &input, int zoneId)
591
9.96k
    : MWAWSubDocument(nullptr, input, MWAWEntry())
592
9.96k
    , m_textParser(parser)
593
9.96k
    , m_id(zoneId) {}
594
595
  //! destructor
596
0
  ~SubDocument() final {}
597
598
  //! operator!=
599
  bool operator!=(MWAWSubDocument const &doc) const final
600
0
  {
601
0
    if (MWAWSubDocument::operator!=(doc)) return true;
602
0
    auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
603
0
    if (!sDoc) return true;
604
0
    if (&m_textParser != &sDoc->m_textParser) return true;
605
0
    if (m_id != sDoc->m_id) return true;
606
0
    return false;
607
0
  }
608
609
  //! the parser function
610
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
611
612
protected:
613
  //! the document manager
614
  ClarisWksText &m_textParser;
615
  //! the subdocument id
616
  int m_id;
617
};
618
619
void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType)
620
43.2k
{
621
43.2k
  if (!listener.get()) {
622
0
    MWAW_DEBUG_MSG(("ClarisWksTextInternal::SubDocument::parse: no listener\n"));
623
0
    return;
624
0
  }
625
43.2k
  if (m_id == -1) { // a number used to send linked frame
626
7.78k
    listener->insertChar(' ');
627
7.78k
    return;
628
7.78k
  }
629
35.5k
  if (m_id == 0) {
630
0
    MWAW_DEBUG_MSG(("ClarisWksTextInternal::SubDocument::parse: unknown zone\n"));
631
0
    return;
632
0
  }
633
634
35.5k
  m_textParser.m_document.sendZone(m_id, listener);
635
35.5k
}
636
637
}
638
639
////////////////////////////////////////////////////////////
640
// constructor/destructor, ...
641
////////////////////////////////////////////////////////////
642
ClarisWksText::ClarisWksText(ClarisWksDocument &document)
643
402k
  : m_document(document)
644
402k
  , m_parserState(document.m_parserState)
645
402k
  , m_state(new ClarisWksTextInternal::State)
646
402k
  , m_mainParser(&document.getMainParser())
647
402k
{
648
402k
}
649
650
ClarisWksText::~ClarisWksText()
651
402k
{
652
402k
}
653
654
int ClarisWksText::version() const
655
5.11M
{
656
5.11M
  if (m_state->m_version < 0)
657
192k
    m_state->m_version = m_parserState->m_version;
658
5.11M
  return m_state->m_version;
659
5.11M
}
660
661
int ClarisWksText::numPages() const
662
135k
{
663
135k
  auto iter = m_state->m_zoneMap.find(1);
664
135k
  if (iter == m_state->m_zoneMap.end())
665
121k
    return 1;
666
14.2k
  int numPage = 1;
667
14.2k
  MWAWInputStreamPtr &input= m_parserState->m_input;
668
14.2k
  long pos = input->tell();
669
15.8k
  for (auto const &entry : iter->second->m_zones) {
670
15.8k
    input->seek(entry.begin()+4, librevenge::RVNG_SEEK_SET);
671
15.8k
    auto numC = int(entry.length()-4);
672
14.0M
    for (int ch = 0; ch < numC; ch++) {
673
13.9M
      auto c = char(input->readULong(1));
674
13.9M
      if (c==0xb || c==0x1)
675
564k
        numPage++;
676
13.9M
    }
677
15.8k
  }
678
14.2k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
679
14.2k
  return numPage;
680
135k
}
681
682
bool ClarisWksText::updatePageSpanList(MWAWPageSpan const &page, std::vector<MWAWPageSpan> &spanList)
683
129k
{
684
129k
  auto it = m_state->m_zoneMap.find(1);
685
129k
  if (it==m_state->m_zoneMap.end() || !it->second || m_parserState->m_kind==MWAWDocument::MWAW_K_PRESENTATION)
686
115k
    return false;
687
14.1k
  auto const &zone=*it->second;
688
14.1k
  size_t numSection=zone.m_sectionList.size();
689
14.1k
  if (!numSection) return false;
690
7.49k
  int nPages=m_document.numPages();
691
7.49k
  int actPage=0;
692
7.49k
  spanList.resize(0);
693
17.7k
  for (size_t i=0; i<numSection; ++i) {
694
10.9k
    auto const &sec=zone.m_sectionList[i];
695
10.9k
    int lastPage=nPages;
696
10.9k
    bool ok=true;
697
12.6k
    while (i+1<numSection) {
698
5.88k
      if (zone.m_sectionList[i+1].m_continuousHF) {
699
1.74k
        ++i;
700
1.74k
        continue;
701
1.74k
      }
702
4.13k
      if (zone.m_sectionList[i+1].m_firstPage<actPage) {
703
708
        MWAW_DEBUG_MSG(("ClarisWksText::updatePageSpanList: problem with the %d first page\n", int(i+1)));
704
708
        ok=false;
705
708
        break;
706
708
      }
707
3.42k
      lastPage=zone.m_sectionList[i+1].m_firstPage;
708
3.42k
      break;
709
4.13k
    }
710
10.9k
    if (!ok)
711
708
      break;
712
10.2k
    if (lastPage>nPages) {
713
1.34k
      MWAW_DEBUG_MSG(("ClarisWksText::updatePageSpanList: some first page seems to big\n"));
714
1.34k
      lastPage=nPages;
715
1.34k
    }
716
10.2k
    if (sec.m_hasTitlePage && actPage<lastPage) {
717
      // title page have no header/footer
718
649
      MWAWPageSpan ps(page);
719
649
      ps.setPageSpan(1);
720
649
      spanList.push_back(ps);
721
649
      ++actPage;
722
649
    }
723
10.2k
    if (actPage<lastPage) {
724
7.66k
      MWAWPageSpan ps(page);
725
7.66k
      ps.setPageSpan(lastPage-actPage);
726
38.3k
      for (int j=0; j<4; ++j) {
727
30.6k
        int zId=sec.m_HFId[j];
728
30.6k
        if (!zId) continue;
729
10.2k
        if ((j%2)==1 && zId==sec.m_HFId[j-1]) continue;
730
        /* try to retrieve the father group zone */
731
9.96k
        auto fIt=m_state->m_zoneMap.find(zId);
732
9.96k
        if (fIt!=m_state->m_zoneMap.end() && fIt->second && fIt->second->m_fatherId)
733
288
          zId=fIt->second->m_fatherId;
734
9.96k
        MWAWHeaderFooter hF(j<2 ? MWAWHeaderFooter::HEADER : MWAWHeaderFooter::FOOTER,
735
9.96k
                            (j%2) ? MWAWHeaderFooter::EVEN : sec.m_HFId[j]==sec.m_HFId[j+1] ?
736
4.68k
                            MWAWHeaderFooter::ALL : MWAWHeaderFooter::ODD);
737
9.96k
        hF.m_subDocument.reset(new ClarisWksTextInternal::SubDocument(*this, m_parserState->m_input, zId));
738
9.96k
        ps.setHeaderFooter(hF);
739
9.96k
      }
740
7.66k
      spanList.push_back(ps);
741
7.66k
    }
742
10.2k
    actPage=lastPage;
743
10.2k
  }
744
7.49k
  if (actPage<nPages) {
745
316
    MWAWPageSpan ps(page);
746
316
    ps.setPageSpan(nPages-actPage);
747
316
    spanList.push_back(ps);
748
316
  }
749
7.49k
  return true;
750
14.1k
}
751
752
////////////////////////////////////////////////////////////
753
// Intermediate level
754
////////////////////////////////////////////////////////////
755
756
////////////////////////////////////////////////////////////
757
// a document part
758
////////////////////////////////////////////////////////////
759
std::shared_ptr<ClarisWksStruct::DSET> ClarisWksText::readDSETZone(ClarisWksStruct::DSET const &zone, MWAWEntry const &entry, bool &complete)
760
1.15M
{
761
1.15M
  complete = false;
762
1.15M
  if (!entry.valid() || zone.m_fileType != 1)
763
0
    return std::shared_ptr<ClarisWksStruct::DSET>();
764
1.15M
  int const vers = version();
765
1.15M
  long pos = entry.begin();
766
1.15M
  MWAWInputStreamPtr &input= m_parserState->m_input;
767
1.15M
  input->seek(pos+8+16, librevenge::RVNG_SEEK_SET); // avoid header+8 generic number
768
1.15M
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
769
1.15M
  libmwaw::DebugStream f;
770
1.15M
  f << "Entries(DSETT):";
771
772
1.15M
  std::shared_ptr<ClarisWksTextInternal::Zone> textZone(new ClarisWksTextInternal::Zone(zone));
773
1.15M
  textZone->m_unknown = static_cast<int>(input->readULong(2)); // alway 0 ?
774
1.15M
  textZone->m_fatherId = static_cast<int>(input->readULong(2));
775
1.15M
  textZone->m_numChar = static_cast<int>(input->readULong(4));
776
1.15M
  textZone->m_numTextZone = static_cast<int>(input->readULong(2));
777
1.15M
  textZone->m_numParagInfo = static_cast<int>(input->readULong(2));
778
1.15M
  textZone->m_numFont = static_cast<int>(input->readULong(2));
779
1.15M
  switch (textZone->m_textType >> 4) {
780
35.4k
  case 2:
781
35.4k
    textZone->m_position = ClarisWksStruct::DSET::P_Header;
782
35.4k
    break;
783
19.2k
  case 4:
784
19.2k
    textZone->m_position = ClarisWksStruct::DSET::P_Footer;
785
19.2k
    break;
786
10.6k
  case 6:
787
10.6k
    textZone->m_position = ClarisWksStruct::DSET::P_Footnote;
788
10.6k
    break;
789
239k
  case 8:
790
239k
    textZone->m_position = ClarisWksStruct::DSET::P_Frame;
791
239k
    break;
792
484k
  case 0xe:
793
484k
    textZone->m_position = ClarisWksStruct::DSET::P_Table;
794
484k
    break;
795
308k
  case 0:
796
308k
    if (zone.m_id==1) {
797
89.3k
      textZone->m_position = ClarisWksStruct::DSET::P_Main;
798
89.3k
      break;
799
89.3k
    }
800
219k
    MWAW_FALLTHROUGH;
801
280k
  default:
802
280k
    MWAW_DEBUG_MSG(("ClarisWksText::readDSETZone: find unknown position %d\n", (textZone->m_textType >> 4)));
803
280k
    f << "#position="<< (textZone->m_textType >> 4) << ",";
804
280k
    break;
805
1.15M
  }
806
  // find 2,3,6,a,b,e,f
807
1.15M
  if (textZone->m_textType != ClarisWksStruct::DSET::P_Unknown)
808
1.15M
    textZone->m_textType &= 0xF;
809
1.15M
  f << *textZone << ",";
810
811
1.15M
  if (long(input->tell())%2)
812
575k
    input->seek(1, librevenge::RVNG_SEEK_CUR);
813
1.15M
  ascFile.addDelimiter(input->tell(), '|');
814
1.15M
  ascFile.addPos(pos);
815
1.15M
  ascFile.addNote(f.str().c_str());
816
817
  // read the last part
818
1.15M
  int data0Length = 0;
819
1.15M
  switch (vers) {
820
211k
  case 1:
821
211k
    data0Length = 24;
822
211k
    break;
823
190k
  case 2:
824
190k
    data0Length = 28;
825
190k
    break;
826
  // case 3: ???
827
71.9k
  case 4:
828
292k
  case 5:
829
541k
  case 6:
830
541k
    data0Length = 30;
831
541k
    break;
832
215k
  default:
833
215k
    break;
834
1.15M
  }
835
836
1.15M
  auto N = int(zone.m_numData);
837
1.15M
  if (long(input->tell())+N*data0Length > entry.end()) {
838
52.5k
    MWAW_DEBUG_MSG(("ClarisWksText::readDSETZone: file is too short\n"));
839
52.5k
    return std::shared_ptr<ClarisWksStruct::DSET>();
840
52.5k
  }
841
842
1.10M
  input->seek(entry.end()-N*data0Length, librevenge::RVNG_SEEK_SET);
843
1.10M
  ClarisWksTextInternal::PLC plc;
844
1.10M
  plc.m_type = ClarisWksTextInternal::P_Child;
845
1.10M
  int numExtraHId=0;
846
1.10M
  if (data0Length) {
847
1.91M
    for (int i = 0; i < N; i++) {
848
      /* definition of a list of text zone ( one by column and one by page )*/
849
1.01M
      pos = input->tell();
850
1.01M
      f.str("");
851
1.01M
      f << "DSETT-" << i << ":";
852
1.01M
      ClarisWksStruct::DSET::Child child;
853
1.01M
      child.m_posC = long(input->readULong(4));
854
1.01M
      child.m_type = ClarisWksStruct::DSET::C_SubText;
855
1.01M
      int dim[2];
856
2.03M
      for (auto &d : dim) d = static_cast<int>(input->readLong(2));
857
1.01M
      child.m_box = MWAWBox2f(MWAWVec2f(0,0), MWAWVec2f(float(dim[0]), float(dim[1])));
858
1.01M
      textZone->m_childs.push_back(child);
859
1.01M
      plc.m_id = i;
860
1.01M
      textZone->m_plcMap.insert(std::map<long, ClarisWksTextInternal::PLC>::value_type(child.m_posC, plc));
861
862
1.01M
      f << child;
863
1.01M
      f << "ptr=" << std::hex << input->readULong(4) << std::dec << ",";
864
1.01M
      f << "f0=" << input->readLong(2) << ","; // a small number : number of line ?
865
1.01M
      f << "y[real]=" << input->readLong(2) << ",";
866
4.06M
      for (int j = 1; j < 4; j++) {
867
3.05M
        auto val = static_cast<int>(input->readLong(2));
868
3.05M
        if (val)
869
945k
          f << "f" << j << "=" << val << ",";
870
3.05M
      }
871
1.01M
      auto order = static_cast<int>(input->readLong(2));
872
      // simple id or 0: main text ?, 1 : header/footnote ?, 2: footer => id or order?
873
1.01M
      if (order)
874
557k
        f << "order?=" << order << ",";
875
876
1.01M
      if (vers>=2) {
877
752k
        long id=static_cast<int>(input->readULong(4));
878
752k
        if (id) {
879
265k
          f << "ID=" << std::hex << id << std::dec << ",";
880
265k
          ++numExtraHId;
881
265k
        }
882
752k
      }
883
1.01M
      long actPos = input->tell();
884
1.01M
      if (actPos != pos && actPos != pos+data0Length)
885
554k
        ascFile.addDelimiter(input->tell(),'|');
886
1.01M
      input->seek(pos+data0Length, librevenge::RVNG_SEEK_SET);
887
888
1.01M
      ascFile.addPos(pos);
889
1.01M
      ascFile.addNote(f.str().c_str());
890
1.01M
    }
891
901k
  }
892
893
1.10M
  input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
894
895
  // now normally three zones: paragraph, font, token
896
1.10M
  bool ok = true;
897
31.6M
  for (int z = 0; z < 4+textZone->m_numTextZone; z++) {
898
31.0M
    pos = input->tell();
899
31.0M
    auto sz = long(input->readULong(4));
900
31.0M
    if (!sz) {
901
28.1M
      f.str("");
902
28.1M
      f << "DSETT-Z" << z;
903
28.1M
      ascFile.addPos(pos);
904
28.1M
      ascFile.addNote(f.str().c_str());
905
28.1M
      continue;
906
28.1M
    }
907
908
2.93M
    MWAWEntry zEntry;
909
2.93M
    zEntry.setBegin(pos);
910
2.93M
    zEntry.setLength(sz+4);
911
912
2.93M
    if (!input->checkPosition(zEntry.end())) {
913
456k
      MWAW_DEBUG_MSG(("ClarisWksText::readDSETZone: entry for %d zone is too short\n", z));
914
456k
      ascFile.addPos(pos);
915
456k
      ascFile.addNote("###");
916
456k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
917
456k
      if (z > 4) {
918
62.0k
        ok = false;
919
62.0k
        break;
920
62.0k
      }
921
394k
      return textZone;
922
456k
    }
923
924
2.47M
    switch (z) {
925
631k
    case 0:
926
631k
      ok = readParagraphs(zEntry, *textZone);
927
631k
      break;
928
618k
    case 1:
929
618k
      ok = readFonts(zEntry, *textZone);
930
618k
      break;
931
134k
    case 2:
932
134k
      ok = readTokens(zEntry, *textZone);
933
134k
      break;
934
536k
    case 3:
935
536k
      ok = readTextZoneSize(zEntry, *textZone);
936
536k
      break;
937
555k
    default:
938
555k
      textZone->m_zones.push_back(zEntry);
939
555k
      break;
940
2.47M
    }
941
2.47M
    if (!ok) {
942
666k
      if (z >= 4) {
943
25.5k
        input->seek(pos, librevenge::RVNG_SEEK_SET);
944
25.5k
        MWAW_DEBUG_MSG(("ClarisWksText::readDSETZone: can not find text %d zone\n", z-4));
945
25.5k
        if (z > 4) break;
946
16.1k
        return textZone;
947
25.5k
      }
948
641k
      f.str("");
949
641k
      f << "DSETT-Z" << z << "#";
950
641k
      ascFile.addPos(pos);
951
641k
      ascFile.addNote(f.str().c_str());
952
641k
    }
953
2.45M
    if (input->tell() < zEntry.end() || !ok)
954
1.17M
      input->seek(zEntry.end(), librevenge::RVNG_SEEK_SET);
955
2.45M
  }
956
957
695k
  if (ok && vers >= 2) {
958
524k
    pos = input->tell();
959
524k
    if (!readTextSection(*textZone))
960
50.5k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
961
524k
  }
962
725k
  for (int i=0; ok && i<numExtraHId; ++i) {
963
69.1k
    pos=input->tell();
964
69.1k
    auto sz=long(input->readULong(4));
965
69.1k
    if (sz<10 || !input->checkPosition(pos+4+sz)) {
966
39.1k
      MWAW_DEBUG_MSG(("ClarisWksText::readDSETZone:: can not read an extra block\n"));
967
39.1k
      ascFile.addPos(pos);
968
39.1k
      ascFile.addNote("DSETT-extra:###");
969
39.1k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
970
39.1k
      ok=false;
971
39.1k
      break;
972
39.1k
    }
973
29.9k
    f.str("");
974
29.9k
    f << "DSETT-extra:";
975
    /* Checkme: no sure how to read this unfrequent structures */
976
29.9k
    auto val=static_cast<int>(input->readLong(2)); // 2 (with size=34 or 4c)|a(with size=a or e)|3c (with size 3c)
977
29.9k
    f << "type?=" << val << ",";
978
29.9k
    int dim[4];
979
119k
    for (auto &d : dim) d=static_cast<int>(input->readLong(2));
980
29.9k
    f << "dim=" << dim[1] << "x" << dim[0] << "<->" << dim[3] << "x" << dim[2] << ",";
981
29.9k
    if (sz!=10) ascFile.addDelimiter(input->tell(),'|');
982
29.9k
    ascFile.addPos(pos);
983
29.9k
    ascFile.addNote(f.str().c_str());
984
29.9k
    input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
985
29.9k
  }
986
6.97M
  for (auto const &token : textZone->m_tokenList) {
987
6.97M
    if (token.m_zoneId > 0)
988
3.48M
      textZone->m_otherChilds.push_back(token.m_zoneId);
989
6.97M
  }
990
991
695k
  if (m_state->m_zoneMap.find(textZone->m_id) != m_state->m_zoneMap.end()) {
992
401k
    MWAW_DEBUG_MSG(("ClarisWksText::readDSETZone: zone %d already exists!!!\n", textZone->m_id));
993
401k
  }
994
293k
  else
995
293k
    m_state->m_zoneMap[textZone->m_id] = textZone;
996
997
695k
  if (ok) {
998
    // look for unparsed zone
999
562k
    pos=input->tell();
1000
562k
    auto sz=long(input->readULong(4));
1001
562k
    if (input->checkPosition(pos+4+sz)) {
1002
131k
      if (sz) {
1003
36.4k
        MWAW_DEBUG_MSG(("ClarisWksText::readDSETZone:: find some extra block\n"));
1004
36.4k
        input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
1005
36.4k
        ascFile.addPos(pos);
1006
36.4k
        ascFile.addNote("Entries(TextEnd):###");
1007
36.4k
      }
1008
94.7k
      else {
1009
        // probably a problem, but...
1010
94.7k
        ascFile.addPos(pos);
1011
94.7k
        ascFile.addNote("_");
1012
94.7k
      }
1013
131k
    }
1014
431k
    else
1015
431k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
1016
562k
  }
1017
695k
  complete = ok;
1018
695k
  return textZone;
1019
1.10M
}
1020
1021
////////////////////////////////////////////////////////////
1022
//
1023
// Low level
1024
//
1025
////////////////////////////////////////////////////////////
1026
1027
////////////////////////////////////////////////////////////
1028
// the fonts properties
1029
////////////////////////////////////////////////////////////
1030
bool ClarisWksText::readFonts(MWAWEntry const &entry, ClarisWksTextInternal::Zone &zone)
1031
618k
{
1032
618k
  long pos = entry.begin();
1033
1034
618k
  int fontSize = 0;
1035
618k
  switch (version()) {
1036
101k
  case 1:
1037
183k
  case 2:
1038
270k
  case 3:
1039
270k
    fontSize = 10;
1040
270k
    break;
1041
33.6k
  case 4:
1042
151k
  case 5:
1043
151k
    fontSize = 12;
1044
151k
    break;
1045
196k
  case 6:
1046
196k
    fontSize = 18;
1047
196k
    break;
1048
0
  default:
1049
0
    break;
1050
618k
  }
1051
618k
  if (fontSize == 0)
1052
0
    return false;
1053
618k
  if ((entry.length()%fontSize) != 4)
1054
361k
    return false;
1055
1056
256k
  auto numElt = int((entry.length()-4)/fontSize);
1057
256k
  long actC = -1;
1058
1059
256k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1060
256k
  input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header
1061
  // first check char pos is ok
1062
706k
  for (int i = 0; i < numElt; i++) {
1063
466k
    pos = input->tell();
1064
466k
    auto newC = long(input->readULong(4));
1065
466k
    if (newC < actC) return false;
1066
449k
    actC = newC;
1067
449k
    input->seek(pos+fontSize, librevenge::RVNG_SEEK_SET);
1068
449k
  }
1069
1070
239k
  pos = entry.begin();
1071
239k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1072
239k
  ascFile.addPos(pos);
1073
239k
  ascFile.addNote("Entries(Font)");
1074
1075
239k
  input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header
1076
239k
  ClarisWksTextInternal::PLC plc;
1077
239k
  plc.m_type = ClarisWksTextInternal::P_Font;
1078
633k
  for (int i = 0; i < numElt; i++) {
1079
394k
    MWAWFont font;
1080
394k
    int posChar;
1081
394k
    if (!m_document.getStyleManager()->readFontAndPos(i, posChar, font)) return false;
1082
394k
    zone.m_fontList.push_back(font);
1083
394k
    plc.m_id = i;
1084
394k
    zone.m_plcMap.insert(std::map<long, ClarisWksTextInternal::PLC>::value_type(posChar, plc));
1085
394k
  }
1086
1087
239k
  return true;
1088
239k
}
1089
1090
////////////////////////////////////////////////////////////
1091
// the paragraphs properties
1092
////////////////////////////////////////////////////////////
1093
bool ClarisWksText::readParagraphs(MWAWEntry const &entry, ClarisWksTextInternal::Zone &zone)
1094
631k
{
1095
631k
  long pos = entry.begin();
1096
1097
631k
  int const vers = version();
1098
631k
  int styleSize = vers==1 ? 6 : 8;
1099
631k
  if ((entry.length()%styleSize) != 4)
1100
107k
    return false;
1101
1102
524k
  auto numElt = int((entry.length()-4)/styleSize);
1103
524k
  long actC = -1;
1104
1105
524k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1106
524k
  input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header
1107
  // first check char pos is ok
1108
1.43M
  for (int i = 0; i < numElt; i++) {
1109
954k
    pos = input->tell();
1110
954k
    auto newC = long(input->readULong(4));
1111
954k
    if (newC < actC) return false;
1112
912k
    actC = newC;
1113
912k
    input->seek(pos+styleSize, librevenge::RVNG_SEEK_SET);
1114
912k
  }
1115
1116
481k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1117
481k
  pos = entry.begin();
1118
481k
  ascFile.addPos(pos);
1119
481k
  ascFile.addNote("Entries(ParaPLC)");
1120
1121
481k
  libmwaw::DebugStream f;
1122
481k
  input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header
1123
481k
  ClarisWksTextInternal::PLC plc;
1124
481k
  plc.m_type = ClarisWksTextInternal::P_Ruler;
1125
1.26M
  for (int i = 0; i < numElt; i++) {
1126
780k
    pos = input->tell();
1127
780k
    ClarisWksTextInternal::ParagraphPLC info;
1128
1129
780k
    auto posC = long(input->readULong(4));
1130
780k
    f.str("");
1131
780k
    f << "ParaPLC-R" << i << ": pos=" << posC << ",";
1132
780k
    info.m_rulerId = static_cast<int>(input->readLong(2));
1133
780k
    if (styleSize >= 8)
1134
709k
      info.m_flags = static_cast<int>(input->readLong(2));
1135
1136
780k
    if (vers > 2) {
1137
602k
      info.m_styleId = info.m_rulerId;
1138
602k
      ClarisWksStyleManager::Style style;
1139
602k
      if (m_document.getStyleManager()->get(info.m_rulerId, style)) {
1140
39.5k
        info.m_rulerId = style.m_rulerId;
1141
#if 0
1142
        f << "[style=" << style << "]";
1143
#endif
1144
39.5k
      }
1145
602k
    }
1146
780k
    f << info;
1147
1148
780k
    if (long(input->tell()) != pos+styleSize)
1149
0
      ascFile.addDelimiter(input->tell(), '|');
1150
780k
    zone.m_paragraphList.push_back(info);
1151
780k
    plc.m_id = i;
1152
780k
    zone.m_plcMap.insert(std::map<long, ClarisWksTextInternal::PLC>::value_type(posC, plc));
1153
780k
    input->seek(pos+styleSize, librevenge::RVNG_SEEK_SET);
1154
780k
    ascFile.addPos(pos);
1155
780k
    ascFile.addNote(f.str().c_str());
1156
780k
  }
1157
1158
481k
  return true;
1159
524k
}
1160
1161
////////////////////////////////////////////////////////////
1162
// zone which corresponds to the token
1163
////////////////////////////////////////////////////////////
1164
bool ClarisWksText::readTokens(MWAWEntry const &entry, ClarisWksTextInternal::Zone &zone)
1165
134k
{
1166
134k
  long pos = entry.begin();
1167
1168
134k
  int dataSize = 0;
1169
134k
  int const vers=version();
1170
134k
  switch (vers) {
1171
34.8k
  case 1:
1172
52.6k
  case 2:
1173
78.1k
  case 3:
1174
88.1k
  case 4:
1175
117k
  case 5:
1176
117k
    dataSize = 32;
1177
117k
    break;
1178
17.5k
  case 6:
1179
17.5k
    dataSize = 36;
1180
17.5k
    break;
1181
0
  default:
1182
0
    break;
1183
134k
  }
1184
134k
  if (!dataSize || (entry.length()%dataSize) != 4)
1185
41.2k
    return false;
1186
1187
93.3k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1188
93.3k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1189
93.3k
  ascFile.addPos(pos);
1190
93.3k
  ascFile.addNote("Entries(Token)");
1191
1192
93.3k
  auto numElt = int((entry.length()-4)/dataSize);
1193
93.3k
  input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header
1194
1195
93.3k
  libmwaw::DebugStream f;
1196
93.3k
  ClarisWksTextInternal::PLC plc;
1197
93.3k
  plc.m_type = ClarisWksTextInternal::P_Token;
1198
93.3k
  int val;
1199
93.3k
  std::vector<int> fieldList;
1200
8.97M
  for (int i = 0; i < numElt; i++) {
1201
8.88M
    pos = input->tell();
1202
1203
8.88M
    auto posC = static_cast<int>(input->readULong(4));
1204
8.88M
    ClarisWksTextInternal::Token token;
1205
1206
8.88M
    auto type = static_cast<int>(input->readLong(2));
1207
8.88M
    f.str("");
1208
8.88M
    switch (type) {
1209
3.44M
    case 0:
1210
3.44M
      token.m_type = ClarisWksTextInternal::TKN_FOOTNOTE;
1211
3.44M
      break;
1212
269k
    case 1:
1213
269k
      token.m_type = ClarisWksTextInternal::TKN_GRAPHIC;
1214
269k
      break;
1215
85.5k
    case 2:
1216
      /* find in v4-v6, does not seem to exist in v1-v2 */
1217
85.5k
      token.m_type = ClarisWksTextInternal::TKN_PAGENUMBER;
1218
85.5k
      break;
1219
119k
    case 3:
1220
119k
      token.m_type = ClarisWksTextInternal::TKN_FIELD;
1221
119k
      fieldList.push_back(i);
1222
119k
      break;
1223
4.95M
    default:
1224
4.95M
      f << "#type=" << type << ",";
1225
4.95M
      break;
1226
8.88M
    }
1227
1228
8.88M
    token.m_unknown[0] = static_cast<int>(input->readLong(2));
1229
8.88M
    token.m_zoneId = static_cast<int>(input->readLong(2));
1230
8.88M
    token.m_unknown[1] = static_cast<int>(input->readLong(1));
1231
8.88M
    token.m_page = static_cast<int>(input->readLong(1));
1232
8.88M
    token.m_unknown[2] = static_cast<int>(input->readLong(2));
1233
26.6M
    for (int j = 0; j < 2; j++)
1234
17.7M
      token.m_size[1-j] = static_cast<int>(input->readLong(2));
1235
35.5M
    for (int j = 0; j < 3; j++) {
1236
26.6M
      val = static_cast<int>(input->readLong(2));
1237
26.6M
      if (val) f << "f" << j << "=" << val << ",";
1238
26.6M
    }
1239
8.88M
    val = static_cast<int>(input->readLong(2));
1240
8.88M
    if (vers>=6) // checkme: ok for v6 & graphic, not for v2
1241
462k
      token.m_descent = val;
1242
8.41M
    else if (val)
1243
5.10M
      f << "f3=" << val << ",";
1244
8.88M
    token.m_extra = f.str();
1245
8.88M
    f.str("");
1246
8.88M
    f << "Token-" << i << ": pos=" << posC << "," << token;
1247
8.88M
    zone.m_tokenList.push_back(token);
1248
8.88M
    plc.m_id = i;
1249
8.88M
    zone.m_plcMap.insert(std::map<long, ClarisWksTextInternal::PLC>::value_type(posC, plc));
1250
1251
8.88M
    if (long(input->tell()) != pos && long(input->tell()) != pos+dataSize)
1252
8.88M
      ascFile.addDelimiter(input->tell(), '|');
1253
8.88M
    ascFile.addPos(pos);
1254
8.88M
    ascFile.addNote(f.str().c_str());
1255
8.88M
    input->seek(pos+dataSize, librevenge::RVNG_SEEK_SET);
1256
8.88M
  }
1257
1258
93.3k
  input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
1259
108k
  for (size_t i=0; i < fieldList.size(); ++i) {
1260
59.7k
    pos=input->tell();
1261
59.7k
    auto sz=long(input->readULong(4));
1262
59.7k
    f.str("");
1263
59.7k
    f << "Token[field-" << i << "]:";
1264
59.7k
    if (!input->checkPosition(pos+sz+4) || long(input->readULong(1))+1!=sz) {
1265
44.2k
      MWAW_DEBUG_MSG(("ClarisWksText::readTokens: can find token field name %d\n", int(i)));
1266
44.2k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
1267
44.2k
      f << "###";
1268
44.2k
      ascFile.addPos(pos);
1269
44.2k
      ascFile.addNote(f.str().c_str());
1270
44.2k
      return false;
1271
44.2k
    }
1272
15.4k
    MWAWEntry fieldEntry;
1273
15.4k
    fieldEntry.setBegin(input->tell());
1274
15.4k
    fieldEntry.setEnd(pos+sz+4);
1275
15.4k
    if (size_t(fieldList[i])<zone.m_tokenList.size())
1276
15.4k
      zone.m_tokenList[size_t(fieldList[i])].m_fieldEntry=fieldEntry;
1277
0
    else {
1278
0
      MWAW_DEBUG_MSG(("ClarisWksText::readTokens: oops the token id seems bad\n"));
1279
0
    }
1280
15.4k
    input->seek(fieldEntry.end(), librevenge::RVNG_SEEK_SET);
1281
15.4k
    f << "###id";
1282
15.4k
    ascFile.addPos(pos);
1283
15.4k
    ascFile.addNote(f.str().c_str());
1284
15.4k
  }
1285
49.0k
  return true;
1286
93.3k
}
1287
1288
// read the different section definition
1289
bool ClarisWksText::readTextSection(ClarisWksTextInternal::Zone &zone)
1290
524k
{
1291
524k
  int const vers = version();
1292
524k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1293
524k
  long pos = input->tell();
1294
524k
  ClarisWksStruct::Struct header;
1295
524k
  if (!header.readHeader(input,true)) {
1296
50.5k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1297
50.5k
    MWAW_DEBUG_MSG(("ClarisWksText::readTextSection: unexpected size\n"));
1298
50.5k
    return false;
1299
50.5k
  }
1300
474k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1301
474k
  if (header.m_size == 0) {
1302
391k
    ascFile.addPos(pos);
1303
391k
    ascFile.addNote("Nop");
1304
391k
    return true;
1305
391k
  }
1306
82.4k
  long endPos =pos+4+header.m_size;
1307
82.4k
  libmwaw::DebugStream f;
1308
82.4k
  f << "Entries(TextSection):";
1309
1310
82.4k
  if ((vers > 3 && header.m_dataSize != 0x4e) || (vers <= 3 && header.m_dataSize < 60)) {
1311
8.81k
    f << "###";
1312
8.81k
    ascFile.addPos(pos);
1313
8.81k
    ascFile.addNote(f.str().c_str());
1314
8.81k
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
1315
8.81k
    MWAW_DEBUG_MSG(("ClarisWksText::readTextSection: unexpected size\n"));
1316
8.81k
    return true;
1317
8.81k
  }
1318
73.6k
  if (header.m_headerSize) {
1319
1.59k
    ascFile.addDelimiter(input->tell(), '|');
1320
1.59k
    input->seek(header.m_headerSize, librevenge::RVNG_SEEK_CUR);
1321
1.59k
  }
1322
73.6k
  ascFile.addPos(pos);
1323
73.6k
  ascFile.addNote(f.str().c_str());
1324
1325
73.6k
  ClarisWksTextInternal::PLC plc;
1326
73.6k
  plc.m_type = ClarisWksTextInternal::P_Section;
1327
352k
  for (long i= 0; i < header.m_numData; i++) {
1328
278k
    ClarisWksTextInternal::Section sec;
1329
1330
278k
    pos = input->tell();
1331
278k
    f.str("");
1332
278k
    sec.m_pos  = input->readLong(4);
1333
278k
    sec.m_firstPage= static_cast<int>(input->readLong(2));
1334
1.11M
    for (int j = 0; j < 3; j++) {
1335
      /** find f0=O| (for second section)[1|2|4]
1336
      f1=0| (for second section [2e,4e,5b] , f2=0|2d|4d|5a */
1337
836k
      auto val = int(input->readLong(2));
1338
836k
      if (val) f << "f" << j << "=" << val << ",";
1339
836k
    }
1340
278k
    sec.m_numColumns  = static_cast<int>(input->readULong(2));
1341
278k
    if (!sec.m_numColumns || sec.m_numColumns > 10) {
1342
229k
      MWAW_DEBUG_MSG(("ClarisWksText::readTextSection: num columns seems odd\n"));
1343
229k
      f << "#numColumns=" << sec.m_numColumns << ",";
1344
229k
      sec.m_numColumns = 1;
1345
229k
    }
1346
651k
    for (int c = 0; c < sec.m_numColumns; c++)
1347
372k
      sec.m_columnsWidth.push_back(static_cast<int>(input->readULong(2)));
1348
278k
    input->seek(pos+32, librevenge::RVNG_SEEK_SET);
1349
651k
    for (int c = 0; c < sec.m_numColumns; c++)
1350
372k
      sec.m_columnsSep.push_back(static_cast<int>(input->readLong(2)));
1351
278k
    input->seek(pos+52, librevenge::RVNG_SEEK_SET);
1352
278k
    auto val = static_cast<int>(input->readULong(2));
1353
278k
    switch ((val&3)) {
1354
51.5k
    case 1:
1355
51.5k
      f << "newPage[begin],";
1356
51.5k
      break;
1357
52.9k
    case 2: // checkme
1358
52.9k
      f << "leftPage[begin],";
1359
52.9k
      break;
1360
19.3k
    case 3: // checkme
1361
19.3k
      f << "rightPage[begin],";
1362
19.3k
      break;
1363
154k
    case 0: // begin on new line
1364
154k
    default:
1365
154k
      break;
1366
278k
    }
1367
278k
    sec.m_startOnNewPage=(val&3)!=0;
1368
278k
    val &=0xFFFC;
1369
278k
    if (val) f << "g0=" << std::hex << val << std::dec << ",";
1370
278k
    val = static_cast<int>(input->readULong(2)); // 0|1
1371
278k
    if (val) f << "g1=" << std::hex << val << std::dec << ",";
1372
278k
    val = static_cast<int>(input->readULong(2)); // 0 or 1
1373
278k
    sec.m_hasTitlePage=(val&1);
1374
278k
    val &= 0xFFFE;
1375
278k
    if (val) f << "g2=" << std::hex << val << std::dec << ",";
1376
1377
278k
    val = static_cast<int>(input->readULong(2)); // 0 or 100
1378
278k
    sec.m_continuousHF=(val&0x100);
1379
278k
    sec.m_leftRightHF=(val&1);
1380
278k
    val &= 0xFEFE;
1381
278k
    if (val) f << "g3=" << std::hex << val << std::dec << ",";
1382
278k
    val = static_cast<int>(input->readULong(2)); // 0 ?
1383
278k
    if (val) f << "g4=" << std::hex << val << std::dec << ",";
1384
278k
    int prevHFId=0;
1385
1.11M
    for (int &j : sec.m_HFId) {
1386
1.11M
      auto hFId=static_cast<int>(input->readLong(4));
1387
1.11M
      j=hFId;
1388
1.11M
      if (!hFId || prevHFId==hFId) continue;
1389
772k
      zone.m_otherChilds.push_back(hFId);
1390
772k
      prevHFId=hFId;
1391
772k
    }
1392
278k
    sec.m_extra = f.str();
1393
278k
    zone.m_sectionList.push_back(sec);
1394
278k
    plc.m_id = int(i);
1395
278k
    zone.m_plcMap.insert(std::map<long, ClarisWksTextInternal::PLC>::value_type(sec.m_pos, plc));
1396
278k
    f.str("");
1397
278k
    f << "TextSection-S" << i << ":" << sec;
1398
278k
    if (input->tell() != pos+header.m_dataSize)
1399
248k
      ascFile.addDelimiter(input->tell(), '|');
1400
278k
    ascFile.addPos(pos);
1401
278k
    ascFile.addNote(f.str().c_str());
1402
278k
    input->seek(pos+header.m_dataSize, librevenge::RVNG_SEEK_SET);
1403
278k
  }
1404
1405
73.6k
  return true;
1406
73.6k
}
1407
1408
////////////////////////////////////////////////////////////
1409
// read the different size for the text
1410
////////////////////////////////////////////////////////////
1411
bool ClarisWksText::readTextZoneSize(MWAWEntry const &entry, ClarisWksTextInternal::Zone &zone)
1412
536k
{
1413
536k
  long pos = entry.begin();
1414
1415
536k
  int dataSize = 10;
1416
536k
  if ((entry.length()%dataSize) != 4)
1417
26.3k
    return false;
1418
1419
510k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1420
510k
  libmwaw::DebugStream f;
1421
510k
  ascFile.addPos(pos);
1422
510k
  ascFile.addNote("Entries(TextZoneSz)");
1423
1424
510k
  auto numElt = int((entry.length()-4)/dataSize);
1425
1426
510k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1427
510k
  input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header
1428
1429
510k
  ClarisWksTextInternal::PLC plc;
1430
510k
  plc.m_type = ClarisWksTextInternal::P_TextZone;
1431
1.58M
  for (int i = 0; i < numElt; i++) {
1432
1.07M
    pos = input->tell();
1433
1.07M
    f.str("");
1434
1.07M
    f << "TextZoneSz-" << i << ":";
1435
1.07M
    ClarisWksTextInternal::TextZoneInfo info;
1436
1.07M
    info.m_pos = long(input->readULong(4));
1437
1.07M
    info.m_N = static_cast<int>(input->readULong(2));
1438
1.07M
    f << info;
1439
1.07M
    zone.m_textZoneList.push_back(info);
1440
1.07M
    plc.m_id = i;
1441
1.07M
    zone.m_plcMap.insert(std::map<long, ClarisWksTextInternal::PLC>::value_type(info.m_pos, plc));
1442
1443
1.07M
    if (long(input->tell()) != pos+dataSize)
1444
1.07M
      ascFile.addDelimiter(input->tell(), '|');
1445
1.07M
    ascFile.addPos(pos);
1446
1.07M
    ascFile.addNote(f.str().c_str());
1447
1.07M
    input->seek(pos+dataSize, librevenge::RVNG_SEEK_SET);
1448
1.07M
  }
1449
1450
510k
  input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
1451
510k
  return true;
1452
536k
}
1453
1454
bool ClarisWksText::canSendTextAsGraphic(ClarisWksTextInternal::Zone const &zone) const
1455
1.53M
{
1456
1.53M
  size_t numSection=zone.m_sectionList.size();
1457
1.53M
  if (numSection>1) return false;
1458
1.53M
  if (numSection==1 && zone.m_sectionList[0].m_numColumns>1)
1459
71.4k
    return false;
1460
2.60M
  for (auto const &tok : zone.m_tokenList) {
1461
2.60M
    if (tok.m_type!=ClarisWksTextInternal::TKN_UNKNOWN &&
1462
981k
        tok.m_type!=ClarisWksTextInternal::TKN_PAGENUMBER &&
1463
833k
        tok.m_type!=ClarisWksTextInternal::TKN_FIELD)
1464
731k
      return false;
1465
2.60M
  }
1466
729k
  return true;
1467
1.46M
}
1468
1469
bool ClarisWksText::sendText(ClarisWksTextInternal::Zone const &zone, MWAWListenerPtr listener)
1470
1.90M
{
1471
1.90M
  zone.m_parsed=true;
1472
1.90M
  bool localListener=false;
1473
1.90M
  if (listener)
1474
1.74M
    localListener=true;
1475
161k
  else
1476
161k
    listener=m_parserState->getMainListener();
1477
1.90M
  if (!listener || !listener->canWriteText()) {
1478
4.89k
    MWAW_DEBUG_MSG(("ClarisWksText::sendText: can not find a listener\n"));
1479
4.89k
    return false;
1480
4.89k
  }
1481
  // Removeme when all is ok
1482
1.90M
  if (listener->isParagraphOpened())
1483
225k
    listener->insertEOL();
1484
1.90M
  long actC = 0;
1485
1.90M
  bool main = zone.m_id == 1;
1486
1.90M
  auto numParaPLC = int(zone.m_paragraphList.size());
1487
1.90M
  auto numParagraphs = int(m_state->m_paragraphsList.size());
1488
1.90M
  int actPage = 1;
1489
1.90M
  size_t numZones = zone.m_zones.size();
1490
1.90M
  if (main) {
1491
36.0k
    if (!localListener)
1492
16.1k
      m_document.newPage(actPage);
1493
19.9k
    else {
1494
19.9k
      MWAW_DEBUG_MSG(("ClarisWksText::sendText: try to send main zone as graphic\n"));
1495
19.9k
      main=false;
1496
19.9k
    }
1497
36.0k
  }
1498
1.90M
  int numCols = 1;
1499
1.90M
  int numSection = 0, numSectionInPage=0;
1500
1.90M
  size_t nextSection = 0;
1501
1.90M
  long nextSectionPos = main ? 0 : -1;
1502
1.90M
  if (zone.m_sectionList.size())
1503
187k
    nextSectionPos = zone.m_sectionList[0].m_pos;
1504
1.90M
  int actListId=-1;
1505
1.90M
  long actListCPos=-1;
1506
1.90M
  MWAWInputStreamPtr &input= m_parserState->m_input;
1507
1.90M
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1508
1.90M
  std::multimap<long, ClarisWksTextInternal::PLC>::const_iterator plcIt;
1509
3.69M
  for (size_t z = 0; z < numZones; z++) {
1510
1.79M
    MWAWEntry const &entry = zone.m_zones[z];
1511
1.79M
    long pos = entry.begin();
1512
1.79M
    libmwaw::DebugStream f, f2;
1513
1514
1.79M
    auto numC = int(entry.length()-4);
1515
1.79M
    bool lastIsSectionBreak=false;
1516
1.79M
    input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip header
1517
1518
241M
    for (int i = 0; i < numC; i++) {
1519
241M
      if (nextSectionPos>=0 && actC >= nextSectionPos) {
1520
159k
        if (actC != nextSectionPos) {
1521
1.70k
          MWAW_DEBUG_MSG(("ClarisWksText::sendText: find a section inside a complex char!!!\n"));
1522
1.70k
          f << "###";
1523
1.70k
        }
1524
159k
        numSection++;
1525
159k
        numSectionInPage++;
1526
159k
        MWAWSection section;
1527
159k
        if (nextSection < zone.m_sectionList.size()) {
1528
148k
          section = zone.m_sectionList[nextSection].getSection();
1529
148k
          if (main && lastIsSectionBreak && zone.m_sectionList[nextSection].m_startOnNewPage)
1530
44
            m_document.newPage(++actPage);
1531
148k
          if (++nextSection < zone.m_sectionList.size())
1532
2.76k
            nextSectionPos = zone.m_sectionList[nextSection].m_pos;
1533
146k
          else
1534
146k
            nextSectionPos = -1;
1535
148k
        }
1536
10.4k
        else {
1537
10.4k
          section=m_document.getMainSection();
1538
10.4k
          nextSectionPos = -1;
1539
10.4k
        }
1540
159k
        numCols = section.numColumns();
1541
159k
        int actCols = localListener ? 1 : listener->getSection().numColumns();
1542
159k
        if (numCols > 1  || actCols > 1) {
1543
111k
          if (listener->isSectionOpened())
1544
71.8k
            listener->closeSection();
1545
111k
          listener->openSection(section);
1546
111k
        }
1547
159k
      }
1548
240M
      else if (numSectionInPage==0)
1549
2.43M
        numSectionInPage++;
1550
241M
      plcIt = zone.m_plcMap.find(actC);
1551
241M
      bool seeToken = false;
1552
290M
      while (plcIt != zone.m_plcMap.end() && plcIt->first<=actC) {
1553
49.4M
        if (actC != plcIt->first) {
1554
0
          MWAW_DEBUG_MSG(("ClarisWksText::sendText: find a plc inside a complex char!!!\n"));
1555
0
          f << "###";
1556
0
        }
1557
49.4M
        auto const &plc = plcIt++->second;
1558
49.4M
        f << "[" << plc << "]";
1559
49.4M
        switch (plc.m_type) {
1560
434k
        case ClarisWksTextInternal::P_Font:
1561
434k
          if (plc.m_id < 0 || plc.m_id >= int(zone.m_fontList.size())) {
1562
0
            MWAW_DEBUG_MSG(("ClarisWksText::sendText: can not find font %d\n", plc.m_id));
1563
0
            f << "###";
1564
0
            break;
1565
0
          }
1566
434k
          listener->setFont(zone.m_fontList[size_t(plc.m_id)]);
1567
434k
          break;
1568
700k
        case ClarisWksTextInternal::P_Ruler: {
1569
700k
          if (plc.m_id < 0 || plc.m_id >= numParaPLC)
1570
0
            break;
1571
700k
          auto const &paraPLC = zone.m_paragraphList[size_t(plc.m_id)];
1572
700k
          f << "[" << paraPLC << "]";
1573
700k
          if (paraPLC.m_rulerId < 0 || paraPLC.m_rulerId >= numParagraphs)
1574
645k
            break;
1575
54.6k
          auto para = m_state->m_paragraphsList[size_t(paraPLC.m_rulerId)];
1576
54.6k
          if (*para.m_listLevelIndex>0 && actC >= actListCPos)
1577
1.86k
            actListId=findListId(zone, actListId, actC, actListCPos);
1578
#if 0
1579
          // to use when the style manager is able to retrieve the correct style name
1580
          if (actListId <= 0 && paraPLC.m_styleId >= 0) {
1581
            std::string styleName;
1582
            if (m_document.getStyleManager()->getRulerName(paraPLC.m_styleId, styleName)) {
1583
              librevenge::RVNGString sfinalName("");
1584
              for (size_t c=0; c < styleName.size(); ++c) {
1585
                int unicode= m_parserState->m_fontConverter->unicode(3, static_cast<unsigned char>(styleName[c]));
1586
                if (unicode==-1)
1587
                  sfinalName.append(char(styleName[c]));
1588
                else
1589
                  libmwaw::appendUnicode(static_cast<uint32_t>(unicode), sfinalName);
1590
              }
1591
              para.m_styleName = librevenge::RVNGString(sfinalName,true).cstr();
1592
            }
1593
          }
1594
#endif
1595
54.6k
          setProperty(*listener, para, actListId);
1596
54.6k
          break;
1597
700k
        }
1598
46.4M
        case ClarisWksTextInternal::P_Token: {
1599
46.4M
          if (plc.m_id < 0 || plc.m_id >= int(zone.m_tokenList.size())) {
1600
0
            MWAW_DEBUG_MSG(("ClarisWksText::sendText: can not find the token %d\n", plc.m_id));
1601
0
            f << "###";
1602
0
            break;
1603
0
          }
1604
46.4M
          auto const &token = zone.m_tokenList[size_t(plc.m_id)];
1605
46.4M
          switch (token.m_type) {
1606
35.4M
          case ClarisWksTextInternal::TKN_FOOTNOTE:
1607
35.4M
            if (m_parserState->m_kind==MWAWDocument::MWAW_K_PAINT) {
1608
478k
              MWAW_DEBUG_MSG(("ClarisWksText::sendText: can not send footnote in a paint file\n"));
1609
478k
              f << "###";
1610
478k
              break;
1611
478k
            }
1612
34.9M
            if (token.m_zoneId>0)
1613
4.85M
              m_document.sendFootnote(token.m_zoneId);
1614
30.1M
            else
1615
30.1M
              f << "###";
1616
34.9M
            break;
1617
654k
          case ClarisWksTextInternal::TKN_PAGENUMBER:
1618
654k
            switch (token.m_unknown[0]) {
1619
127k
            case 1:
1620
204k
            case 2: {
1621
204k
              std::stringstream s;
1622
204k
              int num = token.m_unknown[0]==1 ? numSection : numSectionInPage;
1623
204k
              s << num;
1624
204k
              listener->insertUnicodeString(librevenge::RVNGString(s.str().c_str()));
1625
204k
              break;
1626
127k
            }
1627
24.0k
            case 3:
1628
24.0k
              listener->insertField(MWAWField(MWAWField::PageCount));
1629
24.0k
              break;
1630
193k
            case 0:
1631
426k
            default:
1632
426k
              listener->insertField(MWAWField(MWAWField::PageNumber));
1633
654k
            }
1634
654k
            break;
1635
1.31M
          case ClarisWksTextInternal::TKN_GRAPHIC:
1636
1.31M
            if (m_parserState->m_kind==MWAWDocument::MWAW_K_PAINT) {
1637
8.42k
              MWAW_DEBUG_MSG(("ClarisWksText::sendText: can not send graphic in a paint file\n"));
1638
8.42k
              f << "###";
1639
8.42k
              break;
1640
8.42k
            }
1641
1.31M
            if (m_parserState->m_kind==MWAWDocument::MWAW_K_PRESENTATION) {
1642
156k
              MWAW_DEBUG_MSG(("ClarisWksText::sendText: find a graphic in text zone, may cause some problem\n"));
1643
156k
              f << "#";
1644
156k
            }
1645
1.31M
            if (token.m_zoneId>0) {
1646
              // fixme
1647
645k
              MWAWPosition tPos;
1648
645k
              if (token.m_descent != 0) {
1649
732
                tPos=MWAWPosition(MWAWVec2f(0,float(token.m_descent)), MWAWVec2f(), librevenge::RVNG_POINT);
1650
732
                tPos.setRelativePosition(MWAWPosition::Char, MWAWPosition::XLeft, MWAWPosition::YBottom);
1651
732
              }
1652
645k
              m_document.sendZone(token.m_zoneId, MWAWListenerPtr(), tPos);
1653
645k
            }
1654
664k
            else
1655
664k
              f << "###";
1656
1.31M
            break;
1657
460k
          case ClarisWksTextInternal::TKN_FIELD:
1658
460k
            listener->insertUnicode(0xab);
1659
460k
            if (token.m_fieldEntry.valid() &&
1660
86.1k
                input->checkPosition(token.m_fieldEntry.end())) {
1661
86.1k
              long actPos=input->tell();
1662
86.1k
              input->seek(token.m_fieldEntry.begin(), librevenge::RVNG_SEEK_SET);
1663
86.1k
              long endFPos=token.m_fieldEntry.end();
1664
17.9M
              while (!input->isEnd() && input->tell() < token.m_fieldEntry.end())
1665
17.9M
                listener->insertCharacter(static_cast<unsigned char>(input->readULong(1)), input, endFPos);
1666
86.1k
              input->seek(actPos, librevenge::RVNG_SEEK_SET);
1667
86.1k
            }
1668
374k
            else {
1669
374k
              MWAW_DEBUG_MSG(("ClarisWksText::sendText: can not find field token data\n"));
1670
374k
              listener->insertCharacter(' ');
1671
374k
            }
1672
460k
            listener->insertUnicode(0xbb);
1673
460k
            break;
1674
8.58M
          case ClarisWksTextInternal::TKN_UNKNOWN:
1675
#if !defined(__clang__)
1676
          default:
1677
#endif
1678
8.58M
            break;
1679
46.4M
          }
1680
46.4M
          seeToken = true;
1681
46.4M
          break;
1682
46.4M
        }
1683
        /* checkme: normally, this corresponds to the first
1684
           character following a 0xb/0x1, so we do not need to a
1685
           column/page break here */
1686
985k
        case ClarisWksTextInternal::P_Child:
1687
1.13M
        case ClarisWksTextInternal::P_Section:
1688
1.88M
        case ClarisWksTextInternal::P_TextZone:
1689
1.88M
        case ClarisWksTextInternal::P_Unknown:
1690
#if !defined(__clang__)
1691
        default:
1692
#endif
1693
1.88M
          break;
1694
49.4M
        }
1695
49.4M
      }
1696
241M
      auto c = char(input->readULong(1));
1697
241M
      lastIsSectionBreak=(c==0xc);
1698
241M
      actC++;
1699
241M
      if (c == '\0') {
1700
150M
        if (i == numC-1) break;
1701
149M
        MWAW_DEBUG_MSG(("ClarisWksText::sendText: OOPS, find 0 reading the text\n"));
1702
149M
        f << "###0x0";
1703
149M
        continue;
1704
150M
      }
1705
90.4M
      f << c;
1706
90.4M
      if (seeToken && static_cast<unsigned char>(c) < 32) continue;
1707
89.9M
      switch (c) {
1708
8.75M
      case 0x1: // fixme: column break
1709
8.75M
        if (numCols) {
1710
8.75M
          listener->insertBreak(MWAWListener::ColumnBreak);
1711
8.75M
          break;
1712
8.75M
        }
1713
0
        MWAW_DEBUG_MSG(("ClarisWksText::sendText: Find unexpected char 1\n"));
1714
0
        f << "###";
1715
0
        MWAW_FALLTHROUGH;
1716
1.14M
      case 0xb: // page break
1717
1.14M
        numSectionInPage = 0;
1718
1.14M
        if (main)
1719
55.9k
          m_document.newPage(++actPage);
1720
1.14M
        break;
1721
3.77M
      case 0x2: // token footnote ( normally already done)
1722
3.77M
        break;
1723
1.91M
      case 0x3: // token graphic
1724
1.91M
        break;
1725
1.15M
      case 0x4:
1726
1.15M
        listener->insertField(MWAWField(MWAWField::Date));
1727
1.15M
        break;
1728
1.50M
      case 0x5: {
1729
1.50M
        MWAWField field(MWAWField::Time);
1730
1.50M
        field.m_DTFormat="%H:%M";
1731
1.50M
        listener->insertField(field);
1732
1.50M
        break;
1733
0
      }
1734
862k
      case 0x6: // normally already done, but if we do not find the token, ...
1735
862k
        listener->insertField(MWAWField(MWAWField::PageNumber));
1736
862k
        break;
1737
736k
      case 0x7: // footnote index (ok to ignore : index of the footnote )
1738
736k
        break;
1739
937k
      case 0x8: // potential breaking <<hyphen>>
1740
937k
        break;
1741
641k
      case 0x9:
1742
641k
        listener->insertTab();
1743
641k
        break;
1744
1.96M
      case 0xa:
1745
1.96M
        listener->insertEOL(true);
1746
1.96M
        break;
1747
461k
      case 0xc: // new section: this is treated after, at the beginning of the for loop
1748
461k
        break;
1749
1.24M
      case 0xd:
1750
1.24M
        f2.str("");
1751
1.24M
        f2 << "Entries(TextContent):" << f.str();
1752
1.24M
        ascFile.addPos(pos);
1753
1.24M
        ascFile.addNote(f2.str().c_str());
1754
1.24M
        f.str("");
1755
1.24M
        pos = input->tell();
1756
1757
        // ignore last end of line returns
1758
1.24M
        if (z != numZones-1 || i != numC-2)
1759
1.22M
          listener->insertEOL();
1760
1.24M
        break;
1761
1762
64.8M
      default: {
1763
64.8M
        int extraChar = listener->insertCharacter
1764
64.8M
                        (static_cast<unsigned char>(c), input, input->tell()+(numC-1-i));
1765
64.8M
        if (extraChar) {
1766
147k
          i += extraChar;
1767
147k
          actC += extraChar;
1768
147k
        }
1769
64.8M
      }
1770
89.9M
      }
1771
89.9M
    }
1772
1.79M
    if (f.str().length()) {
1773
0
      f2.str("");
1774
0
      f2 << "Entries(TextContent):" << f.str();
1775
0
      ascFile.addPos(pos);
1776
0
      ascFile.addNote(f2.str().c_str());
1777
0
    }
1778
1.79M
  }
1779
1780
1.90M
  return true;
1781
1.90M
}
1782
1783
int ClarisWksText::findListId(ClarisWksTextInternal::Zone const &zone, int actListId, long actC, long &lastPos)
1784
1.86k
{
1785
  // retrieve the actual list
1786
1.86k
  std::shared_ptr<MWAWList> actList;
1787
1.86k
  if (actListId>0)
1788
195
    actList = m_parserState->m_listManager->getList(actListId);
1789
1790
1.86k
  auto numParaPLC= int(zone.m_paragraphList.size());
1791
1.86k
  auto numParagraphs = int(m_state->m_paragraphsList.size());
1792
1.86k
  auto plcIt=zone.m_plcMap.find(actC);
1793
1.86k
  int listId = -1;
1794
1.86k
  int maxLevelSet = -1;
1795
  // find the last position which can correspond to the actual list
1796
11.4k
  while (plcIt!=zone.m_plcMap.end()) {
1797
9.60k
    lastPos = plcIt->first;
1798
9.60k
    auto const &plc = plcIt++->second;
1799
9.60k
    if (plc.m_type != ClarisWksTextInternal::P_Ruler)
1800
5.55k
      continue;
1801
4.05k
    if (plc.m_id < 0 || plc.m_id >= numParaPLC)
1802
0
      break;
1803
4.05k
    ClarisWksTextInternal::ParagraphPLC const &paraPLC = zone.m_paragraphList[size_t(plc.m_id)];
1804
4.05k
    if (paraPLC.m_rulerId < 0 || paraPLC.m_rulerId >= numParagraphs)
1805
8
      break;
1806
4.04k
    ClarisWksTextInternal::Paragraph const &para=m_state->m_paragraphsList[size_t(paraPLC.m_rulerId)];
1807
4.04k
    int level = *para.m_listLevelIndex;
1808
4.04k
    if (level<=0)
1809
1
      continue;
1810
4.04k
    auto newList = m_parserState->m_listManager->getNewList(actList, level, *para.m_listLevel);
1811
4.04k
    if (!newList)
1812
0
      break;
1813
4.04k
    if (level <= maxLevelSet && newList->getId() != listId)
1814
0
      break;
1815
4.04k
    if (level > maxLevelSet) maxLevelSet=level;
1816
4.04k
    listId = newList->getId();
1817
4.04k
    actList = newList;
1818
4.04k
  }
1819
1.86k
  return listId;
1820
1.86k
}
1821
1822
////////////////////////////////////////////////////////////
1823
// the style definition?
1824
////////////////////////////////////////////////////////////
1825
1826
bool ClarisWksText::readSTYL_RULR(int N, int dataSize)
1827
1.50k
{
1828
1.50k
  if (dataSize == 0 || N== 0) return true;
1829
1.50k
  if (dataSize != 108) {
1830
5
    MWAW_DEBUG_MSG(("ClarisWksText::readSTYL_RULR: Find odd ruler size %d\n", dataSize));
1831
5
  }
1832
1.50k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1833
1.50k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1834
1.50k
  libmwaw::DebugStream f;
1835
13.7k
  for (int i = 0; i < N; i++) {
1836
12.2k
    long pos = input->tell();
1837
12.2k
    if (dataSize != 108 || !readParagraph(i)) {
1838
29
      f.str("");
1839
29
      if (!i)
1840
5
        f << "Entries(RULR)-P0:#";
1841
24
      else
1842
24
        f << "RULR-P" << i << ":#";
1843
29
      ascFile.addPos(pos);
1844
29
      ascFile.addNote(f.str().c_str());
1845
29
    }
1846
12.2k
    input->seek(pos+dataSize, librevenge::RVNG_SEEK_SET);
1847
12.2k
  }
1848
1.50k
  return true;
1849
1.50k
}
1850
1851
////////////////////////////////////////////////////////////
1852
// read a list of rulers
1853
////////////////////////////////////////////////////////////
1854
bool ClarisWksText::readParagraphs()
1855
84.1k
{
1856
84.1k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1857
84.1k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1858
84.1k
  long pos = input->tell();
1859
84.1k
  ClarisWksStruct::Struct header;
1860
84.1k
  if (!header.readHeader(input,true)) {
1861
52.1k
    MWAW_DEBUG_MSG(("ClarisWksText::readParagraphs: can not read the header\n"));
1862
52.1k
    return false;
1863
52.1k
  }
1864
32.0k
  if (header.m_size==0) {
1865
23.7k
    ascFile.addPos(pos);
1866
23.7k
    ascFile.addNote("Nop");
1867
23.7k
    return true;
1868
23.7k
  }
1869
1870
8.27k
  libmwaw::DebugStream f;
1871
8.27k
  f << "Entries(RULR):" << header;
1872
8.27k
  if (header.m_headerSize) {
1873
856
    ascFile.addDelimiter(input->tell(),'|');
1874
856
    input->seek(header.m_headerSize, librevenge::RVNG_SEEK_CUR);
1875
856
  }
1876
1877
8.27k
  ascFile.addPos(pos);
1878
8.27k
  ascFile.addNote(f.str().c_str());
1879
1880
1.84M
  for (long i = 0; i < header.m_numData; i++) {
1881
1.84M
    pos = input->tell();
1882
1.84M
    if (!readParagraph(int(i))) {
1883
6.41k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
1884
6.41k
      return false;
1885
6.41k
    }
1886
1.84M
  }
1887
1.85k
  return true;
1888
8.27k
}
1889
1890
////////////////////////////////////////////////////////////
1891
// read a ruler zone
1892
////////////////////////////////////////////////////////////
1893
bool ClarisWksText::readParagraph(int id)
1894
2.05M
{
1895
2.05M
  int dataSize = 0;
1896
2.05M
  int vers = version();
1897
2.05M
  switch (vers) {
1898
360k
  case 1:
1899
360k
    dataSize = 92;
1900
360k
    break;
1901
1.57M
  case 2:
1902
1.60M
  case 3:
1903
1.60M
    dataSize = 96;
1904
1.60M
    break;
1905
13.8k
  case 4:
1906
61.3k
  case 5:
1907
87.8k
  case 6:
1908
87.8k
    if (id >= 0) dataSize = 108;
1909
75.6k
    else dataSize = 96;
1910
87.8k
    break;
1911
0
  default:
1912
0
    MWAW_DEBUG_MSG(("ClarisWksText::readParagraph: unknown size\n"));
1913
0
    return false;
1914
2.05M
  }
1915
1916
2.05M
  ClarisWksTextInternal::Paragraph ruler;
1917
2.05M
  MWAWInputStreamPtr &input= m_parserState->m_input;
1918
2.05M
  long pos = input->tell();
1919
2.05M
  long endPos = pos+dataSize;
1920
2.05M
  if (!input->checkPosition(endPos)) {
1921
6.41k
    MWAW_DEBUG_MSG(("ClarisWksText::readParagraph: the zone seems too short\n"));
1922
6.41k
    return false;
1923
6.41k
  }
1924
2.04M
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1925
2.04M
  libmwaw::DebugStream f;
1926
1927
2.04M
  int val;
1928
2.04M
  if (vers >= 4 && id >= 0) {
1929
12.2k
    val = static_cast<int>(input->readLong(2));
1930
12.2k
    if (val != -1) f << "f0=" << val << ",";
1931
12.2k
    val = static_cast<int>(input->readLong(4));
1932
12.2k
    f << "f1=" << val << ",";
1933
12.2k
    int dim[2];
1934
24.4k
    for (auto &d : dim) d = static_cast<int>(input->readLong(2));
1935
12.2k
    f << "dim?=" << dim[0] << "x" << dim[1] << ",";
1936
12.2k
    ruler.m_labelType = static_cast<int>(input->readLong(1));
1937
12.2k
    auto listLevel = static_cast<int>(input->readLong(1));
1938
12.2k
    if (listLevel < 0 || listLevel > 10) {
1939
850
      MWAW_DEBUG_MSG(("ClarisWksText::readParagraph: can not determine list level\n"));
1940
850
      f << "##listLevel=" << listLevel << ",";
1941
850
      listLevel = 0;
1942
850
    }
1943
12.2k
    ruler.m_listLevelIndex = listLevel;
1944
12.2k
  }
1945
1946
2.04M
  val = static_cast<int>(input->readLong(2));
1947
2.04M
  f << "num[used]=" << val << ",";
1948
2.04M
  val = static_cast<int>(input->readULong(2));
1949
2.04M
  int align = 0;
1950
2.04M
  switch (vers) {
1951
358k
  case 1:
1952
1.92M
  case 2:
1953
1.95M
  case 3:
1954
1.97M
  case 4:
1955
2.01M
  case 5:
1956
2.01M
    align = (val >> 14);
1957
2.01M
    val &= 0x3FFF;
1958
2.01M
    break;
1959
26.4k
  case 6:
1960
26.4k
    align = (val >> 13) & 3;
1961
26.4k
    val &= 0x9FFF;
1962
26.4k
    break;
1963
0
  default:
1964
0
    break;
1965
2.04M
  }
1966
2.04M
  switch (align) {
1967
1.52M
  case 0:
1968
1.52M
    break;
1969
223k
  case 1:
1970
223k
    ruler.m_justify = MWAWParagraph::JustificationCenter;
1971
223k
    break;
1972
102k
  case 2:
1973
102k
    ruler.m_justify = MWAWParagraph::JustificationRight ;
1974
102k
    break;
1975
191k
  case 3:
1976
191k
    ruler.m_justify = MWAWParagraph::JustificationFull;
1977
191k
    break;
1978
0
  default:
1979
0
    break;
1980
2.04M
  }
1981
1982
1983
2.04M
  bool inPoint = false;
1984
2.04M
  int interline = 0;
1985
2.04M
  switch (vers) {
1986
358k
  case 1:
1987
358k
    inPoint = (val & 0x2000);
1988
358k
    interline = val & 0xFF;
1989
358k
    val &= 0x1F00;
1990
358k
    break;
1991
1.56M
  case 2:
1992
1.59M
  case 3:
1993
1.61M
  case 4:
1994
1.65M
  case 5:
1995
1.68M
  case 6: {
1996
1.68M
    interline = (val >> 3);
1997
1.68M
    bool ok = true;
1998
1.68M
    switch (val & 7) {
1999
873k
    case 0: // PERCENT
2000
873k
      ok = interline <= 18;
2001
873k
      inPoint = false;
2002
873k
      break;
2003
78.4k
    case 6: // display unit pica
2004
199k
    case 5: // display unit cm
2005
310k
    case 4: // display unit mm
2006
399k
    case 3: // display unit Inch
2007
523k
    case 2: // display unit point
2008
523k
      ok = interline <= 512;
2009
523k
      inPoint = true; // data always stored in point
2010
523k
      break;
2011
289k
    default:
2012
289k
      ok = false;
2013
289k
      break;
2014
1.68M
    }
2015
1.68M
    if (ok) val = 0;
2016
789k
    else {
2017
789k
      MWAW_DEBUG_MSG(("ClarisWksText::readParagraph: can not determine interline dimension\n"));
2018
789k
      interline = 0;
2019
789k
    }
2020
1.68M
    break;
2021
1.68M
  }
2022
0
  default:
2023
0
    break;
2024
2.04M
  }
2025
2.04M
  if (interline) {
2026
469k
    if (inPoint)
2027
302k
      ruler.setInterline(interline, librevenge::RVNG_POINT);
2028
166k
    else
2029
166k
      ruler.setInterline(1.0+interline*0.5, librevenge::RVNG_PERCENT);
2030
469k
  }
2031
2.04M
  if (val) f << "#flags=" << std::hex << val << std::dec << ",";
2032
2.04M
  for (auto &margin : ruler.m_margins)
2033
6.13M
    margin = double(input->readLong(2))/72.;
2034
2.04M
  *(ruler.m_margins[2]) -= 28./72.;
2035
2.04M
  if (ruler.m_margins[2].get() < 0.0) ruler.m_margins[2] = 0.0;
2036
2.04M
  if (vers >= 2) {
2037
5.05M
    for (int i = 0; i < 2; i++) {
2038
3.37M
      ruler.m_spacings[i+1] = double(input->readULong(1))/72.;
2039
3.37M
      input->seek(1, librevenge::RVNG_SEEK_CUR); // flags to define the printing unit
2040
3.37M
    }
2041
1.68M
  }
2042
2.04M
  val = static_cast<int>(input->readLong(1));
2043
2.04M
  if (val) f << "unkn1=" << val << ",";
2044
2.04M
  auto numTabs = static_cast<int>(input->readULong(1));
2045
2.04M
  if (long(input->tell())+numTabs*4 > endPos) {
2046
726k
    if (numTabs != 255) { // 0xFF seems to be used in v1, v2
2047
644k
      MWAW_DEBUG_MSG(("ClarisWksText::readParagraph: numTabs is too big\n"));
2048
644k
    }
2049
726k
    f << "numTabs*=" << numTabs << ",";
2050
726k
    numTabs = 0;
2051
726k
  }
2052
4.82M
  for (int i = 0; i < numTabs; i++) {
2053
2.77M
    MWAWTabStop tab;
2054
2.77M
    tab.m_position = double(input->readLong(2))/72.;
2055
2.77M
    val = static_cast<int>(input->readULong(1));
2056
2.77M
    int leaderType = 0;
2057
2.77M
    switch (vers) {
2058
671k
    case 1:
2059
671k
      align = val & 3;
2060
671k
      val &= 0xFC;
2061
671k
      break;
2062
2.00M
    case 2:
2063
2.03M
    case 3:
2064
2.05M
    case 4:
2065
2.09M
    case 5:
2066
2.09M
      align = (val >> 6);
2067
2.09M
      leaderType = (val & 3);
2068
2.09M
      val &= 0x3C;
2069
2.09M
      break;
2070
5.85k
    case 6:
2071
5.85k
      align = (val >> 5);
2072
5.85k
      leaderType = (val & 3);
2073
5.85k
      val &= 0x9C;
2074
5.85k
      break;
2075
0
    default:
2076
0
      break;
2077
2.77M
    }
2078
2.77M
    switch (align&3) {
2079
271k
    case 1:
2080
271k
      tab.m_alignment = MWAWTabStop::CENTER;
2081
271k
      break;
2082
217k
    case 2:
2083
217k
      tab.m_alignment = MWAWTabStop::RIGHT;
2084
217k
      break;
2085
252k
    case 3:
2086
252k
      tab.m_alignment = MWAWTabStop::DECIMAL;
2087
252k
      break;
2088
2.03M
    case 0: // left
2089
2.03M
    default:
2090
2.03M
      break;
2091
2.77M
    }
2092
2.77M
    switch (leaderType) {
2093
395k
    case 1:
2094
395k
      tab.m_leaderCharacter = '.';
2095
395k
      break;
2096
488k
    case 2:
2097
488k
      tab.m_leaderCharacter = '-';
2098
488k
      break;
2099
331k
    case 3:
2100
331k
      tab.m_leaderCharacter = '_';
2101
331k
      break;
2102
1.56M
    case 0:
2103
1.56M
    default:
2104
1.56M
      break;
2105
2.77M
    }
2106
2.77M
    auto decimalChar = char(input->readULong(1));
2107
2.77M
    if (decimalChar) {
2108
2.08M
      int unicode= m_parserState->m_fontConverter->unicode(3, static_cast<unsigned char>(decimalChar));
2109
2.08M
      if (unicode==-1)
2110
1.51M
        tab.m_decimalCharacter = uint16_t(decimalChar);
2111
563k
      else
2112
563k
        tab.m_decimalCharacter = uint16_t(unicode);
2113
2.08M
    }
2114
2.77M
    ruler.m_tabs->push_back(tab);
2115
2.77M
    if (val)
2116
1.78M
      f << "#unkn[tab" << i << "=" << std::hex << val << std::dec << "],";
2117
2.77M
  }
2118
2.04M
  ruler.updateListLevel();
2119
2.04M
  ruler.m_extra = f.str();
2120
  // save the style
2121
2.04M
  if (id >= 0) {
2122
1.85M
    if (int(m_state->m_paragraphsList.size()) <= id)
2123
1.85M
      m_state->m_paragraphsList.resize(size_t(id+1));
2124
1.85M
    m_state->m_paragraphsList[size_t(id)]=ruler;
2125
1.85M
  }
2126
2.04M
  f.str("");
2127
2.04M
  if (id == 0)
2128
9.54k
    f << "Entries(RULR)-P0";
2129
2.03M
  else if (id < 0)
2130
192k
    f << "RULR-P_";
2131
1.84M
  else
2132
1.84M
    f << "RULR-P" << id;
2133
2.04M
  f << ":" << ruler;
2134
2135
2.04M
  if (long(input->tell()) != pos+dataSize)
2136
2.03M
    ascFile.addDelimiter(input->tell(), '|');
2137
2.04M
  ascFile.addPos(pos);
2138
2.04M
  ascFile.addNote(f.str().c_str());
2139
2.04M
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
2140
2.04M
  if (long(input->tell()) != pos+dataSize)
2141
0
    return false;
2142
2.04M
  return true;
2143
2.04M
}
2144
2145
void ClarisWksText::setProperty(MWAWListener &listener, ClarisWksTextInternal::Paragraph const &ruler, int listId)
2146
54.6k
{
2147
54.6k
  if (listId <= 0) {
2148
50.7k
    listener.setParagraph(ruler);
2149
50.7k
    return;
2150
50.7k
  }
2151
3.85k
  MWAWParagraph para=ruler;
2152
3.85k
  para.m_listId=listId;
2153
3.85k
  listener.setParagraph(para);
2154
3.85k
}
2155
2156
bool ClarisWksText::canSendTextAsGraphic(int number) const
2157
1.95M
{
2158
1.95M
  auto iter = m_state->m_zoneMap.find(number);
2159
1.95M
  if (iter == m_state->m_zoneMap.end() || !iter->second)
2160
414k
    return false;
2161
1.53M
  return canSendTextAsGraphic(*iter->second);
2162
1.95M
}
2163
2164
bool ClarisWksText::sendZone(int number, MWAWListenerPtr const &listener)
2165
2.22M
{
2166
2.22M
  auto iter = m_state->m_zoneMap.find(number);
2167
2.22M
  if (iter == m_state->m_zoneMap.end())
2168
420k
    return false;
2169
1.80M
  sendText(*iter->second, listener);
2170
1.80M
  return true;
2171
2.22M
}
2172
2173
void ClarisWksText::flushExtra()
2174
104k
{
2175
104k
  std::shared_ptr<MWAWListener> listener=m_parserState->getMainListener();
2176
104k
  if (!listener) return;
2177
210k
  for (auto const &iter :m_state->m_zoneMap) {
2178
210k
    std::shared_ptr<ClarisWksTextInternal::Zone> zone = iter.second;
2179
210k
    if (!zone || zone->m_parsed)
2180
107k
      continue;
2181
102k
    listener->insertEOL();
2182
102k
    if (zone->m_parsed) // can be a header/footer in draw zone
2183
192
      continue;
2184
101k
    sendText(*zone, listener);
2185
101k
  }
2186
104k
}
2187
2188
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: