Coverage Report

Created: 2026-10-28 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/GreatWksText.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 <cstring>
35
#include <iomanip>
36
#include <iostream>
37
#include <limits>
38
#include <map>
39
#include <sstream>
40
41
#include <librevenge/librevenge.h>
42
43
#include "MWAWDebug.hxx"
44
#include "MWAWFont.hxx"
45
#include "MWAWFontConverter.hxx"
46
#include "MWAWGraphicListener.hxx"
47
#include "MWAWListener.hxx"
48
#include "MWAWParagraph.hxx"
49
#include "MWAWParser.hxx"
50
#include "MWAWPosition.hxx"
51
#include "MWAWSection.hxx"
52
#include "MWAWRSRCParser.hxx"
53
54
#include "GreatWksDocument.hxx"
55
56
#include "GreatWksText.hxx"
57
58
/** Internal: the structures of a GreatWksText */
59
namespace GreatWksTextInternal
60
{
61
/** the different plc type */
62
enum PLCType { P_Font, P_Page,  P_Ruler, P_Token, P_Unknown};
63
64
/** Internal : a PLC: used to store change of properties in GreatWksTextInternal::Zone */
65
struct PLC {
66
  /// the constructor
67
  PLC()
68
69.0k
    : m_type(P_Unknown)
69
69.0k
    , m_id(-1)
70
69.0k
    , m_extra("")
71
69.0k
  {
72
69.0k
  }
73
  //! operator<<
74
  friend std::ostream &operator<<(std::ostream &o, PLC const &plc);
75
  /** the PLC types */
76
  PLCType m_type;
77
  /** the id */
78
  int m_id;
79
  /** extra data */
80
  std::string m_extra;
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_Page:
89
0
    o << "Pg";
90
0
    break;
91
0
  case P_Ruler:
92
0
    o << "R";
93
0
    break;
94
0
  case P_Token:
95
0
    o << "Tn";
96
0
    break;
97
0
  case P_Unknown:
98
#if !defined(__clang__)
99
  default:
100
#endif
101
0
    o << "#Unkn";
102
0
    break;
103
0
  }
104
0
  if (plc.m_id >= 0) o << plc.m_id;
105
0
  else o << "_";
106
0
  if (plc.m_extra.length()) o << ":" << plc.m_extra;
107
0
  return o;
108
0
}
109
110
////////////////////////////////////////
111
//! Internal and low level: structure which stores a token for GreatWksText
112
struct Token {
113
  //! constructor
114
  Token()
115
3.40M
    : m_type(-1)
116
3.40M
    , m_format(0)
117
3.40M
    , m_pictEntry()
118
3.40M
    , m_dataSize(0)
119
3.40M
    , m_dim(0,0)
120
3.40M
    , m_date(0xFFFFFFFF)
121
3.40M
    , m_extra("")
122
3.40M
  {
123
3.40M
  }
124
  //! returns a field format
125
  std::string getDTFormat() const;
126
  //! try to send the token to the listener
127
  bool sendTo(MWAWListener &listener) const;
128
  //! operator<<
129
  friend std::ostream &operator<<(std::ostream &o, Token const &tkn);
130
  //! the token type
131
  int m_type;
132
  //! the token format
133
  int m_format;
134
  //! the picture entry
135
  MWAWEntry m_pictEntry;
136
  //! the picture data size
137
  long m_dataSize;
138
  //! the picture dimension
139
  MWAWVec2f m_dim;
140
  //! the token date (0xFFFFFFFF means actual date)
141
  uint32_t m_date;
142
  //! extra data
143
  std::string m_extra;
144
};
145
146
bool Token::sendTo(MWAWListener &listener) const
147
63.8k
{
148
63.8k
  switch (m_type) {
149
119
  case 2:
150
119
    switch (m_format) {
151
9
    case 1:
152
18
    case 3: // must be roman
153
18
      listener.insertField(MWAWField(MWAWField::PageNumber));
154
18
      break;
155
52
    case 2:
156
58
    case 4: // must be roman
157
58
      listener.insertField(MWAWField(MWAWField::PageNumber));
158
58
      listener.insertUnicodeString(librevenge::RVNGString(" of "));
159
58
      listener.insertField(MWAWField(MWAWField::PageCount));
160
58
      break;
161
43
    default:
162
43
      listener.insertField(MWAWField(MWAWField::PageNumber));
163
43
      break;
164
119
    }
165
119
    return true;
166
644
  case 0x15:
167
660
  case 0x16: {
168
660
    MWAWField field(m_type==0x15 ? MWAWField::Date : MWAWField::Time);
169
660
    field.m_DTFormat=getDTFormat();
170
660
    listener.insertField(field);
171
660
    return true;
172
644
  }
173
63.0k
  default:
174
63.0k
    break;
175
63.8k
  }
176
63.0k
  return false;
177
63.8k
}
178
179
std::string Token::getDTFormat() const
180
660
{
181
660
  switch (m_type) {
182
644
  case 0x15:
183
644
    switch (m_format) {
184
1
    case 0xa:
185
1
      return "%m/%d/%y";
186
590
    case 0xb:
187
590
      return "%b %d, %Y";
188
0
    case 0xc:
189
0
      return "%b %Y";
190
0
    case 0xd:
191
0
      return "%b %d";
192
0
    case 0xe:
193
0
      return "%B %d, %Y";
194
3
    case 0xf:
195
3
      return "%B %Y";
196
15
    case 0x10:
197
15
      return "%B %d";
198
1
    case 0x11:
199
1
      return "%a, %b %d, %Y";
200
9
    case 0x12:
201
9
      return "%A, %B %d, %Y";
202
25
    default:
203
25
      break;
204
644
    }
205
25
    break;
206
25
  case 0x16:
207
16
    switch (m_format) {
208
0
    case 0x14:
209
0
      return "%I:%M %p";
210
1
    case 0x15:
211
1
      return "%I:%M:%S %p";
212
5
    case 0x16:
213
5
      return "%I:%M";
214
0
    case 0x17:
215
0
      return "%I:%M:%S";
216
0
    case 0x18:
217
0
      return "%H:%M";
218
0
    case 0x19:
219
0
      return "%H:%M:%S";
220
10
    default:
221
10
      break;
222
16
    }
223
10
    break;
224
10
  default:
225
0
    break;
226
660
  }
227
35
  return "";
228
660
}
229
230
std::ostream &operator<<(std::ostream &o, Token const &token)
231
0
{
232
0
  switch (token.m_type) {
233
0
  case 0: // none
234
0
    break;
235
0
  case 2:
236
0
    switch (token.m_format) {
237
0
    case 0:
238
0
      o << "page,";
239
0
      break;
240
0
    case 1:
241
0
      o << "page/pagecount,";
242
0
      break;
243
0
    case 2:
244
0
      o << "page[roman],";
245
0
      break;
246
0
    case 3:
247
0
      o << "page/pagecount[roman],";
248
0
      break;
249
0
    default:
250
0
      o << "page[#m_format=" << token.m_format << "],";
251
0
      break;
252
0
    }
253
0
    break;
254
0
  case 4:
255
0
    o << "pict,dim="<< token.m_dim << ",sz=" << std::hex << token.m_dataSize << std::dec << ",";
256
0
    break;
257
0
  case 0x15:
258
0
  case 0x16: {
259
0
    o << (token.m_type==0x15 ? "date" : "time");
260
0
    std::string format=token.getDTFormat();
261
0
    if (!format.empty())
262
0
      o << "[" << format << "]";
263
0
    else
264
0
      o << "[#format=" << token.m_format << "]";
265
0
    if (token.m_date != 0xFFFFFFFF)
266
0
      o << ":val=" << std::hex << token.m_date << std::dec;
267
0
    o << ",";
268
0
    break;
269
0
  }
270
0
  default:
271
0
    MWAW_DEBUG_MSG(("GreatWksTextInternal::Token: unknown type=%d\n", token.m_type));
272
0
    o << "#type=" << token.m_type << ",";
273
0
    if (token.m_format)
274
0
      o << "#format=" << token.m_format << ",";
275
0
  }
276
0
  o << token.m_extra;
277
0
  return o;
278
0
}
279
////////////////////////////////////////
280
//! Internal and low level: structure which stores a text position for GreatWksText
281
struct Frame {
282
  //! constructor
283
  Frame()
284
208k
    : m_pos()
285
208k
    , m_page(0)
286
208k
    , m_extra("")
287
208k
  {
288
208k
  }
289
  //! operator<<
290
  friend std::ostream &operator<<(std::ostream &o, Frame const &frm)
291
0
  {
292
0
    o << "dim=" << frm.m_pos << ",";
293
0
    if (frm.m_page) o << "page=" << frm.m_page << ",";
294
0
    o << frm.m_extra;
295
0
    return o;
296
0
  }
297
298
  //! the frame position
299
  MWAWBox2f m_pos;
300
  //! the page
301
  int m_page;
302
  //! extra data
303
  std::string m_extra;
304
};
305
////////////////////////////////////////
306
//! Internal and low level: structure which stores a text zone header for GreatWksText
307
struct Zone {
308
  //! constructor
309
  Zone()
310
135k
    : m_type(-1)
311
135k
    , m_numFonts(0)
312
135k
    , m_numRulers(0)
313
135k
    , m_numLines(0)
314
135k
    , m_numTokens(0)
315
135k
    , m_numChar(0)
316
135k
    , m_numCharPLC(0)
317
135k
    , m_numFrames(0)
318
135k
    , m_fontList()
319
135k
    , m_rulerList()
320
135k
    , m_tokenList()
321
135k
    , m_frameList()
322
135k
    , m_textEntry()
323
135k
    , m_posPLCMap()
324
135k
    , m_parsed(false)
325
135k
    , m_extra("")
326
135k
  {
327
135k
  }
328
  //! returns true if this is the main zone
329
  bool isMain() const
330
61.5k
  {
331
61.5k
    return m_type==3;
332
61.5k
  }
333
  //! check if the data read are or not ok
334
  bool ok() const
335
43.8k
  {
336
43.8k
    if (m_type<0 || m_type > 5)
337
403
      return false;
338
43.4k
    if (m_numFonts<=0 || m_numRulers<=0 || m_numLines<0 || m_numTokens<=0 || m_numChar<0 ||
339
39.7k
        m_numCharPLC<=0 || m_numFrames<0)
340
7.27k
      return false;
341
36.1k
    return true;
342
43.4k
  }
343
  //! returns the data size
344
  long size() const
345
36.1k
  {
346
36.1k
    return 22*m_numFonts+192*m_numRulers+6*m_numCharPLC
347
36.1k
           +18*m_numTokens+14*m_numLines+22*m_numFrames+m_numChar;
348
36.1k
  }
349
  //! returns true if the data has graphic
350
  bool hasGraphics() const
351
486
  {
352
515
    for (auto const &token : m_tokenList) {
353
515
      if (token.m_type==4)
354
0
        return true;
355
515
    }
356
486
    return false;
357
486
  }
358
  //! operator<<
359
  friend std::ostream &operator<<(std::ostream &o, Zone const &fr)
360
0
  {
361
0
    switch (fr.m_type) {
362
0
    case 1: // in draw=textbox
363
0
      o << "header/footer,";
364
0
      break;
365
0
    case 2:
366
0
      o << "textbox,";
367
0
      break;
368
0
    case 3:
369
0
      o << "main,";
370
0
      break;
371
0
    default:
372
0
      o << "#type=" << fr.m_type << ",";
373
0
      break;
374
0
    }
375
0
    if (fr.m_numFonts)
376
0
      o << "nFonts=" << fr.m_numFonts << ",";
377
0
    if (fr.m_numRulers)
378
0
      o << "nRulers=" << fr.m_numRulers << ",";
379
0
    if (fr.m_numChar)
380
0
      o << "length[text]=" << fr.m_numChar << ",";
381
0
    if (fr.m_numCharPLC)
382
0
      o << "char[plc]=" << fr.m_numCharPLC << ",";
383
0
    if (fr.m_numLines)
384
0
      o << "nLines=" << fr.m_numLines << ",";
385
0
    if (fr.m_numTokens)
386
0
      o << "nTokens=" << fr.m_numTokens << ",";
387
0
    if (fr.m_numFrames)
388
0
      o << "nFrames=" << fr.m_numFrames << ",";
389
0
    o << fr.m_extra;
390
0
    return o;
391
0
  }
392
  //! the main type: 1=auxi, 3=main
393
  int m_type;
394
  //! the number of fonts
395
  int m_numFonts;
396
  //! the number of rulers
397
  int m_numRulers;
398
  //! the number of lines
399
  int m_numLines;
400
  //! the number of token
401
  int m_numTokens;
402
  //! the number of character
403
  long m_numChar;
404
  //! the number of char plc
405
  int m_numCharPLC;
406
  //! the number of frames (ie. one by column and one by pages )
407
  int m_numFrames;
408
  //! the list of font
409
  std::vector<MWAWFont> m_fontList;
410
  //! the list of ruler
411
  std::vector<MWAWParagraph> m_rulerList;
412
  //! the list of token
413
  std::vector<Token> m_tokenList;
414
  //! the list of frame token
415
  std::vector<Frame> m_frameList;
416
  //! the text entry list
417
  MWAWEntry m_textEntry;
418
  //! a map text pos -> PLC
419
  std::multimap<long, PLC> m_posPLCMap;
420
  //! a bool to know if the data are send to the listener
421
  mutable bool m_parsed;
422
  //! extra data
423
  std::string m_extra;
424
};
425
426
////////////////////////////////////////
427
//! Internal: the state of a GreatWksText
428
struct State {
429
  //! constructor
430
  State()
431
112k
    : m_fileIdFontIdMap()
432
112k
    , m_zonesList()
433
112k
    , m_version(-1)
434
112k
    , m_numPages(-1)
435
112k
  {
436
112k
  }
437
  //! returns the font id corresponding to a fId
438
  int getFId(int fileId) const
439
880k
  {
440
880k
    if (m_fileIdFontIdMap.find(fileId)==m_fileIdFontIdMap.end())
441
758k
      return fileId;
442
122k
    return m_fileIdFontIdMap.find(fileId)->second;
443
880k
  }
444
  //! a global map file font id-> fontconverter id
445
  std::map<int, int> m_fileIdFontIdMap;
446
  //! a list of text zone
447
  std::vector<Zone> m_zonesList;
448
  //! the file version
449
  mutable int m_version;
450
  int m_numPages /* the number of pages */;
451
};
452
}
453
454
////////////////////////////////////////////////////////////
455
// constructor/destructor, ...
456
////////////////////////////////////////////////////////////
457
GreatWksText::GreatWksText(GreatWksDocument &document)
458
112k
  : m_document(document)
459
112k
  , m_parserState(document.m_parserState)
460
112k
  , m_state(new GreatWksTextInternal::State)
461
112k
  , m_mainParser(&document.getMainParser())
462
112k
{
463
112k
}
464
465
GreatWksText::~GreatWksText()
466
112k
{ }
467
468
int GreatWksText::version() const
469
0
{
470
0
  if (m_state->m_version < 0)
471
0
    m_state->m_version = m_parserState->m_version;
472
0
  return m_state->m_version;
473
0
}
474
475
int GreatWksText::numPages() const
476
22.1k
{
477
22.1k
  if (m_state->m_numPages >= 0)
478
3.56k
    return m_state->m_numPages;
479
480
18.5k
  int nPages=1;
481
18.5k
  for (auto const &zone : m_state->m_zonesList) {
482
8.78k
    if (!zone.isMain() || zone.m_frameList.empty())
483
3.52k
      continue;
484
5.25k
    if (zone.m_frameList.back().m_page>1)
485
3.56k
      nPages = zone.m_frameList.back().m_page;
486
5.25k
    break;
487
8.78k
  }
488
489
18.5k
  return m_state->m_numPages = nPages;
490
22.1k
}
491
492
int GreatWksText::getFontId(int fileId) const
493
31.1k
{
494
31.1k
  return m_state->getFId(fileId);
495
31.1k
}
496
497
int GreatWksText::numHFZones() const
498
6.39k
{
499
6.39k
  int nHF=0;
500
8.77k
  for (auto const &zone : m_state->m_zonesList) {
501
8.77k
    if (zone.isMain())
502
6.39k
      break;
503
2.38k
    nHF++;
504
2.38k
  }
505
6.39k
  return nHF;
506
6.39k
}
507
508
bool GreatWksText::canSendTextBoxAsGraphic(MWAWEntry const &entry)
509
874
{
510
874
  if (!entry.valid())
511
47
    return false;
512
827
  MWAWInputStreamPtr &input= m_parserState->m_input;
513
827
  long pos=input->tell();
514
827
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
515
827
  GreatWksTextInternal::Zone zone;
516
827
  bool ok=!readZone(zone) ||!zone.hasGraphics();
517
827
  input->seek(pos, librevenge::RVNG_SEEK_SET);
518
827
  return ok;
519
874
}
520
521
bool GreatWksText::sendTextbox(MWAWEntry const &entry, MWAWListenerPtr listener)
522
7.07k
{
523
7.07k
  if (!listener && !m_parserState->getMainListener()) {
524
0
    MWAW_DEBUG_MSG(("GreatWksText::sendTextbox: can not find a listener\n"));
525
0
    return false;
526
0
  }
527
7.07k
  if (!entry.valid())
528
706
    return false;
529
6.37k
  MWAWInputStreamPtr &input= m_parserState->m_input;
530
6.37k
  input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
531
6.37k
  GreatWksTextInternal::Zone zone;
532
6.37k
  if (readZone(zone)) {
533
2.64k
    sendZone(zone, listener);
534
2.64k
    return true;
535
2.64k
  }
536
537
3.72k
  return sendSimpleTextbox(entry, listener);
538
6.37k
}
539
540
////////////////////////////////////////////////////////////
541
// Intermediate level
542
////////////////////////////////////////////////////////////
543
bool GreatWksText::createZones(int expectedHF)
544
21.4k
{
545
21.4k
  MWAWInputStreamPtr &input= m_parserState->m_input;
546
21.4k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
547
21.4k
  libmwaw::DebugStream f;
548
549
21.4k
  long pos = input->tell();
550
21.4k
  f << "Entries(TZoneHeader):";
551
21.4k
  auto val=static_cast<int>(input->readULong(2));
552
21.4k
  if (val)
553
16.2k
    f << "numPages=" << val << ",";
554
21.4k
  val=static_cast<int>(input->readULong(2));
555
21.4k
  if (val) // related to number of header/footer?
556
12.2k
    f << "f0=" << val << ",";
557
21.4k
  f << "height[total]=" << input->readLong(4) << ","; // checkme
558
21.4k
  ascFile.addDelimiter(input->tell(),'|');
559
21.4k
  ascFile.addPos(pos);
560
21.4k
  ascFile.addNote(f.str().c_str());
561
562
21.4k
  pos += 68;
563
21.4k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
564
565
21.4k
  if (!readFontNames()) {
566
15.4k
    MWAW_DEBUG_MSG(("GreatWksText::createZones: can not find the font names\n"));
567
15.4k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
568
15.4k
  }
569
570
21.4k
  bool findMainZone=false;
571
21.4k
  int nAuxi=0;
572
43.1k
  while (!input->isEnd()) {
573
42.3k
    pos=input->tell();
574
42.3k
    GreatWksTextInternal::Zone zone;
575
42.3k
    if (!readZone(zone)) {
576
30.4k
      input->seek(pos,librevenge::RVNG_SEEK_SET);
577
30.4k
      if (findMainZone)
578
6.35k
        break;
579
580
24.1k
      if (!findNextZone() || !readZone(zone)) {
581
14.3k
        input->seek(pos,librevenge::RVNG_SEEK_SET);
582
14.3k
        break;
583
14.3k
      }
584
24.1k
    }
585
21.6k
    m_state->m_zonesList.push_back(zone);
586
21.6k
    if (zone.isMain())
587
6.41k
      findMainZone=true;
588
15.2k
    else
589
15.2k
      nAuxi++;
590
21.6k
  }
591
21.4k
  if (nAuxi!=expectedHF) {
592
4.71k
    MWAW_DEBUG_MSG(("GreatWksText::createZones: unexpected HF zones: %d/%d\n", nAuxi, expectedHF));
593
4.71k
  }
594
21.4k
  return findMainZone;
595
21.4k
}
596
597
bool GreatWksText::findNextZone()
598
24.1k
{
599
24.1k
  MWAWInputStreamPtr &input= m_parserState->m_input;
600
601
24.1k
  long searchPos=input->tell(), pos=searchPos;
602
24.1k
  int const headerSize=24+22+184;
603
24.1k
  if (!input->checkPosition(pos+headerSize))
604
2.35k
    return false;
605
606
  // first look for ruler
607
21.7k
  input->seek(pos+headerSize, librevenge::RVNG_SEEK_SET);
608
9.92M
  while (true) {
609
9.92M
    if (input->isEnd())
610
8.82k
      return false;
611
9.91M
    pos = input->tell();
612
9.91M
    unsigned long val=input->readULong(4);
613
9.91M
    if (val==0x20FFFF)
614
18.0k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
615
9.89M
    else if (val==0x20FFFFFF)
616
17.4k
      input->seek(pos-1, librevenge::RVNG_SEEK_SET);
617
9.88M
    else if (val==0xFFFFFFFF)
618
278k
      input->seek(pos-2, librevenge::RVNG_SEEK_SET);
619
9.60M
    else if (val==0xFFFFFF2E)
620
10.7k
      input->seek(pos-3, librevenge::RVNG_SEEK_SET);
621
9.59M
    else
622
9.59M
      continue;
623
324k
    if (input->readULong(4)!=0x20FFFF || input->readULong(4)!=0xFFFF2E00) {
624
311k
      input->seek(pos+4, librevenge::RVNG_SEEK_SET);
625
311k
      continue;
626
311k
    }
627
    // ok a empty tabs stop
628
58.8k
    while (!input->isEnd()) {
629
58.5k
      pos = input->tell();
630
58.5k
      if (input->readULong(4)!=0x20FFFF || input->readULong(4)!=0xFFFF2E00) {
631
12.6k
        input->seek(pos, librevenge::RVNG_SEEK_SET);
632
12.6k
        break;
633
12.6k
      }
634
58.5k
    }
635
12.9k
    break;
636
324k
  }
637
638
12.9k
  pos=input->tell();
639
12.9k
  int nFonts=0;
640
12.9k
  GreatWksTextInternal::Zone zone;
641
427k
  while (true) {
642
427k
    long hSize=headerSize+22*nFonts++;
643
427k
    if (pos-hSize < searchPos)
644
3.19k
      break;
645
423k
    input->seek(pos-hSize, librevenge::RVNG_SEEK_SET);
646
423k
    if (input->readLong(4))
647
316k
      continue;
648
107k
    auto val=static_cast<int>(input->readULong(2));
649
107k
    if (val & 0xFEFE)
650
24.0k
      continue;
651
83.0k
    input->seek(2,librevenge::RVNG_SEEK_CUR);
652
83.0k
    if (input->readLong(2)!=nFonts)
653
69.1k
      continue;
654
13.8k
    input->seek(pos-hSize, librevenge::RVNG_SEEK_SET);
655
13.8k
    if (readZone(zone)) {
656
9.75k
      input->seek(pos-hSize, librevenge::RVNG_SEEK_SET);
657
9.75k
      return true;
658
9.75k
    }
659
13.8k
  }
660
661
3.19k
  MWAW_DEBUG_MSG(("GreatWksText::findNextZone: can not find begin of zone for pos=%lx\n", static_cast<long unsigned int>(pos)));
662
3.19k
  input->seek(searchPos, librevenge::RVNG_SEEK_SET);
663
3.19k
  return false;
664
12.9k
}
665
666
bool GreatWksText::readZone(GreatWksTextInternal::Zone &zone)
667
73.1k
{
668
73.1k
  zone=GreatWksTextInternal::Zone();
669
73.1k
  MWAWInputStreamPtr &input= m_parserState->m_input;
670
73.1k
  long pos=input->tell();
671
73.1k
  long endPos=pos+24;
672
73.1k
  if (!input->checkPosition(endPos))
673
931
    return false;
674
675
72.2k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
676
72.2k
  libmwaw::DebugStream f;
677
72.2k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
678
72.2k
  if (input->readLong(4)) {
679
22.7k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
680
22.7k
    return false;
681
22.7k
  }
682
144k
  for (int i=0; i < 2; ++i) {
683
97.4k
    auto val=static_cast<int>(input->readLong(1));
684
97.4k
    if (val==0) continue;
685
19.0k
    if (val!=1)
686
2.37k
      return false;
687
16.6k
    if (i==0)
688
6.04k
      f << "smartquote,";
689
10.5k
    else
690
10.5k
      f << "hidepict,";
691
16.6k
  }
692
47.0k
  zone.m_type=static_cast<int>(input->readLong(1));
693
47.0k
  if (input->readLong(1)) { // simple|complex field
694
3.22k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
695
3.22k
    return false;
696
3.22k
  }
697
43.8k
  zone.m_numFonts=static_cast<int>(input->readULong(2));
698
43.8k
  zone.m_numRulers=static_cast<int>(input->readULong(2));
699
43.8k
  zone.m_numCharPLC=static_cast<int>(input->readULong(2));
700
43.8k
  zone.m_numTokens=static_cast<int>(input->readULong(2));
701
43.8k
  zone.m_numLines=static_cast<int>(input->readULong(2));
702
43.8k
  zone.m_numFrames=static_cast<int>(input->readULong(2));
703
43.8k
  zone.m_numChar=long(input->readULong(4));
704
43.8k
  if (!zone.ok() || !input->checkPosition(endPos+zone.size())) {
705
9.34k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
706
9.34k
    return false;
707
9.34k
  }
708
34.5k
  zone.m_extra=f.str();
709
34.5k
  f.str("");
710
34.5k
  f << "Entries(HeaderText):" << zone;
711
34.5k
  if (input->tell()!=endPos) {
712
0
    ascFile.addDelimiter(input->tell(),'|');
713
0
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
714
0
  }
715
34.5k
  ascFile.addPos(pos);
716
34.5k
  ascFile.addNote(f.str().c_str());
717
718
882k
  for (int i=0; i<zone.m_numFonts; ++i) {
719
847k
    pos = input->tell();
720
847k
    f.str("");
721
847k
    f << "Entries(FontDef)-F" << i << ":";
722
723
847k
    MWAWFont font;
724
847k
    if (!readFont(font)) {
725
0
      f << "###";
726
0
      font = MWAWFont();
727
0
      input->seek(pos+22, librevenge::RVNG_SEEK_SET);
728
0
    }
729
847k
    zone.m_fontList.push_back(font);
730
847k
    f << font.getDebugString(m_parserState->m_fontConverter) << ",";
731
847k
    ascFile.addPos(pos);
732
847k
    ascFile.addNote(f.str().c_str());
733
847k
  }
734
34.5k
  MWAWParagraph para;
735
181k
  for (int i=0; i<zone.m_numRulers; ++i) {
736
146k
    pos = input->tell();
737
146k
    f.str("");
738
146k
    f << "Entries(RulerDef)-R" << i << ":";
739
146k
    if (!readRuler(para)) {
740
0
      f << "###";
741
0
      para = MWAWParagraph();
742
0
      input->seek(pos+192, librevenge::RVNG_SEEK_SET);
743
0
    }
744
146k
    zone.m_rulerList.push_back(para);
745
146k
    f << para;
746
146k
    ascFile.addPos(pos);
747
146k
    ascFile.addNote(f.str().c_str());
748
146k
  }
749
750
34.5k
  pos=input->tell();
751
34.5k
  f.str("");
752
34.5k
  f << "Entries(CharPLC):";
753
34.5k
  GreatWksTextInternal::PLC plc;
754
34.5k
  plc.m_type = GreatWksTextInternal::P_Font;
755
34.5k
  long textPos = 0;
756
2.08M
  for (int i=0; i < zone.m_numCharPLC; ++i) {
757
2.04M
    plc.m_id=static_cast<int>(input->readULong(2));
758
2.04M
    zone.m_posPLCMap.insert
759
2.04M
    (std::multimap<long, GreatWksTextInternal::PLC>::value_type(textPos, plc));
760
2.04M
    f << "F" << plc.m_id << ":" << std::hex << textPos << std::dec << ",";
761
2.04M
    textPos+= static_cast<int>(input->readULong(4));
762
2.04M
  }
763
34.5k
  ascFile.addPos(pos);
764
34.5k
  ascFile.addNote(f.str().c_str());
765
  // the token
766
34.5k
  plc.m_type = GreatWksTextInternal::P_Token;
767
34.5k
  textPos = 0;
768
764k
  for (int i=0; i < zone.m_numTokens; ++i) {
769
729k
    pos = input->tell();
770
729k
    f.str("");
771
729k
    plc.m_id=i;
772
729k
    f << "Entries(Token)-Tn" << i << ":";
773
729k
    GreatWksTextInternal::Token tkn;
774
729k
    long numC;
775
729k
    if (!readToken(tkn,numC)) {
776
0
      f << "###";
777
0
      tkn = GreatWksTextInternal::Token();
778
0
      input->seek(pos+2, librevenge::RVNG_SEEK_SET);
779
0
      numC=input->readLong(2);
780
0
    }
781
729k
    zone.m_posPLCMap.insert
782
729k
    (std::multimap<long, GreatWksTextInternal::PLC>::value_type(textPos, plc));
783
729k
    zone.m_tokenList.push_back(tkn);
784
729k
    f << tkn << ",pos=" << std::hex << textPos << std::dec;
785
729k
    textPos+= numC;
786
729k
    input->seek(pos+18, librevenge::RVNG_SEEK_SET);
787
729k
    ascFile.addPos(pos);
788
729k
    ascFile.addNote(f.str().c_str());
789
729k
  }
790
34.5k
  if (!readZonePositions(zone))
791
0
    return false;
792
  // fill the text entry
793
34.5k
  pos=input->tell();
794
34.5k
  zone.m_textEntry.setBegin(pos);
795
34.5k
  zone.m_textEntry.setLength(zone.m_numChar);
796
34.5k
  ascFile.addPos(pos);
797
34.5k
  ascFile.addNote("_");
798
34.5k
  pos += zone.m_numChar;
799
34.5k
  ascFile.addPos(pos);
800
34.5k
  ascFile.addNote("_");
801
802
580k
  for (auto &tkn : zone.m_tokenList) {
803
580k
    if (tkn.m_type != 4)
804
573k
      continue;
805
6.97k
    if (tkn.m_dataSize <= 0 || !input->checkPosition(pos+tkn.m_dataSize)) {
806
6.03k
      MWAW_DEBUG_MSG(("GreatWksText::readZone: can not determine the picture size\n"));
807
6.03k
      break;
808
6.03k
    }
809
935
    tkn.m_pictEntry.setBegin(pos);
810
935
    tkn.m_pictEntry.setLength(tkn.m_dataSize);
811
935
    pos+=tkn.m_dataSize;
812
935
  }
813
34.5k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
814
34.5k
  return true;
815
34.5k
}
816
817
bool GreatWksText::readZonePositions(GreatWksTextInternal::Zone &zone)
818
34.5k
{
819
34.5k
  MWAWInputStreamPtr &input= m_parserState->m_input;
820
34.5k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
821
34.5k
  libmwaw::DebugStream f;
822
823
  // the line
824
34.5k
  GreatWksTextInternal::PLC plc;
825
34.5k
  plc.m_type = GreatWksTextInternal::P_Ruler;
826
34.5k
  long textPos = 0;
827
34.5k
  std::vector<long> linesPos;
828
34.5k
  linesPos.push_back(0);
829
391k
  for (int i=0; i < zone.m_numLines; ++i) {
830
357k
    long pos=input->tell();
831
357k
    f.str("");
832
357k
    plc.m_id=static_cast<int>(input->readULong(2));
833
357k
    auto numC = long(input->readULong(4));
834
357k
    f << "y=" << double(input->readLong(4))/65536.;
835
357k
    f << "->" << double(input->readLong(4))/65536.;
836
357k
    plc.m_extra=f.str();
837
357k
    zone.m_posPLCMap.insert
838
357k
    (std::multimap<long, GreatWksTextInternal::PLC>::value_type(textPos, plc));
839
357k
    f.str("");
840
357k
    f << "Entries(Line)-L" << i << ":" << plc <<  ":"
841
357k
      << std::hex << textPos << std::dec;
842
357k
    textPos+=numC;
843
357k
    linesPos.push_back(textPos);
844
357k
    input->seek(pos+14, librevenge::RVNG_SEEK_SET);
845
357k
    ascFile.addPos(pos);
846
357k
    ascFile.addNote(f.str().c_str());
847
357k
  }
848
  // the page dimension
849
34.5k
  plc.m_type = GreatWksTextInternal::P_Page;
850
243k
  for (int i=0; i < zone.m_numFrames; ++i) {
851
208k
    GreatWksTextInternal::Frame frame;
852
208k
    long pos=input->tell();
853
208k
    plc.m_id=i;
854
208k
    f.str("");
855
208k
    float dim[4];
856
835k
    for (auto &d : dim) d=float(input->readLong(4))/65536.f;
857
208k
    frame.m_pos=MWAWBox2f(MWAWVec2f(dim[1],dim[0]),MWAWVec2f(dim[3],dim[2]));
858
208k
    auto val=static_cast<int>(input->readLong(2)); // always 0
859
208k
    if (val) f << "#unkn=" << val << ",";
860
208k
    frame.m_page=static_cast<int>(input->readLong(2));
861
208k
    auto line=static_cast<int>(input->readLong(2));
862
208k
    plc.m_extra=f.str();
863
208k
    if (line >= 0 && line < int(linesPos.size())) {
864
87.5k
      textPos=linesPos[size_t(line)];
865
87.5k
      zone.m_posPLCMap.insert
866
87.5k
      (std::multimap<long, GreatWksTextInternal::PLC>::value_type(textPos, plc));
867
87.5k
      if (textPos)
868
8.43k
        f << "pos=" << std::hex << textPos << std::dec;
869
87.5k
    }
870
121k
    else {
871
121k
      MWAW_DEBUG_MSG(("GreatWksText::readZone: can not find begin pos for page %d\n",line));
872
121k
      f << "##pos[line]=" << line << ",";
873
121k
    }
874
208k
    frame.m_extra=f.str();
875
208k
    zone.m_frameList.push_back(frame);
876
208k
    f.str("");
877
208k
    f << "Entries(TFrames)-" << i << ":" << frame;
878
208k
    input->seek(pos+22, librevenge::RVNG_SEEK_SET);
879
208k
    ascFile.addPos(pos);
880
208k
    ascFile.addNote(f.str().c_str());
881
208k
  }
882
34.5k
  return true;
883
34.5k
}
884
885
//////////////////////////////////////////////
886
// Fonts
887
//////////////////////////////////////////////
888
bool GreatWksText::readFontNames()
889
77.3k
{
890
77.3k
  MWAWInputStreamPtr &input= m_parserState->m_input;
891
77.3k
  long pos=input->tell();
892
77.3k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
893
77.3k
  libmwaw::DebugStream f;
894
77.3k
  f << "Entries(FontNames):";
895
77.3k
  auto sz= long(input->readULong(4));
896
77.3k
  long endPos = input->tell()+sz;
897
77.3k
  if (sz < 2 || !input->checkPosition(endPos)) {
898
44.2k
    MWAW_DEBUG_MSG(("GreatWksText::readFontNames: can not read field size\n"));
899
44.2k
    f << "###";
900
44.2k
    ascFile.addPos(pos);
901
44.2k
    ascFile.addNote(f.str().c_str());
902
44.2k
    return false;
903
44.2k
  }
904
33.0k
  auto N=static_cast<int>(input->readLong(2));
905
33.0k
  f << "N=" << N << ",";
906
33.0k
  if (N*5+2 > sz) {
907
1.79k
    MWAW_DEBUG_MSG(("GreatWksText::readFontNames: can not read N\n"));
908
1.79k
    f << "###";
909
1.79k
    ascFile.addPos(pos);
910
1.79k
    ascFile.addNote(f.str().c_str());
911
1.79k
    return false;
912
1.79k
  }
913
31.2k
  ascFile.addPos(pos);
914
31.2k
  ascFile.addNote(f.str().c_str());
915
1.82M
  for (int i=0; i < N; ++i) {
916
1.79M
    pos = input->tell();
917
1.79M
    f.str("");
918
1.79M
    f << "FontNames-" << i << ":";
919
1.79M
    auto fId=static_cast<int>(input->readULong(2));
920
1.79M
    f << "fId=" << fId << ",";
921
1.79M
    auto val=static_cast<int>(input->readLong(2)); // always 0 ?
922
1.79M
    if (val)
923
759k
      f << "unkn=" << val << ",";
924
1.79M
    auto fSz=static_cast<int>(input->readULong(1));
925
1.79M
    if (pos+5+fSz>endPos) {
926
7.81k
      MWAW_DEBUG_MSG(("GreatWksText::readFontNames: can not read font %d\n", i));
927
7.81k
      f << "###";
928
7.81k
      ascFile.addPos(pos);
929
7.81k
      ascFile.addNote(f.str().c_str());
930
7.81k
      input->seek(endPos, librevenge::RVNG_SEEK_SET);
931
7.81k
      return i>0;
932
7.81k
    }
933
1.79M
    std::string name("");
934
35.0M
    for (int c=0; c < fSz; ++c)
935
33.2M
      name+=char(input->readULong(1));
936
1.79M
    if (!name.empty())
937
630k
      m_state->m_fileIdFontIdMap[fId]=m_parserState->m_fontConverter->getId(name);
938
1.79M
    if ((fSz%2)==0)
939
1.44M
      input->seek(1, librevenge::RVNG_SEEK_CUR);
940
941
1.79M
    f << "\"" << name << "\",";
942
1.79M
    ascFile.addPos(pos);
943
1.79M
    ascFile.addNote(f.str().c_str());
944
1.79M
  }
945
23.4k
  pos = input->tell();
946
23.4k
  if (pos!=endPos) {
947
11.6k
    MWAW_DEBUG_MSG(("GreatWksText::readFontNames: find extra data\n"));
948
11.6k
    ascFile.addPos(pos);
949
11.6k
    ascFile.addNote("FontNames:###");
950
11.6k
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
951
11.6k
  }
952
23.4k
  return true;
953
31.2k
}
954
955
bool GreatWksText::readFont(MWAWFont &font)
956
847k
{
957
847k
  font=MWAWFont();
958
847k
  MWAWInputStreamPtr &input= m_parserState->m_input;
959
847k
  long pos=input->tell();
960
847k
  long endPos=pos+22;
961
847k
  if (!input->checkPosition(endPos))
962
0
    return false;
963
964
847k
  libmwaw::DebugStream f;
965
847k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
966
847k
  auto val=static_cast<int>(input->readLong(2)); // small number between 0 and 5
967
847k
  if (val==0) f << "unused,";
968
580k
  else if (val!=1) f << "nbUsed=" << val << ",";
969
  // some dim dim[0]>dim[1]: minH, maxH ?
970
847k
  int dim[2];
971
1.69M
  for (auto &d : dim) d=static_cast<int>(input->readLong(2));
972
847k
  f << "dim?=" << dim[1] << "x" << dim[0] << ",";
973
974
2.54M
  for (int i=0; i < 2; ++i) { // f0=0|4(with italic), f1=0
975
1.69M
    val=static_cast<int>(input->readLong(2));
976
1.69M
    if (val) f << "f" << i << "=" << val << ",";
977
1.69M
  }
978
847k
  font.setId(m_state->getFId(static_cast<int>(input->readULong(2))));
979
847k
  auto flag =static_cast<int>(input->readULong(2));
980
847k
  uint32_t flags=0;
981
847k
  if (flag&0x1) flags |= MWAWFont::boldBit;
982
847k
  if (flag&0x2) flags |= MWAWFont::italicBit;
983
847k
  if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
984
847k
  if (flag&0x8) flags |= MWAWFont::embossBit;
985
847k
  if (flag&0x10) flags |= MWAWFont::shadowBit;
986
847k
  if (flag&0x20) font.setDeltaLetterSpacing(-1);
987
847k
  if (flag&0x40) font.setDeltaLetterSpacing(1);
988
847k
  if (flag&0x100) font.set(MWAWFont::Script::super100());
989
847k
  if (flag&0x200) font.set(MWAWFont::Script::sub100());
990
847k
  if (flag&0x800) font.setStrikeOutStyle(MWAWFont::Line::Simple);
991
847k
  if (flag&0x2000) {
992
203k
    font.setUnderlineStyle(MWAWFont::Line::Simple);
993
203k
    font.setUnderlineType(MWAWFont::Line::Double);
994
203k
  }
995
847k
  flag &=0xD480;
996
847k
  if (flag) f << "#fl=" << std::hex << flag << std::dec << ",";
997
847k
  font.setFlags(flags);
998
847k
  font.setSize(float(input->readULong(2)));
999
847k
  unsigned char color[3];
1000
2.54M
  for (auto &c : color) c = static_cast<unsigned char>(input->readULong(2)>>8);
1001
847k
  font.setColor(MWAWColor(color[0],color[1],color[2]));
1002
847k
  font.m_extra=f.str();
1003
1004
847k
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
1005
847k
  return true;
1006
847k
}
1007
//////////////////////////////////////////////
1008
// Ruler
1009
//////////////////////////////////////////////
1010
bool GreatWksText::readRuler(MWAWParagraph &para)
1011
146k
{
1012
146k
  para = MWAWParagraph();
1013
146k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1014
146k
  long pos=input->tell();
1015
146k
  long endPos=pos+192;
1016
146k
  if (!input->checkPosition(endPos))
1017
0
    return false;
1018
1019
146k
  libmwaw::DebugStream f;
1020
146k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1021
146k
  auto val=static_cast<int>(input->readLong(2));
1022
146k
  if (val==0) f << "unused,";
1023
113k
  else if (val!=1) f << "nbUsed=" << val << ",";
1024
146k
  val=static_cast<int>(input->readLong(2));
1025
146k
  switch (val) {
1026
63.7k
  case 0:
1027
63.7k
    break;
1028
21.5k
  case 1:
1029
21.5k
    para.m_justify = MWAWParagraph::JustificationCenter;
1030
21.5k
    break;
1031
1.98k
  case 2:
1032
1.98k
    para.m_justify = MWAWParagraph::JustificationRight ;
1033
1.98k
    break;
1034
1.48k
  case 3:
1035
1.48k
    para.m_justify = MWAWParagraph::JustificationFull;
1036
1.48k
    break;
1037
57.9k
  default:
1038
57.9k
    f << "#align" << val << ",";
1039
57.9k
    break;
1040
146k
  }
1041
146k
  para.m_marginsUnit=librevenge::RVNG_POINT;
1042
146k
  for (auto &margin : para.m_margins)
1043
440k
    margin = double(input->readLong(4))/65536.;
1044
146k
  para.m_margins[0]=*para.m_margins[0]-*para.m_margins[1];
1045
146k
  double spacing[3];
1046
146k
  for (double &i : spacing)
1047
440k
    i = double(input->readLong(4))/65536.;
1048
146k
  int unit[3];
1049
440k
  for (auto &u : unit) u = static_cast<int>(input->readLong(1));
1050
146k
  switch (unit[0]) {
1051
4.46k
  case 1:
1052
6.71k
  case 2:
1053
7.53k
  case 3:
1054
8.90k
  case 4:
1055
15.9k
  case 5:
1056
15.9k
    para.setInterline(spacing[0], librevenge::RVNG_POINT);
1057
15.9k
    break;
1058
17.3k
  case 6:
1059
17.3k
    para.setInterline(spacing[0], librevenge::RVNG_PERCENT);
1060
17.3k
    break;
1061
113k
  default:
1062
113k
    f << "#interline=" << spacing[0] << "[unit=" << unit[0] << "],";
1063
146k
  }
1064
440k
  for (int w=1; w < 3; ++w) {
1065
293k
    if (unit[w]==6)
1066
2.38k
      para.m_spacings[w]=spacing[w]*12./72.;
1067
291k
    else if (unit[w]>0 && unit[w]<6)
1068
72.2k
      para.m_spacings[w]=spacing[w]/72.;
1069
218k
    else
1070
218k
      f << "#spac" << w << "=" << spacing[w] << "[unit=" << unit[w] << "],";
1071
293k
  }
1072
146k
  val = static_cast<int>(input->readLong(1));
1073
146k
  if (val) f << "#f0=" << val << ",";
1074
1075
3.08M
  for (int i=0; i < 20; ++i) {
1076
2.93M
    MWAWTabStop tab;
1077
2.93M
    val = static_cast<int>(input->readLong(1));
1078
2.93M
    switch (val) {
1079
1.59M
    case 0: // left
1080
1.59M
      break;
1081
145k
    case 1:
1082
145k
      tab.m_alignment = MWAWTabStop::CENTER;
1083
145k
      break;
1084
42.7k
    case 2:
1085
42.7k
      tab.m_alignment = MWAWTabStop::RIGHT;
1086
42.7k
      break;
1087
17.2k
    case 3:
1088
17.2k
      tab.m_alignment = MWAWTabStop::DECIMAL;
1089
17.2k
      break;
1090
1.13M
    default:
1091
1.13M
      f << "#tab" << i << "[align]=" << val << ",";
1092
1.13M
      break;
1093
2.93M
    }
1094
2.93M
    auto leaderChar = char(input->readULong(1));
1095
2.93M
    if (leaderChar) {
1096
1.75M
      int unicode= m_parserState->m_fontConverter->unicode(3, static_cast<unsigned char>(leaderChar));
1097
1.75M
      if (unicode==-1)
1098
430k
        tab.m_leaderCharacter = uint16_t(leaderChar);
1099
1.32M
      else
1100
1.32M
        tab.m_leaderCharacter = uint16_t(unicode);
1101
1.75M
    }
1102
2.93M
    long tPos=input->readLong(4);
1103
2.93M
    if (tPos==-1) {
1104
468k
      input->seek(2, librevenge::RVNG_SEEK_CUR);
1105
468k
      continue;
1106
468k
    }
1107
2.46M
    tab.m_position=double(tPos)/72./65536.;
1108
2.46M
    auto decimalChar = char(input->readULong(1));
1109
2.46M
    if (decimalChar) {
1110
1.19M
      int unicode= m_parserState->m_fontConverter->unicode(3, static_cast<unsigned char>(decimalChar));
1111
1.19M
      if (unicode==-1)
1112
544k
        tab.m_decimalCharacter = uint16_t(decimalChar);
1113
653k
      else
1114
653k
        tab.m_decimalCharacter = uint16_t(unicode);
1115
1.19M
    }
1116
2.46M
    val = static_cast<int>(input->readLong(1));
1117
2.46M
    if (val) f << "#tab" << i << "[f0=" << val << ",";
1118
2.46M
    para.m_tabs->push_back(tab);
1119
2.46M
  }
1120
146k
  para.m_extra=f.str();
1121
1122
146k
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
1123
146k
  return true;
1124
146k
}
1125
1126
//////////////////////////////////////////////
1127
// Token
1128
//////////////////////////////////////////////
1129
bool GreatWksText::readToken(GreatWksTextInternal::Token &token, long &nChar)
1130
729k
{
1131
729k
  token = GreatWksTextInternal::Token();
1132
729k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1133
729k
  long pos=input->tell();
1134
729k
  long endPos=pos+18;
1135
729k
  if (!input->checkPosition(endPos))
1136
0
    return false;
1137
1138
729k
  libmwaw::DebugStream f;
1139
729k
  token.m_type=static_cast<int>(input->readULong(1));
1140
729k
  token.m_format=static_cast<int>(input->readULong(1));
1141
729k
  nChar=input->readLong(4);
1142
729k
  if (token.m_type==0x15||token.m_type==0x16)
1143
17.4k
    token.m_date=static_cast<uint32_t>(input->readULong(4));
1144
712k
  else if (token.m_type==4) {
1145
12.5k
    token.m_dataSize = input->readLong(4);
1146
12.5k
    float dim[2];
1147
25.1k
    for (auto &d : dim) d = float(input->readLong(4))/65536.f;
1148
12.5k
    token.m_dim=MWAWVec2f(dim[0],dim[1]); // checkme
1149
12.5k
  }
1150
729k
  int nUnread=int(endPos-input->tell())/2;
1151
4.99M
  for (int i=0; i < nUnread; i++) {
1152
4.26M
    auto val=static_cast<int>(input->readLong(2));
1153
4.26M
    if (val) f << "f" << i << "=" << val << ",";
1154
4.26M
  }
1155
729k
  token.m_extra=f.str();
1156
1157
729k
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
1158
729k
  return true;
1159
729k
}
1160
1161
//////////////////////////////////////////////
1162
// send data
1163
//////////////////////////////////////////////
1164
bool GreatWksText::sendMainText()
1165
12.6k
{
1166
12.6k
  for (auto const &zone : m_state->m_zonesList) {
1167
8.77k
    if (!zone.isMain())
1168
2.38k
      continue;
1169
6.39k
    return sendZone(zone);
1170
8.77k
  }
1171
6.29k
  return false;
1172
12.6k
}
1173
1174
bool GreatWksText::sendHF(int id)
1175
2.26k
{
1176
2.26k
  for (auto const &zone : m_state->m_zonesList) {
1177
2.26k
    if (zone.isMain())
1178
0
      continue;
1179
2.26k
    if (id--==0)
1180
2.26k
      return sendZone(zone);
1181
2.26k
  }
1182
0
  MWAW_DEBUG_MSG(("GreatWksText::sendHF: can not find a header/footer\n"));
1183
0
  return false;
1184
2.26k
}
1185
1186
void GreatWksText::flushExtra()
1187
0
{
1188
0
  MWAWListenerPtr listener=m_parserState->getMainListener();
1189
0
  if (!listener) {
1190
0
    MWAW_DEBUG_MSG(("GreatWksText::flushExtra: can not find a listener\n"));
1191
0
    return;
1192
0
  }
1193
0
  for (auto const &zone : m_state->m_zonesList) {
1194
0
    if (zone.m_parsed)
1195
0
      continue;
1196
0
    sendZone(zone);
1197
0
  }
1198
0
}
1199
1200
bool GreatWksText::sendSimpleTextbox(MWAWEntry const &entry, MWAWListenerPtr listener)
1201
3.72k
{
1202
3.72k
  if (!listener)
1203
1.13k
    listener=m_parserState->getMainListener();
1204
3.72k
  if (!listener || !listener->canWriteText()) {
1205
0
    MWAW_DEBUG_MSG(("GreatWksText::sendSimpleTextbox: can not find a listener\n"));
1206
0
    return false;
1207
0
  }
1208
3.72k
  if (entry.length()<51) {
1209
329
    MWAW_DEBUG_MSG(("GreatWksText::sendSimpleTextbox: the entry seems to short\n"));
1210
329
    return false;
1211
329
  }
1212
3.39k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1213
3.39k
  long pos=entry.begin();
1214
3.39k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1215
1216
3.39k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1217
3.39k
  libmwaw::DebugStream f;
1218
3.39k
  f << "Entries(Texbox):";
1219
3.39k
  bool ok = input->readLong(4)==0;
1220
7.66k
  for (int i=0; ok && i < 2; ++i) {
1221
4.41k
    auto val=static_cast<int>(input->readLong(1));
1222
4.41k
    if (val==0) continue;
1223
529
    if (val!=1) {
1224
140
      f << "#fl" << i << "=" << val << ",";
1225
140
      ok = false;
1226
140
      break;
1227
140
    }
1228
389
    if (i==0)
1229
221
      f << "smartquote,";
1230
168
    else
1231
168
      f << "hidepict,";
1232
389
  }
1233
3.39k
  auto type=static_cast<int>(input->readLong(1));
1234
3.39k
  if (ok) {
1235
2.10k
    if (type<0 || type>5) {
1236
91
      f << "#type=" << type << ",";
1237
91
      ok = false;
1238
91
    }
1239
2.01k
    else  if (type==1)
1240
894
      f << "textbox[draw],";
1241
1.12k
    else if (type!=2)
1242
845
      f << "type=" << type << ",";
1243
2.10k
  }
1244
3.39k
  if (ok) ok=input->readLong(1)==1;
1245
1246
3.39k
  if (!ok) {
1247
1.93k
    MWAW_DEBUG_MSG(("GreatWksText::sendSimpleTextbox: the header seems bad\n"));
1248
1.93k
    f << "###";
1249
1.93k
    ascFile.addPos(pos);
1250
1.93k
    ascFile.addNote(f.str().c_str());
1251
1.93k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1252
1.93k
    return false;
1253
1.93k
  }
1254
1.46k
  MWAWFont font;
1255
1.46k
  font.setId(m_state->getFId(static_cast<int>(input->readULong(2))));
1256
1.46k
  auto flag =static_cast<int>(input->readULong(2));
1257
1.46k
  uint32_t flags=0;
1258
1.46k
  if (flag&0x1) flags |= MWAWFont::boldBit;
1259
1.46k
  if (flag&0x2) flags |= MWAWFont::italicBit;
1260
1.46k
  if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
1261
1.46k
  if (flag&0x8) flags |= MWAWFont::embossBit;
1262
1.46k
  if (flag&0x10) flags |= MWAWFont::shadowBit;
1263
1.46k
  if (flag&0x20) font.setDeltaLetterSpacing(-1);
1264
1.46k
  if (flag&0x40) font.setDeltaLetterSpacing(1);
1265
1.46k
  if (flag&0x100) font.set(MWAWFont::Script::super100());
1266
1.46k
  if (flag&0x200) font.set(MWAWFont::Script::sub100());
1267
1.46k
  if (flag&0x800) font.setStrikeOutStyle(MWAWFont::Line::Simple);
1268
1.46k
  if (flag&0x2000) {
1269
454
    font.setUnderlineStyle(MWAWFont::Line::Simple);
1270
454
    font.setUnderlineType(MWAWFont::Line::Double);
1271
454
  }
1272
1.46k
  flag &=0xD480;
1273
1.46k
  if (flag) f << "font[#fl]=" << std::hex << flag << std::dec << ",";
1274
1.46k
  font.setFlags(flags);
1275
1.46k
  font.setSize(float(input->readULong(2)));
1276
1.46k
  unsigned char color[3];
1277
4.38k
  for (auto &c : color) c = static_cast<unsigned char>(input->readULong(2)>>8);
1278
1.46k
  font.setColor(MWAWColor(color[0],color[1],color[2]));
1279
1.46k
  f << "font=[" << font.getDebugString(m_parserState->m_fontConverter) << "],";
1280
1.46k
  listener->setFont(font);
1281
1282
1.46k
  int val;
1283
1.46k
  f << "unkn=[" << std::hex;
1284
10.2k
  for (int i=0; i<6; i++) { // junk ?
1285
8.77k
    val=static_cast<int>(input->readULong(2));
1286
8.77k
    if (val)
1287
5.15k
      f << val << ",";
1288
3.62k
    else
1289
3.62k
      f << "_,";
1290
8.77k
  }
1291
1.46k
  f << std::dec << "],";
1292
1.46k
  MWAWParagraph para;
1293
1.46k
  val=static_cast<int>(input->readLong(2));
1294
1.46k
  switch (val) {
1295
911
  case 0:
1296
911
    break;
1297
30
  case 1:
1298
30
    para.m_justify = MWAWParagraph::JustificationCenter;
1299
30
    break;
1300
31
  case 2:
1301
31
    para.m_justify = MWAWParagraph::JustificationRight ;
1302
31
    break;
1303
16
  case 3:
1304
16
    para.m_justify = MWAWParagraph::JustificationFull;
1305
16
    break;
1306
475
  default:
1307
475
    f << "#align" << val << ",";
1308
475
    break;
1309
1.46k
  }
1310
1.46k
  f << para <<",";
1311
1.46k
  listener->setParagraph(para);
1312
1.46k
  double dim[4];
1313
5.85k
  for (auto &d : dim) d=double(input->readLong(4))/65536.;
1314
1.46k
  f << "dim=" << dim[1] << "x" << dim[0] << "<->" << dim[3] << "x" << dim[2] << "," ;
1315
1.46k
  auto fSz=static_cast<int>(input->readULong(1));
1316
1.46k
  if (50+fSz > entry.length()) {
1317
74
    MWAW_DEBUG_MSG(("GreatWksText::sendSimpleTextbox: the text size seems too big\n"));
1318
74
    f << "###";
1319
74
    ascFile.addPos(pos);
1320
74
    ascFile.addNote(f.str().c_str());
1321
74
    input->seek(pos, librevenge::RVNG_SEEK_SET);
1322
74
    return false;
1323
74
  }
1324
1325
1.38k
  ascFile.addPos(pos);
1326
1.38k
  ascFile.addNote(f.str().c_str());
1327
1328
1.38k
  pos=input->tell();
1329
1.38k
  f.str("");
1330
1.38k
  f << "Entries(Text):";
1331
88.3k
  for (int i=0; i < fSz; ++i) {
1332
86.9k
    auto c = char(input->readULong(1));
1333
86.9k
    f << c;
1334
86.9k
    switch (c) {
1335
7.42k
    case 0x9:
1336
7.42k
      listener->insertTab();
1337
7.42k
      break;
1338
17.8k
    case 0xd:
1339
17.8k
      listener->insertEOL();
1340
17.8k
      break;
1341
61.6k
    default:
1342
61.6k
      listener->insertCharacter(static_cast<unsigned char>(c));
1343
61.6k
      break;
1344
86.9k
    }
1345
86.9k
  }
1346
1.38k
  input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
1347
1.38k
  ascFile.addPos(pos);
1348
1.38k
  ascFile.addNote(f.str().c_str());
1349
1.38k
  return true;
1350
1.38k
}
1351
1352
bool GreatWksText::sendZone(GreatWksTextInternal::Zone const &zone, MWAWListenerPtr listener)
1353
11.3k
{
1354
11.3k
  bool inLocalZone=false;
1355
11.3k
  if (listener)
1356
1.11k
    inLocalZone=true;
1357
10.1k
  else
1358
10.1k
    listener=m_parserState->getMainListener();
1359
11.3k
  if (!listener || !listener->canWriteText()) {
1360
0
    MWAW_DEBUG_MSG(("GreatWksText::sendZone: can not find a listener\n"));
1361
0
    return false;
1362
0
  }
1363
11.3k
  bool isMain = zone.isMain();
1364
11.3k
  int actPage = 1, actCol = 0, numCol=1;
1365
11.3k
  if (isMain && !listener->canOpenSectionAddBreak()) {
1366
4
    MWAW_DEBUG_MSG(("GreatWksText::sendZone: in a main zone, but can not open section\n"));
1367
4
    isMain = false;
1368
4
  }
1369
11.3k
  else if (isMain) {
1370
6.39k
    m_document.newPage(1);
1371
6.39k
    MWAWSection sec=m_document.getMainSection();
1372
6.39k
    numCol = sec.numColumns();
1373
6.39k
    if (numCol>1) {
1374
4.12k
      if (listener->isSectionOpened())
1375
0
        listener->closeSection();
1376
4.12k
      listener->openSection(sec);
1377
4.12k
    }
1378
6.39k
  }
1379
11.3k
  if (!zone.m_textEntry.valid())
1380
262
    return true;
1381
11.0k
  MWAWInputStreamPtr &input= m_parserState->m_input;
1382
11.0k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1383
11.0k
  libmwaw::DebugStream f;
1384
11.0k
  f << "Entries(Text):";
1385
11.0k
  zone.m_parsed=true;
1386
11.0k
  long pos = zone.m_textEntry.begin(), endPos = zone.m_textEntry.end();
1387
11.0k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
1388
1389
11.0k
  long cPos=0;
1390
1.95M
  while (1) {
1391
1.95M
    long actPos = input->tell();
1392
1.95M
    bool done = input->isEnd() || actPos==endPos;
1393
1394
1.95M
    char c = done ? char(0) : char(input->readULong(1));
1395
1.95M
    if (pos!=actPos && (c==0xd || done)) {
1396
65.9k
      ascFile.addPos(pos);
1397
65.9k
      ascFile.addNote(f.str().c_str());
1398
65.9k
      f.str("");
1399
65.9k
      f << "Text:";
1400
65.9k
      pos = actPos+1;
1401
65.9k
    }
1402
1.95M
    if (done) break;
1403
1404
1.94M
    auto pIt=zone.m_posPLCMap.find(cPos);
1405
1.94M
    GreatWksTextInternal::Token token;
1406
2.11M
    while (pIt!=zone.m_posPLCMap.end() && pIt->first==cPos) {
1407
175k
      GreatWksTextInternal::PLC const &plc=(pIt++)->second;
1408
175k
      f << "[" << plc << "]";
1409
175k
      switch (plc.m_type) {
1410
49.3k
      case GreatWksTextInternal::P_Font:
1411
49.3k
        if (plc.m_id < 0 || plc.m_id >= static_cast<int>(zone.m_fontList.size())) {
1412
4.77k
          MWAW_DEBUG_MSG(("GreatWksText::sendZone: can not find font %d\n", plc.m_id));
1413
4.77k
          break;
1414
4.77k
        }
1415
44.5k
        listener->setFont(zone.m_fontList[size_t(plc.m_id)]);
1416
44.5k
        break;
1417
50.3k
      case GreatWksTextInternal::P_Ruler:
1418
50.3k
        if (plc.m_id < 0 || plc.m_id >= static_cast<int>(zone.m_rulerList.size())) {
1419
4.36k
          MWAW_DEBUG_MSG(("GreatWksText::sendZone: can not find ruler %d\n", plc.m_id));
1420
4.36k
          break;
1421
4.36k
        }
1422
45.9k
        listener->setParagraph(zone.m_rulerList[size_t(plc.m_id)]);
1423
45.9k
        break;
1424
28.7k
      case GreatWksTextInternal::P_Token:
1425
28.7k
        if (plc.m_id < 0 || plc.m_id >= static_cast<int>(zone.m_tokenList.size())) {
1426
0
          MWAW_DEBUG_MSG(("GreatWksText::sendZone: can not find token %d\n", plc.m_id));
1427
0
          break;
1428
0
        }
1429
28.7k
        token=zone.m_tokenList[size_t(plc.m_id)];
1430
28.7k
        break;
1431
47.0k
      case GreatWksTextInternal::P_Page:
1432
47.0k
      case GreatWksTextInternal::P_Unknown:
1433
#if !defined(__clang__)
1434
      default:
1435
#endif
1436
47.0k
        break;
1437
175k
      }
1438
175k
    }
1439
1.94M
    if (c!=0xd) f << c;
1440
1.94M
    switch (c) {
1441
39.3k
    case 0x4: {
1442
39.3k
      f << "[pict]";
1443
39.3k
      if (token.m_type!=4 || !token.m_pictEntry.valid()) {
1444
39.2k
        MWAW_DEBUG_MSG(("GreatWksText::sendZone: can not find a picture\n"));
1445
39.2k
        f << "###";
1446
39.2k
        break;
1447
39.2k
      }
1448
35
      if (inLocalZone) {
1449
0
        MWAW_DEBUG_MSG(("GreatWksText::sendZone: oops, can not send a picture in a graphic zone\n"));
1450
0
        break;
1451
0
      }
1452
35
      MWAWPosition pictPos(MWAWVec2f(0,0), token.m_dim, librevenge::RVNG_POINT);
1453
35
      pictPos.setRelativePosition(MWAWPosition::Char, MWAWPosition::XLeft, MWAWPosition::YBottom);
1454
35
      m_document.sendPicture(token.m_pictEntry, pictPos);
1455
35
      break;
1456
35
    }
1457
17.8k
    case 0x9:
1458
17.8k
      listener->insertTab();
1459
17.8k
      break;
1460
19.9k
    case 0xb:
1461
19.9k
      f << "[colbreak]";
1462
19.9k
      if (!isMain) {
1463
2.34k
        MWAW_DEBUG_MSG(("GreatWksText::sendZone: find column break in auxilliary block\n"));
1464
2.34k
        break;
1465
2.34k
      }
1466
17.6k
      if (actCol < numCol-1 && numCol > 1) {
1467
12.8k
        listener->insertBreak(MWAWListener::ColumnBreak);
1468
12.8k
        actCol++;
1469
12.8k
      }
1470
4.71k
      else {
1471
4.71k
        actCol = 0;
1472
4.71k
        m_document.newPage(++actPage);
1473
4.71k
      }
1474
17.6k
      break;
1475
24.4k
    case 0xc:
1476
24.4k
      f << "[pagebreak]";
1477
24.4k
      if (!isMain) {
1478
2.02k
        f << "###";
1479
2.02k
        MWAW_DEBUG_MSG(("GreatWksText::sendZone: find page break in auxilliary zone\n"));
1480
2.02k
        break;
1481
2.02k
      }
1482
22.4k
      m_document.newPage(++actPage);
1483
22.4k
      actCol = 0;
1484
22.4k
      break;
1485
77.5k
    case 0xd:
1486
77.5k
      listener->insertEOL();
1487
77.5k
      break;
1488
43.1k
    case 2: // page number
1489
56.9k
    case 0x15: // date
1490
63.8k
    case 0x16: // time
1491
63.8k
      if (token.sendTo(*listener))
1492
779
        break;
1493
63.0k
      f << "###";
1494
63.0k
      MWAW_DEBUG_MSG(("GreatWksText::sendZone: can not send token\n"));
1495
63.0k
      break;
1496
1.70M
    default:
1497
1.70M
      listener->insertCharacter(static_cast<unsigned char>(c));
1498
1.70M
      break;
1499
1.94M
    }
1500
1.94M
    cPos++;
1501
1.94M
  }
1502
11.0k
  return true;
1503
11.0k
}
1504
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: