Coverage Report

Created: 2026-09-06 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/WriteNowText.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 "MWAWCell.hxx"
43
#include "MWAWTextListener.hxx"
44
#include "MWAWFont.hxx"
45
#include "MWAWFontConverter.hxx"
46
#include "MWAWParagraph.hxx"
47
#include "MWAWPictMac.hxx"
48
#include "MWAWPosition.hxx"
49
#include "MWAWSection.hxx"
50
#include "MWAWTable.hxx"
51
52
#include "WriteNowParser.hxx"
53
#include "WriteNowEntry.hxx"
54
55
#include "WriteNowText.hxx"
56
57
/** Internal: the structures of a WriteNowText */
58
namespace WriteNowTextInternal
59
{
60
////////////////////////////////////////
61
//! Internal: the fonts
62
struct Font {
63
  //! the constructor
64
  Font()
65
3.52M
    : m_font()
66
3.52M
  {
67
10.5M
    for (auto &fl : m_flags) fl = 0;
68
7.04M
    for (auto &id : m_styleId) id = -1;
69
3.52M
  }
70
  //! operator<<
71
  friend std::ostream &operator<<(std::ostream &o, Font const &font)
72
0
  {
73
0
    for (int i = 0; i < 3; i++) {
74
0
      if (!font.m_flags[i]) continue;
75
0
      o << "ft" << i << "=";
76
0
      if (i == 0) o << std::hex;
77
0
      o << font.m_flags[i] << std::dec << ",";
78
0
    }
79
0
    if (font.m_styleId[0] >= 0) o << "id[charStyle]=" << font.m_styleId[0] << ",";
80
0
    if (font.m_styleId[1] >= 0) o << "id[rulerStyle]=" << font.m_styleId[1] << ",";
81
0
    return o;
82
0
  }
83
84
  //! the font
85
  MWAWFont m_font;
86
  //! the char/ruler style id
87
  int m_styleId[2];
88
  //! some unknown flag
89
  int m_flags[3];
90
};
91
92
/** Internal: class to store the paragraph properties */
93
struct Paragraph final : public MWAWParagraph {
94
  //! constructor
95
  Paragraph()
96
2.42M
    : MWAWParagraph()
97
2.42M
  {
98
2.42M
    m_marginsUnit = librevenge::RVNG_POINT;
99
19.3M
    for (auto &val : m_values) val=0;
100
2.42M
  }
101
359k
  Paragraph(Paragraph const &)=default;
102
3.92M
  Paragraph &operator=(Paragraph const &)=default;
103
1.10M
  Paragraph &operator=(Paragraph &&)=default;
104
  //! destructor
105
  ~Paragraph() final;
106
  //! operator<<
107
  friend std::ostream &operator<<(std::ostream &o, Paragraph const &ind)
108
0
  {
109
0
    o << static_cast<MWAWParagraph const &>(ind);
110
0
    for (int i = 0; i < 8; i++) {
111
0
      if (!ind.m_values[i]) continue;
112
0
      o << "fR" << i << "=" << ind.m_values[i] << ",";
113
0
    }
114
0
    return o;
115
0
  }
116
  //! some unknown value
117
  int m_values[8];
118
};
119
120
Paragraph::~Paragraph()
121
2.78M
{
122
2.78M
}
123
124
/** Internal: class to store a style */
125
struct Style {
126
6.61k
  Style() : m_name(""), m_nextId(-1), m_font(), m_paragraph()
127
6.61k
  {
128
79.3k
    for (auto &val : m_values) val=0;
129
39.6k
    for (auto &fl : m_flags) fl=0;
130
6.61k
  }
131
132
  //! operator<<
133
  friend std::ostream &operator<<(std::ostream &o, Style const &st)
134
0
  {
135
0
    if (st.m_name.length()) o << st.m_name << ",";
136
0
    if (st.m_nextId >= 0) o << "nextId?=" << st.m_nextId << ",";
137
0
    for (int i = 0; i < 12; i++) {
138
0
      if (st.m_values[i])
139
0
        o << "f" << i << "=" << st.m_values[i] << ",";
140
0
    }
141
0
    o << "flags=[" << std::hex;
142
0
    for (auto fl : st.m_flags) {
143
0
      if (fl)
144
0
        o << fl << ",";
145
0
      else
146
0
        o << "_,";
147
0
    }
148
0
    o << "]," << std::dec;
149
0
    return o;
150
0
  }
151
152
  /** the style name */
153
  std::string m_name;
154
  /** the next style ? */
155
  int m_nextId;
156
  /** the font properties */
157
  Font m_font;
158
  /** the paragraph properties */
159
  Paragraph m_paragraph;
160
  /** some unknown value */
161
  int m_values[12];
162
  /** some unknown flags */
163
  int m_flags[6];
164
};
165
166
////////////////////////////////////////
167
//! Internal: the token of a WriteNowText
168
struct Token {
169
  Token()
170
2.85M
    : m_graphicZone(-1)
171
2.85M
    , m_box()
172
2.85M
    , m_error("")
173
2.85M
  {
174
54.3M
    for (auto &val : m_values) val=0;
175
2.85M
  }
176
177
  //! operator<<
178
  friend std::ostream &operator<<(std::ostream &o, Token const &tkn)
179
0
  {
180
0
    o << "bdbox=" << tkn.m_box << ",";
181
0
    if (tkn.m_graphicZone >= 0) o << "zId=" << tkn.m_graphicZone << ",";
182
0
    MWAWVec2i tknSize = tkn.m_box.size();
183
0
    for (int i = 0; i < 2; i++) {
184
0
      if (tknSize == tkn.m_pos[i]) continue;
185
0
      o << "pos" << i << "=" << tkn.m_pos[i] << ",";
186
0
    }
187
0
    for (int i = 0; i < 19; i++) {
188
0
      if (!tkn.m_values[i]) continue;
189
0
      if (tkn.m_values[i]>0 && (tkn.m_values[i]&0xF000))
190
0
        o << "f" << i << "=0x" << std::hex << tkn.m_values[i] << std::dec << ",";
191
0
      else
192
0
        o << "f" << i << "=" << tkn.m_values[i] << ",";
193
0
    }
194
0
    if (tkn.m_error.length()) o << tkn.m_error << ",";
195
0
    return o;
196
0
  }
197
198
  //! the graphic zone id
199
  int m_graphicZone;
200
201
  //! the bdbox
202
  MWAWBox2i m_box;
203
204
  //! two positions ( seen relative to the RB point)
205
  MWAWVec2i m_pos[2];
206
207
  //! some unknown values
208
  int m_values[19];
209
210
  /** the parsing errors */
211
  std::string m_error;
212
};
213
214
////////////////////////////////////////
215
//! Internal: the table of a WriteNowText
216
struct TableData {
217
  //! constructor
218
  TableData()
219
2.79M
    : m_type(-1)
220
2.79M
    , m_box()
221
2.79M
    , m_color(MWAWColor::white())
222
2.79M
    , m_error("")
223
2.79M
  {
224
27.9M
    for (auto &val : m_values) val=0;
225
11.1M
    for (auto &fl : m_flags) fl=0;
226
2.79M
  }
227
228
  //! update a cell
229
  void updateCell(MWAWCell &cell) const
230
354k
  {
231
    // as the cells can overlap a little, we build a new box
232
354k
    cell.setBdBox(MWAWBox2f(MWAWVec2f(m_box.min()), MWAWVec2f(m_box.max()-MWAWVec2i(1,1))));
233
354k
    cell.setBackgroundColor(m_color);
234
1.77M
    for (int i=0; i<4; i++) {
235
1.41M
      MWAWBorder border;
236
1.41M
      switch (m_flags[i]&0x7f) {
237
654k
      case 1:
238
654k
        break;
239
19.9k
      case 3:
240
19.9k
        border.m_width=2;
241
19.9k
        break;
242
34.9k
      case 5:
243
34.9k
        border.m_type=MWAWBorder::Double;
244
34.9k
        break;
245
20.1k
      case 0x11:
246
20.1k
        border.m_style=MWAWBorder::Dot;
247
20.1k
        break;
248
      // 21: ?
249
12.6k
      case 0x61:
250
12.6k
        border.m_width=0.5;
251
12.6k
        break;
252
188k
      case 0:
253
675k
      default:
254
675k
        border.m_width=0;
255
675k
        break;
256
1.41M
      }
257
258
1.41M
      if (!border.isEmpty()) {
259
742k
        static int const wh[4]= { libmwaw::TopBit, libmwaw::RightBit, libmwaw::BottomBit, libmwaw::LeftBit };
260
742k
        cell.setBorders(wh[i], border);
261
742k
      }
262
1.41M
    }
263
354k
  }
264
  //! operator<<
265
  friend std::ostream &operator<<(std::ostream &o, TableData const &table)
266
0
  {
267
0
    switch (table.m_type) {
268
0
    case -1:
269
0
      break;
270
0
    case 0:
271
0
      o << "end,";
272
0
      break;
273
0
    case 1:
274
0
      o << "def,";
275
0
      break;
276
0
    case 2:
277
0
      o << "cell,";
278
0
      break;
279
0
    default:
280
0
      o << "type=#" << table.m_type << ",";
281
0
      break;
282
0
    }
283
0
    if (table.m_box.size()[0] || table.m_box.size()[1])
284
0
      o << table.m_box << ",";
285
0
    if (!table.m_color.isWhite())
286
0
      o << "color=" << table.m_color << ",";
287
0
    for (int i = 0; i < 4; i++) {
288
0
      static char const* const wh[4]= {"T", "R", "B", "L"};
289
0
      if (table.m_flags[i]&0xFF00)
290
0
        o << "#bFlags" << wh[i] << "[high]=" << (table.m_flags[i]>>8) << ",";
291
0
      // flags&0x80 : duplicated?
292
0
      int fl=table.m_flags[i]&0x7F;
293
0
      if (fl==1) continue; // normal
294
0
      o << "bFlags" << wh[i] << "=";
295
0
      switch (fl) {
296
0
      case 0:
297
0
        o << "none,";
298
0
        break;
299
0
      case 3:
300
0
        o << "w=2,";
301
0
        break;
302
0
      case 5:
303
0
        o << "double,";
304
0
        break;
305
0
      case 0x11:
306
0
        o << "dots,";
307
0
        break;
308
0
      // 21: ?
309
0
      case 0x61: // in fact always 0xe1?
310
0
        o << "w=0.5,";
311
0
        break;
312
0
      default:
313
0
        o << "#" << std::hex << fl << std::dec << ",";
314
0
        break;
315
0
      }
316
0
    }
317
0
    if (table.m_values[0]==1) o << "selected,";
318
0
    else if (table.m_values[0]) o << "#selected=" << table.m_values[0] << ",";
319
0
    for (int i = 1; i < 10; i++) {
320
0
      if (!table.m_values[i]) continue;
321
0
      o << "f" << i << "=" << table.m_values[i] << ",";
322
0
    }
323
0
    if (table.m_error.length()) o << table.m_error << ",";
324
0
    return o;
325
0
  }
326
327
  //! the type
328
  int m_type;
329
330
  //! the bdbox
331
  MWAWBox2i m_box;
332
333
  //! the background color
334
  MWAWColor m_color;
335
336
  //! some unknown flags : T, R, B, L
337
  int m_flags[4];
338
  //! some unknown values
339
  int m_values[10];
340
341
  /** the parsing errors */
342
  std::string m_error;
343
};
344
345
////////////////////////////////////////
346
//! Internal: structure used to store the content structure
347
struct ContentZone {
348
  ContentZone()
349
45.0M
    : m_type(-1)
350
45.0M
    , m_value(0)
351
45.0M
  {
352
90.1M
    for (auto &pos : m_pos) pos=-1;
353
45.0M
  }
354
355
  //! operator<<
356
  friend std::ostream &operator<<(std::ostream &o, ContentZone const &z);
357
358
  /** the type 0 : text,
359
      1<->7 : a char flag
360
      8<->f : a zone
361
      10 : used to store a break
362
  */
363
  int m_type;
364
  //! the begin and the end pos
365
  long m_pos[2];
366
  //! a value
367
  int m_value;
368
};
369
370
std::ostream &operator<<(std::ostream &o, ContentZone const &z)
371
0
{
372
0
  switch (z.m_type) {
373
0
  case 0:
374
0
    o << "text,";
375
0
    break;
376
377
0
  case 3:
378
0
    o << "[hyphen],";
379
0
    break;
380
0
  case 4:
381
0
    o << "[footnote],";
382
0
    break;
383
0
  case 5:
384
0
    o << "[header],";
385
0
    break;
386
0
  case 6:
387
0
    o << "[footer],";
388
0
    break;
389
0
  case 9:
390
0
    if (z.m_value<0) o << "sub[fontMod],";
391
0
    else if (z.m_value>0) o << "super[fontMod],";
392
0
    else o << "normal[fontMod],";
393
0
    break;
394
0
  case 0xa: {
395
0
    switch (z.m_value) {
396
0
    case 0:
397
0
      o << "table[end],";
398
0
      break;
399
0
    case 1:
400
0
      o << "table[header],";
401
0
      break;
402
0
    case 2:
403
0
      o << "table[zone],";
404
0
      break;
405
0
    default:
406
0
      o << "table[#" << int(z.m_value>>4) << "#],";
407
0
      break;
408
0
    }
409
0
    break;
410
0
  }
411
0
  case 0xb:
412
0
    o << "decimal[" << char(z.m_value) << "],";
413
0
    break;
414
0
  case 0xc:
415
0
    o << "ruler,";
416
0
    break;
417
0
  case 0xd: {
418
0
    switch (z.m_value) {
419
0
    case 0:
420
0
      o << "page[field],";
421
0
      break;
422
0
    case 1:
423
0
      o << "date[field],";
424
0
      break;
425
0
    case 2:
426
0
      o << "time[field],";
427
0
      break;
428
0
    case 3:
429
0
      o << "note[field],";
430
0
      break;
431
0
    default:
432
0
      o << "#field=" << z.m_value <<",";
433
0
      break;
434
0
    }
435
0
    break;
436
0
  }
437
0
  case 0xe:
438
0
    o << "token,";
439
0
    break;
440
0
  case 0xf:
441
0
    o << "font,";
442
0
    break;
443
0
  case 0x10:
444
0
    o << "break,";
445
0
    break;
446
0
  default:
447
0
    o << "type=#" << z.m_type << ",";
448
0
    break;
449
0
  }
450
0
  return o;
451
0
}
452
453
////////////////////////////////////////
454
//! Internal: structure used to store the content structure
455
struct ContentZones {
456
  ContentZones()
457
641k
    : m_entry()
458
641k
    , m_id(-1)
459
641k
    , m_type(0)
460
641k
    , m_zonesList()
461
641k
    , m_textCalledTypesList()
462
641k
    , m_footnoteList()
463
641k
    , m_sent(false)
464
641k
  {
465
641k
  }
466
  /** returns true if the entry corresponds to a page/column break */
467
  bool hasPageColumnBreak() const
468
921k
  {
469
921k
    return m_type == 0 && (m_entry.m_val[0] >> 4) == 7;
470
921k
  }
471
  int getNumberOfZonesWithType(int type) const
472
19.8k
  {
473
19.8k
    int res = 0;
474
19.8k
    for (auto const &zone : m_zonesList)
475
9.15M
      if (zone.m_type == type) res++;
476
19.8k
    return res;
477
19.8k
  }
478
  //! the general zone
479
  WriteNowEntry m_entry;
480
  //! the zone id
481
  int m_id;
482
  //! the zone type : 0 : main, 1 header/footer, 2 footnote,
483
  int m_type;
484
  //! the list of zone
485
  std::vector<ContentZone> m_zonesList;
486
  //! the list of type of text zone called by this zone
487
  std::vector<int> m_textCalledTypesList;
488
  //! a list to retrieve the footnote content
489
  std::vector<std::shared_ptr<ContentZones> > m_footnoteList;
490
  //! true if this zone was sent to the listener
491
  mutable bool m_sent;
492
};
493
494
495
////////////////////////////////////////
496
//! Internal: the cell of a WriteNowText
497
struct Cell final : public MWAWCell {
498
  //! constructor
499
  explicit Cell(WriteNowText &parser)
500
354k
    : MWAWCell()
501
354k
    , m_parser(parser)
502
354k
    , m_zonesList()
503
354k
    , m_footnoteList()
504
354k
  {
505
354k
  }
506
507
  //! send the content
508
  bool sendContent(MWAWListenerPtr, MWAWTable &) final;
509
  //! the text parser
510
  WriteNowText &m_parser;
511
  //! the list of zone
512
  std::vector<ContentZone> m_zonesList;
513
  //! a list to retrieve the footnote content
514
  std::vector<std::shared_ptr<ContentZones> > m_footnoteList;
515
};
516
517
////////////////////////////////////////
518
////////////////////////////////////////
519
struct Table final : public MWAWTable {
520
  //! constructor
521
  Table()
522
170k
    : MWAWTable()
523
170k
  {
524
170k
  }
525
  //! destructor
526
  ~Table() final;
527
  //! return a cell corresponding to id
528
  Cell *get(int id)
529
0
  {
530
0
    if (id < 0 || id >= numCells()) {
531
0
      MWAW_DEBUG_MSG(("WriteNowTextInternal::Table::get: cell %d does not exists\n",id));
532
0
      return nullptr;
533
0
    }
534
0
    return static_cast<Cell *>(MWAWTable::get(id).get());
535
0
  }
536
};
537
538
Table::~Table()
539
170k
{
540
170k
}
541
542
////////////////////////////////////////
543
//! Internal: structure used to store the content structure
544
struct Zone {
545
  //! constructor
546
  Zone()
547
288k
    : m_zones()
548
288k
  {
549
288k
  }
550
551
  //! a zones
552
  std::vector<std::shared_ptr<ContentZones> > m_zones;
553
};
554
555
////////////////////////////////////////
556
//! Internal: the state of a WriteNowText
557
struct State {
558
  //! constructor
559
  State()
560
96.1k
    : m_version(-1)
561
96.1k
    , m_numColumns(1)
562
96.1k
    , m_numPages(1)
563
96.1k
    , m_actualPage(1)
564
96.1k
    , m_paragraph()
565
96.1k
    , m_header()
566
96.1k
    , m_footer()
567
96.1k
    , m_localFIdMap()
568
96.1k
    , m_styleMap()
569
96.1k
    , m_styleList()
570
96.1k
    , m_contentMap()
571
96.1k
  {
572
96.1k
  }
573
574
  //! return a paragraph corresponding to 0:body, 1: header/footer, 2: footnote
575
  Paragraph getDefaultParagraph(int type) const
576
427k
  {
577
427k
    int styleId = type == 0 ? 0 : type==1 ? 3 : type==2 ? 2 : -1;
578
427k
    if (styleId >= 0 && styleId < int(m_styleList.size()))
579
4.44k
      return m_styleList[size_t(styleId)].m_paragraph;
580
422k
    Paragraph res;
581
422k
    if (m_version>=0 && m_version <= 2 && type==0) {
582
55.3k
      res.m_margins[1] = 90.0;
583
55.3k
      res.m_margins[2] = 60.0-28.0;
584
55.3k
      static double const defPos[2] = {1.5,5.5};
585
110k
      for (double i : defPos) {
586
110k
        MWAWTabStop defTab;
587
110k
        defTab.m_position=i;
588
110k
        res.m_tabs->push_back(defTab);
589
110k
      }
590
55.3k
    }
591
422k
    if (type==1)
592
2.15k
      res.m_justify = MWAWParagraph::JustificationCenter;
593
422k
    if (type==2) res.m_margins[0] = 10;
594
422k
    return res;
595
427k
  }
596
597
598
  //! return a mac font id corresponding to a local id
599
  int getFontId(int localId) const
600
1.61M
  {
601
1.61M
    if (m_localFIdMap.find(localId)==m_localFIdMap.end())
602
1.58M
      return localId;
603
27.2k
    return m_localFIdMap.find(localId)->second;
604
1.61M
  }
605
  //! return the content corresponding to a pos
606
  std::shared_ptr<ContentZones> getContentZone(long pos) const
607
809k
  {
608
809k
    auto it = m_contentMap.find(pos);
609
809k
    if (it == m_contentMap.end())
610
630k
      return std::shared_ptr<ContentZones>();
611
178k
    return it->second;
612
809k
  }
613
614
  //! the file version
615
  mutable int m_version;
616
  //! the number of column
617
  int m_numColumns;
618
619
  int m_numPages /* the number of pages */, m_actualPage /* the actual page */;
620
  /** the paragraph properties */
621
  Paragraph m_paragraph;
622
  //! the header and the footer
623
  std::shared_ptr<ContentZones> m_header, m_footer;
624
  //! a map local fontId->fontId
625
  std::map<int, int> m_localFIdMap;
626
  //! the style indirection table
627
  std::map<int, int> m_styleMap;
628
  //! the list of styles (body, footer, footnote, header, table )
629
  std::vector<Style> m_styleList;
630
631
  //! the three main zone ( text, footnote, header/footer)
632
  Zone m_mainZones[3];
633
634
  //! the list of contentZones
635
  std::map<long, std::shared_ptr<ContentZones> > m_contentMap;
636
};
637
638
bool Cell::sendContent(MWAWListenerPtr, MWAWTable &)
639
342k
{
640
  /** as a cell can be arbitrary cutted in small part,
641
      we must retrieve the last ruler */
642
342k
  Paragraph ruler = m_parser.m_state->m_paragraph;
643
342k
  m_parser.send(m_zonesList, m_footnoteList, ruler);
644
342k
  return true;
645
342k
}
646
647
}
648
649
////////////////////////////////////////////////////////////
650
// constructor/destructor, ...
651
////////////////////////////////////////////////////////////
652
WriteNowText::WriteNowText(WriteNowParser &parser)
653
96.1k
  : m_parserState(parser.getParserState())
654
96.1k
  , m_state(new WriteNowTextInternal::State)
655
96.1k
  , m_entryManager(parser.m_entryManager)
656
96.1k
  , m_mainParser(&parser)
657
96.1k
{
658
96.1k
}
659
660
WriteNowText::~WriteNowText()
661
96.1k
{ }
662
663
int WriteNowText::version() const
664
4.83M
{
665
4.83M
  if (m_state->m_version < 0)
666
29.8k
    m_state->m_version = m_parserState->m_version;
667
4.83M
  return m_state->m_version;
668
4.83M
}
669
670
int WriteNowText::numPages() const
671
29.8k
{
672
29.8k
  int nCol, width;
673
29.8k
  m_state->m_actualPage = m_state->m_numPages = 1;
674
29.8k
  m_mainParser->getColumnInfo(nCol, width);
675
29.8k
  m_state->m_numColumns = nCol;
676
29.8k
  if (nCol >= 2) return 1;
677
678
19.8k
  if (m_state->m_mainZones[0].m_zones.size()==0 ||
679
19.8k
      m_state->m_mainZones[0].m_zones[0]->m_type != 0) {
680
0
    m_state->m_numPages = 1;
681
0
    return 1;
682
0
  }
683
19.8k
  auto mainContent = m_state->m_mainZones[0].m_zones[0];
684
19.8k
  int nPages = 1+mainContent->getNumberOfZonesWithType(0x10);
685
19.8k
  m_state->m_numPages = nPages;
686
19.8k
  return nPages;
687
19.8k
}
688
689
WriteNowEntry WriteNowText::getHeader() const
690
29.8k
{
691
29.8k
  if (!m_state->m_header)
692
28.5k
    return WriteNowEntry();
693
1.31k
  return m_state->m_header->m_entry;
694
29.8k
}
695
696
WriteNowEntry WriteNowText::getFooter() const
697
29.8k
{
698
29.8k
  if (!m_state->m_footer)
699
28.2k
    return WriteNowEntry();
700
1.59k
  return m_state->m_footer->m_entry;
701
29.8k
}
702
703
////////////////////////////////////////////////////////////
704
// Intermediate level
705
////////////////////////////////////////////////////////////
706
////////////////////////////////////////////////////////////
707
// try to find the different zone
708
////////////////////////////////////////////////////////////
709
bool WriteNowText::createZones()
710
29.8k
{
711
29.8k
  auto iter = m_entryManager->m_typeMap.find("FontZone");
712
29.8k
  if (iter != m_entryManager->m_typeMap.end())
713
1.85k
    readFontNames(*iter->second);
714
715
29.8k
  iter = m_entryManager->m_typeMap.find("StylZone");
716
29.8k
  if (iter != m_entryManager->m_typeMap.end())
717
1.84k
    readStyles(*iter->second);
718
719
  // the text zone
720
29.8k
  iter = m_entryManager->m_typeMap.find("TextZone");
721
29.8k
  int numData = 0;
722
29.8k
  std::vector<WriteNowEntry> listData[3];
723
94.8k
  while (iter != m_entryManager->m_typeMap.end()) {
724
66.6k
    if (iter->first != "TextZone")
725
1.60k
      break;
726
65.0k
    int id = iter->second->id();
727
65.0k
    if (id < 0 || id > 2) {
728
0
      MWAW_DEBUG_MSG(("WriteNowText::createZones: find odd zone type:%d\n",id));
729
0
      ++iter;
730
0
      continue;
731
0
    }
732
65.0k
    parseZone(*iter->second, listData[id]);
733
65.0k
    ++iter;
734
65.0k
  }
735
736
  // update the number of columns
737
29.8k
  int nCol, width;
738
29.8k
  m_mainParser->getColumnInfo(nCol, width);
739
29.8k
  m_state->m_numColumns = nCol;
740
741
29.8k
  std::map<long, std::shared_ptr<WriteNowTextInternal::ContentZones> > listDone;
742
  /** we can now create the content zone and type them */
743
119k
  for (int z = 2; z >= 0; z--) {
744
3.80M
    for (auto &data : listData[z]) {
745
3.80M
      auto it = listDone.find(data.begin());
746
3.80M
      if (it != listDone.end()) {
747
1.70M
        m_state->m_mainZones[z].m_zones.push_back(it->second);
748
1.70M
        continue;
749
1.70M
      }
750
751
      // ok we must create the data
752
2.10M
      data.setId(numData++);
753
2.10M
      std::shared_ptr<WriteNowTextInternal::ContentZones> zone;
754
2.10M
      if (m_entryManager->add(data))
755
617k
        zone = parseContent(m_entryManager->m_posMap.find(data.begin())->second);
756
2.10M
      if (zone) {
757
611k
        zone->m_type = z;
758
611k
        m_state->m_mainZones[z].m_zones.push_back(zone);
759
611k
        listDone[data.begin()] = zone;
760
611k
      }
761
2.10M
    }
762
89.5k
  }
763
  /* we can now recreate a zone 0 which contains all the main content zone*/
764
29.8k
  auto &mainZone = m_state->m_mainZones[0];
765
29.8k
  std::vector<std::shared_ptr<WriteNowTextInternal::ContentZones> > headerFooterList;
766
29.8k
  std::vector<int> calledTypesList;
767
768
29.8k
  std::shared_ptr<WriteNowTextInternal::ContentZones> zone0(new WriteNowTextInternal::ContentZones);
769
29.8k
  zone0->m_id = -1;
770
29.8k
  zone0->m_type = 0;
771
1.33M
  for (auto const &content : mainZone.m_zones) {
772
1.33M
    switch (content->m_type) {
773
921k
    case 0:
774
921k
      break;
775
51.6k
    case 1:
776
51.6k
      headerFooterList.push_back(content);
777
51.6k
      break;
778
365k
    case 2:
779
365k
      zone0->m_footnoteList.push_back(content);
780
365k
      break;
781
0
    default:
782
0
      MWAW_DEBUG_MSG(("WriteNowText::createZones: find odd zone type(II):%d\n",content->m_type));
783
0
      break;
784
1.33M
    }
785
1.33M
    if (content->m_type != 0)
786
416k
      continue;
787
921k
    if (content->hasPageColumnBreak()) {
788
43.3k
      WriteNowTextInternal::ContentZone breakF;
789
43.3k
      breakF.m_type=0x10;
790
43.3k
      zone0->m_zonesList.push_back(breakF);
791
43.3k
    }
792
921k
    zone0->m_zonesList.insert(zone0->m_zonesList.end(),
793
921k
                              content->m_zonesList.begin(),
794
921k
                              content->m_zonesList.end());
795
921k
    calledTypesList.insert(calledTypesList.end(),
796
921k
                           content->m_textCalledTypesList.begin(),
797
921k
                           content->m_textCalledTypesList.end());
798
921k
  }
799
29.8k
  mainZone.m_zones.resize(0);
800
29.8k
  mainZone.m_zones.push_back(zone0);
801
  /* try to create the footnote and the header link */
802
29.8k
  int numHeaderFooter = 0; // header+footer
803
804
29.8k
  auto content =  mainZone.m_zones[0];
805
29.8k
  if (content->m_type != 0) return false;
806
807
5.89M
  for (auto called : calledTypesList) {
808
5.89M
    if (called < 5 || called > 6) {
809
0
      MWAW_DEBUG_MSG(("WriteNowText::createZones: unknown content %d\n", called));
810
0
      continue;
811
0
    }
812
5.89M
    int number = numHeaderFooter++;
813
5.89M
    if (number >= int(headerFooterList.size())) {
814
5.86M
      MWAW_DEBUG_MSG(("WriteNowText::createZones: can not find zone for type:%d\n", called));
815
5.86M
      continue;
816
5.86M
    }
817
25.9k
    if (called == 5) {
818
10.3k
      if (m_state->m_header)
819
9.02k
        MWAW_DEBUG_MSG(("WriteNowText::createZones: header is already defined\n"));
820
1.31k
      else
821
1.31k
        m_state->m_header = headerFooterList[size_t(number)];
822
10.3k
    }
823
15.5k
    else { // 6
824
15.5k
      if (m_state->m_footer)
825
13.9k
        MWAW_DEBUG_MSG(("WriteNowText::createZones: footer is already defined\n"));
826
1.59k
      else
827
1.59k
        m_state->m_footer = headerFooterList[size_t(number)];
828
15.5k
    }
829
25.9k
  }
830
831
29.8k
  return true;
832
29.8k
}
833
834
////////////////////////////////////////////////////////////
835
// try to parse a text data zone
836
////////////////////////////////////////////////////////////
837
std::shared_ptr<WriteNowTextInternal::ContentZones> WriteNowText::parseContent(WriteNowEntry const &entry)
838
617k
{
839
617k
  int vers = version();
840
841
617k
  if (m_state->getContentZone(entry.begin())) {
842
0
    MWAW_DEBUG_MSG(("WriteNowText::parseContent: textContent is already parsed\n"));
843
0
    return m_state->getContentZone(entry.begin());
844
0
  }
845
846
617k
  if (!entry.valid()) {
847
0
    MWAW_DEBUG_MSG(("WriteNowText::parseContent: text zone size is invalid\n"));
848
0
    return std::shared_ptr<WriteNowTextInternal::ContentZones>();
849
0
  }
850
851
617k
  MWAWInputStreamPtr &input= m_parserState->m_input;
852
617k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
853
617k
  libmwaw::DebugStream f;
854
617k
  f << "Entries(TextData)[" << entry.id() << "]:";
855
  // print the text zone data
856
617k
  f << std::hex << "txtZone=[" << std::hex << entry.m_val[0] << std::dec
857
617k
    <<",h=" << entry.m_val[1] << "],";
858
617k
  std::shared_ptr<WriteNowTextInternal::ContentZones> text;
859
617k
  if (vers >= 3) {
860
16.3k
    if (entry.length() < 16) {
861
24
      MWAW_DEBUG_MSG(("WriteNowText::parseContent: text zone size is too short\n"));
862
24
      return text;
863
24
    }
864
16.3k
    input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
865
16.3k
    if (input->readLong(4) != entry.length()) {
866
6.04k
      MWAW_DEBUG_MSG(("WriteNowText::parseContent: bad begin of last zone\n"));
867
6.04k
      return text;
868
6.04k
    }
869
870
10.3k
    text.reset(new WriteNowTextInternal::ContentZones);
871
10.3k
    text->m_entry = entry;
872
10.3k
    text->m_id = entry.id();
873
874
    // in 1/3 files, ptr0=begin.pos()+(flag<<15: rev?), but not in other files :-~
875
10.3k
    long val;
876
30.9k
    for (int i=0; i < 2; i++) {
877
20.6k
      val = long(input->readULong(4));
878
20.6k
      if (!val) continue;
879
16.9k
      f << "ptr" << i << "=" << std::hex << (val&0x7FFF) << std::dec;
880
16.9k
      if (val>>15)
881
15.1k
        f << "[" << std::hex << (val>>15) << std::dec << "],";
882
1.78k
      else
883
1.78k
        f << ",";
884
16.9k
    }
885
30.9k
    for (int i = 0; i < 2; i++) {
886
20.6k
      val = input->readLong(2);
887
20.6k
      f << "f" << i << "=" << val << ",";
888
20.6k
    }
889
10.3k
  }
890
601k
  else {
891
601k
    if (entry.length() < 2) {
892
0
      MWAW_DEBUG_MSG(("WriteNowText::parseContent: text zone size is too short\n"));
893
0
      return text;
894
0
    }
895
601k
    input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
896
601k
    if (int(input->readULong(2))+2 != entry.length()) {
897
0
      MWAW_DEBUG_MSG(("WriteNowText::parseContent: bad begin of last zone\n"));
898
0
      return text;
899
0
    }
900
901
601k
    text.reset(new WriteNowTextInternal::ContentZones);
902
601k
    text->m_entry = entry;
903
601k
    text->m_id = entry.id();
904
601k
  }
905
611k
  ascFile.addPos(entry.begin());
906
611k
  ascFile.addNote(f.str().c_str());
907
908
45.6M
  while (long(input->tell()) < entry.end()) {
909
45.0M
    long pos = input->tell();
910
45.0M
    WriteNowTextInternal::ContentZone zone;
911
45.0M
    auto c = static_cast<int>(input->readULong(1));
912
45.0M
    if (c == 0xf0) {
913
17.9k
      MWAW_DEBUG_MSG(("WriteNowText::parseContent: find 0xf0 entry\n"));
914
17.9k
      ascFile.addPos(pos);
915
17.9k
      ascFile.addNote("TextData:##");
916
17.9k
      return text;
917
17.9k
    }
918
44.9M
    int type = 0;
919
44.9M
    if ((c & 0xf0)==0xf0) type = (c & 0xf);
920
44.9M
    zone.m_pos[0] = (type < 8) ? pos : pos+1;
921
44.9M
    zone.m_type = type;
922
44.9M
    if (type == 0) { // text entry
923
224M
      while (long(input->tell()) != entry.end()) {
924
224M
        c = static_cast<int>(input->readULong(1));
925
224M
        if (c == 0xf0) continue;
926
223M
        if ((c&0xf0)==0xf0) {
927
15.6M
          input->seek(-1, librevenge::RVNG_SEEK_CUR);
928
15.6M
          break;
929
15.6M
        }
930
223M
      }
931
15.7M
      zone.m_pos[1] = input->tell();
932
15.7M
    }
933
29.2M
    else if (type >= 8) {
934
3.66M
      bool firstSeen = false;
935
3.66M
      int numChar = 0;
936
3.66M
      zone.m_pos[1] = entry.end();
937
1.23G
      while (long(input->tell()) != entry.end()) {
938
1.23G
        if (input->isEnd() || input->tell() > entry.end()) {
939
1.38k
          MWAW_DEBUG_MSG(("WriteNowText::parseContent: can not read end of text\n"));
940
1.38k
          ascFile.addPos(pos);
941
1.38k
          ascFile.addNote("TextData:##");
942
1.38k
          return text;
943
1.38k
        }
944
1.23G
        c = static_cast<int>(input->readULong(1));
945
1.23G
        if (c==0xf7) {
946
3.41M
          zone.m_pos[1] = long(input->tell())-1;
947
3.41M
          break;
948
3.41M
        }
949
950
1.22G
        if (c==0xf0)
951
3.97M
          c = int(0xf0 | (input->readULong(1) & 0xf));
952
1.22G
        numChar++;
953
1.22G
        if (!firstSeen) {
954
3.62M
          zone.m_value = c;
955
3.62M
          firstSeen = true;
956
3.62M
        }
957
1.22G
      }
958
3.66M
      if ((type == 0xb || type == 0xd) && numChar != 1) {
959
307k
        MWAW_DEBUG_MSG(("WriteNowText::parseContent: find odd size for type %x entry\n", static_cast<unsigned int>(type)));
960
307k
        ascFile.addPos(pos);
961
307k
        ascFile.addNote("TextData:##");
962
963
307k
        continue;
964
307k
      }
965
3.66M
    }
966
25.5M
    else
967
25.5M
      zone.m_pos[1] = pos+1;
968
969
44.6M
    text->m_zonesList.push_back(zone);
970
44.6M
    if (type >= 5 && type <= 6) // header footer
971
4.49M
      text->m_textCalledTypesList.push_back(type);
972
973
44.6M
    f.str("");
974
44.6M
    f << "TextData-" << text->m_id << ":" << zone;
975
44.6M
    ascFile.addPos(pos);
976
44.6M
    ascFile.addNote(f.str().c_str());
977
44.6M
  }
978
979
592k
  entry.setParsed(true);
980
592k
  ascFile.addPos(entry.end());
981
592k
  ascFile.addNote("_");
982
983
592k
  m_state->m_contentMap[entry.begin()] = text;
984
592k
  if (long(input->tell()) != entry.end()) {
985
0
    MWAW_DEBUG_MSG(("WriteNowText::parseContent: go after entry end\n"));
986
0
  }
987
988
592k
  return text;
989
611k
}
990
991
////////////////////////////////////////////////////////////
992
// try to parse a list of content zones
993
////////////////////////////////////////////////////////////
994
bool WriteNowText::parseZone(WriteNowEntry const &entry, std::vector<WriteNowEntry> &listData)
995
65.0k
{
996
65.0k
  listData.resize(0);
997
65.0k
  int vers = version();
998
65.0k
  int dataSz = 16, headerSz = 16, lengthSz=4;
999
65.0k
  if (vers <= 2) {
1000
59.5k
    dataSz = 6;
1001
59.5k
    headerSz = lengthSz = 2;
1002
59.5k
  }
1003
65.0k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1004
65.0k
  if (!entry.valid() || entry.length() < headerSz
1005
65.0k
      || (entry.length()%dataSz) !=(headerSz%dataSz) || !input->checkPosition(entry.end())) {
1006
16.5k
    MWAW_DEBUG_MSG(("WriteNowText::parseZone: text zone size is invalid\n"));
1007
16.5k
    return false;
1008
16.5k
  }
1009
1010
48.4k
  long endPos = entry.end();
1011
48.4k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1012
48.4k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1013
48.4k
  auto sz = long(input->readULong(lengthSz));
1014
48.4k
  if (vers>2 && sz != entry.length()) {
1015
1.47k
    MWAW_DEBUG_MSG(("WriteNowText::parseZone: bad begin of last zone\n"));
1016
1.47k
    return false;
1017
1.47k
  }
1018
1019
47.0k
  libmwaw::DebugStream f;
1020
47.0k
  f << "Entries(TextZone)[";
1021
47.0k
  switch (entry.id()) {
1022
26.2k
  case 0:
1023
26.2k
    f << "main";
1024
26.2k
    break;
1025
9.31k
  case 1:
1026
9.31k
    f << "header/footer";
1027
9.31k
    break;
1028
11.4k
  case 2:
1029
11.4k
    f << "note";
1030
11.4k
    break;
1031
0
  default:
1032
0
    f << entry.id() << "#";
1033
0
    break;
1034
47.0k
  }
1035
47.0k
  f << "]:";
1036
47.0k
  long val;
1037
47.0k
  if (vers > 2) {
1038
    /* seems very simillar that the 2 "ptr" in parseContent:
1039
       sometimes ptr0=begin.pos()+some flag, sometimes not :-~ */
1040
3.77k
    f << "ptr?=" << std::hex << input->readULong(4) << std::dec << ",";
1041
3.77k
    f << "ptr2?=" << std::hex << input->readULong(4) << std::dec << ",";
1042
11.3k
    for (int i = 0; i < 2; i++) {
1043
7.55k
      val = input->readLong(2);
1044
7.55k
      f << "f" << i << "=" << val << ",";
1045
7.55k
    }
1046
3.77k
  }
1047
47.0k
  auto numElts = int((entry.length()-headerSz)/dataSz);
1048
6.62M
  for (int elt=0; elt<numElts; elt++) {
1049
6.57M
    f << "entry" << elt << "=[";
1050
6.57M
    auto flags = static_cast<int>(input->readULong(1));
1051
    /*  find
1052
        40 44 45 : header ?
1053
        60 61 64 65 : text ?
1054
        70 74 7c : text + new page ?
1055
        c0 c4 e0 e4 : empty
1056
1057
        header: 86: even, a6 even|odd, c6|odd ?
1058
    */
1059
6.57M
    f << "fl=" << std::hex << flags << std::dec << ",";
1060
6.63M
    for (int i = 0; i < 3; i++) {
1061
6.61M
      val = long(input->readULong(1));
1062
6.61M
      if (!i && val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1063
6.61M
      if (vers <= 2) break;
1064
6.61M
    }
1065
1066
6.57M
    WriteNowEntry zEntry;
1067
6.57M
    zEntry.setBegin(long(input->readULong(vers <= 2 ? 2 : 4)));
1068
6.57M
    if (vers > 2) zEntry.setLength(long(input->readULong(4)));
1069
6.55M
    else if (zEntry.begin() &&
1070
5.11M
             m_mainParser->checkIfPositionValid(zEntry.begin())) {
1071
3.13M
      long actPos = input->tell();
1072
3.13M
      input->seek(zEntry.begin(), librevenge::RVNG_SEEK_SET);
1073
3.13M
      zEntry.setLength(long(input->readULong(2))+2);
1074
3.13M
      input->seek(actPos, librevenge::RVNG_SEEK_SET);
1075
3.13M
    }
1076
6.57M
    zEntry.setType("TextData");
1077
6.57M
    zEntry.m_fileType = 4;
1078
6.57M
    zEntry.m_val[0] = flags;
1079
6.57M
    zEntry.m_val[1] = static_cast<int>(input->readLong(lengthSz));
1080
1081
6.57M
    if (zEntry.begin() == 0 && zEntry.length()==0) f << "_" << ",";
1082
6.57M
    else {
1083
6.57M
      bool ok = true;
1084
6.57M
      if (zEntry.end() > endPos) {
1085
2.82M
        if (!m_mainParser->checkIfPositionValid(zEntry.end())) {
1086
2.76M
          f << "#";
1087
2.76M
          MWAW_DEBUG_MSG(("WriteNowText::parseZone: odd pointer for text zone %d\n", elt));
1088
2.76M
          ok = false;
1089
2.76M
        }
1090
51.5k
        else
1091
51.5k
          endPos = zEntry.end();
1092
2.82M
      }
1093
6.57M
      if (ok) {
1094
3.80M
        listData.push_back(zEntry);
1095
3.80M
        f << "pos=" << std::hex << zEntry.begin() << std::dec << ",";
1096
3.80M
      }
1097
6.57M
    }
1098
6.57M
    f << "h=" << zEntry.m_val[1] << "],";
1099
6.57M
  }
1100
1101
47.0k
  entry.setParsed(true);
1102
47.0k
  ascFile.addPos(entry.begin());
1103
47.0k
  ascFile.addNote(f.str().c_str());
1104
47.0k
  ascFile.addPos(entry.end());
1105
47.0k
  ascFile.addNote("_");
1106
47.0k
  return true;
1107
47.0k
}
1108
1109
////////////////////////////////////////////////////////////
1110
//
1111
// Low level
1112
//
1113
////////////////////////////////////////////////////////////
1114
1115
////////////////////////////////////////////////////////////
1116
// the fonts names
1117
////////////////////////////////////////////////////////////
1118
bool WriteNowText::readFontNames(WriteNowEntry const &entry)
1119
1.85k
{
1120
1.85k
  if (!entry.valid() || entry.length() < 0x10) {
1121
8
    MWAW_DEBUG_MSG(("WriteNowText::readFontNames: zone size is invalid\n"));
1122
8
    return false;
1123
8
  }
1124
1125
1.84k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1126
1.84k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1127
1.84k
  if (input->readLong(4) != entry.length()) {
1128
171
    MWAW_DEBUG_MSG(("WriteNowText::readFontNames: bad begin of last zone\n"));
1129
171
    return false;
1130
171
  }
1131
1.67k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1132
1.67k
  libmwaw::DebugStream f;
1133
1.67k
  f << "Entries(Fonts):";
1134
1.67k
  f << "ptr?=" << std::hex << input->readULong(4) << std::dec << ",";
1135
1.67k
  f << "ptr2?=" << std::hex << input->readULong(4) << std::dec << ",";
1136
1.67k
  long pos, val;
1137
6.69k
  for (int i = 0; i < 3; i++) { // 6, 0, 0
1138
5.01k
    val = input->readLong(2);
1139
5.01k
    if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1140
5.01k
  }
1141
1.67k
  auto N=static_cast<int>(input->readULong(2));
1142
1.67k
  f << "N=" << N << ",";
1143
5.01k
  for (int i = 0; i < 2; i++) { // 0
1144
3.34k
    val = input->readLong(2);
1145
3.34k
    if (val) f << "g" << i << "=" << val << ",";
1146
3.34k
  }
1147
1.67k
  if (long(input->tell())+N*8 > entry.end()) {
1148
87
    MWAW_DEBUG_MSG(("WriteNowText::readFontNames: the zone is too short\n"));
1149
87
    return false;
1150
87
  }
1151
1.58k
  ascFile.addPos(entry.begin());
1152
1.58k
  ascFile.addNote(f.str().c_str());
1153
1.58k
  std::vector<long> defPos;
1154
5.49k
  for (int n = 0; n < N; n++) {
1155
3.91k
    pos = input->tell();
1156
3.91k
    f.str("");
1157
3.91k
    f << "Fonts[" << n << "]:";
1158
3.91k
    auto type = static_cast<int>(input->readULong(1));
1159
3.91k
    switch (type) {
1160
3.27k
    case 0:
1161
3.27k
      f << "def,";
1162
3.27k
      break;
1163
633
    default:
1164
633
      MWAW_DEBUG_MSG(("WriteNowText::readFontNames: find unknown type %d\n", type));
1165
633
      f << "#type=" << type << ",";
1166
633
      break;
1167
3.91k
    }
1168
15.6k
    for (int i = 0; i < 3; i++) { // always 0
1169
11.7k
      val = input->readLong(1);
1170
11.7k
      if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1171
11.7k
    }
1172
3.91k
    val = long(input->readULong(4));
1173
3.91k
    defPos.push_back(pos+val);
1174
    // fixme: used this to read the data...
1175
3.91k
    f << "defPos=" << std::hex << pos+val << std::dec << ",";
1176
3.91k
    ascFile.addPos(pos);
1177
3.91k
    ascFile.addNote(f.str().c_str());
1178
3.91k
  }
1179
1180
4.86k
  for (int n = 0; n < N; n++) {
1181
3.56k
    pos = defPos[size_t(n)];
1182
3.56k
    if (pos == entry.end()) continue;
1183
3.56k
    if (pos+13 > entry.end()) {
1184
559
      MWAW_DEBUG_MSG(("WriteNowText::readFontNames: can not read entry : %d\n", n));
1185
559
      continue;
1186
559
    }
1187
1188
3.00k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1189
3.00k
    f.str("");
1190
3.00k
    f << "FontsData[" << n << "]:";
1191
3.00k
    val = input->readLong(2);
1192
3.00k
    f << "fId(local)=" << val << ","; // almost always local fid
1193
3.00k
    val = input->readLong(2);
1194
3.00k
    if (val) f << "unkn=" << val << ",";
1195
3.00k
    f << "ptr?=" << std::hex << input->readULong(4) << std::dec << ",";
1196
9.02k
    for (int i = 0; i < 2; i++) { // always 0
1197
6.01k
      val = input->readLong(2);
1198
6.01k
      if (val) f << "f" << i << "=" << val << ",";
1199
6.01k
    }
1200
3.00k
    auto sz = static_cast<int>(input->readULong(1));
1201
3.00k
    if (pos+13+sz > entry.end()) {
1202
284
      MWAW_DEBUG_MSG(("WriteNowText::readFontNames: can not read entry name : %d\n", n));
1203
284
      return false;
1204
284
    }
1205
1206
2.72k
    bool ok = true;
1207
2.72k
    std::string name("");
1208
19.5k
    for (int i = 0; i < sz; i++) {
1209
16.9k
      auto ch = char(input->readULong(1));
1210
16.9k
      if (ch == '\0') {
1211
153
        MWAW_DEBUG_MSG(("WriteNowText::readFontNames: pb with name field %d\n", n));
1212
153
        ok = false;
1213
153
        break;
1214
153
      }
1215
16.7k
      else if (ch & 0x80) {
1216
600
        static bool first = true;
1217
600
        if (first) {
1218
25
          MWAW_DEBUG_MSG(("WriteNowText::readFontNames: find odd font\n"));
1219
25
          first = false;
1220
25
        }
1221
600
        ok = false;
1222
600
      }
1223
16.7k
      name += ch;
1224
16.7k
    }
1225
2.72k
    f << name << ",";
1226
2.72k
    if (name.length() && ok)
1227
2.36k
      m_state->m_localFIdMap[n]= m_parserState->m_fontConverter->getId(name);
1228
2.72k
    ascFile.addPos(pos);
1229
2.72k
    ascFile.addNote(f.str().c_str());
1230
2.72k
  }
1231
1232
1.30k
  entry.setParsed(true);
1233
1.30k
  ascFile.addPos(entry.end());
1234
1.30k
  ascFile.addNote("_");
1235
1.30k
  return true;
1236
1.58k
}
1237
1238
////////////////////////////////////////////////////////////
1239
// the fonts properties
1240
////////////////////////////////////////////////////////////
1241
bool WriteNowText::readFont(MWAWInputStream &input, bool inStyle, WriteNowTextInternal::Font &font)
1242
1.75M
{
1243
1.75M
  font = WriteNowTextInternal::Font();
1244
1.75M
  int vers = version();
1245
1.75M
  libmwaw::DebugStream f;
1246
1247
1.75M
  long pos = input.tell();
1248
1.75M
  int expectedLength = vers <= 2 ? 4 : 14;
1249
1.75M
  input.seek(expectedLength, librevenge::RVNG_SEEK_CUR);
1250
1.75M
  if (pos+expectedLength != long(input.tell())) {
1251
147k
    MWAW_DEBUG_MSG(("WriteNowText::readFont: zone is too short \n"));
1252
147k
    return false;
1253
147k
  }
1254
1.61M
  input.seek(pos, librevenge::RVNG_SEEK_SET);
1255
1256
1.61M
  font.m_font.setId(m_state->getFontId(static_cast<int>(input.readULong(2))));
1257
1.61M
  font.m_font.setSize(float(input.readULong(vers <= 2 ? 1 : 2)));
1258
1.61M
  auto flag = int(input.readULong(1));
1259
1.61M
  uint32_t flags=0;
1260
1.61M
  if (flag&0x1) flags |= MWAWFont::boldBit;
1261
1.61M
  if (flag&0x2) flags |= MWAWFont::italicBit;
1262
1.61M
  if (flag&0x4) font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
1263
1.61M
  if (flag&0x8) flags |= MWAWFont::embossBit;
1264
1.61M
  if (flag&0x10) flags |= MWAWFont::shadowBit;
1265
1.61M
  if (flag&0x20) font.m_font.setDeltaLetterSpacing(-1);
1266
1.61M
  if (flag&0x40) font.m_font.setDeltaLetterSpacing(1);
1267
1.61M
  if (flag&0x80) f << "#flag0[0x80],";
1268
1269
1.61M
  if (vers <= 2) {
1270
1.57M
    font.m_font.setFlags(flags);
1271
1.57M
    font.m_font.m_extra = f.str();
1272
1.57M
    return true;
1273
1.57M
  }
1274
39.3k
  flag = static_cast<int>(input.readULong(1));
1275
39.3k
  if (flag&0x80) font.m_font.setStrikeOutStyle(MWAWFont::Line::Simple);
1276
39.3k
  if (flag&0x7f) f << "#flag1=" << std::hex << (flag&0x7f) << std::dec << ",";
1277
1278
39.3k
  flag = static_cast<int>(input.readULong(1));
1279
39.3k
  if (flag&0x2) {
1280
1.44k
    font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
1281
1.44k
    font.m_font.setUnderlineType(MWAWFont::Line::Double);
1282
1.44k
  }
1283
39.3k
  if (flag&0x4) {
1284
1.66k
    font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
1285
1.66k
    font.m_font.setUnderlineWidth(2.0);
1286
1.66k
  }
1287
39.3k
  if (flag&0x8) {
1288
1.76k
    font.m_font.setUnderlineStyle(MWAWFont::Line::Simple);
1289
1.76k
    font.m_font.setUnderlineColor(MWAWColor(0xb0,0xb0,0xb0));
1290
1.76k
  }
1291
39.3k
  if (flag&0x10) {
1292
1.69k
    font.m_font.setUnderlineStyle(MWAWFont::Line::Wave);
1293
1.69k
    f << "underline[charcoal],";
1294
1.69k
  }
1295
39.3k
  if (flag&0x20) font.m_font.setUnderlineStyle(MWAWFont::Line::Dash);
1296
39.3k
  if (flag&0x40) font.m_font.setUnderlineStyle(MWAWFont::Line::Dot);
1297
39.3k
  if (flag&0x81) f << "#flag2=" << std::hex << (flag&0x81) << std::dec << ",";
1298
1299
39.3k
  auto color = static_cast<int>(input.readULong(1)); // fixme find color map
1300
39.3k
  if (color) {
1301
3.71k
    MWAWColor col;
1302
3.71k
    if (m_mainParser->getColor(color,col))
1303
666
      font.m_font.setColor(col);
1304
3.04k
    else
1305
3.04k
      f << "#colorId=" << color << ",";
1306
3.71k
  }
1307
39.3k
  auto heightDecal = static_cast<int>(input.readLong(2));
1308
39.3k
  if (heightDecal)
1309
4.24k
    font.m_font.set(MWAWFont::Script(float(heightDecal), librevenge::RVNG_POINT));
1310
1311
39.3k
  font.m_font.setFlags(flags);
1312
39.3k
  font.m_font.m_extra = f.str();
1313
1314
39.3k
  int act = 0;
1315
39.3k
  if (inStyle) {
1316
3.48k
    font.m_flags[act++] = static_cast<int>(input.readULong(4));
1317
3.48k
    font.m_flags[act++] = static_cast<int>(input.readLong(2));
1318
3.48k
  }
1319
35.8k
  else {
1320
35.8k
    font.m_flags[act++] = static_cast<int>(input.readLong(1)); // 5: note def, 6: note pos ?
1321
35.8k
    font.m_styleId[0] = static_cast<int>(input.readULong(1))-1;
1322
35.8k
    font.m_styleId[1] = static_cast<int>(input.readULong(1))-1;
1323
35.8k
    font.m_flags[act++] = static_cast<int>(input.readLong(1)); // alway 0
1324
35.8k
  }
1325
39.3k
  return true;
1326
1.61M
}
1327
1328
////////////////////////////////////////////////////////////
1329
// the paragraphs properties
1330
////////////////////////////////////////////////////////////
1331
bool WriteNowText::readParagraph(MWAWInputStream &input, WriteNowTextInternal::Paragraph &ruler)
1332
948k
{
1333
948k
  libmwaw::DebugStream f;
1334
948k
  int vers = version();
1335
948k
  ruler = WriteNowTextInternal::Paragraph();
1336
948k
  long pos = input.tell();
1337
948k
  int expectedLength = vers <= 2 ? 8 : 16;
1338
948k
  input.seek(expectedLength, librevenge::RVNG_SEEK_CUR);
1339
948k
  if (pos+expectedLength != long(input.tell())) {
1340
206k
    MWAW_DEBUG_MSG(("WriteNowText::readParagraph: zone is too short: %ld\n",
1341
206k
                    long(input.tell()) - pos));
1342
206k
    return false;
1343
206k
  }
1344
741k
  input.seek(pos, librevenge::RVNG_SEEK_SET);
1345
741k
  int actVal = 0;
1346
  /* small number, 0, small number < 3 */
1347
741k
  if (vers >= 3) {
1348
29.1k
    for (int i = 0; i < 2; i++)
1349
19.4k
      ruler.m_values[actVal++] = static_cast<int>(input.readULong(1));
1350
9.72k
  }
1351
741k
  ruler.m_margins[1]=static_cast<int>(input.readLong(2));
1352
741k
  ruler.m_margins[2]=static_cast<int>(input.readLong(2));
1353
741k
  ruler.m_margins[0]=static_cast<int>(input.readLong(2));
1354
741k
  *(ruler.m_margins[0])-=ruler.m_margins[1].get();
1355
741k
  int height = 0;
1356
741k
  if (vers >= 3) {
1357
9.72k
    height=static_cast<int>(input.readLong(2));
1358
38.9k
    for (int i = 0; i < 3; i++)
1359
29.1k
      ruler.m_values[actVal++] = static_cast<int>(input.readULong(2));
1360
9.72k
  }
1361
741k
  auto flag = static_cast<int>(input.readULong(1));
1362
741k
  switch (flag & 3) {
1363
313k
  case 0:
1364
313k
    break; // left
1365
254k
  case 1:
1366
254k
    ruler.m_justify = MWAWParagraph::JustificationFull;
1367
254k
    break;
1368
88.2k
  case 2:
1369
88.2k
    ruler.m_justify = MWAWParagraph::JustificationCenter;
1370
88.2k
    break;
1371
84.6k
  case 3:
1372
84.6k
    ruler.m_justify = MWAWParagraph::JustificationRight;
1373
84.6k
    break;
1374
0
  default:
1375
0
    break;
1376
741k
  }
1377
741k
  bool interlineFixed = (flag & 0x80);
1378
741k
  ruler.m_values[actVal++] = (flag & 0x7c);
1379
741k
  if (vers <= 2)
1380
731k
    height=static_cast<int>(input.readULong(1));
1381
9.72k
  else
1382
9.72k
    ruler.m_values[actVal++] = static_cast<int>(input.readULong(1)); // always 0
1383
741k
  ruler.m_tabs->resize(0);
1384
741k
  if (!input.isEnd()) {
1385
710k
    int previousVal = 0;
1386
710k
    int tab = 0;
1387
3.15M
    while (!input.isEnd()) {
1388
3.06M
      MWAWTabStop newTab;
1389
3.06M
      auto newVal = static_cast<int>(input.readULong(2));
1390
3.06M
      if (tab && newVal < previousVal) {
1391
627k
        MWAW_DEBUG_MSG(("WriteNowText::readParagraph: find bad tab pos\n"));
1392
627k
        f << "#tab[" << tab << ",";
1393
627k
        input.seek(-1, librevenge::RVNG_SEEK_CUR);
1394
627k
        break;
1395
627k
      }
1396
2.43M
      previousVal = newVal;
1397
2.43M
      ++tab;
1398
2.43M
      newTab.m_position = (newVal>>2)/72.;
1399
2.43M
      switch (newVal & 3) {
1400
1.02M
      case 0:
1401
1.02M
        break;
1402
546k
      case 1:
1403
546k
        newTab.m_alignment = MWAWTabStop::CENTER;
1404
546k
        break;
1405
389k
      case 2:
1406
389k
        newTab.m_alignment = MWAWTabStop::RIGHT;
1407
389k
        break;
1408
476k
      case 3:
1409
476k
        newTab.m_alignment = MWAWTabStop::DECIMAL;
1410
476k
        break;
1411
0
      default:
1412
0
        break;
1413
2.43M
      }
1414
2.43M
      ruler.m_tabs->push_back(newTab);
1415
2.43M
    }
1416
710k
  }
1417
741k
  if (version() <= 2) // distance from right
1418
731k
    ruler.m_margins[2] = int(72.0*m_mainParser->getPageWidth())-ruler.m_margins[2].get();
1419
741k
  *(ruler.m_margins[2]) -= 28.;
1420
741k
  if (ruler.m_margins[2].get() < 0) ruler.m_margins[2]=0;
1421
741k
  if (!interlineFixed && height>=0)
1422
532k
    ruler.setInterline(height,librevenge::RVNG_POINT,MWAWParagraph::AtLeast);
1423
208k
  else if (height>0)
1424
95.7k
    ruler.setInterline(height,librevenge::RVNG_POINT);
1425
112k
  else
1426
112k
    f << "##interline=" << height << "pt,";
1427
741k
  ruler.m_extra = f.str();
1428
741k
  return true;
1429
741k
}
1430
1431
void WriteNowText::setProperty(WriteNowTextInternal::Paragraph const &ruler)
1432
3.18M
{
1433
3.18M
  m_state->m_paragraph = ruler;
1434
3.18M
  if (!m_parserState->m_textListener) return;
1435
3.18M
  m_parserState->m_textListener->setParagraph(ruler);
1436
3.18M
}
1437
1438
////////////////////////////////////////////////////////////
1439
// the style properties ?
1440
////////////////////////////////////////////////////////////
1441
bool WriteNowText::readStyles(WriteNowEntry const &entry)
1442
1.84k
{
1443
1.84k
  m_state->m_styleList.resize(0);
1444
1.84k
  m_state->m_styleMap.clear();
1445
1446
1.84k
  if (!entry.valid() || entry.length() < 0x10) {
1447
2
    MWAW_DEBUG_MSG(("WriteNowText::readStyles: zone size is invalid\n"));
1448
2
    return false;
1449
2
  }
1450
1451
1.84k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1452
1.84k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1453
1.84k
  if (input->readLong(4) != entry.length()) {
1454
674
    MWAW_DEBUG_MSG(("WriteNowText::readStyles: bad begin of last zone\n"));
1455
674
    return false;
1456
674
  }
1457
1.17k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1458
1.17k
  libmwaw::DebugStream f;
1459
1.17k
  f << "Entries(Styles):";
1460
1.17k
  f << "ptr?=" << std::hex << input->readULong(4) << std::dec << ",";
1461
1.17k
  f << "ptr2?=" << std::hex << input->readULong(4) << std::dec << ",";
1462
1.17k
  long pos, val;
1463
4.69k
  for (int i = 0; i < 3; i++) { // 5, 0|100, 0
1464
3.51k
    val = input->readLong(2);
1465
3.51k
    if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1466
3.51k
  }
1467
1.17k
  auto N=static_cast<int>(input->readULong(2));
1468
1.17k
  f << "N=" << N << ",";
1469
3.51k
  for (int i = 0; i < 2; i++) { // 0
1470
2.34k
    val = input->readLong(2);
1471
2.34k
    if (val) f << "g" << i << "=" << val << ",";
1472
2.34k
  }
1473
1.17k
  if (long(input->tell())+N*8 > entry.end()) {
1474
110
    MWAW_DEBUG_MSG(("WriteNowText::readStyles: the zone is too short\n"));
1475
110
    return false;
1476
110
  }
1477
1.06k
  ascFile.addPos(entry.begin());
1478
1.06k
  ascFile.addNote(f.str().c_str());
1479
1.06k
  std::vector<long> defPos;
1480
1.06k
  std::map<long, int> seenMap;
1481
11.4k
  for (int n = 0; n < N; n++) {
1482
10.4k
    pos = input->tell();
1483
10.4k
    f.str("");
1484
10.4k
    f << "Styles[" << n << "]:";
1485
10.4k
    auto type = static_cast<int>(input->readULong(1));
1486
10.4k
    switch (type) {
1487
1.37k
    case 0:
1488
1.37k
      f << "def[named],";
1489
1.37k
      break;
1490
6.42k
    case 2:
1491
6.42k
      f << "def,";
1492
6.42k
      break;
1493
893
    case 4:
1494
893
      f << "user?[named],";
1495
893
      break;
1496
44
    case 0x80:
1497
44
      f << "none,";
1498
44
      break;
1499
1.70k
    default:
1500
1.70k
      MWAW_DEBUG_MSG(("WriteNowText::readStyles: find unknown type %d\n", type));
1501
1.70k
      f << "#type=" << type << ",";
1502
1.70k
      break;
1503
10.4k
    }
1504
41.7k
    for (int i = 0; i < 3; i++) { // always 0
1505
31.3k
      val = input->readLong(1);
1506
31.3k
      if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1507
31.3k
    }
1508
10.4k
    val = long(input->readULong(4));
1509
10.4k
    if (type != 0x80 && pos+val != entry.end()) {
1510
10.3k
      auto seenIt = seenMap.find(pos+val);
1511
10.3k
      int id = 0;
1512
10.3k
      if (seenIt != seenMap.end())
1513
890
        id = seenIt->second;
1514
9.50k
      else {
1515
9.50k
        id = int(defPos.size());
1516
9.50k
        defPos.push_back(pos+val);
1517
9.50k
        seenMap[pos+val] = id;
1518
9.50k
      }
1519
10.3k
      m_state->m_styleMap[n] = id;
1520
10.3k
      f << "style" << id << ",";
1521
10.3k
    }
1522
10.4k
    ascFile.addPos(pos);
1523
10.4k
    ascFile.addNote(f.str().c_str());
1524
10.4k
  }
1525
1526
7.67k
  for (size_t n = 0; n < defPos.size(); n++) {
1527
7.02k
    pos = defPos[n];
1528
7.02k
    if (pos+34 > entry.end()) {
1529
408
      MWAW_DEBUG_MSG(("WriteNowText::readStyles: can not read entry : %ld\n", long(n)));
1530
408
      return false;
1531
408
    }
1532
1533
6.61k
    WriteNowTextInternal::Style style;
1534
6.61k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1535
6.61k
    f.str("");
1536
6.61k
    auto type = static_cast<int>(input->readULong(1));
1537
6.61k
    style.m_values[0] = int(type>>4);// find 1, 2 or 3
1538
6.61k
    style.m_values[1] = int(type&0xF); // always 0 ?
1539
1540
    /* f2: 0 or 0x30, 35, 36, 37, 38, 4d, 53
1541
       f3: small number : maybe a previous id?
1542
     */
1543
19.8k
    for (int i = 2; i < 4; i++)
1544
13.2k
      style.m_values[i] = static_cast<int>(input->readULong(1));
1545
6.61k
    style.m_nextId = static_cast<int>(input->readLong(1))-1; // almost always n+1, so...
1546
    /* f4: almost always 0, but one time f4=nextId
1547
       f5-f9: always=0
1548
     */
1549
19.8k
    for (int i = 4; i < 6; i++)
1550
13.2k
      style.m_values[i] = static_cast<int>(input->readULong(1));
1551
26.4k
    for (int i = 6; i < 9; i++) // always 0
1552
19.8k
      style.m_values[i] = static_cast<int>(input->readLong(2));
1553
39.6k
    for (auto &fl : style.m_flags) fl=static_cast<int>(input->readULong(2));
1554
19.8k
    for (int i = 9; i < 11; i++) // always 0
1555
13.2k
      style.m_values[i] = static_cast<int>(input->readLong(2));
1556
6.61k
    int relPos[3];
1557
19.8k
    for (auto &rPos : relPos) rPos = static_cast<int>(input->readULong(2));
1558
6.61k
    style.m_values[11] = static_cast<int>(input->readULong(2)); // another pos ?
1559
6.61k
    if (relPos[0]) { // style name
1560
3.39k
      input->seek(pos+relPos[0], librevenge::RVNG_SEEK_SET);
1561
3.39k
      auto sz = static_cast<int>(input->readULong(1));
1562
3.39k
      if (!sz || pos+relPos[0]+1+sz > entry.end()) {
1563
2.60k
        MWAW_DEBUG_MSG(("WriteNowText::readStyles: can not read name for entry : %ld\n", long(n)));
1564
2.60k
        f << "name[length],";
1565
2.60k
      }
1566
795
      else {
1567
795
        std::string name("");
1568
28.6k
        for (int i = 0; i < sz; i++) name+=char(input->readLong(1));
1569
795
        style.m_name = name;
1570
        // maybe followed by an integer
1571
795
      }
1572
3.39k
    }
1573
6.61k
    std::string error = f.str();
1574
6.61k
    f.str("");
1575
6.61k
    f << "StylesData[" << n << ":header]:" << style << ",";
1576
6.61k
    if (error.length()) f << "#errors[" << error << "],";
1577
6.61k
    ascFile.addPos(pos);
1578
6.61k
    ascFile.addNote(f.str().c_str());
1579
6.61k
    if (relPos[1]) { // font ?
1580
5.59k
      f.str("");
1581
5.59k
      f << "StylesData[" << n << ":font]:";
1582
5.59k
      input->seek(pos+relPos[1], librevenge::RVNG_SEEK_SET);
1583
5.59k
      input->pushLimit(relPos[2] ? pos+relPos[2] : entry.end());
1584
1585
5.59k
      WriteNowTextInternal::Font font;
1586
5.59k
      if (readFont(*input, true, font)) {
1587
3.48k
        style.m_font = font;
1588
3.48k
        f << font.m_font.getDebugString(m_parserState->m_fontConverter) << font;
1589
3.48k
      }
1590
2.11k
      else
1591
2.11k
        f << "#";
1592
1593
5.59k
      input->popLimit();
1594
5.59k
      ascFile.addPos(pos+relPos[1]);
1595
5.59k
      ascFile.addNote(f.str().c_str());
1596
5.59k
    }
1597
6.61k
    if (relPos[2]) {
1598
4.97k
      f.str("");
1599
4.97k
      f << "StylesData[" << n << ":ruler]:";
1600
4.97k
      input->seek(pos+relPos[2], librevenge::RVNG_SEEK_SET);
1601
4.97k
      auto sz = static_cast<int>(input->readULong(2));
1602
4.97k
      input->pushLimit(pos+relPos[2]+sz);
1603
4.97k
      WriteNowTextInternal::Paragraph ruler;
1604
4.97k
      if (readParagraph(*input, ruler)) {
1605
2.85k
        style.m_paragraph = ruler;
1606
2.85k
        f << ruler;
1607
2.85k
      }
1608
2.11k
      else
1609
2.11k
        f << "#";
1610
4.97k
      input->popLimit();
1611
4.97k
      ascFile.addPos(pos+relPos[2]);
1612
4.97k
      ascFile.addNote(f.str().c_str());
1613
4.97k
    }
1614
6.61k
    m_state->m_styleList.push_back(style);
1615
6.61k
  }
1616
1617
655
  entry.setParsed(true);
1618
655
  ascFile.addPos(entry.end());
1619
655
  ascFile.addNote("_");
1620
655
  return true;
1621
1.06k
}
1622
1623
////////////////////////////////////////////////////////////
1624
// zone which corresponds to the token
1625
////////////////////////////////////////////////////////////
1626
bool WriteNowText::readToken(MWAWInputStream &input, WriteNowTextInternal::Token &token)
1627
3.89k
{
1628
3.89k
  token=WriteNowTextInternal::Token();
1629
1630
3.89k
  long pos = input.tell();
1631
3.89k
  input.seek(pos+54, librevenge::RVNG_SEEK_SET);
1632
3.89k
  if (pos+54 != long(input.tell())) {
1633
420
    MWAW_DEBUG_MSG(("WriteNowText::readToken: zone is too short \n"));
1634
420
    return false;
1635
420
  }
1636
3.47k
  input.seek(pos, librevenge::RVNG_SEEK_SET);
1637
3.47k
  int dim[4];
1638
13.9k
  for (auto &d : dim) d = static_cast<int>(input.readLong(2));
1639
3.47k
  token.m_box = MWAWBox2i(MWAWVec2i(dim[1], dim[0]), MWAWVec2i(dim[3], dim[2]));
1640
3.47k
  int actVal = 0;
1641
6.95k
  for (auto &st : token.m_pos) {
1642
6.95k
    auto dim0 = static_cast<int>(input.readLong(2));
1643
6.95k
    token.m_values[actVal++] = static_cast<int>(input.readLong(2)); // always 0
1644
6.95k
    token.m_values[actVal++] = static_cast<int>(input.readLong(2)); // always 0
1645
6.95k
    auto dim1 = static_cast<int>(input.readLong(2));
1646
6.95k
    st = MWAWVec2i(dim1, -dim0);
1647
6.95k
  }
1648
  /*
1649
    f0/f2:  0 or 1 ( very similar)
1650
    f1/f3 : 0 or big number
1651
    f8=1, f9=14
1652
    other 0
1653
   */
1654
52.1k
  for (int i = 0; i < 14; i++)
1655
48.6k
    token.m_values[actVal++] = static_cast<int>(input.readULong(2));
1656
3.47k
  token.m_graphicZone = static_cast<int>(input.readLong(2));
1657
3.47k
  return true;
1658
3.89k
}
1659
1660
bool WriteNowText::readTokenV2(MWAWInputStream &input, WriteNowTextInternal::Token &token)
1661
1.42M
{
1662
1.42M
  token=WriteNowTextInternal::Token();
1663
1664
1.42M
  long actPos = input.tell();
1665
1.42M
  int dim[2];
1666
2.85M
  for (auto &d : dim) d = static_cast<int>(input.readLong(2));
1667
1.42M
  MWAWVec2i box(dim[1], dim[0]);
1668
1.42M
  token.m_box=MWAWBox2i(MWAWVec2i(0,0), box);
1669
  // we need to get the size, so...
1670
3.54M
  while (!input.isEnd())
1671
2.11M
    input.seek(0x100, librevenge::RVNG_SEEK_CUR);
1672
1.42M
  long endPos = input.tell();
1673
1.42M
  long sz = endPos-actPos-4;
1674
1.42M
  if (sz <= 0) return false;
1675
1.31M
  input.seek(actPos+4, librevenge::RVNG_SEEK_SET);
1676
1.31M
  MWAWInputStreamPtr ip(&input,MWAW_shared_ptr_noop_deleter<MWAWInputStream>());
1677
1.31M
  std::shared_ptr<MWAWPict> pict(MWAWPictData::get(ip, static_cast<int>(sz)));
1678
1.31M
  if (!pict) {
1679
158k
    MWAW_DEBUG_MSG(("WriteNowParser::readTokenV2: can not read the picture\n"));
1680
158k
    return false;
1681
158k
  }
1682
1.15M
  if (!m_parserState->m_textListener) return true;
1683
1684
1.15M
  MWAWPosition pictPos;
1685
1.15M
  if (box.x() > 0 && box.y() > 0) {
1686
454k
    pictPos=MWAWPosition(MWAWVec2f(0,0),MWAWVec2f(box), librevenge::RVNG_POINT);
1687
454k
    pictPos.setNaturalSize(pict->getBdBox().size());
1688
454k
  }
1689
701k
  else
1690
701k
    pictPos=MWAWPosition(MWAWVec2f(0,0),pict->getBdBox().size(), librevenge::RVNG_POINT);
1691
1.15M
  pictPos.setRelativePosition(MWAWPosition::Char);
1692
1693
1.15M
  MWAWEmbeddedObject picture;
1694
1.15M
  if (pict->getBinary(picture))
1695
1.15M
    m_parserState->m_textListener->insertPicture(pictPos, picture);
1696
1697
1.15M
  return true;
1698
1.15M
}
1699
1700
////////////////////////////////////////////////////////////
1701
// zone which corresponds to the table
1702
////////////////////////////////////////////////////////////
1703
bool WriteNowText::readTable(MWAWInputStream &input, WriteNowTextInternal::TableData &table)
1704
1.39M
{
1705
1.39M
  table=WriteNowTextInternal::TableData();
1706
1.39M
  long pos = input.tell();
1707
1708
1.39M
  table.m_type = static_cast<int>(input.readULong(1));
1709
1.39M
  if (input.isEnd()) {
1710
16.0k
    if (table.m_type!=0) {
1711
7.74k
      MWAW_DEBUG_MSG(("WriteNowText::readTable: find a zone will 0 size\n"));
1712
7.74k
      return false;
1713
7.74k
    }
1714
8.29k
    return true;
1715
16.0k
  }
1716
1.38M
  input.seek(pos+28, librevenge::RVNG_SEEK_SET);
1717
1.38M
  if (pos+28 != long(input.tell())) {
1718
215k
    MWAW_DEBUG_MSG(("WriteNowText::readTable: zone is too short \n"));
1719
215k
    return false;
1720
215k
  }
1721
1.16M
  input.seek(pos+1, librevenge::RVNG_SEEK_SET);
1722
1.16M
  int actVal = 0;
1723
1.16M
  table.m_values[actVal++] = static_cast<int>(input.readLong(1));
1724
1.16M
  table.m_values[actVal++] = static_cast<int>(input.readLong(1));
1725
1.16M
  auto backColor = static_cast<int>(input.readULong(1));
1726
1.16M
  MWAWColor col;
1727
1.16M
  if (m_mainParser->getColor(backColor,col))
1728
24
    table.m_color = col;
1729
1.16M
  else {
1730
1.16M
    MWAW_DEBUG_MSG(("WriteNowText::readTable: can not read backgroundColor: %d\n", backColor));
1731
1.16M
  }
1732
4.67M
  for (int &flag : table.m_flags) {
1733
4.67M
    flag = static_cast<int>(input.readULong(1)); // 0x80, 0x81, 5, 0 : border flag ?
1734
4.67M
    table.m_values[actVal++] = static_cast<int>(input.readLong(1)); //  always 0 ?
1735
4.67M
  }
1736
4.67M
  for (int i = 0; i < 3; i++)
1737
3.50M
    table.m_values[actVal++] = static_cast<int>(input.readLong(2)); // alway 0
1738
1.16M
  int dim[4];
1739
4.67M
  for (auto &d : dim) d = static_cast<int>(input.readLong(2));
1740
1.16M
  table.m_box = MWAWBox2i(MWAWVec2i(dim[1], dim[0]), MWAWVec2i(dim[3], dim[2]));
1741
1.16M
  table.m_values[actVal++] = static_cast<int>(input.readLong(2));
1742
1743
1.16M
  return true;
1744
1.38M
}
1745
1746
////////////////////////////////////////////////////////////
1747
// send data to the listener
1748
bool WriteNowText::send(WriteNowEntry const &entry)
1749
191k
{
1750
191k
  std::shared_ptr<WriteNowTextInternal::ContentZones> text(m_state->getContentZone(entry.begin()));
1751
191k
  if (!text) {
1752
12.5k
    MWAW_DEBUG_MSG(("WriteNowText::send: can not find entry\n"));
1753
12.5k
    return false;
1754
12.5k
  }
1755
178k
  auto ruler=m_state->getDefaultParagraph(text->m_type);
1756
178k
  text->m_sent = true;
1757
178k
  return send(text->m_zonesList, text->m_footnoteList, ruler);
1758
191k
}
1759
1760
bool WriteNowText::send(std::vector<WriteNowTextInternal::ContentZone> &listZones,
1761
                        std::vector<std::shared_ptr<WriteNowTextInternal::ContentZones> > &footnoteList,
1762
                        WriteNowTextInternal::Paragraph &ruler)
1763
709k
{
1764
709k
  MWAWTextListenerPtr listener=m_parserState->m_textListener;
1765
709k
  if (!listener) {
1766
0
    MWAW_DEBUG_MSG(("WriteNowText::send: can not find the listener\n"));
1767
0
    return false;
1768
0
  }
1769
709k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1770
709k
  libmwaw::DebugStream f;
1771
709k
  int vers = version();
1772
709k
  MWAWFont actFont(3,12); // default geneva 12pt
1773
709k
  listener->setFont(actFont);
1774
709k
  int extraDecal = 0; // for v2
1775
709k
  bool rulerSet = false;
1776
709k
  int numLineTabs = 0, actTabs = 0, numFootnote = 0;
1777
1778
709k
  std::shared_ptr<WriteNowTextInternal::Table> table;
1779
709k
  std::shared_ptr<WriteNowTextInternal::Cell> cell;
1780
1781
709k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1782
74.0M
  for (auto const &zone : listZones) {
1783
74.0M
    if (table && cell) {
1784
8.80M
      bool done = true;
1785
8.80M
      switch (zone.m_type) {
1786
578k
      case 0xa:
1787
578k
        done = false;
1788
578k
        break;
1789
897k
      case 4:
1790
897k
        if (numFootnote < int(footnoteList.size())) {
1791
77.9k
          cell->m_footnoteList.push_back(footnoteList[size_t(numFootnote)]);
1792
77.9k
          numFootnote++;
1793
77.9k
        }
1794
897k
        cell->m_zonesList.push_back(zone);
1795
897k
        break;
1796
5.06k
      case 0x10:
1797
5.06k
        MWAW_DEBUG_MSG(("WriteNowText::send: find a page/column break in table(ignored)\n"));
1798
5.06k
        break;
1799
7.32M
      default:
1800
7.32M
        cell->m_zonesList.push_back(zone);
1801
7.32M
        break;
1802
8.80M
      }
1803
8.80M
      if (done)
1804
8.22M
        continue;
1805
8.80M
    }
1806
65.2M
    else if (table && zone.m_type != 0xa) {
1807
5.50M
      static bool first=true;
1808
5.50M
      if (first) {
1809
25
        first = false;
1810
25
        MWAW_DEBUG_MSG(("WriteNowText::send: find some data in table but outside cell\n"));
1811
25
      }
1812
5.50M
    }
1813
65.8M
    switch (zone.m_type) {
1814
2.77M
    case 4:
1815
2.77M
      if (numFootnote < int(footnoteList.size())) {
1816
188k
        m_mainParser->sendFootnote(footnoteList[size_t(numFootnote)]->m_entry);
1817
188k
        numFootnote++;
1818
188k
      }
1819
2.58M
      else {
1820
2.58M
        MWAW_DEBUG_MSG(("WriteNowText::send: can not find footnote:%d\n", numFootnote));
1821
2.58M
      }
1822
2.77M
      break;
1823
95.4k
    case 0xd:
1824
95.4k
      switch (zone.m_value) {
1825
17.7k
      case 0:
1826
17.7k
        listener->insertField(MWAWField(MWAWField::PageNumber));
1827
17.7k
        break;
1828
42.0k
      case 1: {
1829
42.0k
        MWAWField date(MWAWField::Date);
1830
42.0k
        date.m_DTFormat = "%a, %b %d, %Y";
1831
42.0k
        listener->insertField(date);
1832
42.0k
        break;
1833
0
      }
1834
17.4k
      case 2: {
1835
17.4k
        MWAWField time(MWAWField::Time);
1836
17.4k
        time.m_DTFormat="%H:%M";
1837
17.4k
        listener->insertField(time);
1838
17.4k
        break;
1839
0
      }
1840
2.30k
      case 3: // note field : ok
1841
18.2k
      default:
1842
18.2k
        break;
1843
95.4k
      }
1844
95.4k
      break;
1845
95.4k
    case 0x10:
1846
38.2k
      if (m_state->m_numColumns <= 1 && ++m_state->m_actualPage <= m_state->m_numPages)
1847
23.0k
        m_mainParser->newPage(m_state->m_actualPage);
1848
15.2k
      else if (m_state->m_numColumns > 1 && listener)
1849
15.2k
        listener->insertBreak(MWAWTextListener::ColumnBreak);
1850
38.2k
      break;
1851
62.9M
    default:
1852
62.9M
      break;
1853
65.8M
    }
1854
1855
65.8M
    if ((zone.m_type>0 && zone.m_type < 8) || zone.m_type == 0xd || zone.m_type == 0x10)
1856
38.7M
      continue;
1857
1858
27.0M
    librevenge::RVNGBinaryData data;
1859
27.0M
    input->seek(zone.m_pos[0],librevenge::RVNG_SEEK_SET); //000a2f
1860
1.78G
    while (int(input->tell()) < zone.m_pos[1]) {
1861
1.75G
      auto ch = static_cast<int>(input->readULong(1));
1862
1.75G
      if (ch == 0xf0) {
1863
3.47M
        ch = static_cast<int>(input->readULong(1));
1864
3.47M
        if (ch & 0xf0) {
1865
2.77M
          MWAW_DEBUG_MSG(("WriteNowText::send: find odd 0xF0 following\n"));
1866
2.77M
          continue;
1867
2.77M
        }
1868
700k
        data.append(static_cast<unsigned char>(0xf0 | ch));
1869
700k
      }
1870
1.75G
      else
1871
1.75G
        data.append(static_cast<unsigned char>(ch));
1872
1.75G
    }
1873
1874
27.0M
    f.str("");
1875
27.0M
    if (zone.m_type == 0) {
1876
20.8M
      auto sz = long(data.size());
1877
20.8M
      const unsigned char *buffer = data.getDataBuffer();
1878
20.8M
      if (!buffer && sz) {
1879
0
        MWAW_DEBUG_MSG(("WriteNowText::send: can find data buffer\n"));
1880
0
        sz = 0;
1881
0
      }
1882
20.8M
      if (sz && !rulerSet) {
1883
556k
        setProperty(ruler);
1884
556k
        numLineTabs = int(ruler.m_tabs->size());
1885
556k
        rulerSet = true;
1886
556k
      }
1887
380M
      for (long i = 0; i < sz; i++) {
1888
360M
        unsigned char c = *(buffer++);
1889
360M
        f << c;
1890
360M
        switch (c) {
1891
2.19M
        case 0x9:
1892
2.19M
          if (actTabs++ < numLineTabs)
1893
382k
            listener->insertTab();
1894
1.81M
          else
1895
1.81M
            listener->insertChar(' ');
1896
2.19M
          break;
1897
1.88M
        case 0xd:
1898
          // this is marks the end of a paragraph
1899
1.88M
          listener->insertEOL();
1900
1.88M
          setProperty(ruler);
1901
1.88M
          actTabs = 0;
1902
1.88M
          break;
1903
355M
        default:
1904
355M
          listener->insertCharacter(c);
1905
355M
          break;
1906
360M
        }
1907
360M
      }
1908
20.8M
      ascFile.addPos(zone.m_pos[0]);
1909
20.8M
      ascFile.addNote(f.str().c_str());
1910
20.8M
      continue;
1911
20.8M
    }
1912
6.21M
    MWAWInputStreamPtr dataInput;
1913
6.21M
    if (data.size())
1914
6.14M
      dataInput=MWAWInputStream::get(data, false);
1915
6.21M
    switch (zone.m_type) {
1916
181k
    case 0x9: { // only in v2
1917
181k
      extraDecal = zone.m_value;
1918
181k
      MWAWFont font(actFont);
1919
181k
      if (extraDecal > 0) font.set(MWAWFont::Script::super100());
1920
42.9k
      else if (extraDecal < 0) font.set(MWAWFont::Script::sub100());
1921
181k
      listener->setFont(font);
1922
181k
      break;
1923
0
    }
1924
1.40M
    case 0xa: {  // only in writenow 4.0 : related to a table ?
1925
1.40M
      if (!dataInput) break;
1926
1.39M
      WriteNowTextInternal::TableData tableData;
1927
1.39M
      if (readTable(*dataInput, tableData)) {
1928
1.17M
        f << tableData;
1929
1930
1.17M
        bool needSendTable = false;
1931
1.17M
        bool needCreateCell = false;
1932
1.17M
        switch (zone.m_value) {
1933
187k
        case 0:
1934
187k
          if (table)
1935
87.1k
            needSendTable = true;
1936
100k
          else {
1937
100k
            MWAW_DEBUG_MSG(("WriteNowText::send: Find odd end of table\n"));
1938
100k
          }
1939
187k
          break;
1940
372k
        case 1:
1941
372k
          if (!table)
1942
170k
            table.reset(new WriteNowTextInternal::Table);
1943
201k
          else {
1944
201k
            MWAW_DEBUG_MSG(("WriteNowText::send: Find a table in a table\n"));
1945
201k
          }
1946
372k
          break;
1947
459k
        case 2:
1948
459k
          if (table)
1949
354k
            needCreateCell = true;
1950
104k
          else {
1951
104k
            MWAW_DEBUG_MSG(("WriteNowText::send: Find cell outside a table\n"));
1952
104k
          }
1953
459k
          break;
1954
157k
        default:
1955
157k
          break;
1956
1.17M
        }
1957
1958
1.17M
        if (needSendTable) {
1959
87.1k
          if (table) {
1960
87.1k
            if (!table->sendTable(listener))
1961
61.6k
              table->sendAsText(listener);
1962
87.1k
          }
1963
0
          else {
1964
0
            MWAW_DEBUG_MSG(("WriteNowText::send: can not find the cell to send...\n"));
1965
0
          }
1966
87.1k
          table.reset();
1967
87.1k
        }
1968
1.17M
        if (needCreateCell) {
1969
354k
          cell.reset(new WriteNowTextInternal::Cell(*this));
1970
354k
          tableData.updateCell(*cell);
1971
354k
          table->add(cell);
1972
354k
        }
1973
1.17M
      }
1974
222k
      else
1975
222k
        f << "#";
1976
1.39M
      break;
1977
1.39M
    }
1978
1.39M
    case 0xb: {
1979
      // this seems to define the leader than add a tab
1980
50.8k
      if (actTabs++ < numLineTabs)
1981
19.6k
        listener->insertTab();
1982
31.2k
      else
1983
31.2k
        listener->insertChar(' ');
1984
50.8k
      break;
1985
1.39M
    }
1986
958k
    case 0xc: {
1987
958k
      if (!dataInput) break;
1988
943k
      WriteNowTextInternal::Paragraph newParagraph;
1989
943k
      if (readParagraph(*dataInput, newParagraph)) {
1990
738k
        ruler = newParagraph;
1991
738k
        numLineTabs = int(ruler.m_tabs->size());
1992
738k
        setProperty(ruler);
1993
738k
        rulerSet = true;
1994
738k
        f << ruler;
1995
738k
      }
1996
204k
      else
1997
204k
        f << "#";
1998
943k
      break;
1999
958k
    }
2000
1.44M
    case 0xe: {
2001
1.44M
      if (!dataInput) break;
2002
1.42M
      WriteNowTextInternal::Token token;
2003
1.42M
      if (vers >= 3) {
2004
3.89k
        if (readToken(*dataInput, token)) {
2005
3.47k
          m_mainParser->sendGraphic(token.m_graphicZone, token.m_box);
2006
3.47k
          f << token;
2007
3.47k
        }
2008
420
        else
2009
420
          f << "#";
2010
3.89k
      }
2011
1.42M
      else {
2012
1.42M
        if (readTokenV2(*dataInput, token))
2013
1.15M
          f << token;
2014
269k
        else
2015
269k
          f << "#";
2016
1.42M
      }
2017
1.42M
      break;
2018
1.44M
    }
2019
1.78M
    case 0xf: {
2020
1.78M
      if (!dataInput) break;
2021
1.75M
      WriteNowTextInternal::Font font;
2022
1.75M
      if (readFont(*dataInput, false, font)) {
2023
1.60M
        actFont=font.m_font;
2024
1.60M
        f << font.m_font.getDebugString(m_parserState->m_fontConverter) << font;
2025
1.60M
        if (extraDecal > 0) font.m_font.set(MWAWFont::Script::super100());
2026
1.28M
        else if (extraDecal < 0) font.m_font.set(MWAWFont::Script::sub100());
2027
1.60M
        listener->setFont(font.m_font);
2028
1.60M
      }
2029
145k
      else
2030
145k
        f << "#";
2031
1.75M
      break;
2032
1.78M
    }
2033
389k
    default:
2034
389k
      MWAW_DEBUG_MSG(("WriteNowText::send: find keyword %x\n",static_cast<unsigned int>(zone.m_type)));
2035
389k
      break;
2036
6.21M
    }
2037
2038
6.21M
    ascFile.addPos(zone.m_pos[0]-1);
2039
6.21M
    ascFile.addNote(f.str().c_str());
2040
6.21M
  }
2041
2042
709k
  if (table) {
2043
83.2k
    MWAW_DEBUG_MSG(("WriteNowText::send: a table is not closed\n"));
2044
83.2k
    if (!table->sendTable(listener))
2045
59.7k
      table->sendAsText(listener);
2046
83.2k
  }
2047
2048
709k
  return true;
2049
709k
}
2050
2051
void WriteNowText::sendZone(int id)
2052
89.5k
{
2053
89.5k
  MWAWTextListenerPtr listener=m_parserState->m_textListener;
2054
89.5k
  if (!listener) {
2055
0
    MWAW_DEBUG_MSG(("WriteNowText::sendZone: can not find the listener\n"));
2056
0
    return;
2057
0
  }
2058
89.5k
  if (id < 0 || id >= 3) {
2059
0
    MWAW_DEBUG_MSG(("WriteNowText::sendZone: called with id=%d\n",id));
2060
0
    return;
2061
0
  }
2062
89.5k
  if (id == 0) {
2063
59.6k
    int nCol, width=0;
2064
59.6k
    m_mainParser->getColumnInfo(nCol, width);
2065
59.6k
    if (m_state->m_numColumns > 1) {
2066
20.0k
      if (width <= 0)
2067
4.28k
        width = int((72.0*m_mainParser->getPageWidth())/m_state->m_numColumns);
2068
20.0k
      if (listener->isSectionOpened())
2069
10.0k
        listener->closeSection();
2070
20.0k
      MWAWSection sec;
2071
20.0k
      sec.setColumns(m_state->m_numColumns, double(width), librevenge::RVNG_POINT);
2072
20.0k
      listener->openSection(sec);
2073
20.0k
    }
2074
59.6k
  }
2075
89.5k
  auto &mZone = m_state->m_mainZones[id];
2076
89.5k
  auto ruler=m_state->getDefaultParagraph(id);
2077
89.5k
  if (id==0)
2078
59.6k
    listener->setParagraph(ruler);
2079
642k
  for (auto &zone : mZone.m_zones) {
2080
642k
    if (zone->m_sent) continue;
2081
188k
    if (id == 0 && zone->m_type) continue;
2082
188k
    if (id) ruler=m_state->getDefaultParagraph(id);
2083
188k
    send(zone->m_zonesList, zone->m_footnoteList, ruler);
2084
188k
    zone->m_sent = true;
2085
188k
  }
2086
89.5k
}
2087
2088
void WriteNowText::flushExtra()
2089
29.8k
{
2090
119k
  for (int z = 0; z < 3; z++) {
2091
89.5k
    if (z == 1) continue;
2092
59.6k
    sendZone(z);
2093
59.6k
  }
2094
29.8k
}
2095
2096
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: